2023-07-10 10:18:33 +08:00
|
|
|
// SPDX-FileCopyrightText: 2023 UnionTech Software Technology Co., Ltd.
|
|
|
|
//
|
|
|
|
// SPDX-License-Identifier: LGPL-3.0-or-later
|
2023-08-07 14:25:22 +08:00
|
|
|
#include "dbus/applicationmanager1service.h"
|
|
|
|
#include "dbus/applicationmanager1adaptor.h"
|
2023-08-10 14:32:09 +08:00
|
|
|
#include "systemdsignaldispatcher.h"
|
2023-07-24 14:12:59 +08:00
|
|
|
#include <QFile>
|
|
|
|
#include <unistd.h>
|
2023-07-10 10:18:33 +08:00
|
|
|
|
2023-07-17 14:49:35 +08:00
|
|
|
ApplicationManager1Service::~ApplicationManager1Service() = default;
|
|
|
|
|
2023-07-24 14:12:59 +08:00
|
|
|
ApplicationManager1Service::ApplicationManager1Service(std::unique_ptr<Identifier> ptr, QDBusConnection &connection)
|
|
|
|
: m_identifier(std::move(ptr))
|
|
|
|
{
|
|
|
|
if (!connection.registerService(DDEApplicationManager1ServiceName)) {
|
|
|
|
qFatal() << connection.lastError();
|
|
|
|
}
|
|
|
|
|
2023-08-11 10:12:46 +08:00
|
|
|
new ApplicationManager1Adaptor{this};
|
|
|
|
|
|
|
|
if (!registerObjectToDBus(this, DDEApplicationManager1ObjectPath, getDBusInterface<ApplicationManager1Adaptor>())) {
|
2023-07-24 14:12:59 +08:00
|
|
|
std::terminate();
|
|
|
|
}
|
|
|
|
|
|
|
|
m_jobManager.reset(new JobManager1Service(this));
|
2023-08-08 15:10:32 +08:00
|
|
|
|
|
|
|
auto &dispatcher = SystemdSignalDispatcher::instance();
|
|
|
|
|
|
|
|
connect(&dispatcher,
|
|
|
|
&SystemdSignalDispatcher::SystemdUnitNew,
|
|
|
|
this,
|
2023-08-11 14:53:05 +08:00
|
|
|
[this](const QString &unitName, const QDBusObjectPath &systemdUnitPath) {
|
|
|
|
auto pair = processUnitName(unitName);
|
|
|
|
auto appId = pair.first;
|
|
|
|
auto instanceId = pair.second;
|
2023-08-08 15:10:32 +08:00
|
|
|
if (appId.isEmpty()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2023-08-11 14:53:05 +08:00
|
|
|
auto appIt = std::find_if(m_applicationList.cbegin(),
|
|
|
|
m_applicationList.cend(),
|
|
|
|
[&appId](const QSharedPointer<ApplicationService> &app) { return app->id() == appId; });
|
|
|
|
|
|
|
|
if (appIt == m_applicationList.cend()) [[unlikely]] {
|
|
|
|
qWarning() << "couldn't find app" << appId << "in application manager.";
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
const auto &appRef = *appIt;
|
|
|
|
const auto &applicationPath = appRef->m_applicationPath.path();
|
|
|
|
|
|
|
|
if (!appRef->addOneInstance(instanceId, applicationPath, systemdUnitPath.path())) [[likely]] {
|
|
|
|
qCritical() << "add Instance failed:" << applicationPath << unitName << systemdUnitPath.path();
|
2023-08-08 15:10:32 +08:00
|
|
|
}
|
|
|
|
|
2023-08-11 14:53:05 +08:00
|
|
|
return;
|
2023-08-08 15:10:32 +08:00
|
|
|
});
|
|
|
|
|
|
|
|
connect(&dispatcher,
|
|
|
|
&SystemdSignalDispatcher::SystemdUnitRemoved,
|
|
|
|
this,
|
2023-08-10 14:32:09 +08:00
|
|
|
[this](const QString &serviceName, QDBusObjectPath systemdUnitPath) {
|
2023-08-11 14:53:05 +08:00
|
|
|
auto pair = processUnitName(serviceName);
|
2023-08-10 14:32:09 +08:00
|
|
|
auto appId = pair.first;
|
|
|
|
auto instanceId = pair.second;
|
2023-08-08 15:10:32 +08:00
|
|
|
if (appId.isEmpty()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto appIt = std::find_if(m_applicationList.cbegin(),
|
|
|
|
m_applicationList.cend(),
|
2023-08-10 14:32:09 +08:00
|
|
|
[&appId](const QSharedPointer<ApplicationService> &app) { return app->id() == appId; });
|
2023-08-08 15:10:32 +08:00
|
|
|
|
|
|
|
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<InstanceService> &value) {
|
2023-08-10 14:32:09 +08:00
|
|
|
return value->systemdUnitPath() == systemdUnitPath;
|
2023-08-08 15:10:32 +08:00
|
|
|
});
|
|
|
|
|
|
|
|
if (instanceIt != appRef->m_Instances.cend()) [[likely]] {
|
|
|
|
appRef->removeOneInstance(instanceIt.key());
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2023-08-11 14:53:05 +08:00
|
|
|
QPair<QString, QString> ApplicationManager1Service::processUnitName(const QString &unitName) noexcept
|
2023-08-08 15:10:32 +08:00
|
|
|
{
|
|
|
|
QString instanceId;
|
|
|
|
QString applicationId;
|
|
|
|
|
2023-08-11 14:53:05 +08:00
|
|
|
if (unitName.endsWith(".service")) {
|
|
|
|
auto lastDotIndex = unitName.lastIndexOf('.');
|
|
|
|
auto app = unitName.sliced(0, lastDotIndex - 1); // remove suffix
|
2023-08-08 15:10:32 +08:00
|
|
|
|
|
|
|
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.
|
2023-08-11 14:53:05 +08:00
|
|
|
} else if (unitName.endsWith(".scope")) {
|
|
|
|
auto lastDotIndex = unitName.lastIndexOf('.');
|
|
|
|
auto app = unitName.sliced(0, lastDotIndex - 1);
|
2023-08-08 15:10:32 +08:00
|
|
|
|
|
|
|
auto components = app.split('-');
|
|
|
|
instanceId = components.takeLast();
|
|
|
|
applicationId = components.takeLast();
|
|
|
|
} else {
|
2023-08-11 14:53:05 +08:00
|
|
|
qDebug() << "it's not service or scope:" << unitName << "ignore.";
|
2023-08-08 15:10:32 +08:00
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
|
|
|
if (instanceId.isEmpty()) {
|
|
|
|
instanceId = QUuid::createUuid().toString(QUuid::Id128);
|
|
|
|
}
|
|
|
|
|
2023-08-11 10:12:46 +08:00
|
|
|
return qMakePair(unescapeApplicationId(applicationId), std::move(instanceId));
|
2023-07-24 14:12:59 +08:00
|
|
|
}
|
2023-07-10 10:18:33 +08:00
|
|
|
|
2023-07-21 14:47:40 +08:00
|
|
|
QList<QDBusObjectPath> ApplicationManager1Service::list() const
|
|
|
|
{
|
|
|
|
return m_applicationList.keys();
|
|
|
|
}
|
2023-07-10 10:18:33 +08:00
|
|
|
|
2023-08-10 14:32:09 +08:00
|
|
|
void ApplicationManager1Service::removeOneApplication(const QDBusObjectPath &application)
|
2023-07-10 10:18:33 +08:00
|
|
|
{
|
2023-08-10 14:32:09 +08:00
|
|
|
unregisterObjectFromDBus(application.path());
|
|
|
|
m_applicationList.remove(application);
|
2023-07-10 10:18:33 +08:00
|
|
|
}
|
|
|
|
|
2023-07-21 14:47:40 +08:00
|
|
|
void ApplicationManager1Service::removeAllApplication()
|
|
|
|
{
|
2023-08-10 14:32:09 +08:00
|
|
|
for (const auto &app : m_applicationList.keys()) {
|
|
|
|
removeOneApplication(app);
|
|
|
|
}
|
2023-07-21 14:47:40 +08:00
|
|
|
}
|
2023-07-17 14:49:35 +08:00
|
|
|
|
2023-07-24 14:12:59 +08:00
|
|
|
QDBusObjectPath ApplicationManager1Service::Application(const QString &id) const
|
2023-07-10 10:18:33 +08:00
|
|
|
{
|
2023-07-24 14:12:59 +08:00
|
|
|
auto ret = std::find_if(m_applicationList.cbegin(), m_applicationList.cend(), [&id](const auto &app) {
|
2023-08-10 14:32:09 +08:00
|
|
|
return static_cast<bool>(app->id() == id);
|
2023-07-24 14:12:59 +08:00
|
|
|
});
|
|
|
|
|
|
|
|
if (ret != m_applicationList.cend()) {
|
|
|
|
return ret.key();
|
|
|
|
}
|
2023-07-10 10:18:33 +08:00
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
|
|
|
QString ApplicationManager1Service::Identify(const QDBusUnixFileDescriptor &pidfd,
|
|
|
|
QDBusObjectPath &application,
|
|
|
|
QDBusObjectPath &application_instance)
|
|
|
|
{
|
2023-07-24 14:12:59 +08:00
|
|
|
if (!pidfd.isValid()) {
|
|
|
|
qWarning() << "pidfd isn't a valid unix file descriptor";
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
|
|
|
Q_ASSERT_X(static_cast<bool>(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.";
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
|
|
|
const auto ret = m_identifier->Identify(pid);
|
|
|
|
|
|
|
|
auto app = std::find_if(m_applicationList.cbegin(), m_applicationList.cend(), [&ret](const auto &appPtr) {
|
|
|
|
return appPtr->id() == ret.ApplicationId;
|
|
|
|
});
|
|
|
|
if (app == m_applicationList.cend()) {
|
|
|
|
qWarning() << "can't find application:" << ret.ApplicationId;
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
application = m_applicationList.key(*app);
|
|
|
|
application_instance = (*app)->findInstance(ret.InstanceId);
|
|
|
|
return ret.ApplicationId;
|
2023-07-10 10:18:33 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
QDBusObjectPath ApplicationManager1Service::Launch(const QString &id,
|
2023-07-17 14:49:35 +08:00
|
|
|
const QString &actions,
|
2023-07-10 10:18:33 +08:00
|
|
|
const QStringList &fields,
|
|
|
|
const QVariantMap &options)
|
|
|
|
{
|
2023-07-24 14:12:59 +08:00
|
|
|
auto app = Application(id);
|
|
|
|
if (app.path().isEmpty()) {
|
|
|
|
qWarning() << "No such application.";
|
|
|
|
return {};
|
2023-07-17 14:49:35 +08:00
|
|
|
}
|
2023-07-24 14:12:59 +08:00
|
|
|
const auto &value = m_applicationList.value(app);
|
|
|
|
return value->Launch(actions, fields, options);
|
2023-07-10 10:18:33 +08:00
|
|
|
}
|
2023-07-24 14:12:59 +08:00
|
|
|
|
2023-08-10 14:32:09 +08:00
|
|
|
void ApplicationManager1Service::UpdateApplicationInfo(const QStringList &app_id)
|
|
|
|
{
|
|
|
|
auto XDGDataDirs = QString::fromLocal8Bit(qgetenv("XDG_DATA_DIRS")).split(':', Qt::SkipEmptyParts);
|
|
|
|
std::for_each(XDGDataDirs.begin(), XDGDataDirs.end(), [](QString &str) {
|
|
|
|
if (!str.endsWith(QDir::separator())) {
|
|
|
|
str.append(QDir::separator());
|
|
|
|
}
|
|
|
|
str.append("applications");
|
|
|
|
});
|
|
|
|
|
|
|
|
for (auto id : app_id) {
|
|
|
|
auto destApp = std::find_if(m_applicationList.begin(),
|
|
|
|
m_applicationList.end(),
|
|
|
|
[&id](const QSharedPointer<ApplicationService> &value) { return value->id() == id; });
|
|
|
|
|
|
|
|
if (destApp == m_applicationList.end()) { // new app
|
|
|
|
qInfo() << "add a new application:" << id;
|
|
|
|
do {
|
|
|
|
for (const auto &suffix : XDGDataDirs) {
|
|
|
|
QFileInfo info{suffix + id};
|
|
|
|
if (info.exists()) {
|
|
|
|
ParseError err;
|
|
|
|
auto file = DesktopFile::searchDesktopFile(info.absoluteFilePath(), err);
|
|
|
|
|
|
|
|
if (!file.has_value()) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!addApplication(std::move(file).value())) {
|
|
|
|
id.clear();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (id.isEmpty()) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto hyphenIndex = id.indexOf('-');
|
|
|
|
if (hyphenIndex == -1) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
id[hyphenIndex] = QDir::separator();
|
|
|
|
} while (true);
|
|
|
|
} else { // remove or update
|
|
|
|
if (!(*destApp)->m_isPersistence) [[unlikely]] {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto filePath = (*destApp)->m_desktopSource.m_file.filePath();
|
|
|
|
if (QFileInfo::exists(filePath)) { // update
|
|
|
|
qInfo() << "update application:" << id;
|
|
|
|
struct stat buf;
|
|
|
|
if (auto ret = stat(filePath.toLatin1().data(), &buf); ret == -1) {
|
|
|
|
qWarning() << "get file" << filePath << "state failed:" << std::strerror(errno) << ", skip...";
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((*destApp)->m_desktopSource.m_file.modified(
|
|
|
|
static_cast<std::size_t>(buf.st_mtim.tv_sec * 1e9 + buf.st_mtim.tv_nsec))) {
|
|
|
|
auto newEntry = new DesktopEntry{};
|
|
|
|
auto err = newEntry->parse((*destApp)->m_desktopSource.m_file);
|
|
|
|
if (err != ParseError::NoError and err != ParseError::EntryKeyInvalid) {
|
|
|
|
qWarning() << "update desktop file failed:" << err << ", content wouldn't change.";
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
(*destApp)->m_entry.reset(newEntry);
|
|
|
|
}
|
|
|
|
} else { // remove
|
|
|
|
qInfo() << "remove application:" << id;
|
|
|
|
removeOneApplication((*destApp)->m_applicationPath);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|