feat: add ProcessGuesser1 service

Signed-off-by: ComixHe <heyuming@deepin.org>
This commit is contained in:
ComixHe 2023-10-18 18:29:52 +08:00 committed by Comix
parent f796535233
commit 8f6628c5d2
11 changed files with 217 additions and 37 deletions

View File

@ -0,0 +1,9 @@
<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "https://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
<node>
<interface name="org.desktopspec.ProcessGuesser1">
<method name="GuessApplicationId">
<arg type="h" name="pidfd" direction="in" />
<arg type="s" name="appId" direction="out" />
</method>
</interface>
</node>

View File

@ -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/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.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.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 target_sources(dde_am_dbus PRIVATE
${dde_am_dbus_SOURCE} ${dde_am_dbus_SOURCE}

View File

@ -9,8 +9,10 @@
#include "propertiesForwarder.h" #include "propertiesForwarder.h"
#include "applicationHooks.h" #include "applicationHooks.h"
#include "desktopfilegenerator.h" #include "desktopfilegenerator.h"
#include "processguesser1service.h"
#include <QFile> #include <QFile>
#include <QDBusMessage> #include <QDBusMessage>
#include <QStringBuilder>
#include <unistd.h> #include <unistd.h>
ApplicationManager1Service::~ApplicationManager1Service() = default; ApplicationManager1Service::~ApplicationManager1Service() = default;
@ -28,6 +30,8 @@ void ApplicationManager1Service::initService(QDBusConnection &connection) noexce
qFatal("%s", connection.lastError().message().toLocal8Bit().data()); qFatal("%s", connection.lastError().message().toLocal8Bit().data());
} }
new (std::nothrow) ProcessGuesser1Service{connection, this};
if (auto *tmp = new (std::nothrow) ApplicationManager1Adaptor{this}; tmp == nullptr) { if (auto *tmp = new (std::nothrow) ApplicationManager1Adaptor{this}; tmp == nullptr) {
qCritical() << "new Application Manager Adaptor failed."; qCritical() << "new Application Manager Adaptor failed.";
std::terminate(); std::terminate();
@ -397,37 +401,15 @@ QString ApplicationManager1Service::Identify(const QDBusUnixFileDescriptor &pidf
Q_ASSERT_X(static_cast<bool>(m_identifier), "Identify", "Broken Identifier."); Q_ASSERT_X(static_cast<bool>(m_identifier), "Identify", "Broken Identifier.");
QString fdFilePath = QString{"/proc/self/fdinfo/%1"}.arg(pidfd.fileDescriptor()); auto pid = getPidFromPidFd(pidfd);
QFile fdFile{fdFilePath}; if (pid == 0) {
if (!fdFile.open(QFile::ExistingOnly | QFile::ReadOnly | QFile::Text)) { sendErrorReply(QDBusError::Failed, "pid is invalid");
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.";
return {}; return {};
} }
const auto ret = m_identifier->Identify(pid); const auto ret = m_identifier->Identify(pid);
if (ret.ApplicationId.isEmpty()) { if (ret.ApplicationId.isEmpty()) {
qInfo() << "Identify failed."; sendErrorReply(QDBusError::Failed, "Identify failed.");
return {}; return {};
} }
@ -436,14 +418,14 @@ QString ApplicationManager1Service::Identify(const QDBusUnixFileDescriptor &pidf
}); });
if (app == m_applicationList.cend()) { if (app == m_applicationList.cend()) {
qWarning() << "can't find application:" << ret.ApplicationId; sendErrorReply(QDBusError::Failed, "can't find application:" % ret.ApplicationId);
return {}; return {};
} }
auto instancePath = (*app)->findInstance(ret.InstanceId); auto instancePath = (*app)->findInstance(ret.InstanceId);
if (auto path = instancePath.path(); path.isEmpty()) { if (auto path = instancePath.path(); path.isEmpty()) {
qWarning() << "can't find instance:" << path; sendErrorReply(QDBusError::Failed, "can't find instance:" % path);
return {}; return {};
} }
@ -457,7 +439,6 @@ QString ApplicationManager1Service::Identify(const QDBusUnixFileDescriptor &pidf
void ApplicationManager1Service::updateApplication(const QSharedPointer<ApplicationService> &destApp, void ApplicationManager1Service::updateApplication(const QSharedPointer<ApplicationService> &destApp,
DesktopFile desktopFile) noexcept DesktopFile desktopFile) noexcept
{ {
// TODO: add propertyChanged
if (auto app = m_applicationList.find(destApp->applicationPath()); app == m_applicationList.cend()) { if (auto app = m_applicationList.find(destApp->applicationPath()); app == m_applicationList.cend()) {
return; return;
} }

View File

@ -20,7 +20,7 @@
class ApplicationService; class ApplicationService;
class ApplicationManager1Service final : public QObject, public QDBusContext class ApplicationManager1Service final : public QObject, protected QDBusContext
{ {
Q_OBJECT Q_OBJECT
public: public:
@ -43,6 +43,7 @@ public:
findApplicationsByIds(const QStringList &appIds) const noexcept; findApplicationsByIds(const QStringList &appIds) const noexcept;
void updateApplication(const QSharedPointer<ApplicationService> &destApp, DesktopFile desktopFile) noexcept; void updateApplication(const QSharedPointer<ApplicationService> &destApp, DesktopFile desktopFile) noexcept;
[[nodiscard]] const auto &Applications() const noexcept { return m_applicationList; }
[[nodiscard]] JobManager1Service &jobManager() noexcept { return *m_jobManager; } [[nodiscard]] JobManager1Service &jobManager() noexcept { return *m_jobManager; }
[[nodiscard]] const JobManager1Service &jobManager() const noexcept { return *m_jobManager; } [[nodiscard]] const JobManager1Service &jobManager() const noexcept { return *m_jobManager; }
[[nodiscard]] const QStringList &applicationHooks() const noexcept { return m_hookElements; } [[nodiscard]] const QStringList &applicationHooks() const noexcept { return m_hookElements; }

View File

@ -928,6 +928,12 @@ QVariant ApplicationService::findEntryValue(const QString &group,
bool ok{false}; bool ok{false};
switch (type) { switch (type) {
case EntryValueType::Raw: {
auto valStr = val.toString();
if (!valStr.isEmpty()) {
ret = QVariant::fromValue(valStr);
}
} break;
case EntryValueType::String: { case EntryValueType::String: {
auto valStr = toString(val); auto valStr = toString(val);
if (!valStr.isEmpty()) { if (!valStr.isEmpty()) {

View File

@ -28,7 +28,7 @@
QString getDeepinWineScaleFactor(const QString &appId) noexcept; QString getDeepinWineScaleFactor(const QString &appId) noexcept;
double getScaleFactor() noexcept; double getScaleFactor() noexcept;
class ApplicationService : public QObject, public QDBusContext class ApplicationService : public QObject, protected QDBusContext
{ {
Q_OBJECT Q_OBJECT
public: public:
@ -118,6 +118,10 @@ public:
} }
void resetEntry(DesktopEntry *newEntry) noexcept; void resetEntry(DesktopEntry *newEntry) noexcept;
void detachAllInstance() 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]] LaunchTask unescapeExec(const QString &str, const QStringList &fields) noexcept;
[[nodiscard]] static QStringList unescapeExecArgs(const QString &str) noexcept; [[nodiscard]] static QStringList unescapeExecArgs(const QString &str) noexcept;
@ -168,10 +172,6 @@ private:
DesktopFile m_desktopSource; DesktopFile m_desktopSource;
QSharedPointer<DesktopEntry> m_entry{nullptr}; QSharedPointer<DesktopEntry> m_entry{nullptr};
QMap<QDBusObjectPath, QSharedPointer<InstanceService>> m_Instances; QMap<QDBusObjectPath, QSharedPointer<InstanceService>> m_Instances;
[[nodiscard]] QVariant findEntryValue(const QString &group,
const QString &valueKey,
EntryValueType type,
const QLocale &locale = getUserLocale()) const noexcept;
void updateAfterLaunch(bool isLaunch) noexcept; void updateAfterLaunch(bool isLaunch) noexcept;
static bool shouldBeShown(const std::unique_ptr<DesktopEntry> &entry) noexcept; static bool shouldBeShown(const std::unique_ptr<DesktopEntry> &entry) noexcept;
[[nodiscard]] bool autostartCheck(const QString &linkPath) const noexcept; [[nodiscard]] bool autostartCheck(const QString &linkPath) const noexcept;

View File

@ -13,7 +13,7 @@
class ApplicationManager1Service; class ApplicationManager1Service;
class MimeManager1Service : public QObject, public QDBusContext class MimeManager1Service : public QObject, protected QDBusContext
{ {
Q_OBJECT Q_OBJECT
public: public:

View File

@ -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<ApplicationService> &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;
}

View File

@ -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 <QObject>
#include <QDBusContext>
#include <QDBusUnixFileDescriptor>
#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<ApplicationManager1Service *>(QObject::parent()); }
[[nodiscard]] const ApplicationManager1Service *myParent() const
{
return dynamic_cast<ApplicationManager1Service *>(QObject::parent());
}
public Q_SLOTS:
QString GuessApplicationId(const QDBusUnixFileDescriptor &pidfd) noexcept;
private:
[[nodiscard]] static bool checkTryExec(QString tryExec) noexcept;
};
#endif

View File

@ -17,7 +17,7 @@
enum class EntryContext { Unknown, EntryOuter, Entry, Done }; enum class EntryContext { Unknown, EntryOuter, Entry, Done };
enum class EntryValueType { String, LocaleString, Boolean, IconString }; enum class EntryValueType { String, LocaleString, Boolean, IconString, Raw };
struct DesktopFileGuard; struct DesktopFileGuard;

View File

@ -16,6 +16,7 @@
#include <QDir> #include <QDir>
#include <QRegularExpression> #include <QRegularExpression>
#include <QDBusObjectPath> #include <QDBusObjectPath>
#include <QDBusUnixFileDescriptor>
#include <QDBusArgument> #include <QDBusArgument>
#include <QDBusMessage> #include <QDBusMessage>
#include <unistd.h> #include <unistd.h>
@ -576,4 +577,38 @@ inline QByteArray getCurrentSessionId()
return id.value<QDBusVariant>().variant().toByteArray(); return id.value<QDBusVariant>().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 #endif