From 8f6628c5d267a1e0d615af6635d64e9c27d19b34 Mon Sep 17 00:00:00 2001 From: ComixHe Date: Wed, 18 Oct 2023 18:29:52 +0800 Subject: [PATCH] feat: add ProcessGuesser1 service Signed-off-by: ComixHe --- api/dbus/org.desktopspec.ProcessGuesser1.xml | 9 ++ src/dbus/CMakeLists.txt | 1 + src/dbus/applicationmanager1service.cpp | 39 ++----- src/dbus/applicationmanager1service.h | 3 +- src/dbus/applicationservice.cpp | 6 + src/dbus/applicationservice.h | 10 +- src/dbus/mimemanager1service.h | 2 +- src/dbus/processguesser1service.cpp | 111 +++++++++++++++++++ src/dbus/processguesser1service.h | 36 ++++++ src/desktopentry.h | 2 +- src/global.h | 35 ++++++ 11 files changed, 217 insertions(+), 37 deletions(-) create mode 100644 api/dbus/org.desktopspec.ProcessGuesser1.xml create mode 100644 src/dbus/processguesser1service.cpp create mode 100644 src/dbus/processguesser1service.h diff --git a/api/dbus/org.desktopspec.ProcessGuesser1.xml b/api/dbus/org.desktopspec.ProcessGuesser1.xml new file mode 100644 index 0000000..002d5c1 --- /dev/null +++ b/api/dbus/org.desktopspec.ProcessGuesser1.xml @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/src/dbus/CMakeLists.txt b/src/dbus/CMakeLists.txt index 92ffdd6..5006efe 100644 --- a/src/dbus/CMakeLists.txt +++ b/src/dbus/CMakeLists.txt @@ -14,6 +14,7 @@ qt_add_dbus_adaptor(dde_am_dbus_SOURCE ${PROJECT_SOURCE_DIR}/api/dbus/org.deskto qt_add_dbus_adaptor(dde_am_dbus_SOURCE ${PROJECT_SOURCE_DIR}/api/dbus/org.desktopspec.ObjectManager1.xml dbus/applicationmanager1service.h ApplicationManager1Service AMobjectmanager1adaptor AMObjectManagerAdaptor) qt_add_dbus_adaptor(dde_am_dbus_SOURCE ${PROJECT_SOURCE_DIR}/api/dbus/org.desktopspec.ObjectManager1.xml dbus/applicationservice.h ApplicationService APPobjectmanager1adaptor APPObjectManagerAdaptor) qt_add_dbus_adaptor(dde_am_dbus_SOURCE ${PROJECT_SOURCE_DIR}/api/dbus/org.desktopspec.MimeManager1.xml dbus/mimemanager1service.h MimeManager1Service) +qt_add_dbus_adaptor(dde_am_dbus_SOURCE ${PROJECT_SOURCE_DIR}/api/dbus/org.desktopspec.ProcessGuesser1.xml dbus/processguesser1service.h ProcessGuesser1Service) target_sources(dde_am_dbus PRIVATE ${dde_am_dbus_SOURCE} diff --git a/src/dbus/applicationmanager1service.cpp b/src/dbus/applicationmanager1service.cpp index 1608a6f..2a1a128 100644 --- a/src/dbus/applicationmanager1service.cpp +++ b/src/dbus/applicationmanager1service.cpp @@ -9,8 +9,10 @@ #include "propertiesForwarder.h" #include "applicationHooks.h" #include "desktopfilegenerator.h" +#include "processguesser1service.h" #include #include +#include #include ApplicationManager1Service::~ApplicationManager1Service() = default; @@ -28,6 +30,8 @@ void ApplicationManager1Service::initService(QDBusConnection &connection) noexce qFatal("%s", connection.lastError().message().toLocal8Bit().data()); } + new (std::nothrow) ProcessGuesser1Service{connection, this}; + if (auto *tmp = new (std::nothrow) ApplicationManager1Adaptor{this}; tmp == nullptr) { qCritical() << "new Application Manager Adaptor failed."; std::terminate(); @@ -397,37 +401,15 @@ QString ApplicationManager1Service::Identify(const QDBusUnixFileDescriptor &pidf Q_ASSERT_X(static_cast(m_identifier), "Identify", "Broken Identifier."); - QString fdFilePath = QString{"/proc/self/fdinfo/%1"}.arg(pidfd.fileDescriptor()); - QFile fdFile{fdFilePath}; - if (!fdFile.open(QFile::ExistingOnly | QFile::ReadOnly | QFile::Text)) { - qWarning() << "open " << fdFilePath << "failed: " << fdFile.errorString(); - return {}; - } - auto content = fdFile.readAll(); - QTextStream stream{content}; - QString appPid; - while (!stream.atEnd()) { - auto line = stream.readLine(); - if (line.startsWith("Pid")) { - appPid = line.split(":").last().trimmed(); - break; - } - } - if (appPid.isEmpty()) { - qWarning() << "can't find pid which corresponding with the instance of this application."; - return {}; - } - bool ok; - auto pid = appPid.toUInt(&ok); - if (!ok) { - qWarning() << "AppId is failed to convert to uint."; + auto pid = getPidFromPidFd(pidfd); + if (pid == 0) { + sendErrorReply(QDBusError::Failed, "pid is invalid"); return {}; } const auto ret = m_identifier->Identify(pid); - if (ret.ApplicationId.isEmpty()) { - qInfo() << "Identify failed."; + sendErrorReply(QDBusError::Failed, "Identify failed."); return {}; } @@ -436,14 +418,14 @@ QString ApplicationManager1Service::Identify(const QDBusUnixFileDescriptor &pidf }); if (app == m_applicationList.cend()) { - qWarning() << "can't find application:" << ret.ApplicationId; + sendErrorReply(QDBusError::Failed, "can't find application:" % ret.ApplicationId); return {}; } auto instancePath = (*app)->findInstance(ret.InstanceId); if (auto path = instancePath.path(); path.isEmpty()) { - qWarning() << "can't find instance:" << path; + sendErrorReply(QDBusError::Failed, "can't find instance:" % path); return {}; } @@ -457,7 +439,6 @@ QString ApplicationManager1Service::Identify(const QDBusUnixFileDescriptor &pidf void ApplicationManager1Service::updateApplication(const QSharedPointer &destApp, DesktopFile desktopFile) noexcept { - // TODO: add propertyChanged if (auto app = m_applicationList.find(destApp->applicationPath()); app == m_applicationList.cend()) { return; } diff --git a/src/dbus/applicationmanager1service.h b/src/dbus/applicationmanager1service.h index 7eea1e1..a480f3a 100644 --- a/src/dbus/applicationmanager1service.h +++ b/src/dbus/applicationmanager1service.h @@ -20,7 +20,7 @@ class ApplicationService; -class ApplicationManager1Service final : public QObject, public QDBusContext +class ApplicationManager1Service final : public QObject, protected QDBusContext { Q_OBJECT public: @@ -43,6 +43,7 @@ public: findApplicationsByIds(const QStringList &appIds) const noexcept; void updateApplication(const QSharedPointer &destApp, DesktopFile desktopFile) noexcept; + [[nodiscard]] const auto &Applications() const noexcept { return m_applicationList; } [[nodiscard]] JobManager1Service &jobManager() noexcept { return *m_jobManager; } [[nodiscard]] const JobManager1Service &jobManager() const noexcept { return *m_jobManager; } [[nodiscard]] const QStringList &applicationHooks() const noexcept { return m_hookElements; } diff --git a/src/dbus/applicationservice.cpp b/src/dbus/applicationservice.cpp index 61201ff..ccbdd4a 100644 --- a/src/dbus/applicationservice.cpp +++ b/src/dbus/applicationservice.cpp @@ -928,6 +928,12 @@ QVariant ApplicationService::findEntryValue(const QString &group, bool ok{false}; switch (type) { + case EntryValueType::Raw: { + auto valStr = val.toString(); + if (!valStr.isEmpty()) { + ret = QVariant::fromValue(valStr); + } + } break; case EntryValueType::String: { auto valStr = toString(val); if (!valStr.isEmpty()) { diff --git a/src/dbus/applicationservice.h b/src/dbus/applicationservice.h index 2f620b7..3e09581 100644 --- a/src/dbus/applicationservice.h +++ b/src/dbus/applicationservice.h @@ -28,7 +28,7 @@ QString getDeepinWineScaleFactor(const QString &appId) noexcept; double getScaleFactor() noexcept; -class ApplicationService : public QObject, public QDBusContext +class ApplicationService : public QObject, protected QDBusContext { Q_OBJECT public: @@ -118,6 +118,10 @@ public: } void resetEntry(DesktopEntry *newEntry) noexcept; void detachAllInstance() noexcept; + [[nodiscard]] QVariant findEntryValue(const QString &group, + const QString &valueKey, + EntryValueType type, + const QLocale &locale = getUserLocale()) const noexcept; [[nodiscard]] LaunchTask unescapeExec(const QString &str, const QStringList &fields) noexcept; [[nodiscard]] static QStringList unescapeExecArgs(const QString &str) noexcept; @@ -168,10 +172,6 @@ private: DesktopFile m_desktopSource; QSharedPointer m_entry{nullptr}; QMap> m_Instances; - [[nodiscard]] QVariant findEntryValue(const QString &group, - const QString &valueKey, - EntryValueType type, - const QLocale &locale = getUserLocale()) const noexcept; void updateAfterLaunch(bool isLaunch) noexcept; static bool shouldBeShown(const std::unique_ptr &entry) noexcept; [[nodiscard]] bool autostartCheck(const QString &linkPath) const noexcept; diff --git a/src/dbus/mimemanager1service.h b/src/dbus/mimemanager1service.h index b507b41..747c965 100644 --- a/src/dbus/mimemanager1service.h +++ b/src/dbus/mimemanager1service.h @@ -13,7 +13,7 @@ class ApplicationManager1Service; -class MimeManager1Service : public QObject, public QDBusContext +class MimeManager1Service : public QObject, protected QDBusContext { Q_OBJECT public: diff --git a/src/dbus/processguesser1service.cpp b/src/dbus/processguesser1service.cpp new file mode 100644 index 0000000..af0006e --- /dev/null +++ b/src/dbus/processguesser1service.cpp @@ -0,0 +1,111 @@ +// SPDX-FileCopyrightText: 2023 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#include "processguesser1adaptor.h" +#include "applicationservice.h" +#include "global.h" + +ProcessGuesser1Service::ProcessGuesser1Service(QDBusConnection &connection, ApplicationManager1Service *parent) noexcept + : QObject(parent) +{ + if (!connection.registerService("org.desktopspec.ProcessGuesser1")) { + qFatal("%s", connection.lastError().message().toLocal8Bit().data()); + } + + if (auto *tmp = new (std::nothrow) ProcessGuesser1Adaptor{this}; tmp == nullptr) { + qFatal("new Process Guesser Adaptor failed."); + } + + if (!registerObjectToDBus(this, "/org/desktopspec/ProcessGuesser1", "org.desktopspec.ProcessGuesser1")) { + std::terminate(); + } +} + +bool ProcessGuesser1Service::checkTryExec(QString tryExec) noexcept +{ + if (!tryExec.isEmpty()) { + return false; + } + + QFileInfo exeBin{tryExec}; + if (!exeBin.isAbsolute()) { + tryExec = QStandardPaths::findExecutable(tryExec); + } + + if (!tryExec.isEmpty()) { + exeBin.setFile(tryExec); + if (!exeBin.exists() or !exeBin.isFile() or !exeBin.isExecutable()) { + return false; + } + } + + return true; +} + +QString ProcessGuesser1Service::GuessApplicationId(const QDBusUnixFileDescriptor &pidfd) noexcept +{ + if (!pidfd.isValid()) { + sendErrorReply(QDBusError::InvalidArgs); + return {}; + } + + auto pid = getPidFromPidFd(pidfd); + if (pid == 0) { + sendErrorReply(QDBusError::Failed, "Pid is invalid"); + return {}; + } + + QString exePath = QString{"/proc/%1/exe"}.arg(pid); + QFileInfo info{exePath}; + + if (!info.exists()) { + sendErrorReply(QDBusError::Failed, "Pid is invalid."); + return {}; + } + const auto &binary = info.symLinkTarget(); + + const auto &applications = myParent()->Applications(); + QString appId; + + for (const QSharedPointer &app : applications) { + auto exec = app->findEntryValue(DesktopFileEntryKey, "Exec", EntryValueType::Raw).toString(); + if (exec.isEmpty()) { // NOTE: Exec is not required in desktop file. + continue; + } + + auto execList = ApplicationService::unescapeExecArgs(exec); + if (execList.isEmpty()) { + sendErrorReply(QDBusError::InternalError); + return {}; + } + + auto execBin = execList.first(); + QFileInfo execInfo{execBin}; + if (!execInfo.isAbsolute()) { + execBin = QStandardPaths::findExecutable(execBin); + } + + if (!execBin.isEmpty() and execBin == binary) { + if (!appId.isEmpty()) { + sendErrorReply(QDBusError::Failed, "Multiple binary have been detected."); + return {}; + } + appId = app->id(); + continue; + } + + auto tryExec = app->findEntryValue(DesktopFileEntryKey, "TryExec", EntryValueType::String).toString(); + if (!checkTryExec(tryExec)) { + sendErrorReply(QDBusError::Failed, "Couldn't find the binary which corresponding with process."); + return {}; + } + } + + if (appId.isEmpty()) { + sendErrorReply(QDBusError::Failed, "Couldn't found application."); + return {}; + } + + return appId; +} diff --git a/src/dbus/processguesser1service.h b/src/dbus/processguesser1service.h new file mode 100644 index 0000000..82a67ca --- /dev/null +++ b/src/dbus/processguesser1service.h @@ -0,0 +1,36 @@ +// SPDX-FileCopyrightText: 2023 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#ifndef PROCESSGUESSER1SERVICE_H +#define PROCESSGUESSER1SERVICE_H + +#include +#include +#include +#include "applicationmanager1service.h" + +class ProcessGuesser1Service : public QObject, protected QDBusContext +{ + Q_OBJECT +public: + ProcessGuesser1Service(QDBusConnection &connection, ApplicationManager1Service *parent) noexcept; + ~ProcessGuesser1Service() override = default; + ProcessGuesser1Service(const ProcessGuesser1Service &) = delete; + ProcessGuesser1Service(ProcessGuesser1Service &&) = delete; + ProcessGuesser1Service &operator=(const ProcessGuesser1Service &) = delete; + ProcessGuesser1Service &operator=(ProcessGuesser1Service &&) = delete; + ApplicationManager1Service *myParent() { return dynamic_cast(QObject::parent()); } + [[nodiscard]] const ApplicationManager1Service *myParent() const + { + return dynamic_cast(QObject::parent()); + } + +public Q_SLOTS: + QString GuessApplicationId(const QDBusUnixFileDescriptor &pidfd) noexcept; + +private: + [[nodiscard]] static bool checkTryExec(QString tryExec) noexcept; +}; + +#endif diff --git a/src/desktopentry.h b/src/desktopentry.h index c445cf7..e17b731 100644 --- a/src/desktopentry.h +++ b/src/desktopentry.h @@ -17,7 +17,7 @@ enum class EntryContext { Unknown, EntryOuter, Entry, Done }; -enum class EntryValueType { String, LocaleString, Boolean, IconString }; +enum class EntryValueType { String, LocaleString, Boolean, IconString, Raw }; struct DesktopFileGuard; diff --git a/src/global.h b/src/global.h index f6986a1..395bf39 100644 --- a/src/global.h +++ b/src/global.h @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -576,4 +577,38 @@ inline QByteArray getCurrentSessionId() return id.value().variant().toByteArray(); } +inline uint getPidFromPidFd(const QDBusUnixFileDescriptor &pidfd) noexcept +{ + QString fdFilePath = QString{"/proc/self/fdinfo/%1"}.arg(pidfd.fileDescriptor()); + QFile fdFile{fdFilePath}; + if (!fdFile.open(QFile::ExistingOnly | QFile::ReadOnly | QFile::Text)) { + qWarning() << "open " << fdFilePath << "failed: " << fdFile.errorString(); + return 0; + } + + auto content = fdFile.readAll(); + QTextStream stream{content}; + QString appPid; + while (!stream.atEnd()) { + auto line = stream.readLine(); + if (line.startsWith("Pid")) { + appPid = line.split(":").last().trimmed(); + break; + } + } + + if (appPid.isEmpty()) { + qWarning() << "can't find pid which corresponding with the instance of this application."; + return 0; + } + bool ok{false}; + auto pid = appPid.toUInt(&ok); + if (!ok) { + qWarning() << "AppId is failed to convert to uint."; + return 0; + } + + return pid; +} + #endif