From 6adc02375f8b6fe69bc7599e1829695a40bca385 Mon Sep 17 00:00:00 2001 From: black-desk Date: Mon, 28 Aug 2023 17:57:21 +0800 Subject: [PATCH] feat: scan existing systemd unit We should scan existing systemd unit when we start application manager. As application manager crash and restarted later, we can get the launched application instances back. --- apps/dde-application-manager/src/main.cpp | 14 -- src/dbus/applicationmanager1service.cpp | 149 ++++++++++++++-------- src/dbus/applicationmanager1service.h | 5 + src/global.h | 32 ++++- 4 files changed, 134 insertions(+), 66 deletions(-) diff --git a/apps/dde-application-manager/src/main.cpp b/apps/dde-application-manager/src/main.cpp index 3082c4a..b92d65e 100644 --- a/apps/dde-application-manager/src/main.cpp +++ b/apps/dde-application-manager/src/main.cpp @@ -44,21 +44,7 @@ int main(int argc, char *argv[]) registerComplexDbusType(); ApplicationManager1Service AMService{std::make_unique(), AMBus}; - QList fileList{}; - const auto &desktopFileDirs = getDesktopFileDirs(); - applyIteratively(QList(desktopFileDirs.cbegin(), desktopFileDirs.cend()), [&AMService](const QFileInfo &info) -> bool { - DesktopErrorCode err{DesktopErrorCode::NoError}; - auto ret = DesktopFile::searchDesktopFileByPath(info.absoluteFilePath(), err); - if (!ret.has_value()) { - qWarning() << "failed to search File:" << err; - return false; - } - if (!AMService.addApplication(std::move(ret).value())) { - qWarning() << "add Application failed, skip..."; - } - return false; // means to apply this function to the rest of the files - }); #ifdef PROFILING_MODE auto end = std::chrono::high_resolution_clock::now(); qCInfo(DDEAMProf) << std::chrono::duration_cast(end - start).count() << "ms"; diff --git a/src/dbus/applicationmanager1service.cpp b/src/dbus/applicationmanager1service.cpp index 17001dc..142155a 100644 --- a/src/dbus/applicationmanager1service.cpp +++ b/src/dbus/applicationmanager1service.cpp @@ -7,6 +7,7 @@ #include "dbus/AMobjectmanager1adaptor.h" #include "systemdsignaldispatcher.h" #include +#include #include ApplicationManager1Service::~ApplicationManager1Service() = default; @@ -41,66 +42,112 @@ ApplicationManager1Service::ApplicationManager1Service(std::unique_ptr &app) { return app->id() == appId; }); - - if (appIt == m_applicationList.cend()) [[unlikely]] { - qWarning() << "couldn't find app" << appId << "in application manager."; - return; - } - - const auto &applicationPath = (*appIt)->applicationPath().path(); - - if (!(*appIt)->addOneInstance(instanceId, applicationPath, systemdUnitPath.path())) [[likely]] { - qCritical() << "add Instance failed:" << applicationPath << unitName << systemdUnitPath.path(); - } - - return; - }); + connect(&dispatcher, &SystemdSignalDispatcher::SystemdUnitNew, this, &ApplicationManager1Service::addInstanceToApplication); connect(&dispatcher, &SystemdSignalDispatcher::SystemdUnitRemoved, this, - [this](const QString &serviceName, QDBusObjectPath systemdUnitPath) { - auto pair = processUnitName(serviceName); - auto appId = pair.first; - auto instanceId = pair.second; - if (appId.isEmpty()) { - return; - } + &ApplicationManager1Service::removeInstanceFromApplication); - auto appIt = std::find_if(m_applicationList.cbegin(), - m_applicationList.cend(), - [&appId](const QSharedPointer &app) { return app->id() == appId; }); + this->scanApplications(); + this->scanInstances(); - if (appIt == m_applicationList.cend()) [[unlikely]] { - qWarning() << "couldn't find app" << appId << "in application manager."; - return; - } + // TODO: send custom event; +} - const auto &appIns = (*appIt)->applicationInstances(); +void ApplicationManager1Service::addInstanceToApplication(const QString &unitName, const QDBusObjectPath &systemdUnitPath) +{ + auto pair = processUnitName(unitName); + auto appId = pair.first; + auto instanceId = pair.second; + if (appId.isEmpty()) { + return; + } - auto instanceIt = std::find_if( - appIns.cbegin(), appIns.cend(), [&systemdUnitPath](const QSharedPointer &value) { - return value->systemdUnitPath() == systemdUnitPath; - }); + auto appIt = std::find_if(m_applicationList.cbegin(), + m_applicationList.cend(), + [&appId](const QSharedPointer &app) { return app->id() == appId; }); - if (instanceIt != appIns.cend()) [[likely]] { - (*appIt)->removeOneInstance(instanceIt.key()); - } - }); + if (appIt == m_applicationList.cend()) [[unlikely]] { + qWarning() << "couldn't find app" << appId << "in application manager."; + return; + } + + const auto &applicationPath = (*appIt)->applicationPath().path(); + + if (!(*appIt)->addOneInstance(instanceId, applicationPath, systemdUnitPath.path())) [[likely]] { + qCritical() << "add Instance failed:" << applicationPath << unitName << systemdUnitPath.path(); + } + + return; +} + +void ApplicationManager1Service::removeInstanceFromApplication(const QString &unitName, const QDBusObjectPath &systemdUnitPath) +{ + auto pair = processUnitName(unitName); + auto appId = pair.first; + auto instanceId = pair.second; + if (appId.isEmpty()) { + return; + } + + auto appIt = std::find_if(m_applicationList.cbegin(), + m_applicationList.cend(), + [&appId](const QSharedPointer &app) { return app->id() == appId; }); + + if (appIt == m_applicationList.cend()) [[unlikely]] { + qWarning() << "couldn't find app" << appId << "in application manager."; + return; + } + + const auto &appIns = (*appIt)->applicationInstances(); + + auto instanceIt = + std::find_if(appIns.cbegin(), appIns.cend(), [&systemdUnitPath](const QSharedPointer &value) { + return value->systemdUnitPath() == systemdUnitPath; + }); + + if (instanceIt != appIns.cend()) [[likely]] { + (*appIt)->removeOneInstance(instanceIt.key()); + } +} + +void ApplicationManager1Service::scanApplications() noexcept +{ + QList fileList{}; + const auto &desktopFileDirs = getDesktopFileDirs(); + + applyIteratively(QList(desktopFileDirs.cbegin(), desktopFileDirs.cend()), [this](const QFileInfo &info) -> bool { + DesktopErrorCode err{DesktopErrorCode::NoError}; + auto ret = DesktopFile::searchDesktopFileByPath(info.absoluteFilePath(), err); + if (!ret.has_value()) { + qWarning() << "failed to search File:" << err; + return false; + } + if (!this->addApplication(std::move(ret).value())) { + qWarning() << "add Application failed, skip..."; + } + return false; // means to apply this function to the rest of the files + }); +} + +void ApplicationManager1Service::scanInstances() noexcept +{ + auto &conn = ApplicationManager1DBus::instance().globalDestBus(); + auto call_message = QDBusMessage::createMethodCall(SystemdService, SystemdObjectPath, SystemdInterfaceName, "ListUnits"); + auto result = conn.call(call_message); + if (result.type() == QDBusMessage::ErrorMessage) { + qCritical() << "failed to scan existing instances: call to ListUnits failed:" << result.errorMessage(); + return; + } + + auto v = result.arguments().first(); + QList units; + v.value() >> units; + for (const auto &unit : units) { + // FIXME(black_desk): Should check this unit is active or not. + this->addInstanceToApplication(unit.name, unit.objectPath); + } } QList ApplicationManager1Service::list() const diff --git a/src/dbus/applicationmanager1service.h b/src/dbus/applicationmanager1service.h index 69276f0..a3e0124 100644 --- a/src/dbus/applicationmanager1service.h +++ b/src/dbus/applicationmanager1service.h @@ -53,6 +53,11 @@ private: std::unique_ptr m_identifier; QScopedPointer m_jobManager{nullptr}; QMap> m_applicationList; + + void scanApplications() noexcept; + void scanInstances() noexcept; + void addInstanceToApplication(const QString &unitName, const QDBusObjectPath &systemdUnitPath); + void removeInstanceFromApplication(const QString &unitName, const QDBusObjectPath &systemdUnitPath); }; #endif diff --git a/src/global.h b/src/global.h index 7a0007a..deaa112 100644 --- a/src/global.h +++ b/src/global.h @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -35,6 +36,30 @@ Q_DECLARE_METATYPE(ObjectInterfaceMap) Q_DECLARE_METATYPE(ObjectMap) Q_DECLARE_METATYPE(PropMap) +struct SystemdUnitDBusMessage +{ + QString name; + QDBusObjectPath objectPath; +}; + +inline const QDBusArgument &operator>>(const QDBusArgument &argument, QList &units) +{ + argument.beginArray(); + while (!argument.atEnd()) { + argument.beginStructure(); + QString _str; + uint32_t _uint; + QDBusObjectPath _path; + SystemdUnitDBusMessage unit; + argument >> unit.name >> _str >> _str >> _str >> _str >> _str >> unit.objectPath >> _uint >> _str >> _path; + units.push_back(unit); + argument.endStructure(); + } + argument.endArray(); + + return argument; +} + inline QString getApplicationLauncherBinary() { static const QString bin = []() { @@ -398,6 +423,7 @@ inline QStringList getAutoStartDirs() inline QPair processUnitName(const QString &unitName) { + // FIXME: rewrite, using regexp. QString instanceId; QString applicationId; @@ -417,10 +443,14 @@ inline QPair processUnitName(const QString &unitName) auto app = unitName.sliced(0, lastDotIndex); auto components = app.split('-'); + if (components.size() < 3) { + qDebug() << unitName << "is not a xdg application ignore"; + return {}; + } instanceId = components.takeLast(); applicationId = components.takeLast(); } else { - qDebug() << "it's not service or scope:" << unitName << "ignore."; + qDebug() << "it's not service or scope:" << unitName << "ignore"; return {}; }