feat: implementation of the major feature of dbus service
Log: Signed-off-by: ComixHe <heyuming@deepin.org>
This commit is contained in:
parent
839383ecae
commit
fe284e78b6
1
.gitignore
vendored
1
.gitignore
vendored
@ -1 +1,2 @@
|
|||||||
build
|
build
|
||||||
|
.vscode
|
||||||
|
@ -44,9 +44,9 @@ foreach(obj IN LISTS AdaptorLib)
|
|||||||
endforeach()
|
endforeach()
|
||||||
|
|
||||||
add_subdirectory(src)
|
add_subdirectory(src)
|
||||||
# add_subdirectory(docs)
|
add_subdirectory(plugin)
|
||||||
# add_subdirectory(plugin)
|
|
||||||
|
|
||||||
|
# add_subdirectory(docs)
|
||||||
include(CTest)
|
include(CTest)
|
||||||
|
|
||||||
if(BUILD_TESTING)
|
if(BUILD_TESTING)
|
||||||
|
@ -3,9 +3,7 @@
|
|||||||
<interface name="org.desktopspec.ApplicationManager1.Application">
|
<interface name="org.desktopspec.ApplicationManager1.Application">
|
||||||
<annotation
|
<annotation
|
||||||
name="org.freedesktop.DBus.Description"
|
name="org.freedesktop.DBus.Description"
|
||||||
value="This interface is designed to
|
value="This interface is designed to provide a dbus interface of desktop file. Missing fields will be added later."
|
||||||
provide a dbus interface of desktop file.
|
|
||||||
Missing fields will be added later."
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<property name="Actions" type="as" access="read">
|
<property name="Actions" type="as" access="read">
|
||||||
|
@ -2,6 +2,15 @@
|
|||||||
<node>
|
<node>
|
||||||
<interface name="org.desktopspec.ApplicationManager1">
|
<interface name="org.desktopspec.ApplicationManager1">
|
||||||
<property type="ao" access="read" name="List" />
|
<property type="ao" access="read" name="List" />
|
||||||
|
<method name="UpdateApplicationInfo">
|
||||||
|
<arg type="as" name="update_path" direction="in" />
|
||||||
|
<annotation
|
||||||
|
name="org.freedesktop.DBus.Description"
|
||||||
|
value="This method is used to update the desktop file cache when needed.
|
||||||
|
You can pass a list of absolute paths to files or directories that you want to update.
|
||||||
|
Note: Application Manager only supports directory paths in $XDG_DATA_DIRS."
|
||||||
|
/>
|
||||||
|
</method>
|
||||||
|
|
||||||
<method name="Application">
|
<method name="Application">
|
||||||
<arg type="s" name="id" direction="in" />
|
<arg type="s" name="id" direction="in" />
|
||||||
|
1
plugin/CMakeLists.txt
Normal file
1
plugin/CMakeLists.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
add_subdirectory(appLauncher)
|
13
plugin/appLauncher/CMakeLists.txt
Normal file
13
plugin/appLauncher/CMakeLists.txt
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
include(GNUInstallDirs)
|
||||||
|
|
||||||
|
set(APP_LAUNCHER_BIN_NAME am-launcher)
|
||||||
|
|
||||||
|
find_package(PkgConfig REQUIRED)
|
||||||
|
pkg_check_modules(SYSTEMD REQUIRED IMPORTED_TARGET libsystemd)
|
||||||
|
|
||||||
|
add_executable(${APP_LAUNCHER_BIN_NAME} launcher.cpp)
|
||||||
|
target_link_libraries(${APP_LAUNCHER_BIN_NAME} PRIVATE
|
||||||
|
PkgConfig::SYSTEMD
|
||||||
|
)
|
||||||
|
|
||||||
|
install(TARGETS ${APP_LAUNCHER_BIN_NAME} DESTINATION ${CMAKE_INSTALL_LIBEXECDIR}/deepin/application-manager/)
|
374
plugin/appLauncher/launcher.cpp
Normal file
374
plugin/appLauncher/launcher.cpp
Normal file
@ -0,0 +1,374 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2023 UnionTech Software Technology Co., Ltd.
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||||
|
|
||||||
|
#include <systemd/sd-bus.h>
|
||||||
|
#include <systemd/sd-journal.h>
|
||||||
|
#include <string_view>
|
||||||
|
#include <vector>
|
||||||
|
#include <memory>
|
||||||
|
#include <iostream>
|
||||||
|
#include <string>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <map>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
|
enum class ExitCode { SystemdError = -3, InvalidInput = -2, InternalError = -1, Done = 0, Waiting = 1 };
|
||||||
|
|
||||||
|
struct JobRemoveResult
|
||||||
|
{
|
||||||
|
std::string_view id;
|
||||||
|
int removedFlag{0};
|
||||||
|
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 *;
|
||||||
|
|
||||||
|
static ExitCode fromString(const std::string &str)
|
||||||
|
{
|
||||||
|
if (str == "done") {
|
||||||
|
return ExitCode::Done;
|
||||||
|
} else if (str == "canceled" or str == "timeout" or str == "failed" or str == "dependency" or str == "skipped") {
|
||||||
|
return ExitCode::SystemdError;
|
||||||
|
} else if (str == "internalError") {
|
||||||
|
return ExitCode::InternalError;
|
||||||
|
} else if (str == "invalidInput") {
|
||||||
|
return ExitCode::InvalidInput;
|
||||||
|
}
|
||||||
|
__builtin_unreachable();
|
||||||
|
}
|
||||||
|
|
||||||
|
static ExitCode fromString(const char *str)
|
||||||
|
{
|
||||||
|
if (!str) {
|
||||||
|
return ExitCode::Waiting;
|
||||||
|
}
|
||||||
|
std::string tmp{str};
|
||||||
|
return fromString(tmp);
|
||||||
|
}
|
||||||
|
|
||||||
|
[[noreturn]] static void releaseRes(sd_bus_error &error, msg_ptr &msg, bus_ptr &bus, ExitCode ret)
|
||||||
|
{
|
||||||
|
sd_bus_error_free(&error);
|
||||||
|
sd_bus_message_unref(msg);
|
||||||
|
sd_bus_unref(bus);
|
||||||
|
|
||||||
|
std::exit(static_cast<int>(ret));
|
||||||
|
}
|
||||||
|
|
||||||
|
static int processExecStart(msg_ptr &msg, const std::vector<std::string_view> &execArgs)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
if (ret = sd_bus_message_append(msg, "s", "ExecStart"); ret < 0) {
|
||||||
|
sd_journal_perror("append ExecStart failed.");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ret = sd_bus_message_open_container(msg, SD_BUS_TYPE_VARIANT, "a(sasb)"); ret < 0) {
|
||||||
|
sd_journal_perror("open variant of execStart failed.");
|
||||||
|
if (auto tmp = sd_bus_message_close_container(msg))
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ret = sd_bus_message_open_container(msg, SD_BUS_TYPE_ARRAY, "(sasb)"); ret < 0) {
|
||||||
|
sd_journal_perror("open array of execStart failed.");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ret = sd_bus_message_open_container(msg, SD_BUS_TYPE_STRUCT, "sasb"); ret < 0) {
|
||||||
|
sd_journal_perror("open struct of execStart failed.");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ret = sd_bus_message_append(msg, "s", execArgs[0].data()); ret < 0) {
|
||||||
|
sd_journal_perror("append binary of execStart failed.");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ret = sd_bus_message_open_container(msg, SD_BUS_TYPE_ARRAY, "s"); ret < 0) {
|
||||||
|
sd_journal_perror("open array of execStart variant failed.");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
for (std::size_t i = 0; i < execArgs.size(); ++i) {
|
||||||
|
if (ret = sd_bus_message_append(msg, "s", execArgs[i].data()); ret < 0) {
|
||||||
|
sd_journal_perror("append args of execStart failed.");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ret = sd_bus_message_close_container(msg); ret < 0) {
|
||||||
|
sd_journal_perror("close array of execStart failed.");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ret = sd_bus_message_append(msg, "b", 0);
|
||||||
|
ret < 0) { // this value indicate that systemd should be considered a failure if the process exits uncleanly
|
||||||
|
sd_journal_perror("append boolean of execStart failed.");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ret = sd_bus_message_close_container(msg); ret < 0) {
|
||||||
|
sd_journal_perror("close struct of execStart failed.");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ret = sd_bus_message_close_container(msg); ret < 0) {
|
||||||
|
sd_journal_perror("close array of execStart failed.");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ret = sd_bus_message_close_container(msg); ret < 0) {
|
||||||
|
sd_journal_perror("close variant of execStart failed.");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int processKVPair(msg_ptr &msg, const std::map<std::string_view, std::string_view> &props)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
if (!props.empty()) {
|
||||||
|
for (auto [key, value] : props) {
|
||||||
|
// NOTE: append if necessary
|
||||||
|
// usage:
|
||||||
|
// sd_bus_message_append(msg,"s",key.data());
|
||||||
|
// sd_bus_message_open_container(msg,SD_BUS_TYPE_VARIANT,"[corresponding type]");
|
||||||
|
// append content....
|
||||||
|
// sd_bus_message_close_container(msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::string cmdParse(msg_ptr &msg, const std::vector<std::string_view> cmdLines)
|
||||||
|
{
|
||||||
|
std::string serviceName{"internalError"};
|
||||||
|
std::map<std::string_view, std::string_view> props;
|
||||||
|
for (auto str : cmdLines) { // avoid stl exception
|
||||||
|
if (str.size() < 2) {
|
||||||
|
sd_journal_print(LOG_WARNING, "invalid option %s.", str.data());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (str.substr(0, 2) != "--") {
|
||||||
|
sd_journal_print(LOG_INFO, "unknown option %s.", str.data());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto kvStr = str.substr(2);
|
||||||
|
if (!kvStr.empty()) [[likely]] {
|
||||||
|
auto it = kvStr.cbegin();
|
||||||
|
if (it = std::find(it, kvStr.cend(), '='); it == kvStr.cend()) {
|
||||||
|
sd_journal_print(LOG_WARNING, "invalid k-v pair: %s", kvStr.data());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
auto splitIndex = std::distance(kvStr.cbegin(), it);
|
||||||
|
if (++it == kvStr.cend()) {
|
||||||
|
sd_journal_print(LOG_WARNING, "invalid k-v pair: %s", kvStr.data());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto key = kvStr.substr(0, splitIndex);
|
||||||
|
if (key == "Type") { // type must be exec
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
props[key] = kvStr.substr(splitIndex + 1);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Processing of the binary file and its parameters that am want to launch
|
||||||
|
auto it = std::find(cmdLines.cbegin(), cmdLines.cend(), str);
|
||||||
|
std::vector execArgs(++it, cmdLines.cend());
|
||||||
|
if (execArgs.empty()) {
|
||||||
|
sd_journal_print(LOG_ERR, "param exec is empty.");
|
||||||
|
serviceName = "invalidInput";
|
||||||
|
return serviceName;
|
||||||
|
}
|
||||||
|
int ret;
|
||||||
|
if (props.find("unitName") == props.cend()) {
|
||||||
|
sd_journal_perror("unitName doesn't exists.");
|
||||||
|
serviceName = "invalidInput";
|
||||||
|
return serviceName;
|
||||||
|
}
|
||||||
|
if (ret = sd_bus_message_append(msg, "s", props["unitName"].data()); ret < 0) { // unitName
|
||||||
|
sd_journal_perror("append unitName failed.");
|
||||||
|
return serviceName;
|
||||||
|
} else {
|
||||||
|
serviceName = props["unitName"];
|
||||||
|
props.erase("unitName");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ret = sd_bus_message_append(msg, "s", "replace"); ret < 0) { // start mode
|
||||||
|
sd_journal_perror("append startMode failed.");
|
||||||
|
return serviceName;
|
||||||
|
}
|
||||||
|
|
||||||
|
// process properties: a(sv)
|
||||||
|
if (ret = sd_bus_message_open_container(msg, SD_BUS_TYPE_ARRAY, "(sv)"); ret < 0) {
|
||||||
|
sd_journal_perror("open array failed.");
|
||||||
|
return serviceName;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ret = sd_bus_message_append(msg, "(sv)", "Type", "s", "exec"); ret < 0) {
|
||||||
|
sd_journal_perror("append type failed.");
|
||||||
|
return serviceName;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ret = sd_bus_message_open_container(msg, SD_BUS_TYPE_STRUCT, "sv"); ret < 0) {
|
||||||
|
sd_journal_perror("open struct failed.");
|
||||||
|
return serviceName;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ret = processKVPair(msg, props); ret < 0) { // process props
|
||||||
|
serviceName = "invalidInput";
|
||||||
|
return serviceName;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ret = processExecStart(msg, execArgs); ret < 0) {
|
||||||
|
serviceName = "invalidInput";
|
||||||
|
return serviceName;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ret = sd_bus_message_close_container(msg); ret < 0) {
|
||||||
|
sd_journal_perror("close struct failed.");
|
||||||
|
return serviceName;
|
||||||
|
}
|
||||||
|
if (ret = sd_bus_message_close_container(msg); ret < 0) {
|
||||||
|
sd_journal_perror("close array failed.");
|
||||||
|
return serviceName;
|
||||||
|
}
|
||||||
|
|
||||||
|
// append aux, it's unused for now
|
||||||
|
if (ret = sd_bus_message_append(msg, "a(sa(sv))", 0); ret < 0) {
|
||||||
|
sd_journal_perror("append aux failed.");
|
||||||
|
return serviceName;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return serviceName;
|
||||||
|
}
|
||||||
|
|
||||||
|
int jobRemovedReceiver(sd_bus_message *m, void *userdata, sd_bus_error *ret_error)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
if (ret = sd_bus_error_is_set(ret_error); ret != 0) {
|
||||||
|
sd_journal_print(LOG_ERR, "JobRemoved error: [%s,%s]", ret_error->name, ret_error->message);
|
||||||
|
} else {
|
||||||
|
const char *serviceId{nullptr}, *jobResult{nullptr};
|
||||||
|
if (ret = sd_bus_message_read(m, "uoss", nullptr, nullptr, &serviceId, &jobResult); ret < 0) {
|
||||||
|
sd_journal_perror("read from JobRemoved failed.");
|
||||||
|
} else {
|
||||||
|
auto ptr = reinterpret_cast<JobRemoveResult *>(userdata);
|
||||||
|
if (ptr->id == serviceId) {
|
||||||
|
ptr->removedFlag = 1;
|
||||||
|
ptr->result = fromString(jobResult);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ret_error and ret_error->_need_free) {
|
||||||
|
sd_bus_error_free(ret_error);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int process_dbus_message(sd_bus *bus)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
ret = sd_bus_process(bus, nullptr);
|
||||||
|
if (ret < 0) {
|
||||||
|
sd_journal_print(LOG_ERR, "event loop error.");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ret > 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = sd_bus_wait(bus, std::numeric_limits<uint64_t>::max());
|
||||||
|
if (ret < 0) {
|
||||||
|
sd_journal_print(LOG_ERR, "sd-bus event loop error:%s", strerror(-ret));
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, const char *argv[])
|
||||||
|
{
|
||||||
|
sd_bus_error error{SD_BUS_ERROR_NULL};
|
||||||
|
sd_bus_message *msg{nullptr};
|
||||||
|
sd_bus *bus{nullptr};
|
||||||
|
std::string serviceId;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (ret = sd_bus_open_user(&bus); ret < 0) {
|
||||||
|
sd_journal_perror("Failed to connect to user bus.");
|
||||||
|
releaseRes(error, msg, bus, ExitCode::InternalError);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ret = sd_bus_message_new_method_call(
|
||||||
|
bus, &msg, SystemdService, SystemdObjectPath, SystemdInterfaceName, "StartTransientUnit");
|
||||||
|
ret < 0) {
|
||||||
|
sd_journal_perror("Failed to create D-Bus call message");
|
||||||
|
releaseRes(error, msg, bus, ExitCode::InternalError);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::string_view> args;
|
||||||
|
for (std::size_t i = 1; i < argc; ++i) {
|
||||||
|
args.emplace_back(argv[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
serviceId = cmdParse(msg, args);
|
||||||
|
if (serviceId == "internalError") {
|
||||||
|
releaseRes(error, msg, bus, ExitCode::InternalError);
|
||||||
|
} else if (serviceId == "invalidInput") {
|
||||||
|
releaseRes(error, msg, bus, ExitCode::InvalidInput);
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *path{nullptr};
|
||||||
|
JobRemoveResult resultData{serviceId};
|
||||||
|
|
||||||
|
if (ret = sd_bus_match_signal(
|
||||||
|
bus, nullptr, SystemdService, SystemdObjectPath, SystemdInterfaceName, "JobRemoved", jobRemovedReceiver, &resultData);
|
||||||
|
ret < 0) {
|
||||||
|
sd_journal_perror("add signal matcher failed.");
|
||||||
|
releaseRes(error, msg, bus, ExitCode::InternalError);
|
||||||
|
}
|
||||||
|
|
||||||
|
sd_bus_message *reply{nullptr};
|
||||||
|
if (ret = sd_bus_call(bus, msg, 0, &error, &reply); ret < 0) {
|
||||||
|
sd_bus_error_get_errno(&error);
|
||||||
|
sd_journal_print(LOG_ERR, "launch failed: [%s,%s]", error.name, error.message);
|
||||||
|
releaseRes(error, msg, bus, ExitCode::InternalError);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ret = sd_bus_message_read(reply, "o", &path); ret < 0) {
|
||||||
|
sd_journal_perror("failed to parse response message.");
|
||||||
|
sd_bus_message_unref(reply);
|
||||||
|
releaseRes(error, msg, bus, ExitCode::InternalError);
|
||||||
|
}
|
||||||
|
|
||||||
|
sd_bus_message_unref(reply);
|
||||||
|
|
||||||
|
while (true) { // wait for jobRemoved
|
||||||
|
int ret = process_dbus_message(bus);
|
||||||
|
if (ret < 0) {
|
||||||
|
releaseRes(error, msg, bus, ExitCode::InternalError);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (resultData.removedFlag) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
releaseRes(error, msg, bus, resultData.result);
|
||||||
|
}
|
@ -19,8 +19,13 @@ target_link_libraries(${LIB_NAME} PUBLIC
|
|||||||
)
|
)
|
||||||
|
|
||||||
add_executable(${BIN_NAME} main.cpp utils.cpp)
|
add_executable(${BIN_NAME} main.cpp utils.cpp)
|
||||||
|
|
||||||
target_link_libraries(${BIN_NAME} PRIVATE
|
target_link_libraries(${BIN_NAME} PRIVATE
|
||||||
${LIB_NAME}
|
${LIB_NAME}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
target_include_directories(${BIN_NAME} PRIVATE
|
||||||
|
${PROJECT_BINARY_DIR}
|
||||||
|
)
|
||||||
|
|
||||||
install(TARGETS ${BIN_NAME} DESTINATION ${CMAKE_INSTALL_BINDIR})
|
install(TARGETS ${BIN_NAME} DESTINATION ${CMAKE_INSTALL_BINDIR})
|
||||||
|
61
src/cgroupsidentifier.cpp
Normal file
61
src/cgroupsidentifier.cpp
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2023 UnionTech Software Technology Co., Ltd.
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||||
|
|
||||||
|
#include "cgroupsidentifier.h"
|
||||||
|
#include "global.h"
|
||||||
|
#include <QFile>
|
||||||
|
#include <QString>
|
||||||
|
#include <QDebug>
|
||||||
|
|
||||||
|
IdentifyRet CGroupsIdentifier::Identify(pid_t pid)
|
||||||
|
{
|
||||||
|
auto AppCgroupPath = QString("/proc/%1/cgroup").arg(pid);
|
||||||
|
QFile AppCgroupFile{AppCgroupPath};
|
||||||
|
if (!AppCgroupFile.open(QFile::ExistingOnly | QFile::ReadOnly | QFile::Text)) {
|
||||||
|
qWarning() << "open " << AppCgroupPath << "failed: " << AppCgroupFile.errorString();
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
auto appInstance = parseCGroupsPath(
|
||||||
|
AppCgroupFile.readAll().split(':').last().trimmed()); // FIXME: support CGroup version detection and multi-line parsing
|
||||||
|
auto appInstanceComponent = appInstance.split('@');
|
||||||
|
if (appInstanceComponent.count() != 2) {
|
||||||
|
qWarning() << "Application Instance format is invalid." << appInstanceComponent;
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
return {appInstanceComponent.first(), appInstanceComponent.last()};
|
||||||
|
}
|
||||||
|
|
||||||
|
QString CGroupsIdentifier::parseCGroupsPath(QString CGP) const noexcept
|
||||||
|
{
|
||||||
|
if (CGP.isEmpty()) {
|
||||||
|
qWarning() << "CGroupPath is empty.";
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
auto unescapedCGP = unescapeString(CGP);
|
||||||
|
auto CGPSlices = unescapedCGP.split('/');
|
||||||
|
auto curUID = getCurrentUID();
|
||||||
|
|
||||||
|
if (CGPSlices.first() != "user.slice") {
|
||||||
|
qWarning() << "unrecognized process.";
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
auto processUIDStr = CGPSlices[1].split('.').first().split('-').last();
|
||||||
|
|
||||||
|
if (processUIDStr.isEmpty()) {
|
||||||
|
qWarning() << "no uid found.";
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (auto processUID = processUIDStr.toInt(); processUID != getCurrentUID()) {
|
||||||
|
qWarning() << "process is not in CGroups of current user, ignore....";
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
auto appInstance = CGPSlices.last().split('.').first();
|
||||||
|
if (appInstance.isEmpty()) {
|
||||||
|
qWarning() << "get AppId failed.";
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
return appInstance;
|
||||||
|
}
|
@ -2,12 +2,27 @@
|
|||||||
//
|
//
|
||||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||||
#include "applicationmanager1service.h"
|
#include "applicationmanager1service.h"
|
||||||
#include "applicationadaptor.h"
|
#include "applicationmanager1adaptor.h"
|
||||||
#include "global.h"
|
#include <QFile>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
ApplicationManager1Service::~ApplicationManager1Service() = default;
|
ApplicationManager1Service::~ApplicationManager1Service() = default;
|
||||||
|
|
||||||
ApplicationManager1Service::ApplicationManager1Service() = default;
|
ApplicationManager1Service::ApplicationManager1Service(std::unique_ptr<Identifier> ptr, QDBusConnection &connection)
|
||||||
|
: m_identifier(std::move(ptr))
|
||||||
|
{
|
||||||
|
if (!connection.registerService(DDEApplicationManager1ServiceName)) {
|
||||||
|
qFatal() << connection.lastError();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!registerObjectToDBus(new ApplicationManager1Adaptor{this},
|
||||||
|
DDEApplicationManager1ObjectPath,
|
||||||
|
getDBusInterface<ApplicationManager1Adaptor>())) {
|
||||||
|
std::terminate();
|
||||||
|
}
|
||||||
|
|
||||||
|
m_jobManager.reset(new JobManager1Service(this));
|
||||||
|
}
|
||||||
|
|
||||||
QList<QDBusObjectPath> ApplicationManager1Service::list() const
|
QList<QDBusObjectPath> ApplicationManager1Service::list() const
|
||||||
{
|
{
|
||||||
@ -24,9 +39,18 @@ void ApplicationManager1Service::removeAllApplication()
|
|||||||
m_applicationList.clear();
|
m_applicationList.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
QDBusObjectPath ApplicationManager1Service::Application(const QString &id)
|
QDBusObjectPath ApplicationManager1Service::Application(const QString &id) const
|
||||||
{
|
{
|
||||||
// TODO: impl
|
auto ret = std::find_if(m_applicationList.cbegin(), m_applicationList.cend(), [&id](const auto &app) {
|
||||||
|
if (app->id() == id) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (ret != m_applicationList.cend()) {
|
||||||
|
return ret.key();
|
||||||
|
}
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -34,8 +58,52 @@ QString ApplicationManager1Service::Identify(const QDBusUnixFileDescriptor &pidf
|
|||||||
QDBusObjectPath &application,
|
QDBusObjectPath &application,
|
||||||
QDBusObjectPath &application_instance)
|
QDBusObjectPath &application_instance)
|
||||||
{
|
{
|
||||||
// TODO: impl
|
if (!pidfd.isValid()) {
|
||||||
return {};
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
QDBusObjectPath ApplicationManager1Service::Launch(const QString &id,
|
QDBusObjectPath ApplicationManager1Service::Launch(const QString &id,
|
||||||
@ -43,18 +111,13 @@ QDBusObjectPath ApplicationManager1Service::Launch(const QString &id,
|
|||||||
const QStringList &fields,
|
const QStringList &fields,
|
||||||
const QVariantMap &options)
|
const QVariantMap &options)
|
||||||
{
|
{
|
||||||
// TODO: impl reset of Launch
|
auto app = Application(id);
|
||||||
QString objectPath;
|
if (app.path().isEmpty()) {
|
||||||
if (id.contains('.')) {
|
qWarning() << "No such application.";
|
||||||
objectPath = id.split('.').first();
|
return {};
|
||||||
}
|
}
|
||||||
objectPath.prepend(DDEApplicationManager1ApplicationObjectPath);
|
const auto &value = m_applicationList.value(app);
|
||||||
QSharedPointer<ApplicationService> app{new ApplicationService{id}};
|
return value->Launch(actions, fields, options);
|
||||||
auto *ptr = app.data();
|
|
||||||
if (registerObjectToDbus(new ApplicationAdaptor(ptr), objectPath, getDBusInterface<ApplicationAdaptor>())) {
|
|
||||||
QDBusObjectPath path{objectPath};
|
|
||||||
m_applicationList.insert(path, app);
|
|
||||||
return path;
|
|
||||||
}
|
|
||||||
return {};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ApplicationManager1Service::UpdateApplicationInfo(const QStringList &update_path) {}
|
||||||
|
@ -9,14 +9,19 @@
|
|||||||
#include <QDBusObjectPath>
|
#include <QDBusObjectPath>
|
||||||
#include <QDBusUnixFileDescriptor>
|
#include <QDBusUnixFileDescriptor>
|
||||||
#include <QSharedPointer>
|
#include <QSharedPointer>
|
||||||
|
#include <QScopedPointer>
|
||||||
|
#include <memory>
|
||||||
#include <QMap>
|
#include <QMap>
|
||||||
|
#include "jobmanager1service.h"
|
||||||
#include "applicationservice.h"
|
#include "applicationservice.h"
|
||||||
|
#include "applicationadaptor.h"
|
||||||
|
#include "identifier.h"
|
||||||
|
|
||||||
class ApplicationManager1Service final : public QObject
|
class ApplicationManager1Service final : public QObject
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
ApplicationManager1Service();
|
explicit ApplicationManager1Service(std::unique_ptr<Identifier> ptr, QDBusConnection &connection);
|
||||||
~ApplicationManager1Service() override;
|
~ApplicationManager1Service() override;
|
||||||
ApplicationManager1Service(const ApplicationManager1Service &) = delete;
|
ApplicationManager1Service(const ApplicationManager1Service &) = delete;
|
||||||
ApplicationManager1Service(ApplicationManager1Service &&) = delete;
|
ApplicationManager1Service(ApplicationManager1Service &&) = delete;
|
||||||
@ -25,16 +30,35 @@ public:
|
|||||||
|
|
||||||
Q_PROPERTY(QList<QDBusObjectPath> List READ list)
|
Q_PROPERTY(QList<QDBusObjectPath> List READ list)
|
||||||
QList<QDBusObjectPath> list() const;
|
QList<QDBusObjectPath> list() const;
|
||||||
void addApplication(const QString &ID, const QStringList &actions, bool AutoStart = false);
|
template <typename T>
|
||||||
|
bool addApplication(T &&desktopFileSource)
|
||||||
|
{
|
||||||
|
QSharedPointer<ApplicationService> application{new ApplicationService{std::forward<T>(desktopFileSource), this}};
|
||||||
|
if (!application) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!registerObjectToDBus(new ApplicationAdaptor{application.data()},
|
||||||
|
application->m_applicationPath.path(),
|
||||||
|
getDBusInterface<ApplicationAdaptor>())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
m_applicationList.insert(application->m_applicationPath, application);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
bool removeOneApplication(const QDBusObjectPath &application);
|
bool removeOneApplication(const QDBusObjectPath &application);
|
||||||
void removeAllApplication();
|
void removeAllApplication();
|
||||||
|
|
||||||
|
JobManager1Service &jobManager() noexcept { return *m_jobManager; }
|
||||||
|
|
||||||
public Q_SLOTS:
|
public Q_SLOTS:
|
||||||
QDBusObjectPath Application(const QString &id);
|
QDBusObjectPath Application(const QString &id) const;
|
||||||
QString Identify(const QDBusUnixFileDescriptor &pidfd, QDBusObjectPath &application, QDBusObjectPath &application_instance);
|
QString Identify(const QDBusUnixFileDescriptor &pidfd, QDBusObjectPath &application, QDBusObjectPath &application_instance);
|
||||||
QDBusObjectPath Launch(const QString &id, const QString &action, const QStringList &fields, const QVariantMap &options);
|
QDBusObjectPath Launch(const QString &id, const QString &action, const QStringList &fields, const QVariantMap &options);
|
||||||
|
void UpdateApplicationInfo(const QStringList &update_path);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
std::unique_ptr<Identifier> m_identifier;
|
||||||
|
QScopedPointer<JobManager1Service> m_jobManager{nullptr};
|
||||||
QMap<QDBusObjectPath, QSharedPointer<ApplicationService>> m_applicationList;
|
QMap<QDBusObjectPath, QSharedPointer<ApplicationService>> m_applicationList;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -3,61 +3,220 @@
|
|||||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||||
|
|
||||||
#include "applicationservice.h"
|
#include "applicationservice.h"
|
||||||
#include "applicationadaptor.h"
|
#include "applicationmanager1service.h"
|
||||||
#include "instanceadaptor.h"
|
#include "instanceadaptor.h"
|
||||||
|
#include "pwd.h"
|
||||||
#include <QUuid>
|
#include <QUuid>
|
||||||
|
#include <QStringList>
|
||||||
|
#include <QList>
|
||||||
|
#include <QUrl>
|
||||||
|
#include <QRegularExpression>
|
||||||
|
#include <QProcess>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
ApplicationService::ApplicationService(QString ID)
|
ApplicationService::~ApplicationService()
|
||||||
: m_AutoStart(false)
|
|
||||||
, m_ID(std::move(ID))
|
|
||||||
, m_applicationPath(DDEApplicationManager1ApplicationObjectPath + m_ID)
|
|
||||||
{
|
{
|
||||||
|
m_desktopSource.destruct(m_isPersistence);
|
||||||
}
|
}
|
||||||
|
|
||||||
ApplicationService::~ApplicationService() = default;
|
|
||||||
|
|
||||||
QString ApplicationService::GetActionName(const QString &identifier, const QStringList &env)
|
QString ApplicationService::GetActionName(const QString &identifier, const QStringList &env)
|
||||||
{
|
{
|
||||||
// TODO: impl
|
const auto &supportedActions = actions();
|
||||||
return {};
|
if (supportedActions.isEmpty()) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
if (auto index = supportedActions.indexOf(identifier); index == -1) {
|
||||||
|
qWarning() << "can't find " << identifier << " in supported actions List.";
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto &actionHeader = QString{"%1%2"}.arg(DesktopFileActionKey, identifier);
|
||||||
|
const auto &actionName = m_entry->value(actionHeader, "Name");
|
||||||
|
if (!actionName) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
QString locale{""};
|
||||||
|
bool ok;
|
||||||
|
if (!env.isEmpty()) {
|
||||||
|
QString lcStr;
|
||||||
|
for (const auto &lc : env) {
|
||||||
|
if (lc.startsWith("LANG")) {
|
||||||
|
locale = lc.split('=').last();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lc.startsWith("LC_ALL")) {
|
||||||
|
locale = lc.split('=').last();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QLocale lc = locale.isEmpty() ? getUserLocale() : QLocale{locale};
|
||||||
|
|
||||||
|
const auto &name = actionName->toLocaleString(lc, ok);
|
||||||
|
if (!ok) {
|
||||||
|
qWarning() << "convert to locale string failed, dest locale:" << lc;
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
QDBusObjectPath ApplicationService::Launch(const QString &action, const QStringList &fields, const QVariantMap &options)
|
QDBusObjectPath ApplicationService::Launch(QString action, QStringList fields, QVariantMap options)
|
||||||
{
|
{
|
||||||
// TODO: impl launch app from systemd.
|
QString execStr;
|
||||||
QString objectPath{m_applicationPath.path() + "/" + QUuid::createUuid().toString()};
|
bool ok;
|
||||||
QSharedPointer<InstanceService> ins{new InstanceService{objectPath, ""}};
|
const auto &supportedActions = actions();
|
||||||
auto *ptr = ins.data();
|
|
||||||
if (registerObjectToDbus(new InstanceAdaptor(ptr), objectPath, getDBusInterface<InstanceAdaptor>())) {
|
while (!action.isEmpty() and !supportedActions.isEmpty()) { // break trick
|
||||||
m_Instances.insert(QDBusObjectPath{objectPath}, ins);
|
if (auto index = supportedActions.indexOf(action); index == -1) {
|
||||||
return QDBusObjectPath{objectPath};
|
qWarning() << "can't find " << action << " in supported actions List. application will use default action to launch.";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto &actionHeader = QString{"%1%2"}.arg(DesktopFileEntryKey, action);
|
||||||
|
const auto &actionExec = m_entry->value(actionHeader, "Exec");
|
||||||
|
if (!actionExec) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
execStr = actionExec->toString(ok);
|
||||||
|
if (!ok) {
|
||||||
|
qWarning() << "exec value to string failed, try default action.";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
return {};
|
|
||||||
|
if (execStr.isEmpty()) {
|
||||||
|
auto Actions = m_entry->value(DesktopFileEntryKey, "Exec");
|
||||||
|
if (!Actions) {
|
||||||
|
qWarning() << "application has isn't executable.";
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
execStr = Actions->toString(ok);
|
||||||
|
if (!ok) {
|
||||||
|
qWarning() << "default action value to string failed. abort...";
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto [bin, cmds, res] = unescapeExec(execStr, fields);
|
||||||
|
|
||||||
|
uid_t uid;
|
||||||
|
auto it = options.find("uid");
|
||||||
|
if (it != options.cend()) {
|
||||||
|
uid = it->toUInt(&ok);
|
||||||
|
if (!ok) {
|
||||||
|
qWarning() << "convert uid string to uint failed: " << *it;
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (uid != getCurrentUID()) {
|
||||||
|
cmds.prepend(userNameLookup(uid));
|
||||||
|
cmds.prepend("--user");
|
||||||
|
cmds.prepend("pkexec");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
uid = getCurrentUID();
|
||||||
|
}
|
||||||
|
|
||||||
|
cmds.prepend("--");
|
||||||
|
|
||||||
|
auto &jobManager = m_parent->jobManager();
|
||||||
|
|
||||||
|
return jobManager.addJob(
|
||||||
|
m_applicationPath.path(),
|
||||||
|
[this, binary = std::move(bin), commands = std::move(cmds)](QVariant variantValue) mutable -> QVariant {
|
||||||
|
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(
|
||||||
|
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 addOneInstance(instanceRandomUUID, m_applicationPath.path()); // TODO: pass correct Systemd Unit Path
|
||||||
|
}
|
||||||
|
|
||||||
|
int location{0};
|
||||||
|
location = commands.indexOf(R"(%f)");
|
||||||
|
if (location != -1) { // due to std::move, there only remove once
|
||||||
|
commands.remove(location);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto url = QUrl::fromUserInput(resourceFile);
|
||||||
|
if (!url.isValid()) { // if url is invalid, passing to launcher directly
|
||||||
|
auto scheme = url.scheme();
|
||||||
|
if (!scheme.isEmpty()) {
|
||||||
|
// TODO: resourceFile = processRemoteFile(resourceFile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// resourceFile must be available in the following contexts
|
||||||
|
auto tmp = commands;
|
||||||
|
tmp.insert(location, resourceFile);
|
||||||
|
auto instanceRandomUUID = QUuid::createUuid().toString(QUuid::Id128);
|
||||||
|
tmp.push_front(QString{R"(--unitName=DDE-%1@%2.service)"}.arg(this->id(), instanceRandomUUID));
|
||||||
|
QProcess process;
|
||||||
|
process.start(ApplicationLauncherBinary, tmp);
|
||||||
|
process.waitForFinished();
|
||||||
|
auto exitCode = process.exitCode();
|
||||||
|
if (exitCode != 0) {
|
||||||
|
qWarning() << "Launch Application Failed:" << binary << tmp;
|
||||||
|
}
|
||||||
|
return addOneInstance(instanceRandomUUID, m_applicationPath.path());
|
||||||
|
},
|
||||||
|
std::move(res));
|
||||||
}
|
}
|
||||||
|
|
||||||
QStringList ApplicationService::actions() const noexcept
|
QStringList ApplicationService::actions() const noexcept
|
||||||
{
|
{
|
||||||
return m_actions;
|
if (m_entry.isNull()) {
|
||||||
|
qWarning() << "desktop entry is empty, isPersistence:" << m_isPersistence;
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto &actions = m_entry->value(DesktopFileEntryKey, "Actions");
|
||||||
|
if (!actions) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ok{false};
|
||||||
|
const auto &str = actions->toString(ok);
|
||||||
|
if (!ok) {
|
||||||
|
qWarning() << "Actions convert to String failed.";
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
auto actionList = str.split(";");
|
||||||
|
actionList.removeAll("");
|
||||||
|
|
||||||
|
return actionList;
|
||||||
}
|
}
|
||||||
|
|
||||||
QStringList &ApplicationService::actionsRef() noexcept
|
QString ApplicationService::id() const noexcept
|
||||||
{
|
{
|
||||||
return m_actions;
|
if (m_isPersistence) {
|
||||||
}
|
return m_desktopSource.m_file.desktopId();
|
||||||
|
}
|
||||||
QString ApplicationService::iD() const noexcept
|
return {};
|
||||||
{
|
|
||||||
return m_ID;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
IconMap ApplicationService::icons() const
|
IconMap ApplicationService::icons() const
|
||||||
{
|
{
|
||||||
return m_Icons;
|
if (m_Icons) {
|
||||||
|
return m_Icons->icons();
|
||||||
|
}
|
||||||
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
IconMap &ApplicationService::iconsRef()
|
IconMap &ApplicationService::iconsRef()
|
||||||
{
|
{
|
||||||
return m_Icons;
|
return m_Icons->iconsRef();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ApplicationService::isAutoStart() const noexcept
|
bool ApplicationService::isAutoStart() const noexcept
|
||||||
@ -75,12 +234,168 @@ QList<QDBusObjectPath> ApplicationService::instances() const noexcept
|
|||||||
return m_Instances.keys();
|
return m_Instances.keys();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ApplicationService::removeOneInstance(const QDBusObjectPath &instance)
|
bool ApplicationService::addOneInstance(const QString &instanceId, const QString &application, const QString &systemdUnitPath)
|
||||||
{
|
{
|
||||||
return m_Instances.remove(instance) != 0;
|
auto service = new InstanceService{instanceId, application, systemdUnitPath};
|
||||||
|
auto adaptor = new InstanceAdaptor(service);
|
||||||
|
QString objectPath{DDEApplicationManager1InstanceObjectPath + instanceId};
|
||||||
|
|
||||||
|
if (registerObjectToDBus(adaptor, objectPath, getDBusInterface<InstanceAdaptor>())) {
|
||||||
|
m_Instances.insert(QDBusObjectPath{objectPath}, QSharedPointer<InstanceService>{service});
|
||||||
|
service->moveToThread(this->thread());
|
||||||
|
adaptor->moveToThread(this->thread());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
adaptor->deleteLater();
|
||||||
|
service->deleteLater();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ApplicationService::removeOneInstance(const QDBusObjectPath &instance)
|
||||||
|
{
|
||||||
|
m_Instances.remove(instance);
|
||||||
|
unregisterObjectFromDBus(instance.path());
|
||||||
}
|
}
|
||||||
|
|
||||||
void ApplicationService::removeAllInstance()
|
void ApplicationService::removeAllInstance()
|
||||||
{
|
{
|
||||||
m_Instances.clear();
|
for (const auto &instance : m_Instances.keys()) {
|
||||||
|
removeOneInstance(instance);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QDBusObjectPath ApplicationService::findInstance(const QString &instanceId) const
|
||||||
|
{
|
||||||
|
for (const auto &[path, ptr] : m_Instances.asKeyValueRange()) {
|
||||||
|
if (ptr->instanceId() == instanceId) {
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
QString ApplicationService::userNameLookup(uid_t uid)
|
||||||
|
{
|
||||||
|
auto pws = getpwuid(uid);
|
||||||
|
return pws->pw_name;
|
||||||
|
}
|
||||||
|
|
||||||
|
LaunchTask ApplicationService::unescapeExec(const QString &str, const QStringList &fields)
|
||||||
|
{
|
||||||
|
LaunchTask task;
|
||||||
|
QStringList execList = str.split(' ');
|
||||||
|
task.LaunchBin = execList.first();
|
||||||
|
|
||||||
|
QRegularExpression re{"%[fFuUickdDnNvm]"};
|
||||||
|
auto matcher = re.match(str);
|
||||||
|
if (!matcher.hasMatch()) {
|
||||||
|
task.command.append(std::move(execList));
|
||||||
|
return task;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto list = matcher.capturedTexts();
|
||||||
|
if (list.count() > 1) {
|
||||||
|
qWarning() << "invalid exec format, all filed code will be ignored.";
|
||||||
|
for (const auto &code : list) {
|
||||||
|
execList.removeOne(code);
|
||||||
|
}
|
||||||
|
task.command.append(std::move(execList));
|
||||||
|
return task;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto filesCode = list.first().back().toLatin1();
|
||||||
|
auto codeStr = QString(R"(%%1)").arg(filesCode);
|
||||||
|
auto location = execList.indexOf(codeStr);
|
||||||
|
|
||||||
|
switch (filesCode) {
|
||||||
|
case 'f': { // Defer to async job
|
||||||
|
task.command.append(std::move(execList));
|
||||||
|
for (auto &field : fields) {
|
||||||
|
task.Resources.emplace_back(std::move(field));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'u': {
|
||||||
|
execList.removeAt(location);
|
||||||
|
if (fields.count() > 1) {
|
||||||
|
qDebug() << R"(fields count is greater than one, %u will only take first element.)";
|
||||||
|
}
|
||||||
|
execList.insert(location, fields.first());
|
||||||
|
task.command.append(execList);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'F':
|
||||||
|
[[fallthrough]];
|
||||||
|
case 'U': {
|
||||||
|
execList.removeAt(location);
|
||||||
|
auto it = execList.begin() + location;
|
||||||
|
for (const auto &field : fields) {
|
||||||
|
it = execList.insert(it, field);
|
||||||
|
}
|
||||||
|
task.command.append(std::move(execList));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'i': {
|
||||||
|
execList.removeAt(location);
|
||||||
|
auto val = m_entry->value(DesktopFileEntryKey, "Icon");
|
||||||
|
if (!val) {
|
||||||
|
qDebug() << R"(Application Icons can't be found. %i will be ignored.)";
|
||||||
|
task.command.append(std::move(execList));
|
||||||
|
return task;
|
||||||
|
}
|
||||||
|
bool ok;
|
||||||
|
auto iconStr = val->toIconString(ok);
|
||||||
|
if (!ok) {
|
||||||
|
qDebug() << R"(Icons Convert to string failed. %i will be ignored.)";
|
||||||
|
task.command.append(std::move(execList));
|
||||||
|
return task;
|
||||||
|
}
|
||||||
|
auto it = execList.insert(location, iconStr);
|
||||||
|
execList.insert(it, "--icon");
|
||||||
|
task.command.append(std::move(execList));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'c': {
|
||||||
|
execList.removeAt(location);
|
||||||
|
auto val = m_entry->value(DesktopFileEntryKey, "Name");
|
||||||
|
if (!val) {
|
||||||
|
qDebug() << R"(Application Name can't be found. %c will be ignored.)";
|
||||||
|
task.command.append(std::move(execList));
|
||||||
|
return task;
|
||||||
|
}
|
||||||
|
bool ok;
|
||||||
|
auto NameStr = val->toLocaleString(getUserLocale(), ok);
|
||||||
|
if (!ok) {
|
||||||
|
qDebug() << R"(Name Convert to locale string failed. %c will be ignored.)";
|
||||||
|
task.command.append(std::move(execList));
|
||||||
|
return task;
|
||||||
|
}
|
||||||
|
execList.insert(location, NameStr);
|
||||||
|
task.command.append(std::move(execList));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'k': { // ignore all desktop file location for now.
|
||||||
|
execList.removeAt(location);
|
||||||
|
task.command.append(std::move(execList));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'd':
|
||||||
|
case 'D':
|
||||||
|
case 'n':
|
||||||
|
case 'N':
|
||||||
|
case 'v':
|
||||||
|
[[fallthrough]]; // Deprecated field codes should be removed from the command line and ignored.
|
||||||
|
case 'm': {
|
||||||
|
execList.removeAt(location);
|
||||||
|
task.command.append(std::move(execList));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (task.Resources.isEmpty()) {
|
||||||
|
task.Resources.emplace_back(QString{""}); // mapReduce should run once at least
|
||||||
|
}
|
||||||
|
|
||||||
|
return task;
|
||||||
}
|
}
|
||||||
|
@ -11,8 +11,14 @@
|
|||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QDBusUnixFileDescriptor>
|
#include <QDBusUnixFileDescriptor>
|
||||||
#include <QSharedPointer>
|
#include <QSharedPointer>
|
||||||
|
#include <QUuid>
|
||||||
|
#include <QTextStream>
|
||||||
|
#include <QFile>
|
||||||
#include "instanceservice.h"
|
#include "instanceservice.h"
|
||||||
#include "global.h"
|
#include "global.h"
|
||||||
|
#include "desktopentry.h"
|
||||||
|
#include "desktopicons.h"
|
||||||
|
#include "jobmanager1service.h"
|
||||||
|
|
||||||
class ApplicationService : public QObject
|
class ApplicationService : public QObject
|
||||||
{
|
{
|
||||||
@ -26,10 +32,9 @@ public:
|
|||||||
|
|
||||||
Q_PROPERTY(QStringList Actions READ actions CONSTANT)
|
Q_PROPERTY(QStringList Actions READ actions CONSTANT)
|
||||||
QStringList actions() const noexcept;
|
QStringList actions() const noexcept;
|
||||||
QStringList &actionsRef() noexcept;
|
|
||||||
|
|
||||||
Q_PROPERTY(QString ID READ iD CONSTANT)
|
Q_PROPERTY(QString ID READ id CONSTANT)
|
||||||
QString iD() const noexcept;
|
QString id() const noexcept;
|
||||||
|
|
||||||
Q_PROPERTY(IconMap Icons READ icons)
|
Q_PROPERTY(IconMap Icons READ icons)
|
||||||
IconMap icons() const;
|
IconMap icons() const;
|
||||||
@ -42,23 +47,93 @@ public:
|
|||||||
Q_PROPERTY(QList<QDBusObjectPath> Instances READ instances)
|
Q_PROPERTY(QList<QDBusObjectPath> Instances READ instances)
|
||||||
QList<QDBusObjectPath> instances() const noexcept;
|
QList<QDBusObjectPath> instances() const noexcept;
|
||||||
|
|
||||||
|
QDBusObjectPath findInstance(const QString &instanceId) const;
|
||||||
|
|
||||||
|
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 = "/");
|
||||||
void recoverInstances(const QList<QDBusObjectPath>) noexcept;
|
void recoverInstances(const QList<QDBusObjectPath>) noexcept;
|
||||||
bool removeOneInstance(const QDBusObjectPath &instance);
|
void removeOneInstance(const QDBusObjectPath &instance); // TODO: remove instance when app closed
|
||||||
void removeAllInstance();
|
void removeAllInstance();
|
||||||
|
|
||||||
public Q_SLOTS:
|
public Q_SLOTS:
|
||||||
QString GetActionName(const QString &identifier, const QStringList &env);
|
QString GetActionName(const QString &identifier, const QStringList &env);
|
||||||
QDBusObjectPath Launch(const QString &action, const QStringList &fields, const QVariantMap &options);
|
QDBusObjectPath Launch(QString action, QStringList fields, QVariantMap options);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
friend class ApplicationManager1Service;
|
friend class ApplicationManager1Service;
|
||||||
explicit ApplicationService(QString ID);
|
template <typename T>
|
||||||
bool m_AutoStart;
|
ApplicationService(T &&source, ApplicationManager1Service *parent)
|
||||||
QString m_ID;
|
: m_parent(parent)
|
||||||
|
, m_desktopSource(std::forward<T>(source))
|
||||||
|
{
|
||||||
|
static_assert(std::is_same_v<T, DesktopFile> or std::is_same_v<T, QString>, "param type must be QString or DesktopFile.");
|
||||||
|
QString objectPath{DDEApplicationManager1ApplicationObjectPath};
|
||||||
|
QTextStream sourceStream;
|
||||||
|
QFile sourceFile;
|
||||||
|
auto dbusAppid = m_desktopSource.m_file.desktopId();
|
||||||
|
|
||||||
|
if constexpr (std::is_same_v<T, DesktopFile>) {
|
||||||
|
m_applicationPath = QDBusObjectPath{objectPath + escapeToObjectPath(dbusAppid)};
|
||||||
|
m_isPersistence = true;
|
||||||
|
sourceFile.setFileName(m_desktopSource.m_file.filePath());
|
||||||
|
if (!sourceFile.open(QFile::ExistingOnly | QFile::ReadOnly | QFile::Text)) {
|
||||||
|
qCritical() << "desktop file can't open:" << m_desktopSource.m_file.filePath() << sourceFile.errorString();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
sourceStream.setDevice(&sourceFile);
|
||||||
|
} else if (std::is_same_v<T, QString>) {
|
||||||
|
m_applicationPath = QDBusObjectPath{objectPath + QUuid::createUuid().toString(QUuid::Id128)};
|
||||||
|
m_isPersistence = false;
|
||||||
|
sourceStream.setString(&m_desktopSource.m_temp, QTextStream::ReadOnly | QTextStream::Text);
|
||||||
|
}
|
||||||
|
m_entry.reset(new DesktopEntry());
|
||||||
|
if (auto error = m_entry->parse(sourceStream); error != ParseError::NoError) {
|
||||||
|
if (error != ParseError::EntryKeyInvalid) {
|
||||||
|
m_entry.reset(nullptr);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// TODO: icon lookup
|
||||||
|
}
|
||||||
|
|
||||||
|
bool m_AutoStart{false};
|
||||||
|
bool m_isPersistence;
|
||||||
|
ApplicationManager1Service *m_parent{nullptr};
|
||||||
QDBusObjectPath m_applicationPath;
|
QDBusObjectPath m_applicationPath;
|
||||||
QStringList m_actions;
|
QString m_launcher{ApplicationLauncherBinary};
|
||||||
|
union DesktopSource
|
||||||
|
{
|
||||||
|
template <typename T, std::enable_if_t<std::is_same_v<T, DesktopFile>, bool> = true>
|
||||||
|
DesktopSource(T &&source)
|
||||||
|
: m_file(std::forward<T>(source))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, std::enable_if_t<std::is_same_v<T, QString>, bool> = true>
|
||||||
|
DesktopSource(T &&source)
|
||||||
|
: m_temp(std::forward<T>(source))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void destruct(bool isPersistence)
|
||||||
|
{
|
||||||
|
if (isPersistence) {
|
||||||
|
m_file.~DesktopFile();
|
||||||
|
} else {
|
||||||
|
m_temp.~QString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
~DesktopSource(){};
|
||||||
|
QString m_temp;
|
||||||
|
DesktopFile m_file;
|
||||||
|
} m_desktopSource;
|
||||||
|
QSharedPointer<DesktopEntry> m_entry{nullptr};
|
||||||
|
QSharedPointer<DesktopIcons> m_Icons{nullptr};
|
||||||
QMap<QDBusObjectPath, QSharedPointer<InstanceService>> m_Instances;
|
QMap<QDBusObjectPath, QSharedPointer<InstanceService>> m_Instances;
|
||||||
IconMap m_Icons;
|
QString userNameLookup(uid_t uid);
|
||||||
|
[[nodiscard]] LaunchTask unescapeExec(const QString &str, const QStringList &fields);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -4,8 +4,9 @@
|
|||||||
|
|
||||||
#include "instanceservice.h"
|
#include "instanceservice.h"
|
||||||
|
|
||||||
InstanceService::InstanceService(QString application, QString systemdUnitPath)
|
InstanceService::InstanceService(QString instanceId, QString application, QString systemdUnitPath)
|
||||||
: m_Application(std::move(application))
|
: m_instanceId(std::move(instanceId))
|
||||||
|
, m_Application(std::move(application))
|
||||||
, m_SystemdUnitPath(std::move(systemdUnitPath))
|
, m_SystemdUnitPath(std::move(systemdUnitPath))
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
@ -18,15 +18,18 @@ public:
|
|||||||
InstanceService &operator=(const InstanceService &) = delete;
|
InstanceService &operator=(const InstanceService &) = delete;
|
||||||
InstanceService &operator=(InstanceService &&) = delete;
|
InstanceService &operator=(InstanceService &&) = delete;
|
||||||
|
|
||||||
Q_PROPERTY(QDBusObjectPath Application READ application CONSTANT)
|
Q_PROPERTY(QDBusObjectPath Application READ application)
|
||||||
QDBusObjectPath application() const;
|
QDBusObjectPath application() const;
|
||||||
|
|
||||||
Q_PROPERTY(QDBusObjectPath SystemdUnitPath READ systemdUnitPath CONSTANT)
|
Q_PROPERTY(QDBusObjectPath SystemdUnitPath READ systemdUnitPath)
|
||||||
QDBusObjectPath systemdUnitPath() const;
|
QDBusObjectPath systemdUnitPath() const;
|
||||||
|
|
||||||
|
const QString &instanceId() const noexcept { return m_instanceId; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
friend class ApplicationService;
|
friend class ApplicationService;
|
||||||
InstanceService(QString application, QString systemdUnitPath);
|
InstanceService(QString instanceId, QString application, QString systemdUnitPath);
|
||||||
|
QString m_instanceId;
|
||||||
const QDBusObjectPath m_Application;
|
const QDBusObjectPath m_Application;
|
||||||
const QDBusObjectPath m_SystemdUnitPath;
|
const QDBusObjectPath m_SystemdUnitPath;
|
||||||
};
|
};
|
||||||
|
@ -3,7 +3,38 @@
|
|||||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||||
|
|
||||||
#include "jobmanager1service.h"
|
#include "jobmanager1service.h"
|
||||||
|
#include "jobmanager1adaptor.h"
|
||||||
|
|
||||||
JobManager1Service::JobManager1Service() = default;
|
LaunchTask::LaunchTask()
|
||||||
|
{
|
||||||
|
qRegisterMetaType<LaunchTask>();
|
||||||
|
}
|
||||||
|
|
||||||
|
JobManager1Service::JobManager1Service(ApplicationManager1Service *parent)
|
||||||
|
: m_parent(parent)
|
||||||
|
{
|
||||||
|
if (!registerObjectToDBus(
|
||||||
|
new JobManager1Adaptor{this}, DDEApplicationManager1JobManagerObjectPath, getDBusInterface<JobManager1Adaptor>())) {
|
||||||
|
std::terminate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
JobManager1Service::~JobManager1Service() = default;
|
JobManager1Service::~JobManager1Service() = default;
|
||||||
|
|
||||||
|
bool JobManager1Service::removeOneJob(const QDBusObjectPath &path)
|
||||||
|
{
|
||||||
|
decltype(m_jobs)::size_type removeCount{0};
|
||||||
|
{
|
||||||
|
QMutexLocker locker{&m_mutex};
|
||||||
|
removeCount = m_jobs.remove(path);
|
||||||
|
}
|
||||||
|
// removeCount means m_jobs can't find value which corresponding with path
|
||||||
|
// and we shouldn't emit jobRemoved signal because this signal may already has been emit
|
||||||
|
if (removeCount == 0) {
|
||||||
|
qWarning() << "Job already has been removed: " << path.path();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
unregisterObjectFromDBus(path.path());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
@ -14,14 +14,30 @@
|
|||||||
#include <QtConcurrent>
|
#include <QtConcurrent>
|
||||||
#include <QFuture>
|
#include <QFuture>
|
||||||
#include <QUuid>
|
#include <QUuid>
|
||||||
#include "jobadaptor.h"
|
|
||||||
#include "global.h"
|
#include "global.h"
|
||||||
|
#include "jobadaptor.h"
|
||||||
|
|
||||||
|
class ApplicationManager1Service;
|
||||||
|
|
||||||
|
struct LaunchTask
|
||||||
|
{
|
||||||
|
LaunchTask();
|
||||||
|
~LaunchTask() = default;
|
||||||
|
LaunchTask(const LaunchTask &) = default;
|
||||||
|
LaunchTask(LaunchTask &&) = default;
|
||||||
|
LaunchTask &operator=(const LaunchTask &) = default;
|
||||||
|
LaunchTask &operator=(LaunchTask &&) = default;
|
||||||
|
QString LaunchBin;
|
||||||
|
QStringList command;
|
||||||
|
QVariantList Resources;
|
||||||
|
};
|
||||||
|
|
||||||
|
Q_DECLARE_METATYPE(LaunchTask)
|
||||||
|
|
||||||
class JobManager1Service final : public QObject
|
class JobManager1Service final : public QObject
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
JobManager1Service();
|
|
||||||
JobManager1Service(const JobManager1Service &) = delete;
|
JobManager1Service(const JobManager1Service &) = delete;
|
||||||
JobManager1Service(JobManager1Service &&) = delete;
|
JobManager1Service(JobManager1Service &&) = delete;
|
||||||
JobManager1Service &operator=(const JobManager1Service &) = delete;
|
JobManager1Service &operator=(const JobManager1Service &) = delete;
|
||||||
@ -29,48 +45,48 @@ public:
|
|||||||
|
|
||||||
~JobManager1Service() override;
|
~JobManager1Service() override;
|
||||||
template <typename F>
|
template <typename F>
|
||||||
void addJob(const QDBusObjectPath &source, F func, QVariantList args)
|
QDBusObjectPath addJob(QString source, F func, QVariantList args)
|
||||||
{
|
{
|
||||||
static_assert(std::is_invocable_v<F, QVariant>, "param type must be QVariant.");
|
static_assert(std::is_invocable_v<F, QVariant>, "param type must be QVariant.");
|
||||||
|
|
||||||
QString objectPath{DDEApplicationManager1JobObjectPath + QUuid::createUuid().toString(QUuid::Id128)};
|
QString objectPath{DDEApplicationManager1JobObjectPath + QUuid::createUuid().toString(QUuid::Id128)};
|
||||||
auto future = QtConcurrent::mappedReduced(args.begin(),
|
QFuture<QVariantList> future = QtConcurrent::mappedReduced(std::move(args),
|
||||||
args.end(),
|
func,
|
||||||
func,
|
qOverload<QVariantList::parameter_type>(&QVariantList::append),
|
||||||
qOverload<QVariantList::parameter_type>(&QVariantList::append),
|
QVariantList{},
|
||||||
QVariantList{},
|
QtConcurrent::ReduceOption::OrderedReduce);
|
||||||
QtConcurrent::ReduceOption::OrderedReduce);
|
|
||||||
QSharedPointer<JobService> job{new JobService{future}};
|
QSharedPointer<JobService> job{new JobService{future}};
|
||||||
|
if (!registerObjectToDBus(new JobAdaptor(job.data()), objectPath, getDBusInterface<JobAdaptor>())) {
|
||||||
|
qCritical() << "can't register job to dbus.";
|
||||||
|
future.cancel();
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
auto path = QDBusObjectPath{objectPath};
|
auto path = QDBusObjectPath{objectPath};
|
||||||
{
|
{
|
||||||
QMutexLocker locker{&m_mutex};
|
QMutexLocker locker{&m_mutex};
|
||||||
m_jobs.insert(path, job); // Insertion is always successful
|
m_jobs.insert(path, std::move(job)); // Insertion is always successful
|
||||||
}
|
}
|
||||||
emit JobNew(path, source);
|
emit JobNew(path, QDBusObjectPath{source});
|
||||||
registerObjectToDbus(new JobAdaptor(job.data()), objectPath, getDBusInterface<JobAdaptor>());
|
|
||||||
auto emitRemove = [this, job, path, future](QVariantList value) {
|
auto emitRemove = [this, job, path, future](QVariantList value) {
|
||||||
decltype(m_jobs)::size_type removeCount{0};
|
if (!removeOneJob(path)) {
|
||||||
{
|
|
||||||
QMutexLocker locker{&m_mutex};
|
|
||||||
removeCount = m_jobs.remove(path);
|
|
||||||
}
|
|
||||||
// removeCount means m_jobs can't find value which corresponding with path
|
|
||||||
// and we shouldn't emit jobRemoved signal because this signal may already has been emit
|
|
||||||
if (removeCount == 0) {
|
|
||||||
qCritical() << "Job already has been removed: " << path.path();
|
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString result{job->status()};
|
QString result{job->status()};
|
||||||
for (const auto &val : future.result()) {
|
for (const auto &val : future.result()) {
|
||||||
if (val.template canConvert<QDBusError>()) {
|
if (val.canConvert<QDBusError>()) {
|
||||||
result = "failed";
|
result = "failed";
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
emit this->JobRemoved(path, result, future.result());
|
emit JobRemoved(path, result, future.result());
|
||||||
return value;
|
return value;
|
||||||
};
|
};
|
||||||
future.then(emitRemove);
|
|
||||||
|
future.then(this, emitRemove);
|
||||||
|
return path;
|
||||||
}
|
}
|
||||||
|
|
||||||
Q_SIGNALS:
|
Q_SIGNALS:
|
||||||
@ -78,8 +94,12 @@ Q_SIGNALS:
|
|||||||
void JobRemoved(const QDBusObjectPath &job, const QString &status, const QVariantList &result);
|
void JobRemoved(const QDBusObjectPath &job, const QString &status, const QVariantList &result);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
bool removeOneJob(const QDBusObjectPath &path);
|
||||||
|
friend class ApplicationManager1Service;
|
||||||
|
JobManager1Service(ApplicationManager1Service *parent);
|
||||||
QMutex m_mutex;
|
QMutex m_mutex;
|
||||||
QMap<QDBusObjectPath, QSharedPointer<JobService>> m_jobs;
|
QMap<QDBusObjectPath, QSharedPointer<JobService>> m_jobs;
|
||||||
|
ApplicationManager1Service *m_parent{nullptr};
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -24,8 +24,10 @@ auto DesktopEntry::parserGroupHeader(const QString &str) noexcept
|
|||||||
|
|
||||||
ParseError DesktopEntry::parseEntry(const QString &str, decltype(m_entryMap)::iterator ¤tGroup) noexcept
|
ParseError DesktopEntry::parseEntry(const QString &str, decltype(m_entryMap)::iterator ¤tGroup) noexcept
|
||||||
{
|
{
|
||||||
if (str.startsWith("#"))
|
if (str.startsWith("#")) {
|
||||||
return ParseError::NoError;
|
return ParseError::NoError;
|
||||||
|
}
|
||||||
|
|
||||||
auto splitCharIndex = str.indexOf(']');
|
auto splitCharIndex = str.indexOf(']');
|
||||||
if (splitCharIndex != -1) {
|
if (splitCharIndex != -1) {
|
||||||
for (; splitCharIndex < str.size(); ++splitCharIndex) {
|
for (; splitCharIndex < str.size(); ++splitCharIndex) {
|
||||||
@ -60,7 +62,6 @@ ParseError DesktopEntry::parseEntry(const QString &str, decltype(m_entryMap)::it
|
|||||||
if (auto locale = matcher.captured("LOCALE"); !locale.isEmpty()) {
|
if (auto locale = matcher.captured("LOCALE"); !locale.isEmpty()) {
|
||||||
valueKey = locale;
|
valueKey = locale;
|
||||||
}
|
}
|
||||||
qDebug() << valueKey << valueStr;
|
|
||||||
if (auto cur = currentGroup->find(key); cur == currentGroup->end()) {
|
if (auto cur = currentGroup->find(key); cur == currentGroup->end()) {
|
||||||
currentGroup->insert(keyStr, {{valueKey, valueStr}});
|
currentGroup->insert(keyStr, {{valueKey, valueStr}});
|
||||||
return ParseError::NoError;
|
return ParseError::NoError;
|
||||||
@ -93,8 +94,10 @@ std::optional<DesktopFile> DesktopFile::searchDesktopFile(const QString &desktop
|
|||||||
qDebug() << "Current Application Dirs:" << XDGDataDirs;
|
qDebug() << "Current Application Dirs:" << XDGDataDirs;
|
||||||
for (const auto &d : XDGDataDirs) {
|
for (const auto &d : XDGDataDirs) {
|
||||||
auto dirPath = QDir::cleanPath(d);
|
auto dirPath = QDir::cleanPath(d);
|
||||||
QDirIterator it{
|
QDirIterator it{dirPath,
|
||||||
dirPath, {desktopFile}, QDir::AllEntries | QDir::NoSymLinks | QDir::NoDotAndDotDot, QDirIterator::Subdirectories};
|
{desktopFile},
|
||||||
|
QDir::Files | QDir::NoSymLinks | QDir::NoDotAndDotDot | QDir::Readable,
|
||||||
|
QDirIterator::Subdirectories};
|
||||||
if (it.hasNext()) {
|
if (it.hasNext()) {
|
||||||
path = it.next();
|
path = it.next();
|
||||||
break;
|
break;
|
||||||
@ -107,8 +110,8 @@ std::optional<DesktopFile> DesktopFile::searchDesktopFile(const QString &desktop
|
|||||||
err = ParseError::NotFound;
|
err = ParseError::NotFound;
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
auto tmp = path.chopped(8); // remove ".desktop"
|
||||||
auto components = path.split(QDir::separator()).toList();
|
auto components = tmp.split(QDir::separator()).toList();
|
||||||
auto it = std::find(components.cbegin(), components.cend(), "applications");
|
auto it = std::find(components.cbegin(), components.cend(), "applications");
|
||||||
if (it == components.cend()) {
|
if (it == components.cend()) {
|
||||||
qWarning() << "custom location detected, Id wouldn't be generated.";
|
qWarning() << "custom location detected, Id wouldn't be generated.";
|
||||||
@ -117,12 +120,24 @@ std::optional<DesktopFile> DesktopFile::searchDesktopFile(const QString &desktop
|
|||||||
++it;
|
++it;
|
||||||
while (it != components.cend())
|
while (it != components.cend())
|
||||||
FileId += (*(it++) + "-");
|
FileId += (*(it++) + "-");
|
||||||
id = FileId.chopped(1);
|
id = FileId.chopped(1); // remove extra "-""
|
||||||
}
|
}
|
||||||
err = ParseError::NoError;
|
err = ParseError::NoError;
|
||||||
return DesktopFile{std::move(path), std::move(id)};
|
return DesktopFile{std::move(path), std::move(id)};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ParseError DesktopEntry::parse(const DesktopFile &desktopFile) noexcept
|
||||||
|
{
|
||||||
|
auto file = QFile(desktopFile.filePath());
|
||||||
|
if (!file.open(QFile::ExistingOnly | QFile::ReadOnly | QFile::Text)) {
|
||||||
|
qWarning() << desktopFile.filePath() << "can't open.";
|
||||||
|
return ParseError::OpenFailed;
|
||||||
|
}
|
||||||
|
|
||||||
|
QTextStream in{&file};
|
||||||
|
return parse(in);
|
||||||
|
}
|
||||||
|
|
||||||
ParseError DesktopEntry::parse(QTextStream &stream) noexcept
|
ParseError DesktopEntry::parse(QTextStream &stream) noexcept
|
||||||
{
|
{
|
||||||
if (stream.atEnd())
|
if (stream.atEnd())
|
||||||
@ -135,6 +150,10 @@ ParseError DesktopEntry::parse(QTextStream &stream) noexcept
|
|||||||
while (!stream.atEnd()) {
|
while (!stream.atEnd()) {
|
||||||
auto line = stream.readLine().trimmed();
|
auto line = stream.readLine().trimmed();
|
||||||
|
|
||||||
|
if (line.isEmpty()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (line.startsWith("[")) {
|
if (line.startsWith("[")) {
|
||||||
if (!line.endsWith("]"))
|
if (!line.endsWith("]"))
|
||||||
return ParseError::GroupHeaderInvalid;
|
return ParseError::GroupHeaderInvalid;
|
||||||
@ -150,11 +169,27 @@ ParseError DesktopEntry::parse(QTextStream &stream) noexcept
|
|||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
QMap<QString, DesktopEntry::Value> DesktopEntry::group(const QString &key) const noexcept
|
std::optional<QMap<QString, DesktopEntry::Value>> DesktopEntry::group(const QString &key) const noexcept
|
||||||
{
|
{
|
||||||
if (auto group = m_entryMap.find(key); group != m_entryMap.cend())
|
if (auto group = m_entryMap.find(key); group != m_entryMap.cend())
|
||||||
return *group;
|
return *group;
|
||||||
return {};
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<DesktopEntry::Value> DesktopEntry::value(const QString &groupKey, const QString &valueKey) const noexcept
|
||||||
|
{
|
||||||
|
const auto &destGroup = group(groupKey);
|
||||||
|
if (!destGroup) {
|
||||||
|
qWarning() << "group " << groupKey << " can't be found.";
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto it = destGroup->find(valueKey);
|
||||||
|
if (it == destGroup->cend()) {
|
||||||
|
qWarning() << "value " << valueKey << " can't be found.";
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
return *it;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString DesktopEntry::Value::unescape(const QString &str) const noexcept
|
QString DesktopEntry::Value::unescape(const QString &str) const noexcept
|
||||||
|
0
src/desktopicons.cpp
Normal file
0
src/desktopicons.cpp
Normal file
19
src/include/cgroupsidentifier.h
Normal file
19
src/include/cgroupsidentifier.h
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2023 UnionTech Software Technology Co., Ltd.
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||||
|
|
||||||
|
#ifndef CGROUPSIDENTIFIER_H
|
||||||
|
#define CGROUPSIDENTIFIER_H
|
||||||
|
|
||||||
|
#include "identifier.h"
|
||||||
|
|
||||||
|
class CGroupsIdentifier : public Identifier
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
IdentifyRet Identify(pid_t pid) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
[[nodiscard]] QString parseCGroupsPath(QString path) const noexcept;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
@ -13,6 +13,29 @@ constexpr static auto defaultKeyStr = "default";
|
|||||||
|
|
||||||
enum class ParseError { NoError, NotFound, MismatchedFile, InvalidLocation, OpenFailed, GroupHeaderInvalid, EntryKeyInvalid };
|
enum class ParseError { NoError, NotFound, MismatchedFile, InvalidLocation, OpenFailed, GroupHeaderInvalid, EntryKeyInvalid };
|
||||||
|
|
||||||
|
struct DesktopFile
|
||||||
|
{
|
||||||
|
DesktopFile(const DesktopFile &) = default;
|
||||||
|
DesktopFile(DesktopFile &&) = default;
|
||||||
|
DesktopFile &operator=(const DesktopFile &) = default;
|
||||||
|
DesktopFile &operator=(DesktopFile &&) = default;
|
||||||
|
~DesktopFile() = default;
|
||||||
|
|
||||||
|
const QString &filePath() const { return m_filePath; }
|
||||||
|
const QString &desktopId() const { return m_desktopId; }
|
||||||
|
|
||||||
|
static std::optional<DesktopFile> searchDesktopFile(const QString &desktopFilePath, ParseError &err) noexcept;
|
||||||
|
|
||||||
|
private:
|
||||||
|
DesktopFile(QString &&path, QString &&fileId)
|
||||||
|
: m_filePath(std::move(path))
|
||||||
|
, m_desktopId(std::move(fileId))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
QString m_filePath{""};
|
||||||
|
QString m_desktopId{""};
|
||||||
|
};
|
||||||
|
|
||||||
class DesktopEntry
|
class DesktopEntry
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@ -33,39 +56,17 @@ public:
|
|||||||
|
|
||||||
DesktopEntry() = default;
|
DesktopEntry() = default;
|
||||||
~DesktopEntry() = default;
|
~DesktopEntry() = default;
|
||||||
|
[[nodiscard]] ParseError parse(const DesktopFile &file) noexcept;
|
||||||
[[nodiscard]] ParseError parse(QTextStream &stream) noexcept;
|
[[nodiscard]] ParseError parse(QTextStream &stream) noexcept;
|
||||||
[[nodiscard]] QMap<QString, Value> group(const QString &key) const noexcept;
|
[[nodiscard]] std::optional<QMap<QString, Value>> group(const QString &key) const noexcept;
|
||||||
|
[[nodiscard]] std::optional<Value> value(const QString &key, const QString &valueKey) const noexcept;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QMap<QString, QMap<QString, Value>> m_entryMap;
|
QMap<QString, QMap<QString, Value>> m_entryMap;
|
||||||
|
|
||||||
auto parserGroupHeader(const QString &str) noexcept;
|
auto parserGroupHeader(const QString &str) noexcept;
|
||||||
ParseError parseEntry(const QString &str, decltype(m_entryMap)::iterator ¤tGroup) noexcept;
|
ParseError parseEntry(const QString &str, decltype(m_entryMap)::iterator ¤tGroup) noexcept;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct DesktopFile
|
|
||||||
{
|
|
||||||
DesktopFile(const DesktopFile &) = default;
|
|
||||||
DesktopFile(DesktopFile &&) = default;
|
|
||||||
DesktopFile &operator=(const DesktopFile &) = default;
|
|
||||||
DesktopFile &operator=(DesktopFile &&) = default;
|
|
||||||
~DesktopFile() = default;
|
|
||||||
|
|
||||||
const QString &filePath() const { return m_filePath; }
|
|
||||||
const QString &desktopId() const { return m_desktopId; }
|
|
||||||
|
|
||||||
static std::optional<DesktopFile> searchDesktopFile(const QString &desktopFilePath, ParseError &err) noexcept;
|
|
||||||
|
|
||||||
private:
|
|
||||||
DesktopFile(QString &&path, QString &&fileId)
|
|
||||||
: m_filePath(std::move(path))
|
|
||||||
, m_desktopId(std::move(fileId))
|
|
||||||
{
|
|
||||||
}
|
|
||||||
QString m_filePath;
|
|
||||||
QString m_desktopId;
|
|
||||||
};
|
|
||||||
|
|
||||||
QDebug operator<<(QDebug debug, const DesktopEntry::Value &v);
|
QDebug operator<<(QDebug debug, const DesktopEntry::Value &v);
|
||||||
|
|
||||||
QDebug operator<<(QDebug debug, const ParseError &v);
|
QDebug operator<<(QDebug debug, const ParseError &v);
|
||||||
|
21
src/include/desktopicons.h
Normal file
21
src/include/desktopicons.h
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2023 UnionTech Software Technology Co., Ltd.
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||||
|
|
||||||
|
#ifndef DESKTOPICONS_H
|
||||||
|
#define DESKTOPICONS_H
|
||||||
|
|
||||||
|
#include "global.h"
|
||||||
|
|
||||||
|
class DesktopIcons
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
DesktopIcons() = default;
|
||||||
|
const IconMap &icons() const noexcept { return m_icons; }
|
||||||
|
IconMap &iconsRef() noexcept { return m_icons; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
IconMap m_icons;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
@ -13,14 +13,27 @@
|
|||||||
#include <QDBusError>
|
#include <QDBusError>
|
||||||
#include <QMetaType>
|
#include <QMetaType>
|
||||||
#include <QMetaClassInfo>
|
#include <QMetaClassInfo>
|
||||||
|
#include <QLocale>
|
||||||
|
#include <QRegularExpression>
|
||||||
|
#include <QDBusObjectPath>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
using IconMap = QMap<QString, QMap<uint, QMap<QString, QDBusUnixFileDescriptor>>>;
|
using IconMap = QMap<QString, QMap<uint, QMap<QString, QDBusUnixFileDescriptor>>>;
|
||||||
|
|
||||||
constexpr auto DDEApplicationManager1ServiceName = u8"org.deepin.dde.ApplicationManager1";
|
constexpr auto DDEApplicationManager1ServiceName = u8"org.deepin.dde.ApplicationManager1";
|
||||||
constexpr auto DDEApplicationManager1ObjectPath = u8"/org/deepin/dde/ApplicationManager1";
|
constexpr auto DDEApplicationManager1ObjectPath = u8"/org/deepin/dde/ApplicationManager1";
|
||||||
constexpr auto DDEApplicationManager1ApplicationObjectPath = u8"/org/deepin/dde/ApplicationManager1/Application/";
|
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 DDEApplicationManager1JobManagerObjectPath = u8"/org/deepin/dde/ApplicationManager1/JobManager1";
|
||||||
constexpr auto DDEApplicationManager1JobObjectPath = u8"/org/deepin/dde/ApplicationManager1/JobManager1/Job/";
|
constexpr auto DDEApplicationManager1JobObjectPath = u8"/org/deepin/dde/ApplicationManager1/JobManager1/Job/";
|
||||||
|
constexpr auto DesktopFileEntryKey = u8"Desktop Entry";
|
||||||
|
constexpr auto DesktopFileActionKey = u8"Desktop Action ";
|
||||||
|
constexpr auto ApplicationLauncherBinary =
|
||||||
|
u8"/home/heyuming/workspace/dde-application-manager/build/plugin/appLauncher/am-launcher";
|
||||||
|
constexpr auto ApplicationManagerDBusName = u8"deepin_application_manager_bus";
|
||||||
|
|
||||||
|
enum class DBusType { Session = QDBusConnection::SessionBus, System = QDBusConnection::SystemBus, Custom };
|
||||||
|
|
||||||
class ApplicationManager1DBus
|
class ApplicationManager1DBus
|
||||||
{
|
{
|
||||||
@ -30,15 +43,52 @@ public:
|
|||||||
ApplicationManager1DBus &operator=(const ApplicationManager1DBus &) = delete;
|
ApplicationManager1DBus &operator=(const ApplicationManager1DBus &) = delete;
|
||||||
ApplicationManager1DBus &operator=(ApplicationManager1DBus &&) = delete;
|
ApplicationManager1DBus &operator=(ApplicationManager1DBus &&) = delete;
|
||||||
const QString &BusAddress() { return m_busAddress; }
|
const QString &BusAddress() { return m_busAddress; }
|
||||||
void setBusAddress(const QString &address) { m_busAddress = address; }
|
void init(DBusType type, const QString &busAddress = "")
|
||||||
QDBusConnection &CustomBus()
|
|
||||||
{
|
{
|
||||||
static auto con = QDBusConnection::connectToBus(m_busAddress, "org.deepin.dde.ApplicationManager1");
|
if (m_initFlag) {
|
||||||
if (!con.isConnected()) {
|
return;
|
||||||
qWarning() << con.lastError();
|
|
||||||
std::terminate();
|
|
||||||
}
|
}
|
||||||
return con;
|
|
||||||
|
m_busAddress = busAddress;
|
||||||
|
m_type = type;
|
||||||
|
m_initFlag = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QDBusConnection &globalBus()
|
||||||
|
{
|
||||||
|
if (m_connection.has_value()) {
|
||||||
|
return m_connection.value();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!m_initFlag) {
|
||||||
|
qFatal() << "invoke init at first.";
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (m_type) {
|
||||||
|
case DBusType::Session:
|
||||||
|
[[fallthrough]];
|
||||||
|
case DBusType::System: {
|
||||||
|
m_connection.emplace(
|
||||||
|
QDBusConnection::connectToBus(static_cast<QDBusConnection::BusType>(m_type), ApplicationManagerDBusName));
|
||||||
|
if (!m_connection->isConnected()) {
|
||||||
|
qFatal() << m_connection->lastError();
|
||||||
|
}
|
||||||
|
return m_connection.value();
|
||||||
|
}
|
||||||
|
case DBusType::Custom: {
|
||||||
|
if (m_busAddress.isEmpty()) {
|
||||||
|
qFatal() << "connect to custom dbus must init this object by custom dbus address";
|
||||||
|
}
|
||||||
|
m_connection.emplace(
|
||||||
|
QDBusConnection::connectToBus(static_cast<QDBusConnection::BusType>(m_type), ApplicationManagerDBusName));
|
||||||
|
if (!m_connection->isConnected()) {
|
||||||
|
qFatal() << m_connection->lastError();
|
||||||
|
}
|
||||||
|
return m_connection.value();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Q_UNREACHABLE();
|
||||||
}
|
}
|
||||||
static ApplicationManager1DBus &instance()
|
static ApplicationManager1DBus &instance()
|
||||||
{
|
{
|
||||||
@ -49,10 +99,14 @@ public:
|
|||||||
private:
|
private:
|
||||||
ApplicationManager1DBus() = default;
|
ApplicationManager1DBus() = default;
|
||||||
~ApplicationManager1DBus() = default;
|
~ApplicationManager1DBus() = default;
|
||||||
|
bool m_initFlag;
|
||||||
|
DBusType m_type;
|
||||||
QString m_busAddress;
|
QString m_busAddress;
|
||||||
|
std::optional<QDBusConnection> m_connection{std::nullopt};
|
||||||
};
|
};
|
||||||
|
|
||||||
bool registerObjectToDbus(QObject *o, const QString &path, const QString &interface);
|
bool registerObjectToDBus(QObject *o, const QString &path, const QString &interface);
|
||||||
|
void unregisterObjectFromDBus(const QString &path);
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
QString getDBusInterface()
|
QString getDBusInterface()
|
||||||
@ -62,7 +116,71 @@ QString getDBusInterface()
|
|||||||
if (auto infoIndex = infoObject->indexOfClassInfo("D-Bus Interface"); infoIndex != -1) {
|
if (auto infoIndex = infoObject->indexOfClassInfo("D-Bus Interface"); infoIndex != -1) {
|
||||||
return infoObject->classInfo(infoIndex).value();
|
return infoObject->classInfo(infoIndex).value();
|
||||||
}
|
}
|
||||||
|
qWarning() << "no interface found.";
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline uid_t getCurrentUID()
|
||||||
|
{
|
||||||
|
return getuid(); // current use linux getuid
|
||||||
|
}
|
||||||
|
|
||||||
|
inline QLocale getUserLocale()
|
||||||
|
{
|
||||||
|
return QLocale::system(); // current use env
|
||||||
|
}
|
||||||
|
|
||||||
|
inline QString unescapeString(const QString &input)
|
||||||
|
{
|
||||||
|
QRegularExpression regex("\\\\x([0-9A-Fa-f]{2})");
|
||||||
|
QRegularExpressionMatchIterator it = regex.globalMatch(input);
|
||||||
|
QString output{input};
|
||||||
|
|
||||||
|
while (it.hasNext()) {
|
||||||
|
QRegularExpressionMatch match = it.next();
|
||||||
|
bool ok;
|
||||||
|
// Get the hexadecimal value from the match and convert it to a number.
|
||||||
|
int asciiValue = match.captured(1).toInt(&ok, 16);
|
||||||
|
if (ok) {
|
||||||
|
// Convert the ASCII value to a QChar and perform the replacement.
|
||||||
|
output.replace(match.capturedStart(), match.capturedLength(), QChar(asciiValue));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline QString escapeToObjectPath(const QString &str)
|
||||||
|
{
|
||||||
|
if (str.isEmpty()) {
|
||||||
|
return "_";
|
||||||
|
}
|
||||||
|
|
||||||
|
auto ret = str;
|
||||||
|
QRegularExpression re{R"([^a-zA-Z0-9])"};
|
||||||
|
auto matcher = re.globalMatch(ret);
|
||||||
|
while (matcher.hasNext()) {
|
||||||
|
auto replaceList = matcher.next().capturedTexts();
|
||||||
|
replaceList.removeDuplicates();
|
||||||
|
for (const auto &c : replaceList) {
|
||||||
|
auto hexStr = QString::number(static_cast<uint>(c.front().toLatin1()), 16);
|
||||||
|
ret.replace(c, QString{R"(_%1)"}.arg(hexStr));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline QString unescapeFromObjectPath(const QString &str)
|
||||||
|
{
|
||||||
|
auto ret = str;
|
||||||
|
for (qsizetype i = 0; i < str.size(); ++i) {
|
||||||
|
if (str[i] == '_' and i + 2 < str.size()) {
|
||||||
|
auto hexStr = str.sliced(i + 1, 2);
|
||||||
|
ret.replace(hexStr, QChar::fromLatin1(hexStr.toUInt(nullptr, 16)));
|
||||||
|
i += 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
24
src/include/identifier.h
Normal file
24
src/include/identifier.h
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2023 UnionTech Software Technology Co., Ltd.
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||||
|
|
||||||
|
#ifndef IDENTIFIER_H
|
||||||
|
#define IDENTIFIER_H
|
||||||
|
|
||||||
|
#include <QString>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
struct IdentifyRet
|
||||||
|
{
|
||||||
|
QString ApplicationId;
|
||||||
|
QString InstanceId;
|
||||||
|
};
|
||||||
|
|
||||||
|
class Identifier
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual ~Identifier() = default;
|
||||||
|
virtual IdentifyRet Identify(pid_t pid) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
59
src/main.cpp
59
src/main.cpp
@ -1,4 +1,59 @@
|
|||||||
int main()
|
// SPDX-FileCopyrightText: 2023 UnionTech Software Technology Co., Ltd.
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||||
|
|
||||||
|
#include "global.h"
|
||||||
|
#include <QDBusConnection>
|
||||||
|
#include <QCoreApplication>
|
||||||
|
#include <QDir>
|
||||||
|
#include "applicationmanager1service.h"
|
||||||
|
#include "cgroupsidentifier.h"
|
||||||
|
#include "global.h"
|
||||||
|
|
||||||
|
static void registerComplexDbusType()
|
||||||
{
|
{
|
||||||
return 0;
|
qDBusRegisterMetaType<QMap<QString, QDBusUnixFileDescriptor>>();
|
||||||
|
qDBusRegisterMetaType<QMap<uint, QMap<QString, QDBusUnixFileDescriptor>>>();
|
||||||
|
qDBusRegisterMetaType<IconMap>();
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
QCoreApplication app{argc, argv};
|
||||||
|
auto &bus = ApplicationManager1DBus::instance();
|
||||||
|
bus.init(DBusType::Session);
|
||||||
|
auto &AMBus = bus.globalBus();
|
||||||
|
|
||||||
|
registerComplexDbusType();
|
||||||
|
ApplicationManager1Service AMService{std::make_unique<CGroupsIdentifier>(), AMBus};
|
||||||
|
QList<DesktopFile> fileList{};
|
||||||
|
auto pathEnv = qgetenv("XDG_DATA_DIRS");
|
||||||
|
if (pathEnv.isEmpty()) {
|
||||||
|
qFatal() << "environment variable $XDG_DATA_DIRS is empty.";
|
||||||
|
}
|
||||||
|
auto desktopFileDirs = pathEnv.split(':');
|
||||||
|
|
||||||
|
for (const auto &dir : desktopFileDirs) {
|
||||||
|
auto dirPath = QDir{QDir::cleanPath(dir) + "/applications"};
|
||||||
|
if (!dirPath.exists()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
QDirIterator it{dirPath.absolutePath(),
|
||||||
|
{"*.desktop"},
|
||||||
|
QDir::Files | QDir::NoSymLinks | QDir::NoDotAndDotDot | QDir::Readable,
|
||||||
|
QDirIterator::Subdirectories};
|
||||||
|
while (it.hasNext()) {
|
||||||
|
auto file = it.next();
|
||||||
|
ParseError err;
|
||||||
|
auto ret = DesktopFile::searchDesktopFile(file, err);
|
||||||
|
if (!ret.has_value()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!AMService.addApplication(std::move(ret).value())) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return app.exec();
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,22 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2023 UnionTech Software Technology Co., Ltd.
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||||
|
|
||||||
#include "global.h"
|
#include "global.h"
|
||||||
|
|
||||||
bool registerObjectToDbus(QObject *o, const QString &path, const QString &interface)
|
bool registerObjectToDBus(QObject *o, const QString &path, const QString &interface)
|
||||||
{
|
{
|
||||||
auto &con = ApplicationManager1DBus::instance().CustomBus();
|
auto &con = ApplicationManager1DBus::instance().globalBus();
|
||||||
if (!con.registerObject(path, interface, o)) {
|
if (!con.registerObject(path, interface, o, QDBusConnection::RegisterOption::ExportAllContents)) {
|
||||||
qCritical() << "register object failed:" << path << interface << con.lastError();
|
qFatal() << "register object failed:" << path << interface << con.lastError();
|
||||||
return false;
|
} else {
|
||||||
|
qInfo() << "register object:" << path << interface;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void unregisterObjectFromDBus(const QString &path)
|
||||||
|
{
|
||||||
|
auto &con = ApplicationManager1DBus::instance().globalBus();
|
||||||
|
con.unregisterObject(path);
|
||||||
|
}
|
||||||
|
@ -23,6 +23,7 @@ target_link_libraries(${BIN_NAME} PRIVATE
|
|||||||
)
|
)
|
||||||
|
|
||||||
target_compile_options(${BIN_NAME} PRIVATE
|
target_compile_options(${BIN_NAME} PRIVATE
|
||||||
|
-fno-access-control
|
||||||
-fsanitize=undefined
|
-fsanitize=undefined
|
||||||
-fsanitize=address
|
-fsanitize=address
|
||||||
)
|
)
|
||||||
|
@ -57,10 +57,10 @@ TEST_F(TestDesktopEntry, prase)
|
|||||||
ASSERT_EQ(err, ParseError::NoError);
|
ASSERT_EQ(err, ParseError::NoError);
|
||||||
|
|
||||||
auto group = entry.group("Desktop Entry");
|
auto group = entry.group("Desktop Entry");
|
||||||
ASSERT_FALSE(group.isEmpty());
|
ASSERT_TRUE(group);
|
||||||
|
|
||||||
auto name = group.constFind("Name");
|
auto name = group->constFind("Name");
|
||||||
ASSERT_NE(name, group.cend());
|
ASSERT_NE(name, group->cend());
|
||||||
|
|
||||||
bool ok;
|
bool ok;
|
||||||
name->toBoolean(ok);
|
name->toBoolean(ok);
|
||||||
|
@ -3,16 +3,18 @@
|
|||||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||||
|
|
||||||
#include "jobmanager1service.h"
|
#include "jobmanager1service.h"
|
||||||
#include "jobservice.h"
|
|
||||||
#include <gtest/gtest.h>
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
class TestJobManager : public testing::Test
|
class TestJobManager : public testing::Test
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
JobManager1Service &service() { return m_jobManager; }
|
static void SetUpTestCase() { m_jobManager = new JobManager1Service(nullptr); }
|
||||||
|
|
||||||
|
static void TearDownTestCase() { delete m_jobManager; }
|
||||||
|
JobManager1Service &service() { return *m_jobManager; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
JobManager1Service m_jobManager;
|
static inline JobManager1Service *m_jobManager{nullptr};
|
||||||
};
|
};
|
||||||
|
|
||||||
TEST_F(TestJobManager, addJob)
|
TEST_F(TestJobManager, addJob)
|
||||||
@ -35,7 +37,7 @@ TEST_F(TestJobManager, addJob)
|
|||||||
});
|
});
|
||||||
|
|
||||||
manager.addJob(
|
manager.addJob(
|
||||||
sourcePath,
|
sourcePath.path(),
|
||||||
[](auto value) -> QVariant {
|
[](auto value) -> QVariant {
|
||||||
EXPECT_TRUE(value.toString() == "Application");
|
EXPECT_TRUE(value.toString() == "Application");
|
||||||
return QVariant::fromValue(true);
|
return QVariant::fromValue(true);
|
||||||
|
@ -1,6 +1,15 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2023 UnionTech Software Technology Co., Ltd.
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||||
|
|
||||||
#include "global.h"
|
#include "global.h"
|
||||||
|
|
||||||
bool registerObjectToDbus(QObject *, const QString &, const QString &)
|
bool registerObjectToDBus(QObject *, const QString &, const QString &)
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void unregisterObjectFromDBus(const QString &)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user