diff --git a/apps/app-launch-helper/src/CMakeLists.txt b/apps/app-launch-helper/src/CMakeLists.txt index e3ab8a4..e324c2e 100644 --- a/apps/app-launch-helper/src/CMakeLists.txt +++ b/apps/app-launch-helper/src/CMakeLists.txt @@ -6,5 +6,9 @@ target_link_libraries(${APP_LAUNCH_HELPER_BIN} PRIVATE PkgConfig::SYSTEMD ) +target_include_directories(${APP_LAUNCH_HELPER_BIN} PRIVATE + ${PROJECT_SOURCE_DIR}/src +) + include(GNUInstallDirs) install(TARGETS ${APP_LAUNCH_HELPER_BIN} DESTINATION ${CMAKE_INSTALL_LIBEXECDIR}/deepin/application-manager/) diff --git a/apps/app-launch-helper/src/main.cpp b/apps/app-launch-helper/src/main.cpp index af532da..740e635 100644 --- a/apps/app-launch-helper/src/main.cpp +++ b/apps/app-launch-helper/src/main.cpp @@ -15,6 +15,7 @@ #include #include #include +#include "constant.h" enum class ExitCode { SystemdError = -3, InvalidInput = -2, InternalError = -1, Done = 0, Waiting = 1 }; @@ -25,10 +26,6 @@ struct JobRemoveResult ExitCode result{ExitCode::Waiting}; }; -constexpr static auto SystemdService = "org.freedesktop.systemd1"; -constexpr static auto SystemdObjectPath = "/org/freedesktop/systemd1"; -constexpr static auto SystemdInterfaceName = "org.freedesktop.systemd1.Manager"; - using msg_ptr = sd_bus_message *; using bus_ptr = sd_bus *; diff --git a/apps/dde-application-manager/src/main.cpp b/apps/dde-application-manager/src/main.cpp index a751cf3..ef7a864 100644 --- a/apps/dde-application-manager/src/main.cpp +++ b/apps/dde-application-manager/src/main.cpp @@ -21,17 +21,19 @@ int main(int argc, char *argv[]) { QCoreApplication app{argc, argv}; auto &bus = ApplicationManager1DBus::instance(); - bus.init(DBusType::Session); - auto &AMBus = bus.globalBus(); + bus.initGlobalServerBus(DBusType::Session); + bus.setDestBus(""); + auto &AMBus = bus.globalServerBus(); registerComplexDbusType(); ApplicationManager1Service AMService{std::make_unique(), AMBus}; QList fileList{}; - auto pathEnv = qgetenv("XDG_DATA_DIRS"); - if (pathEnv.isEmpty()) { - qFatal() << "environment variable $XDG_DATA_DIRS is empty."; + QByteArray XDGDataDirs; + XDGDataDirs = qgetenv("XDG_DATA_DIRS"); + if (XDGDataDirs.isEmpty()) { + XDGDataDirs.append("/usr/local/share/:/usr/share/"); } - auto desktopFileDirs = pathEnv.split(':'); + auto desktopFileDirs = XDGDataDirs.split(':'); for (const auto &dir : desktopFileDirs) { auto dirPath = QDir{QDir::cleanPath(dir) + "/applications"}; diff --git a/apps/dde-application-manager/src/utils.cpp b/apps/dde-application-manager/src/utils.cpp index a208258..8b241dc 100644 --- a/apps/dde-application-manager/src/utils.cpp +++ b/apps/dde-application-manager/src/utils.cpp @@ -6,7 +6,7 @@ bool registerObjectToDBus(QObject *o, const QString &path, const QString &interface) { - auto &con = ApplicationManager1DBus::instance().globalBus(); + auto &con = ApplicationManager1DBus::instance().globalServerBus(); if (!con.registerObject(path, interface, o, QDBusConnection::RegisterOption::ExportAllContents)) { qFatal() << "register object failed:" << path << interface << con.lastError(); } else { @@ -17,6 +17,7 @@ bool registerObjectToDBus(QObject *o, const QString &path, const QString &interf void unregisterObjectFromDBus(const QString &path) { - auto &con = ApplicationManager1DBus::instance().globalBus(); + auto &con = ApplicationManager1DBus::instance().globalServerBus(); con.unregisterObject(path); + qInfo() << "unregister object:" << path; } diff --git a/src/constant.h b/src/constant.h new file mode 100644 index 0000000..bf71405 --- /dev/null +++ b/src/constant.h @@ -0,0 +1,22 @@ +// SPDX-FileCopyrightText: 2023 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#ifndef CONSTANT_H +#define CONSTANT_H + +constexpr auto SystemdService = u8"org.freedesktop.systemd1"; +constexpr auto SystemdObjectPath = u8"/org/freedesktop/systemd1"; +constexpr auto SystemdInterfaceName = u8"org.freedesktop.systemd1.Manager"; +constexpr auto DDEApplicationManager1ServiceName = u8"org.deepin.dde.ApplicationManager1"; +constexpr auto DDEApplicationManager1ObjectPath = u8"/org/deepin/dde/ApplicationManager1"; +constexpr auto DDEApplicationManager1ApplicationObjectPath = u8"/org/deepin/dde/ApplicationManager1/Application/"; +constexpr auto DDEApplicationManager1InstanceObjectPath = u8"/org/deepin/dde/ApplicationManager1/Instance/"; +constexpr auto DDEApplicationManager1JobManagerObjectPath = u8"/org/deepin/dde/ApplicationManager1/JobManager1"; +constexpr auto DDEApplicationManager1JobObjectPath = u8"/org/deepin/dde/ApplicationManager1/JobManager1/Job/"; +constexpr auto DesktopFileEntryKey = u8"Desktop Entry"; +constexpr auto DesktopFileActionKey = u8"Desktop Action "; +constexpr auto ApplicationManagerServerDBusName = u8"deepin_application_manager_server_bus"; +constexpr auto ApplicationManagerDestDBusName = "deepin_application_manager_dest_bus"; + +#endif diff --git a/src/dbus/applicationmanager1service.cpp b/src/dbus/applicationmanager1service.cpp index dd99fed..ce397e7 100644 --- a/src/dbus/applicationmanager1service.cpp +++ b/src/dbus/applicationmanager1service.cpp @@ -22,6 +22,105 @@ ApplicationManager1Service::ApplicationManager1Service(std::unique_ptrid() == appId) [[unlikely]] { + const auto &applicationPath = app->m_applicationPath.path(); + if (!app->addOneInstance(instanceId, applicationPath, systemdUnitPath.path())) { + qWarning() << "add Instance failed:" << applicationPath << serviceName << systemdUnitPath.path(); + } + return; + } + } + + qWarning() << "couldn't find application:" << serviceName << "in application manager."; + }); + + connect(&dispatcher, + &SystemdSignalDispatcher::SystemdUnitRemoved, + this, + [this](QString serviceName, QDBusObjectPath systemdUnitPath) { + auto pair = processServiceName(serviceName); + auto appId = pair.first, instanceId = pair.second; + if (appId.isEmpty()) { + return; + } + + auto appIt = std::find_if(m_applicationList.cbegin(), + m_applicationList.cend(), + [&appId](const QSharedPointer &app) { + if (app->id() == appId) { + return true; + } + return false; + }); + + if (appIt == m_applicationList.cend()) [[unlikely]] { + qWarning() << "couldn't find app" << appId << "in application manager."; + return; + } + + const auto &appRef = *appIt; + + auto instanceIt = std::find_if(appRef->m_Instances.cbegin(), + appRef->m_Instances.cend(), + [&systemdUnitPath](const QSharedPointer &value) { + if (value->systemdUnitPath() == systemdUnitPath) { + return true; + } + return false; + }); + + if (instanceIt != appRef->m_Instances.cend()) [[likely]] { + appRef->removeOneInstance(instanceIt.key()); + } + }); +} + +QPair ApplicationManager1Service::processServiceName(const QString &serviceName) +{ + QString instanceId; + QString applicationId; + + if (serviceName.endsWith(".service")) { + auto lastDotIndex = serviceName.lastIndexOf('.'); + auto app = serviceName.sliced(0, lastDotIndex - 1); // remove suffix + + if (app.contains('@')) { + auto atIndex = app.indexOf('@'); + instanceId = app.sliced(atIndex + 1); + app.remove(atIndex, instanceId.length() + 1); + } + + applicationId = app.split('-').last(); // drop launcher if it exists. + } else if (serviceName.endsWith(".scope")) { + auto lastDotIndex = serviceName.lastIndexOf('.'); + auto app = serviceName.sliced(0, lastDotIndex - 1); + + auto components = app.split('-'); + instanceId = components.takeLast(); + applicationId = components.takeLast(); + } else { + qDebug() << "it's not service or slice or scope."; + return {}; + } + + if (instanceId.isEmpty()) { + instanceId = QUuid::createUuid().toString(QUuid::Id128); + } + + return qMakePair(std::move(applicationId), std::move(instanceId)); } QList ApplicationManager1Service::list() const diff --git a/src/dbus/applicationmanager1service.h b/src/dbus/applicationmanager1service.h index a296dc5..8d4a410 100644 --- a/src/dbus/applicationmanager1service.h +++ b/src/dbus/applicationmanager1service.h @@ -60,6 +60,8 @@ private: std::unique_ptr m_identifier; QScopedPointer m_jobManager{nullptr}; QMap> m_applicationList; + + QPair processServiceName(const QString &serviceName); }; #endif diff --git a/src/dbus/applicationservice.cpp b/src/dbus/applicationservice.cpp index 9be823d..f9b31e4 100644 --- a/src/dbus/applicationservice.cpp +++ b/src/dbus/applicationservice.cpp @@ -19,6 +19,16 @@ ApplicationService::~ApplicationService() m_desktopSource.destruct(m_isPersistence); } +qsizetype ApplicationService::applicationCheck(const QString &serviceName) +{ + const auto &ApplicationId = id(); + if (!serviceName.startsWith(ApplicationId)) [[likely]] { + return 0; + } + + return ApplicationId.size(); +} + QString ApplicationService::GetActionName(const QString &identifier, const QStringList &env) { const auto &supportedActions = actions(); @@ -130,16 +140,16 @@ QDBusObjectPath ApplicationService::Launch(QString action, QStringList fields, Q auto resourceFile = variantValue.toString(); if (resourceFile.isEmpty()) { auto instanceRandomUUID = QUuid::createUuid().toString(QUuid::Id128); - commands.push_front(QString{R"(--unitName=DDE-%1@%2.service)"}.arg( + commands.push_front(QString{R"(--unitName=app-DDE-%1@%2.service)"}.arg( this->id(), instanceRandomUUID)); // launcher should use this instanceId QProcess process; process.start(m_launcher, commands); process.waitForFinished(); if (auto code = process.exitCode(); code != 0) { qWarning() << "Launch Application Failed. exitCode:" << code; - return false; + return QString{""}; } - return addOneInstance(instanceRandomUUID, m_applicationPath.path()); // TODO: pass correct Systemd Unit Path + return DDEApplicationManager1InstanceObjectPath + instanceRandomUUID; } int location{0}; @@ -167,8 +177,9 @@ QDBusObjectPath ApplicationService::Launch(QString action, QStringList fields, Q auto exitCode = process.exitCode(); if (exitCode != 0) { qWarning() << "Launch Application Failed:" << binary << tmp; + return QString{""}; } - return addOneInstance(instanceRandomUUID, m_applicationPath.path()); + return DDEApplicationManager1InstanceObjectPath + instanceRandomUUID; }, std::move(res)); } @@ -254,8 +265,8 @@ bool ApplicationService::addOneInstance(const QString &instanceId, const QString void ApplicationService::removeOneInstance(const QDBusObjectPath &instance) { - m_Instances.remove(instance); unregisterObjectFromDBus(instance.path()); + m_Instances.remove(instance); } void ApplicationService::removeAllInstance() diff --git a/src/dbus/applicationservice.h b/src/dbus/applicationservice.h index 56d8ec9..c59fd36 100644 --- a/src/dbus/applicationservice.h +++ b/src/dbus/applicationservice.h @@ -18,6 +18,7 @@ #include "global.h" #include "desktopentry.h" #include "desktopicons.h" +#include "systemdsignaldispatcher.h" #include "dbus/jobmanager1service.h" class ApplicationService : public QObject @@ -52,9 +53,9 @@ public: const QString &getLauncher() const noexcept { return m_launcher; } void setLauncher(const QString &launcher) noexcept { m_launcher = launcher; } - bool addOneInstance(const QString &instanceId, const QString &application, const QString &systemdUnitPath = "/"); + bool addOneInstance(const QString &instanceId, const QString &application, const QString &systemdUnitPath); void recoverInstances(const QList) noexcept; - void removeOneInstance(const QDBusObjectPath &instance); // TODO: remove instance when app closed + void removeOneInstance(const QDBusObjectPath &instance); void removeAllInstance(); public Q_SLOTS: @@ -133,6 +134,7 @@ private: QSharedPointer m_Icons{nullptr}; QMap> m_Instances; QString userNameLookup(uid_t uid); + qsizetype applicationCheck(const QString &serviceName); [[nodiscard]] LaunchTask unescapeExec(const QString &str, const QStringList &fields); }; diff --git a/src/dbus/jobmanager1service.h b/src/dbus/jobmanager1service.h index 4f7fb7e..d01f883 100644 --- a/src/dbus/jobmanager1service.h +++ b/src/dbus/jobmanager1service.h @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include "global.h" @@ -76,7 +77,7 @@ public: QString result{job->status()}; for (const auto &val : future.result()) { - if (val.canConvert()) { + if (val.metaType().id() == QMetaType::fromType().id()) { result = "failed"; } break; diff --git a/src/global.h b/src/global.h index 5df6fc8..65b4fdf 100644 --- a/src/global.h +++ b/src/global.h @@ -18,19 +18,11 @@ #include #include #include - +#include "constant.h" #include "config.h" using IconMap = QMap>>; -constexpr auto DDEApplicationManager1ServiceName = u8"org.deepin.dde.ApplicationManager1"; -constexpr auto DDEApplicationManager1ObjectPath = u8"/org/deepin/dde/ApplicationManager1"; -constexpr auto DDEApplicationManager1ApplicationObjectPath = u8"/org/deepin/dde/ApplicationManager1/Application/"; -constexpr auto DDEApplicationManager1InstanceObjectPath = u8"/org/deepin/dde/ApplicationManager1/Instance/"; -constexpr auto DDEApplicationManager1JobManagerObjectPath = u8"/org/deepin/dde/ApplicationManager1/JobManager1"; -constexpr auto DDEApplicationManager1JobObjectPath = u8"/org/deepin/dde/ApplicationManager1/JobManager1/Job/"; -constexpr auto DesktopFileEntryKey = u8"Desktop Entry"; -constexpr auto DesktopFileActionKey = u8"Desktop Action "; inline QString getApplicationLauncherBinary() { auto value = qgetenv("DEEPIN_APPLICATION_MANAGER_APP_LAUNCH_HELPER_BIN"); @@ -41,7 +33,6 @@ inline QString getApplicationLauncherBinary() return value; } } -constexpr auto ApplicationManagerDBusName = u8"deepin_application_manager_bus"; enum class DBusType { Session = QDBusConnection::SessionBus, System = QDBusConnection::SystemBus, Custom }; @@ -52,52 +43,53 @@ public: ApplicationManager1DBus(ApplicationManager1DBus &&) = delete; ApplicationManager1DBus &operator=(const ApplicationManager1DBus &) = delete; ApplicationManager1DBus &operator=(ApplicationManager1DBus &&) = delete; - const QString &BusAddress() { return m_busAddress; } - void init(DBusType type, const QString &busAddress = "") + const QString &globalDestBusAddress() const { return m_destBusAddress; } + const QString &globalServerBusAddress() const { return m_serverBusAddress; } + void initGlobalServerBus(DBusType type, const QString &busAddress = "") { if (m_initFlag) { return; } - m_busAddress = busAddress; - m_type = type; + m_serverBusAddress = busAddress; + m_serverType = type; m_initFlag = true; return; } - QDBusConnection &globalBus() + QDBusConnection &globalServerBus() { - if (m_connection.has_value()) { - return m_connection.value(); + if (m_serverConnection.has_value()) { + return m_serverConnection.value(); } if (!m_initFlag) { qFatal() << "invoke init at first."; } - switch (m_type) { + switch (m_serverType) { case DBusType::Session: [[fallthrough]]; case DBusType::System: { - m_connection.emplace( - QDBusConnection::connectToBus(static_cast(m_type), ApplicationManagerDBusName)); - if (!m_connection->isConnected()) { - qFatal() << m_connection->lastError(); + m_serverConnection.emplace(QDBusConnection::connectToBus(static_cast(m_serverType), + ApplicationManagerServerDBusName)); + if (!m_serverConnection->isConnected()) { + qFatal() << m_serverConnection->lastError(); } - return m_connection.value(); + return m_serverConnection.value(); } case DBusType::Custom: { - if (m_busAddress.isEmpty()) { + if (m_serverBusAddress.isEmpty()) { qFatal() << "connect to custom dbus must init this object by custom dbus address"; } - m_connection.emplace( - QDBusConnection::connectToBus(static_cast(m_type), ApplicationManagerDBusName)); - if (!m_connection->isConnected()) { - qFatal() << m_connection->lastError(); + m_serverConnection.emplace(QDBusConnection::connectToBus(m_serverBusAddress, ApplicationManagerServerDBusName)); + if (!m_serverConnection->isConnected()) { + qFatal() << m_serverConnection->lastError(); } - return m_connection.value(); + return m_serverConnection.value(); } } + Q_UNREACHABLE(); } static ApplicationManager1DBus &instance() @@ -106,13 +98,52 @@ public: return dbus; } + QDBusConnection &globalDestBus() + { + if (!m_destConnection) { + qFatal() << "please set which bus should application manager to use to invoke other D-Bus service's method."; + } + return m_destConnection.value(); + } + + void setDestBus(const QString &destAddress) + { + if (m_destConnection) { + m_destConnection->disconnectFromBus(ApplicationManagerDestDBusName); + } + + m_destBusAddress = destAddress; + + if (m_destBusAddress.isEmpty()) { + m_destConnection.emplace( + QDBusConnection::connectToBus(QDBusConnection::BusType::SessionBus, ApplicationManagerDestDBusName)); + if (!m_destConnection->isConnected()) { + qFatal() << m_destConnection->lastError(); + } + return; + } else { + if (m_destBusAddress.isEmpty()) { + qFatal() << "connect to custom dbus must init this object by custom dbus address"; + } + m_destConnection.emplace(QDBusConnection::connectToBus(m_destBusAddress, ApplicationManagerDestDBusName)); + if (!m_destConnection->isConnected()) { + qFatal() << m_destConnection->lastError(); + } + return; + } + + Q_UNREACHABLE(); + } + private: ApplicationManager1DBus() = default; ~ApplicationManager1DBus() = default; bool m_initFlag; - DBusType m_type; - QString m_busAddress; - std::optional m_connection{std::nullopt}; + DBusType m_serverType; + QString m_serverBusAddress; + QString m_destBusAddress; + std::optional m_destConnection{std::nullopt}; + std::optional m_serverConnection{std::nullopt}; }; bool registerObjectToDBus(QObject *o, const QString &path, const QString &interface); diff --git a/src/systemdsignaldispatcher.cpp b/src/systemdsignaldispatcher.cpp new file mode 100644 index 0000000..bdda93b --- /dev/null +++ b/src/systemdsignaldispatcher.cpp @@ -0,0 +1,50 @@ +// SPDX-FileCopyrightText: 2023 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#include "systemdsignaldispatcher.h" + +bool SystemdSignalDispatcher::connectToSignals() noexcept +{ + auto &con = ApplicationManager1DBus::instance().globalDestBus(); + + if (!con.connect(SystemdService, + SystemdObjectPath, + SystemdInterfaceName, + "UnitNew", + this, + SLOT(onUnitNew(QString, QDBusObjectPath)))) { + qCritical() << "can't connect to UnitNew signal of systemd service."; + return false; + } + + if (!con.connect(SystemdService, + SystemdObjectPath, + SystemdInterfaceName, + "UnitRemoved", + this, + SLOT(onUnitRemoved(QString, QDBusObjectPath)))) { + qCritical() << "can't connect to UnitRemoved signal of systemd service."; + return false; + } + + return true; +} + +void SystemdSignalDispatcher::onUnitNew(QString serviceName, QDBusObjectPath systemdUnitPath) +{ + if (!serviceName.startsWith("app-")) { + return; + } + + emit SystemdUnitNew(serviceName.sliced(4), systemdUnitPath); // remove "app-" +} + +void SystemdSignalDispatcher::onUnitRemoved(QString serviceName, QDBusObjectPath systemdUnitPath) +{ + if (!serviceName.startsWith("app-")) { + return; + } + + emit SystemdUnitRemoved(serviceName.sliced(4), systemdUnitPath); +} diff --git a/src/systemdsignaldispatcher.h b/src/systemdsignaldispatcher.h new file mode 100644 index 0000000..06c097e --- /dev/null +++ b/src/systemdsignaldispatcher.h @@ -0,0 +1,39 @@ +// SPDX-FileCopyrightText: 2023 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#ifndef SYSTEMDSIGNALDISPATCHER_H +#define SYSTEMDSIGNALDISPATCHER_H + +#include "global.h" + +class SystemdSignalDispatcher : public QObject +{ + Q_OBJECT +public: + ~SystemdSignalDispatcher() = default; + static SystemdSignalDispatcher &instance() + { + static SystemdSignalDispatcher dispatcher; + return dispatcher; + } +Q_SIGNALS: + void SystemdUnitNew(QString serviceName, QDBusObjectPath systemdUnitPath); + void SystemdUnitRemoved(QString serviceName, QDBusObjectPath systemdUnitPath); + +private Q_SLOTS: + void onUnitNew(QString serviceName, QDBusObjectPath systemdUnitPath); + void onUnitRemoved(QString serviceName, QDBusObjectPath systemdUnitPath); + +private: + explicit SystemdSignalDispatcher(QObject *parent = nullptr) + : QObject(parent) + { + if (!connectToSignals()) { + std::terminate(); + } + } + bool connectToSignals() noexcept; +}; + +#endif