From 0b22bb3adf1400c66d73e6b4feb53980907f56a5 Mon Sep 17 00:00:00 2001 From: tanfeng Date: Wed, 30 Mar 2022 17:56:27 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=88=9D=E5=A7=8B=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit AM初始代码,迁移自gitlabwh Log: Task: https://pms.uniontech.com/task-view-108539.html Influence: Change-Id: I6096f97e5d68d13796ff5dc51d9858c0f40264a0 --- CMakeLists.txt | 9 + DBus/org.desktopspec.Application.xml | 15 + DBus/org.desktopspec.ApplicationInstance.xml | 8 + DBus/org.desktopspec.ApplicationManager.xml | 20 + README.md | 7 + debian/changelog | 5 + debian/control | 23 + debian/copyright | 21 + debian/rules | 6 + debian/source/format | 1 + misc/CMakeLists.txt | 1 + misc/systemd/CMakeLists.txt | 6 + ...org.deskspec.application.instance@.service | 8 + .../org.deskspec.application.manager.service | 7 + src/CMakeLists.txt | 15 + src/define.h.in | 6 + src/loader/CMakeLists.txt | 39 ++ src/loader/main.cpp | 252 ++++++++ src/modules/applicationhelper/helper.h | 79 +++ src/modules/methods/basic.h | 18 + src/modules/methods/instance.hpp | 25 + src/modules/methods/quit.hpp | 27 + src/modules/methods/registe.hpp | 29 + src/modules/methods/task.hpp | 36 ++ src/modules/socket/client.cpp | 124 ++++ src/modules/socket/client.h | 25 + src/modules/socket/server.cpp | 147 +++++ src/modules/socket/server.h | 27 + src/modules/tools/desktop_deconstruction.hpp | 157 +++++ src/modules/tools/query.h | 11 + src/modules/util/common.cpp | 50 ++ src/modules/util/common.h | 64 +++ src/modules/util/debug/debug.cpp | 126 ++++ src/modules/util/debug/debug.h | 34 ++ src/modules/util/filesystem.cpp | 182 ++++++ src/modules/util/filesystem.h | 151 +++++ src/modules/util/json.h | 58 ++ src/modules/util/logger.cpp | 42 ++ src/modules/util/logger.h | 107 ++++ src/modules/util/macro.h | 19 + src/modules/util/oci_runtime.h | 544 ++++++++++++++++++ src/modules/util/platform.cpp | 70 +++ src/modules/util/platform.h | 31 + src/modules/util/semaphore.cpp | 73 +++ src/modules/util/semaphore.h | 43 ++ src/modules/util/util.h | 44 ++ src/service/CMakeLists.txt | 55 ++ src/service/impl/application.cpp | 168 ++++++ src/service/impl/application.h | 60 ++ src/service/impl/application_instance.cpp | 169 ++++++ src/service/impl/application_instance.h | 44 ++ src/service/impl/application_manager.cpp | 195 +++++++ src/service/impl/application_manager.h | 43 ++ src/service/main.cpp | 81 +++ 54 files changed, 3607 insertions(+) create mode 100644 CMakeLists.txt create mode 100644 DBus/org.desktopspec.Application.xml create mode 100644 DBus/org.desktopspec.ApplicationInstance.xml create mode 100644 DBus/org.desktopspec.ApplicationManager.xml create mode 100644 README.md create mode 100644 debian/changelog create mode 100644 debian/control create mode 100644 debian/copyright create mode 100755 debian/rules create mode 100644 debian/source/format create mode 100644 misc/CMakeLists.txt create mode 100644 misc/systemd/CMakeLists.txt create mode 100644 misc/systemd/org.deskspec.application.instance@.service create mode 100644 misc/systemd/org.deskspec.application.manager.service create mode 100644 src/CMakeLists.txt create mode 100644 src/define.h.in create mode 100644 src/loader/CMakeLists.txt create mode 100644 src/loader/main.cpp create mode 100644 src/modules/applicationhelper/helper.h create mode 100644 src/modules/methods/basic.h create mode 100644 src/modules/methods/instance.hpp create mode 100644 src/modules/methods/quit.hpp create mode 100644 src/modules/methods/registe.hpp create mode 100644 src/modules/methods/task.hpp create mode 100644 src/modules/socket/client.cpp create mode 100644 src/modules/socket/client.h create mode 100644 src/modules/socket/server.cpp create mode 100644 src/modules/socket/server.h create mode 100644 src/modules/tools/desktop_deconstruction.hpp create mode 100644 src/modules/tools/query.h create mode 100644 src/modules/util/common.cpp create mode 100644 src/modules/util/common.h create mode 100644 src/modules/util/debug/debug.cpp create mode 100644 src/modules/util/debug/debug.h create mode 100644 src/modules/util/filesystem.cpp create mode 100644 src/modules/util/filesystem.h create mode 100644 src/modules/util/json.h create mode 100644 src/modules/util/logger.cpp create mode 100644 src/modules/util/logger.h create mode 100644 src/modules/util/macro.h create mode 100644 src/modules/util/oci_runtime.h create mode 100644 src/modules/util/platform.cpp create mode 100644 src/modules/util/platform.h create mode 100644 src/modules/util/semaphore.cpp create mode 100644 src/modules/util/semaphore.h create mode 100644 src/modules/util/util.h create mode 100644 src/service/CMakeLists.txt create mode 100644 src/service/impl/application.cpp create mode 100644 src/service/impl/application.h create mode 100644 src/service/impl/application_instance.cpp create mode 100644 src/service/impl/application_instance.h create mode 100644 src/service/impl/application_manager.cpp create mode 100644 src/service/impl/application_manager.h create mode 100644 src/service/main.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..8cc2c55 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,9 @@ +cmake_minimum_required(VERSION 3.11) + +project(deepin-application-manager) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +add_subdirectory(src) +add_subdirectory(misc) diff --git a/DBus/org.desktopspec.Application.xml b/DBus/org.desktopspec.Application.xml new file mode 100644 index 0000000..d3c2f4f --- /dev/null +++ b/DBus/org.desktopspec.Application.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/DBus/org.desktopspec.ApplicationInstance.xml b/DBus/org.desktopspec.ApplicationInstance.xml new file mode 100644 index 0000000..af4d27c --- /dev/null +++ b/DBus/org.desktopspec.ApplicationInstance.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/DBus/org.desktopspec.ApplicationManager.xml b/DBus/org.desktopspec.ApplicationManager.xml new file mode 100644 index 0000000..1ef868f --- /dev/null +++ b/DBus/org.desktopspec.ApplicationManager.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/README.md b/README.md new file mode 100644 index 0000000..6bd7c43 --- /dev/null +++ b/README.md @@ -0,0 +1,7 @@ +## Build Dep + +* Qt + * Core + * Network + * DBus +* [nlohmann/json](https://github.com/nlohmann/json) \ No newline at end of file diff --git a/debian/changelog b/debian/changelog new file mode 100644 index 0000000..7bcba30 --- /dev/null +++ b/debian/changelog @@ -0,0 +1,5 @@ +dde-application-manager (1.0.0-1) UNRELEASED; urgency=medium + + * Initial release + + -- Wed, 30 Mar 2022 11:49:45 +0800 \ No newline at end of file diff --git a/debian/control b/debian/control new file mode 100644 index 0000000..9c0953c --- /dev/null +++ b/debian/control @@ -0,0 +1,23 @@ +Source: dde-application-manager +Section: devel +Priority: optional +Maintainer: Deepin Packages Builder +Build-Depends: + cmake, + debhelper-compat (= 11), + pkg-config, + nlohmann-json3-dev, + libcap-dev, + qt5-qmake, + qtbase5-dev, + qttools5-dev, +Standards-Version: 4.1.3 +Homepage: https://www.deepin.org + +Package: dde-application-manager +Architecture: any +Depends: + ${misc:Depends}, + ${shlibs:Depends}, +Description: dde application manager module + Application manager for DDE. diff --git a/debian/copyright b/debian/copyright new file mode 100644 index 0000000..6df1124 --- /dev/null +++ b/debian/copyright @@ -0,0 +1,21 @@ +Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ +Upstream-Name: dde-application-manager + +Files: * +Copyright: 2015 Deepin Technology Co., Ltd. +License: GPL-3+ + This package is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + . + This package is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + . + You should have received a copy of the GNU General Public License + along with this program. If not, see + . + On Debian systems, the complete text of the GNU General + Public License version 3 can be found in "/usr/share/common-licenses/GPL-3". diff --git a/debian/rules b/debian/rules new file mode 100755 index 0000000..a140efe --- /dev/null +++ b/debian/rules @@ -0,0 +1,6 @@ +#!/usr/bin/make -f + +include /usr/share/dpkg/default.mk + +%: + dh $@ --buildsystem=cmake diff --git a/debian/source/format b/debian/source/format new file mode 100644 index 0000000..163aaf8 --- /dev/null +++ b/debian/source/format @@ -0,0 +1 @@ +3.0 (quilt) diff --git a/misc/CMakeLists.txt b/misc/CMakeLists.txt new file mode 100644 index 0000000..eaa79cc --- /dev/null +++ b/misc/CMakeLists.txt @@ -0,0 +1 @@ +add_subdirectory(systemd) \ No newline at end of file diff --git a/misc/systemd/CMakeLists.txt b/misc/systemd/CMakeLists.txt new file mode 100644 index 0000000..2edcb8e --- /dev/null +++ b/misc/systemd/CMakeLists.txt @@ -0,0 +1,6 @@ +set(SYSTEMD_FILES + org.deskspec.application.instance@.service + org.deskspec.application.manager.service +) + +install(FILES ${SYSTEMD_FILES} DESTINATION lib/systemd/user/) diff --git a/misc/systemd/org.deskspec.application.instance@.service b/misc/systemd/org.deskspec.application.instance@.service new file mode 100644 index 0000000..6f3f842 --- /dev/null +++ b/misc/systemd/org.deskspec.application.instance@.service @@ -0,0 +1,8 @@ +[Unit] +Description=Deepin Application Loader is managing %I + +[Service] +Type=simple +Environment="DAM_TASK_HASH=%I" +Environment="DAM_TASK_TYPE=freedesktop" +ExecStart=/usr/bin/deepin-application-loader diff --git a/misc/systemd/org.deskspec.application.manager.service b/misc/systemd/org.deskspec.application.manager.service new file mode 100644 index 0000000..d926260 --- /dev/null +++ b/misc/systemd/org.deskspec.application.manager.service @@ -0,0 +1,7 @@ +[Unit] +Description=Deepin Application Manager service + +[Service] +Type=DBus +BusName=org.deskspec.ApplicationManager +ExecStart=/usr/bin/deepin-application-service diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt new file mode 100644 index 0000000..502633d --- /dev/null +++ b/src/CMakeLists.txt @@ -0,0 +1,15 @@ +include(FindPkgConfig) +find_package(PkgConfig REQUIRED) + +if(CMAKE_BUILD_TYPE STREQUAL "Debug") + add_definitions(-DDEFINE_LOADER_PATH) + set(LOADER_PATH ${PROJECT_BINARY_DIR}/src/loader/deepin-application-loader) +endif() + +configure_file( + "${PROJECT_SOURCE_DIR}/src/define.h.in" + "${PROJECT_BINARY_DIR}/src/define.h" +) + +add_subdirectory("service") +add_subdirectory("loader") diff --git a/src/define.h.in b/src/define.h.in new file mode 100644 index 0000000..f3b0638 --- /dev/null +++ b/src/define.h.in @@ -0,0 +1,6 @@ +#ifndef DEFINE_H_IN +#define DEFINE_H_IN + +#define LOADER_PATH "@LOADER_PATH@" + +#endif // DEFINE_H_IN diff --git a/src/loader/CMakeLists.txt b/src/loader/CMakeLists.txt new file mode 100644 index 0000000..3e5386f --- /dev/null +++ b/src/loader/CMakeLists.txt @@ -0,0 +1,39 @@ +pkg_check_modules(LIBCAP REQUIRED IMPORTED_TARGET libcap) + +set(SRCS + ./main.cpp + ../modules/tools/desktop_deconstruction.hpp + ../modules/socket/client.h + ../modules/socket/client.cpp + ../modules/methods/task.hpp + ../modules/methods/basic.h + ../modules/methods/instance.hpp + ../modules/methods/quit.hpp + ../modules/methods/registe.hpp + ../modules/util/common.cpp + ../modules/util/common.h + ../modules/util/debug + ../modules/util/filesystem.cpp + ../modules/util/filesystem.h + ../modules/util/json.h + ../modules/util/logger.cpp + ../modules/util/logger.h + ../modules/util/macro.h + ../modules/util/oci_runtime.h + ../modules/util/platform.cpp + ../modules/util/platform.h + ../modules/util/semaphore.cpp + ../modules/util/semaphore.h + ../modules/util/util.h + ../modules/util/debug/debug.h + ../modules/util/debug/debug.cpp +) + +add_executable(deepin-application-loader ${SRCS}) + +target_link_libraries(deepin-application-loader + pthread + PkgConfig::LIBCAP + stdc++fs +) +install(TARGETS deepin-application-loader DESTINATION bin) diff --git a/src/loader/main.cpp b/src/loader/main.cpp new file mode 100644 index 0000000..3cd9a29 --- /dev/null +++ b/src/loader/main.cpp @@ -0,0 +1,252 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "../modules/methods/basic.h" +#include "../modules/methods/instance.hpp" +#include "../modules/methods/quit.hpp" +#include "../modules/methods/registe.hpp" +#include "../modules/methods/task.hpp" +#include "../modules/socket/client.h" +#include "../modules/tools/desktop_deconstruction.hpp" +#include "../modules/util/oci_runtime.h" + +extern char** environ; + +// from linglong +#define LINGLONG 118 +#define LL_VAL(str) #str +#define LL_TOSTRING(str) LL_VAL(str) + +struct App { + std::string type; + std::string prefix; + std::string id; +}; + +static App parseApp(const std::string& app) +{ + std::vector strings; + std::istringstream stream(app); + std::string s; + while (getline(stream, s, '/')) { + if (s.empty()) { + continue; + } + strings.push_back(s); + } + + App result; + result.prefix = strings[0]; + result.type = strings[1]; + result.id = strings[2]; + + return result; +} + +void quit() {} + +void sig_handler(int num) +{ + int status; + pid_t pid; + /* 由于该信号不能叠加,所以可能同时有多个子进程已经结束 所以循环wait */ + while ((pid = waitpid(0, &status, WNOHANG)) > 0) { + if (WIFEXITED(status)) // 判断子进程的退出状态 是否是正常退出 + printf("-----child %d exit with %d\n", pid, WEXITSTATUS(status)); + else if (WIFSIGNALED(status)) // 判断子进程是否是 通过信号退出 + printf("child %d killed by the %dth signal\n", pid, WTERMSIG(status)); + } +} + +int runLinglong(void* _arg) +{ + return 0; +} + +int child(void* _arg) +{ + Methods::Task* task = (Methods::Task*) _arg; + + prctl(PR_SET_PDEATHSIG, SIGKILL); + prctl(PR_SET_PDEATHSIG, SIGTERM); + prctl(PR_SET_PDEATHSIG, SIGHUP); + + App app = parseApp(task->runId); + std::string path{ "/usr/share/applications/" + app.id + ".desktop" }; + if (app.type == "user") { + struct passwd* user = getpwuid(getuid()); + path = std::string(user->pw_dir) + "/.local/share/applications/" + app.id + ".desktop"; + } + DesktopDeconstruction dd(path); + dd.beginGroup("Desktop Entry"); + std::cout << dd.value("Exec") << std::endl; + + linglong::Runtime runtime; + linglong::Annotations annotations; + linglong::Root root; + linglong::Mount mount; + annotations.container_root_path = "/run/user/1000/DAM/" + task->id; + annotations.native = { { mount } }; + root.path = annotations.container_root_path + "/root"; + mount.destination = "/"; + mount.source = "/"; + mount.type = "bind"; + mount.data = { "ro" }; + runtime.hostname = "hostname"; + runtime.process.cwd = "/"; + std::filesystem::path container_root_path(annotations.container_root_path); + if (!std::filesystem::exists(container_root_path)) { + if (!std::filesystem::create_directories(container_root_path)) { + std::cout << "[Loader] [Warning] cannot create container root path." << std::endl; + return -1; + } + } + std::transform(task->environments.begin(), task->environments.end(), std::back_inserter(runtime.process.env), + [](const std::pair& pair) -> std::string { return pair.first + "=" + pair.second; }); + + std::istringstream stream(dd.value("Exec")); + std::string s; + while (getline(stream, s, ' ')) { + if (s.empty()) { + continue; + } + + // TODO: %U + if (s.length() == 2 && s[0] == '%') { + continue; + } + runtime.process.args.push_back(s); + } + + std::cout << nlohmann::json(runtime).dump() << std::endl; + + int pipeEnds[2]; + if (pipe(pipeEnds) != 0) { + return EXIT_FAILURE; + } + + pid_t pid = fork(); + if (pid == -1) { + perror("fork()"); + return -1; + } + + if (pid == 0) { + (void) close(pipeEnds[1]); + if (dup2(pipeEnds[0], LINGLONG) == -1) { + return EXIT_FAILURE; + } + (void) close(pipeEnds[0]); + char const* const args[] = { "/usr/bin/ll-box", LL_TOSTRING(LINGLONG), nullptr }; + int ret = execvp(args[0], (char**) args); + std::cout << "[Loader] [Fork] " << ret << std::endl; + //std::filesystem::remove(container_root_path); + exit(ret); + } + else { + nlohmann::json json = runtime; + const std::string data = std::move(json.dump()); + close(pipeEnds[0]); + write(pipeEnds[1], data.c_str(), data.size()); + close(pipeEnds[1]); + } + + return pid; +} + +#define DAM_TASK_HASH "DAM_TASK_HASH" +#define DAM_TASK_TYPE "DAM_TASK_TYPE" + +int main(int argc, char* argv[]) +{ + const char* dam_task_hash = getenv(DAM_TASK_HASH); + if (!dam_task_hash) { + return -1; + } + const char* dam_task_type = getenv(DAM_TASK_TYPE); + if (!dam_task_type) { + return -2; + } + + // TODO: move to a utils.h + std::string socketPath{ "/run/user/1000/deepin-application-manager.socket" }; + + // register client and run quitConnect + Socket::Client client; + client.connect(socketPath); + + Methods::Registe registe; + registe.id = dam_task_type; + registe.hash = dam_task_hash; + + Methods::Registe registe_result; + registe_result.state = false; + auto result = client.get(registe); + if (!result.is_null()) { + registe_result = result; + } + if (!registe_result.state) { + return -3; + } + + Methods::Instance instance; + instance.hash = registe_result.hash; + std::cout << "get task" << std::endl; + result = client.get(instance); + Methods::Task task = result; + std::cout << "[result] " << result << std::endl; + + pthread_attr_t attr; + size_t stack_size; + pthread_attr_init(&attr); + pthread_attr_getstacksize(&attr, &stack_size); + pthread_attr_destroy(&attr); + + /* 先将SIGCHLD信号阻塞 保证在子进程结束前设置父进程的捕捉函数 */ + sigset_t nmask, omask; + sigemptyset(&nmask); + sigaddset(&nmask, SIGCHLD); + sigprocmask(SIG_BLOCK, &nmask, &omask); + + //char* stack = (char*) malloc(stack_size); + //pid_t pid = clone(child, stack + stack_size, CLONE_NEWPID | SIGCHLD, static_cast(&task)); + pid_t pid = child(&task); + // TODO: 启动线程,创建新的连接去接受服务器的消息 + + /* 设置捕捉函数 */ + struct sigaction sig; + sig.sa_handler = sig_handler; + sigemptyset(&sig.sa_mask); + sig.sa_flags = 0; + sigaction(SIGCHLD, &sig, NULL); + /* 然后再unblock */ + sigdelset(&omask, SIGCHLD); + sigprocmask(SIG_SETMASK, &omask, NULL); + + int exitCode; + waitpid(pid, &exitCode, 0); + + Methods::Quit quit; + quit.code = exitCode; + quit.id = task.id; + client.send(quit); + + return exitCode; +} diff --git a/src/modules/applicationhelper/helper.h b/src/modules/applicationhelper/helper.h new file mode 100644 index 0000000..d0972ba --- /dev/null +++ b/src/modules/applicationhelper/helper.h @@ -0,0 +1,79 @@ +#ifndef A7B4B7B1_0422_4EC9_8441_778273A85F9C +#define A7B4B7B1_0422_4EC9_8441_778273A85F9C + +#include "../tools/desktop_deconstruction.hpp" + +#include + +namespace modules { +namespace ApplicationHelper { +class Helper { + QString m_file; + +public: + Helper(const QString &desktop) + : m_file(desktop) + { + + } + + inline QString desktop() const { return m_file; } + + template + T value(const QString &key) const + { + QSettings settings = DesktopDeconstruction(m_file); + settings.beginGroup("Desktop Entry"); + return settings.value(key).value(); + } + + QStringList categories() const + { + QStringList result; + QStringList tmp{ value("Categories").split(";") }; + for (auto t : tmp) { + if (t.isEmpty()) { + continue; + } + result << t; + } + return result; + } + + QString icon() const + { + return value("Icon"); + } + + QString id() const + { + return m_file.split("/").last().split(".").first(); + } + + QStringList mimetypes() const + { + QStringList result; + QStringList tmp{ value("MimeType").split(";") }; + for (auto t : tmp) { + if (t.isEmpty()) { + continue; + } + result << t; + } + return result; + } + + QString comment(const QString &locale) const + { + return value(QString("Comment[%1]").arg(locale)); + } + + QString name(const QString &name) const + { + return value(QString("Name[%1]").arg(name)); + } +}; +} // namespace ApplicationHelper +} // namespace modules + +#endif /* A7B4B7B1_0422_4EC9_8441_778273A85F9C */ diff --git a/src/modules/methods/basic.h b/src/modules/methods/basic.h new file mode 100644 index 0000000..78598a5 --- /dev/null +++ b/src/modules/methods/basic.h @@ -0,0 +1,18 @@ +#ifndef BASIC_H_ +#define BASIC_H_ + +#include + +namespace Methods { +struct Basic { + std::string type; +}; + +using json = nlohmann::json; +inline void from_json(const json &j, Basic &basic) { + j.at("type").get_to(basic.type); +} + +} // namespace Methods + +#endif // BASIC_H_ diff --git a/src/modules/methods/instance.hpp b/src/modules/methods/instance.hpp new file mode 100644 index 0000000..8f12adc --- /dev/null +++ b/src/modules/methods/instance.hpp @@ -0,0 +1,25 @@ +#ifndef C664E26D_6517_412B_950F_07E20963349E +#define C664E26D_6517_412B_950F_07E20963349E + +#include + +namespace Methods { +struct Instance { + std::string hash; + std::string type{ "instance" }; +}; + +using json = nlohmann::json; +inline void to_json(json &j, const Instance &instance) +{ + j = json{ { "type", instance.type }, { "hash", instance.hash } }; +} + +inline void from_json(const json &j, Instance &instance) +{ + j.at("hash").get_to(instance.hash); +} + +} // namespace Methods + +#endif /* C664E26D_6517_412B_950F_07E20963349E */ diff --git a/src/modules/methods/quit.hpp b/src/modules/methods/quit.hpp new file mode 100644 index 0000000..82d9dc4 --- /dev/null +++ b/src/modules/methods/quit.hpp @@ -0,0 +1,27 @@ +#ifndef QUIT_H_ +#define QUIT_H_ + +#include + +namespace Methods { +struct Quit { + std::string date; + std::string type{ "quit" }; + std::string id; + int code; +}; +using json = nlohmann::json; +inline void to_json(json &j, const Quit &quit) +{ + j = json{ { "type", quit.type }, { "date", quit.date }, { "id", quit.id }, { "code", quit.code } }; +} + +inline void from_json(const json &j, Quit &quit) +{ + j.at("id").get_to(quit.id); + j.at("date").get_to(quit.date); + j.at("code").get_to(quit.code); +} +} // namespace Methods + +#endif // QUIT_H_ diff --git a/src/modules/methods/registe.hpp b/src/modules/methods/registe.hpp new file mode 100644 index 0000000..0696e39 --- /dev/null +++ b/src/modules/methods/registe.hpp @@ -0,0 +1,29 @@ +#ifndef REGISTER_H_ +#define REGISTER_H_ + +#include + +namespace Methods { +struct Registe { + std::string date; + std::string id; + std::string type{ "registe" }; + std::string hash; + bool state; +}; + +using json = nlohmann::json; +inline void to_json(json &j, const Registe ®iste) +{ + j = json{ { "type", registe.type }, { "id", registe.id }, { "hash", registe.hash }, { "state", registe.state }, { "date", registe.date } }; +} +inline void from_json(const json &j, Registe ®iste) +{ + j.at("id").get_to(registe.id); + j.at("date").get_to(registe.date); + j.at("hash").get_to(registe.hash); + j.at("state").get_to(registe.state); +} +} // namespace Methods + +#endif // REGISTER_H_ diff --git a/src/modules/methods/task.hpp b/src/modules/methods/task.hpp new file mode 100644 index 0000000..a33b125 --- /dev/null +++ b/src/modules/methods/task.hpp @@ -0,0 +1,36 @@ +#ifndef B0B88BD6_CF1E_4E87_926A_E6DBE6B9B19C +#define B0B88BD6_CF1E_4E87_926A_E6DBE6B9B19C + +#include +#include +#include +#include + +namespace Methods { +struct Task { + std::string id; + std::string runId; + std::string type{ "task" }; + std::string date; + std::vector arguments; + std::multimap environments; +}; + +using json = nlohmann::json; +inline void to_json(json &j, const Task &task) +{ + j = json{ { "type", task.type }, { "id", task.id }, { "run_id", task.runId }, { "date", task.date }, { "arguments", task.arguments }, { "environments", task.environments } }; +} + +inline void from_json(const json &j, Task &task) +{ + j.at("id").get_to(task.id); + j.at("run_id").get_to(task.runId); + j.at("date").get_to(task.date); + j.at("arguments").get_to(task.arguments); + j.at("environments").get_to(task.environments); +} + +} // namespace Methods + +#endif /* B0B88BD6_CF1E_4E87_926A_E6DBE6B9B19C */ diff --git a/src/modules/socket/client.cpp b/src/modules/socket/client.cpp new file mode 100644 index 0000000..b1fa1df --- /dev/null +++ b/src/modules/socket/client.cpp @@ -0,0 +1,124 @@ +#include "client.h" + +#include +#include +#include + +#include + +namespace Socket { +struct ClientPrivate { + Client* q_ptr; + int socket_fd; + std::function&)> readFunc; + std::thread* readThread; + + ClientPrivate(Client* client) : q_ptr(client), readThread(nullptr) {} + ~ClientPrivate() + { + if (readThread) { + close(socket_fd); + readThread->join(); + delete readThread; + } + } + bool connect(const std::string& host) + { + sockaddr_un address; + + if ((socket_fd = socket(AF_LOCAL, SOCK_STREAM, 0)) < 0) { + printf("socket() failed\n"); + return false; + } + + memset(&address, 0, sizeof(struct sockaddr_un)); + + address.sun_family = AF_LOCAL; + snprintf(address.sun_path, host.size() + 1, "%s", host.c_str()); + if (::connect(socket_fd, (struct sockaddr*) &address, sizeof(struct sockaddr_un)) != 0) { + printf("connect() failed\n"); + return false; + } + + if (readThread) { + return false; + } + + if (readFunc) { + readThread = new std::thread([=] { + char buf[512]; + std::vector result; + int bytesRead; + while ((bytesRead = recv(socket_fd, buf, 512, 0)) > 0) { + if (bytesRead == -1) { + return; + } + for (int i = 0; i < bytesRead; i++) { + result.push_back(buf[i]); + } + if (buf[bytesRead - 1] == '\0') { + readFunc(result); + result.clear(); + } + }; + }); + readThread->detach(); + } + + return true; + } + nlohmann::json get(const nlohmann::json& call) + { + send(call); + + char buf[512]; + std::string result; + int bytesRead; + while ((bytesRead = recv(socket_fd, buf, 512, 0)) > 0) { + for (int i = 0; i < bytesRead; i++) { + result += buf[i]; + } + if (buf[bytesRead - 1] == '\0') { + break; + } + }; + return nlohmann::json::parse(result); + } + + size_t send(const nlohmann::json& call) + { + std::string data = call.dump(); + data += '\0'; + return write(socket_fd, data.c_str(), data.length()); + } +}; + +Client::Client() : d_ptr(new ClientPrivate(this)) {} + +Client::~Client() {} + +bool Client::connect(const std::string& host) +{ + return d_ptr->connect(host); +} + +nlohmann::json Client::get(const nlohmann::json& call) +{ + return d_ptr->get(call); +} + +size_t Client::send(const nlohmann::json& call) +{ + return d_ptr->send(call); +} + +void Client::onReadyRead(std::function&)> func) +{ + d_ptr->readFunc = func; +} + +void Client::waitForFinished() +{ + d_ptr->readThread->join(); +} +} // namespace Socket diff --git a/src/modules/socket/client.h b/src/modules/socket/client.h new file mode 100644 index 0000000..2cc3694 --- /dev/null +++ b/src/modules/socket/client.h @@ -0,0 +1,25 @@ +#ifndef B1D5EB4F_7645_4BDA_87D6_6B80A4910014 +#define B1D5EB4F_7645_4BDA_87D6_6B80A4910014 + +#include +#include +#include +#include + +namespace Socket { +class ClientPrivate; +class Client { + std::unique_ptr d_ptr; + +public: + Client(); + ~Client(); + bool connect(const std::string& host); + nlohmann::json get(const nlohmann::json& call); + size_t send(const nlohmann::json& call); + void onReadyRead(std::function&)> func); + void waitForFinished(); +}; +} // namespace Socket + +#endif /* B1D5EB4F_7645_4BDA_87D6_6B80A4910014 */ diff --git a/src/modules/socket/server.cpp b/src/modules/socket/server.cpp new file mode 100644 index 0000000..30f9c1f --- /dev/null +++ b/src/modules/socket/server.cpp @@ -0,0 +1,147 @@ +#include "server.h" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace Socket { +class ServerPrivate : public QObject { + Q_OBJECT +public: + Server *q_ptr; + int socket_fd; + QThread *workThread; + +Q_SIGNALS: + void requestStart(); + +public: + ServerPrivate(Server *server) : QObject(), q_ptr(server), socket_fd(-1) + { + if ((socket_fd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) { + std::cout << "socket() failed" << std::endl; + return; + } + + connect(this, &ServerPrivate::requestStart, this, &ServerPrivate::work, Qt::QueuedConnection); + } + ~ServerPrivate() {} + + void work() + { + // start a thread to listen client read + while (true) { + int socket = accept(socket_fd, nullptr, nullptr); + if (socket == -1) { + std::cout << "accept() failed" << std::endl; + return; + } + QtConcurrent::run([=] { + int readBytes = 0; + char buffer[1024]; + std::vector data; + while (true) { + readBytes = recv(socket, buffer, 1024, 0); + if (readBytes == -1) { + std::cout << "client connect closed" << std::endl; + break; + } + + if (readBytes == 0) { + break; + } + + for (int i = 0; i != readBytes; i++) { + data.push_back(buffer[i]); + } + + if (buffer[readBytes - 1] == '\0') { + emit q_ptr->onReadyRead(socket, data); + data.clear(); + } + } + }); + } + } + + bool listen(const std::string &host) + { + if (socket_fd < 0) { + return false; + } + + struct sockaddr_un addr; + memset(&addr, 0, sizeof(struct sockaddr_un)); + addr.sun_family = AF_UNIX; + snprintf(addr.sun_path, host.size() + 1, "%s", host.c_str()); + + if (remove(host.c_str()) == -1 && errno != ENOENT) { + std::cout << "remove() failed" << std::endl; + return false; + } + if (bind(socket_fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + std::cout << "bind() failed" << std::endl; + return false; + } + + if (::listen(socket_fd, sizeof(uint)) < 0) { + std::cout << "listen() failed" << std::endl; + return false; + } + + return true; + } + + void write(int socket, const std::vector &data) + { + ::write(socket, data.data(), data.size()); + } + + void closeClient(int socket) + { + ::close(socket); + } +}; + +Server::Server() : QObject(nullptr), d_ptr(new ServerPrivate(this)) +{ + qRegisterMetaType>("VectorChar"); +} +Server::~Server() {} +bool Server::listen(const std::string &host) +{ + if (d_ptr->workThread) { + return false; + } + + const bool result = d_ptr->listen(host); + if (!result) { + return result; + } + + d_ptr->workThread = new QThread; + d_ptr->moveToThread(d_ptr->workThread); + d_ptr->workThread->start(); + emit d_ptr->requestStart(); + return result; +} + +void Server::write(int socket, const std::vector &data) +{ + d_ptr->write(socket, data); +} + +void Server::close(int socket) +{ + d_ptr->closeClient(socket); +} +} // namespace Socket + +#include "server.moc" diff --git a/src/modules/socket/server.h b/src/modules/socket/server.h new file mode 100644 index 0000000..c5fd118 --- /dev/null +++ b/src/modules/socket/server.h @@ -0,0 +1,27 @@ +#ifndef F358257E_94E5_4A6C_91A8_4B6E57999E7B +#define F358257E_94E5_4A6C_91A8_4B6E57999E7B + +#include +#include +#include +#include +#include + +namespace Socket { +class ServerPrivate; +class Server : public QObject { + Q_OBJECT + std::unique_ptr d_ptr; + +public: + Server(); + ~Server(); + bool listen(const std::string& host); + void write(int socket, const std::vector& data); + void close(int socket); +Q_SIGNALS: + void onReadyRead(int socket, const std::vector& data) const; +}; +} // namespace Socket + +#endif /* F358257E_94E5_4A6C_91A8_4B6E57999E7B */ diff --git a/src/modules/tools/desktop_deconstruction.hpp b/src/modules/tools/desktop_deconstruction.hpp new file mode 100644 index 0000000..933f640 --- /dev/null +++ b/src/modules/tools/desktop_deconstruction.hpp @@ -0,0 +1,157 @@ +#ifndef BB9B5BB3_BEAF_4D25_B4F6_55273B263973 +#define BB9B5BB3_BEAF_4D25_B4F6_55273B263973 + +#ifdef USE_QT + +#include +#include + +static QSettings DesktopDeconstruction(const QString &desktop) +{ + auto IniReadFunc = [](QIODevice &device, QSettings::SettingsMap &settingsMap) -> bool { + QTextStream stream(&device); + QString group; + while (!stream.atEnd()) { + QString line = stream.readLine(); + if (group.isEmpty()) { + if (line.front() == '[' && line.back() == ']') { + group = line.mid(1, line.size() - 2); + } + } + else { + if (line.isEmpty()) { + group.clear(); + } + else { + int index = line.indexOf("="); + if (index != -1) { + QString name = group + "/" + line.mid(0, index); + QVariant value = QVariant(line.mid(index + 1)); + settingsMap.insert(name, value); + } + } + } + } + return true; + }; + + return QSettings(desktop, QSettings::registerFormat("ini", IniReadFunc, nullptr)); +} + +#else + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +class DesktopDeconstruction +{ + std::string m_path; + std::string m_group; + + struct Entry { + typedef std::string Key; + typedef std::any Value; + std::string name; + std::vector> pairs; + }; + + std::vector> _parse() + { + std::ifstream file(m_path); + if (!file) { + return {}; + } + + std::string line; + std::regex group_regex("\\[(.*?)\\]"); + std::vector> entrys; + std::shared_ptr currentEntry; + while (std::getline(file, line)) { + // 匹配 group,如果是,就进入判断 + if (std::regex_match(line, group_regex)) { + std::smatch result; + if (std::regex_search(line, result, group_regex)) { + currentEntry = std::make_shared(); + currentEntry.get()->name = result.str(); + entrys.push_back(currentEntry); + } + continue; + } + if (!currentEntry) { + continue; + } + + // 跳过 # 开头的注释 + if (!line.empty() && line[0] == '#') { + continue; + } + + const size_t index = line.find('='); + const std::string key = line.substr(0, index); + const std::any value = line.substr(index + 1, line.size()); + currentEntry.get()->pairs.push_back({key, value}); + } + file.close(); + + return entrys; + } + +public: + DesktopDeconstruction(const std::string &path) + : m_path(path) + { + } + ~DesktopDeconstruction() + { + } + + void beginGroup(const std::string group) + { + m_group = group; + } + + void endGroup() + { + m_group.clear(); + } + + std::vector listKeys() + { + std::vector> entrys{ _parse() }; + std::vector result; + for (const std::shared_ptr entry : entrys) { + for (const auto pair : entry.get()->pairs) { + result.push_back(pair.first); + } + } + return result; + } + + template + T value(const std::string key) + { + std::vector> entrys{ _parse() }; + for (const std::shared_ptr entry : entrys) { + if (entry.get()->name == "[" + m_group + "]") { + for (const auto pair : entry.get()->pairs) { + if (pair.first == key) { + return std::any_cast(pair.second); + } + } + } + } + return {}; + } +}; + +#endif + +#endif /* BB9B5BB3_BEAF_4D25_B4F6_55273B263973 */ diff --git a/src/modules/tools/query.h b/src/modules/tools/query.h new file mode 100644 index 0000000..4e6e3b3 --- /dev/null +++ b/src/modules/tools/query.h @@ -0,0 +1,11 @@ +#ifndef QUERY_H +#define QUERY_H + +#include + +namespace Tools { + + +} + +#endif // QUERY_H diff --git a/src/modules/util/common.cpp b/src/modules/util/common.cpp new file mode 100644 index 0000000..51ae901 --- /dev/null +++ b/src/modules/util/common.cpp @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2021-2021. Uniontech Software Ltd. All rights reserved. + * + * Author: Iceyer + * + * Maintainer: Iceyer + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include "common.h" + +namespace linglong { +namespace util { + +std::string str_vec_join(const str_vec &vec, char sep) +{ + if (vec.empty()) { + return ""; + } + + std::string s; + for (auto iterator = vec.begin(); iterator != std::prev(vec.end()); ++iterator) { + s += *iterator + sep; + } + s += vec.back(); + return s; +} + +str_vec str_spilt(const std::string &s, const std::string &sep) +{ + str_vec vec; + size_t pos_begin = 0; + size_t pos_end = 0; + while ((pos_end = s.find(sep, pos_begin)) != std::string::npos) { + auto t = s.substr(pos_begin, pos_end - pos_begin); + if (!t.empty()) { + vec.push_back(t); + } + pos_begin = pos_end + sep.size(); + } + auto t = s.substr(pos_begin, s.size() - pos_begin); + if (!t.empty()) { + vec.push_back(t); + } + return vec; +} + +} // namespace util +} // namespace linglong \ No newline at end of file diff --git a/src/modules/util/common.h b/src/modules/util/common.h new file mode 100644 index 0000000..3004d43 --- /dev/null +++ b/src/modules/util/common.h @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2020-2021. Uniontech Software Ltd. All rights reserved. + * + * Author: Iceyer + * + * Maintainer: Iceyer + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#ifndef LINGLONG_BOX_SRC_UTIL_COMMON_H_ +#define LINGLONG_BOX_SRC_UTIL_COMMON_H_ + +#include +#include +#include + +#include +#include +#include + +namespace linglong { +namespace util { + +typedef std::vector str_vec; + +str_vec str_spilt(const std::string &s, const std::string &sep); + +std::string str_vec_join(const str_vec &vec, char sep); + +inline std::string format(const std::string fmt, ...) +{ + int n = ((int)fmt.size()) * 2; + std::unique_ptr formatted; + va_list ap; + while (true) { + formatted.reset(new char[n]); + strcpy(&formatted[0], fmt.c_str()); + va_start(ap, fmt); + int final_n = vsnprintf(&formatted[0], n, fmt.c_str(), ap); + va_end(ap); + if (final_n < 0 || final_n >= n) + n += abs(final_n - n + 1); + else + break; + } + return std::string {formatted.get()}; +} + +} // namespace util +} // namespace linglong + +template +std::ostream &operator<<(std::ostream &out, const std::vector &v) +{ + if (!v.empty()) { + out << '['; + std::copy(v.begin(), v.end(), std::ostream_iterator(out, ", ")); + out << "\b\b]"; + } + return out; +} + +#endif /* LINGLONG_BOX_SRC_UTIL_COMMON_H_ */ diff --git a/src/modules/util/debug/debug.cpp b/src/modules/util/debug/debug.cpp new file mode 100644 index 0000000..c78c5ea --- /dev/null +++ b/src/modules/util/debug/debug.cpp @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2020-2021. Uniontech Software Ltd. All rights reserved. + * + * Author: Iceyer + * + * Maintainer: Iceyer + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include +#include +#include +#include +#include + +#include "../logger.h" +#include "debug.h" + +namespace linglong { + +#define DUMP_DBG(func, line) (linglong::util::Logger(linglong::util::Logger::Debug, func, line)) + +void DumpIDMap() +{ + logDbg() << "DumpIDMap Start -----------"; + std::ifstream uidMap("/proc/self/uid_map"); + for (std::string line; getline(uidMap, line);) { + logDbg() << "uid_map of pid:" << getpid() << line; + } + + std::ifstream gidMap("/proc/self/gid_map"); + for (std::string line; getline(gidMap, line);) { + logDbg() << "gid_map of pid:" << getpid() << line; + } + + auto setgroupsPath = util::format("/proc/self/setgroups"); + std::ifstream setgroupsFileRead(setgroupsPath); + std::string line; + std::getline(setgroupsFileRead, line); + logDbg() << "setgroups of pid:" << getpid() << line; + logDbg() << "DumpIDMap end -----------"; +} + +void DumpUidGidGroup() +{ + logDbg() << "DumpUidGidGroup Start -----------"; + // __uid_t uid = getuid(); // you can change this to be the uid that you want + // + // struct passwd *pw = getpwuid(uid); + // if (pw == NULL) { + // perror("getpwuid error: "); + // } + // + // int ngroups = 0; + // + // // this call is just to get the correct ngroups + // getgrouplist(pw->pw_name, pw->pw_gid, NULL, &ngroups); + // __gid_t groups[ngroups]; + // + // // here we actually get the groups + // getgrouplist(pw->pw_name, pw->pw_gid, groups, &ngroups); + // + // // example to print the groups name + // for (int i = 0; i < ngroups; i++) { + // struct group *gr = getgrgid(groups[i]); + // if (gr == NULL) { + // perror("getgrgid error: "); + // } + // printf("%s\n", gr->gr_name); + // } + + logDbg() << "getuid" << getuid() << "geteuid" << geteuid(); + logDbg() << "getgid" << getgid() << "getegid" << getegid(); + const int groupSize = getgroups(0, NULL); + __gid_t list[groupSize + 1]; + getgroups(groupSize, list); + + std::string groupListStr; + for (int i = 0; i < groupSize; ++i) { + groupListStr += util::format("%d ", list[i]); + } + logDbg() << "getgroups size " << groupSize << ", list:" << groupListStr; + logDbg() << "DumpUidGidGroup end -----------"; +} + +void DumpFilesystem(const std::string &path, const char *func, int line) +{ + if (nullptr == func) { + func = const_cast(__FUNCTION__); + } + DUMP_DBG(func, line) << "DumpFilesystem begin -----------" << path; + DIR *dir; + if ((dir = opendir(path.c_str())) != NULL) { + struct dirent *ent; + /* print all the files and directories within directory */ + while ((ent = readdir(dir)) != NULL) { + DUMP_DBG(func, line) << path + "/" + ent->d_name; + } + closedir(dir); + } else { + /* could not open directory */ + logErr() << linglong::util::errnoString() << errno; + return; + } + DUMP_DBG(func, line) << "DumpFilesystem end -----------" << path; +} + +void DumpFileInfo(const std::string &path) +{ + DumpFileInfo1(path, __FUNCTION__, __LINE__); +} + +void DumpFileInfo1(const std::string &path, const char *func, int line) +{ + struct stat st { + }; + auto ret = lstat(path.c_str(), &st); + if (0 != ret) { + DUMP_DBG(func, line) << path << util::RetErrString(ret); + } else { + DUMP_DBG(func, line) << path << st.st_uid << st.st_gid << ((st.st_mode & S_IFMT) == S_IFDIR); + } +} + +} // namespace linglong diff --git a/src/modules/util/debug/debug.h b/src/modules/util/debug/debug.h new file mode 100644 index 0000000..3b3aa5e --- /dev/null +++ b/src/modules/util/debug/debug.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2020-2021. Uniontech Software Ltd. All rights reserved. + * + * Author: Iceyer + * + * Maintainer: Iceyer + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#ifndef LINGLONG_BOX_SRC_UTIL_DEBUG_DEBUG_H_ +#define LINGLONG_BOX_SRC_UTIL_DEBUG_DEBUG_H_ + +#include + +namespace linglong { + +#define DUMP_FILESYSTEM(path) DumpFilesystem(path, __FUNCTION__, __LINE__) + +#define DUMP_FILE_INFO(path) DumpFileInfo1(path, __FUNCTION__, __LINE__) + +void DumpIDMap(); + +void DumpUidGidGroup(); + +void DumpFilesystem(const std::string &path, const char *func = nullptr, int line = -1); + +void DumpFileInfo(const std::string &path); + +void DumpFileInfo1(const std::string &path, const char *func = nullptr, int line = -1); + +} // namespace linglong + +#endif /* LINGLONG_BOX_SRC_UTIL_DEBUG_DEBUG_H_ */ diff --git a/src/modules/util/filesystem.cpp b/src/modules/util/filesystem.cpp new file mode 100644 index 0000000..763ad6d --- /dev/null +++ b/src/modules/util/filesystem.cpp @@ -0,0 +1,182 @@ +/* + * Copyright (c) 2020-2021. Uniontech Software Ltd. All rights reserved. + * + * Author: Iceyer + * + * Maintainer: Iceyer + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include +#include +#include +#include + +#include "filesystem.h" +#include "logger.h" + +namespace linglong { +namespace util { +namespace fs { + +using namespace std; + +bool create_directory(const path &p, __mode_t mode) +{ + return mkdir(p.string().c_str(), mode); +} + +bool create_directories(const path &p, __mode_t mode) +{ + std::string fullPath; + for (const auto &e : p.components()) { + fullPath += "/" + e; + if (is_dir(fullPath)) { + continue; + } + + auto ret = mkdir(fullPath.c_str(), mode); + if (0 != ret) { + logErr() << util::RetErrString(ret) << fullPath << mode; + return false; + } + } + return true; +} + +bool is_dir(const std::string &s) +{ + struct stat st { + }; + + if (0 != lstat(s.c_str(), &st)) { + return false; + } + + switch (st.st_mode & S_IFMT) { + case S_IFDIR: + return true; + default: + return false; + } +} + +bool exists(const std::string &s) +{ + struct stat st { + }; + + if (0 != lstat(s.c_str(), &st)) { + return false; + } + return true; +} + +path read_symlink(const path &p) +{ + char buf[PATH_MAX]; + if (readlink(p.string().c_str(), buf, sizeof(buf)) < 0) { + return p; + } else { + return path(string(buf)); + } +} + +file_status status(const path &p, std::error_code &ec) +{ + file_type ft; + perms perm = no_perms; + + struct stat st { + }; + + if (0 != lstat(p.string().c_str(), &st)) { + if (errno == ENOENT) { + ft = file_not_found; + } else { + ft = status_error; + } + return file_status(ft, perm); + } + + // FIXME: perms + // https://www.boost.org/doc/libs/1_75_0/libs/filesystem/doc/reference.html#file_status + // int st_perm = st.st_mode & 0xFFFF; + + // switch (st_perm) { + // case S_IRUSR: + // perm = owner_read; + // break; + // case S_IWUSR: + // case S_IXUSR: + // case S_IRWXU: + // case S_IRGRP: + // } + + switch (st.st_mode & S_IFMT) { + case S_IFREG: + ft = regular_file; + break; + case S_IFDIR: + ft = directory_file; + break; + case S_IFLNK: + ft = symlink_file; + break; + case S_IFBLK: + ft = block_file; + break; + case S_IFCHR: + ft = character_file; + break; + case S_IFIFO: + ft = fifo_file; + case S_IFSOCK: + break; + default: + ft = type_unknown; + break; + } + + return file_status(ft, perm); +} + +file_status::file_status() noexcept +{ +} + +file_status::file_status(file_type ft, perms perms) noexcept + : ft(ft) + , p(perms) +{ +} + +file_status::file_status(const file_status &fs) noexcept +{ + ft = fs.ft; + p = fs.p; +} + +file_status &file_status::operator=(const file_status &fs) noexcept +{ + ft = fs.ft; + p = fs.p; + return *this; +} + +file_status::~file_status() noexcept = default; + +file_type file_status::type() const noexcept +{ + return ft; +} + +perms file_status::permissions() const noexcept +{ + return p; +} + +} // namespace fs +} // namespace util +} // namespace linglong \ No newline at end of file diff --git a/src/modules/util/filesystem.h b/src/modules/util/filesystem.h new file mode 100644 index 0000000..a94472b --- /dev/null +++ b/src/modules/util/filesystem.h @@ -0,0 +1,151 @@ +/* + * Copyright (c) 2020-2021. Uniontech Software Ltd. All rights reserved. + * + * Author: Iceyer + * + * Maintainer: Iceyer + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#ifndef LINGLONG_BOX_SRC_UTIL_FILESYSTEM_H_ +#define LINGLONG_BOX_SRC_UTIL_FILESYSTEM_H_ + +#include +#include +#include + +#include "common.h" + +namespace linglong { +namespace util { +namespace fs { + +class path : public std::basic_string +{ +public: + explicit path(const std::string &s) + : p(util::str_spilt(s, "/")) + { + } + + path &operator=(const std::string &s) + { + p = util::str_spilt(s, "/"); + return *this; + } + + path &operator=(const path &p1) = default; + + bool operator==(const path &s) const { return this->p == s.p; } + + bool operator!=(const path &s) const { return this->p != s.p; } + + path operator/(const path &p1) const + { + auto np = *this; + std::copy(p1.p.begin(), p1.p.end(), back_inserter(np.p)); + return np; + } + + path operator/(const std::string &str) const { return operator/(path(str)); } + + path parent_path() const + { + path pn(*this); + pn.p.pop_back(); + return pn; + } + + std::string string() const { return "/" + str_vec_join(p, '/'); } + + str_vec components() const { return p; } + +private: + friend std::ostream &operator<<(std::ostream &cout, path obj); + std::vector p; +}; + +inline std::ostream &operator<<(std::ostream &cout, path obj) +{ + for (auto const &s : obj.p) { + cout << "/" << s; + } + return cout; +} + +bool create_directories(const path &p, __mode_t mode); + +enum file_type { + status_error, + file_not_found, + regular_file, + directory_file, + symlink_file, + block_file, + character_file, + fifo_file, + socket_file, + reparse_file, + type_unknown +}; + +enum perms { + no_perms, + owner_read, + owner_write, + owner_exe, + owner_all, + group_read, + group_write, + group_exe, + group_all, + others_read, + others_write, + others_exe, + others_all, + all_all, + set_uid_on_exe, + set_gid_on_exe, + sticky_bit, + perms_mask, + perms_not_known, + add_perms, + remove_perms, + symlink_perms +}; + +class file_status +{ +public: + // constructors + file_status() noexcept; + explicit file_status(file_type ft, perms p = perms_not_known) noexcept; + + // compiler generated + file_status(const file_status &) noexcept; + file_status &operator=(const file_status &) noexcept; + ~file_status() noexcept; + + // observers + file_type type() const noexcept; + perms permissions() const noexcept; + +private: + file_type ft; + perms p; +}; + +bool is_dir(const std::string &s); + +bool exists(const std::string &s); + +file_status status(const path &p, std::error_code &ec); + +path read_symlink(const path &p); + +} // namespace fs +} // namespace util +} // namespace linglong + +#endif /* LINGLONG_BOX_SRC_UTIL_FILESYSTEM_H_ */ diff --git a/src/modules/util/json.h b/src/modules/util/json.h new file mode 100644 index 0000000..e2b6f54 --- /dev/null +++ b/src/modules/util/json.h @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2020-2021. Uniontech Software Ltd. All rights reserved. + * + * Author: Iceyer + * + * Maintainer: Iceyer + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#ifndef LINGLONG_BOX_SRC_UTIL_JSON_H_ +#define LINGLONG_BOX_SRC_UTIL_JSON_H_ + +#define JSON_USE_IMPLICIT_CONVERSIONS 0 + +#include +#include + +#define tl std + +namespace nlohmann { + +template +inline void from_json(const J &j, tl::optional &v) +{ + if (j.is_null()) { + v = tl::nullopt; + } else { + v = j.template get(); + } +} + +template +inline void to_json(J &j, const tl::optional &o) +{ + if (o.has_value()) { + j = o.value(); + } +} + +} // namespace nlohmann + +namespace linglong { + +template +tl::optional optional(const nlohmann::json &j, const char *key) +{ + tl::optional o; + auto iter = j.template find(key); + if (iter != j.end()) { + o = iter->template get>(); + } + return o; +} + +} // namespace linglong + +#endif /* LINGLONG_BOX_SRC_UTIL_JSON_H_ */ diff --git a/src/modules/util/logger.cpp b/src/modules/util/logger.cpp new file mode 100644 index 0000000..962a7df --- /dev/null +++ b/src/modules/util/logger.cpp @@ -0,0 +1,42 @@ +#include "logger.h" + +namespace linglong { +namespace util { + +std::string errnoString() +{ + return util::format("errno(%d): %s", errno, strerror(errno)); +} + +std::string RetErrString(int ret) +{ + return util::format("ret(%d),errno(%d): %s", ret, errno, strerror(errno)); +} + +static Logger::Level getLogLevelFromStr(std::string str) +{ + if (str == "Debug") { + return Logger::Debug; + } else if (str == "Info") { + return Logger::Info; + } else if (str == "Warning") { + return Logger::Warring; + } else if (str == "Error") { + return Logger::Error; + } else if (str == "Fatal") { + return Logger::Fatal; + } else { + return Logger::Info; + } +} + +static Logger::Level initLogLevel() +{ + auto env = getenv("LINGLONG_LOG_LEVEL"); + return getLogLevelFromStr(env ? env : ""); +} + +Logger::Level Logger::LOGLEVEL = initLogLevel(); + +} // namespace util +} // namespace linglong diff --git a/src/modules/util/logger.h b/src/modules/util/logger.h new file mode 100644 index 0000000..d4bb638 --- /dev/null +++ b/src/modules/util/logger.h @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2020-2021. Uniontech Software Ltd. All rights reserved. + * + * Author: Iceyer + * + * Maintainer: Iceyer + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#ifndef LINGLONG_BOX_SRC_UTIL_LOGGER_H_ +#define LINGLONG_BOX_SRC_UTIL_LOGGER_H_ + +#include "util.h" + +#include + +#include +#include +#include +#include +#include + +namespace linglong { +namespace util { +std::string errnoString(); +std::string RetErrString(int); + +class Logger +{ +public: + enum Level { + Debug, + Info, + Warring, + Error, + Fatal, + }; + + explicit Logger(Level l, const char *fn, int line) + : level(l) + , function(fn) + , line(line) {}; + + ~Logger() + { + std::string prefix; + if (level < LOGLEVEL) { + return; + } + switch (level) { + case Debug: + prefix = "[DBG |"; + std::cout << prefix << getpid() << " | " << function << ":" << line << " ] " << ss.str() << std::endl; + break; + case Info: + prefix = "[IFO |"; + std::cout << "\033[1;96m"; + std::cout << prefix << getpid() << " | " << function << ":" << line << " ] " << ss.str(); + std::cout << "\033[0m" << std::endl; + break; + case Warring: + prefix = "[WAN |"; + std::cout << "\033[1;93m"; + std::cout << prefix << getpid() << " | " << function << ":" << line << " ] " << ss.str(); + std::cout << "\033[0m" << std::endl; + break; + case Error: + prefix = "[ERR |"; + std::cout << "\033[1;31m"; + std::cout << prefix << getpid() << " | " << function << ":" << line << " ] " << ss.str(); + std::cout << "\033[0m" << std::endl; + break; + case Fatal: + prefix = "[FAL |"; + std::cout << "\033[1;91m"; + std::cout << prefix << getpid() << " | " << function << ":" << line << " ] " << ss.str(); + std::cout << "\033[0m" << std::endl; + exit(-1); + break; + } + } + + template + Logger &operator<<(const T &x) + { + ss << x << " "; + return *this; + } + +private: + static Level LOGLEVEL; + Level level = Debug; + const char *function; + int line; + std::ostringstream ss; +}; +} // namespace util +} // namespace linglong + +#define logDbg() (linglong::util::Logger(linglong::util::Logger::Debug, __FUNCTION__, __LINE__)) +#define logWan() (linglong::util::Logger(linglong::util::Logger::Warring, __FUNCTION__, __LINE__)) +#define logInf() (linglong::util::Logger(linglong::util::Logger::Info, __FUNCTION__, __LINE__)) +#define logErr() (linglong::util::Logger(linglong::util::Logger::Error, __FUNCTION__, __LINE__)) +#define logFal() (linglong::util::Logger(linglong::util::Logger::Fatal, __FUNCTION__, __LINE__)) + +#endif /* LINGLONG_BOX_SRC_UTIL_LOGGER_H_ */ diff --git a/src/modules/util/macro.h b/src/modules/util/macro.h new file mode 100644 index 0000000..e75ce59 --- /dev/null +++ b/src/modules/util/macro.h @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2020-2021. Uniontech Software Ltd. All rights reserved. + * + * Author: Iceyer + * + * Maintainer: Iceyer + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#ifndef LINGLONG_BOX_SRC_UTIL_MACRO_H_ +#define LINGLONG_BOX_SRC_UTIL_MACRO_H_ + +#define LINGLONG 118 + +#define LL_VAL(str) #str +#define LL_TOSTRING(str) LL_VAL(str) + +#endif /* LINGLONG_BOX_SRC_UTIL_MACRO_H_ */ diff --git a/src/modules/util/oci_runtime.h b/src/modules/util/oci_runtime.h new file mode 100644 index 0000000..33b5dc1 --- /dev/null +++ b/src/modules/util/oci_runtime.h @@ -0,0 +1,544 @@ +/* + * Copyright (c) 2020-2021. Uniontech Software Ltd. All rights reserved. + * + * Author: Iceyer + * + * Maintainer: Iceyer + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#ifndef LINGLONG_BOX_SRC_UTIL_OCI_RUNTIME_H_ +#define LINGLONG_BOX_SRC_UTIL_OCI_RUNTIME_H_ + +#include + +#include "util.h" + +namespace linglong { + +#define LLJS_FROM(KEY) (o.KEY = j.at(#KEY).get()) +#define LLJS_FROM_OPT(KEY) (o.KEY = optional(j, #KEY)) + +#define LLJS_TO(KEY) (j[#KEY] = o.KEY) + +#define LLJS_FROM_OBJ(TYPE) inline void from_json(const nlohmann::json &j, TYPE &o) +#define LLJS_TO_OBJ(TYPE) inline void to_json(nlohmann::json &j, const TYPE &o) + +#undef linux + +struct Root { + std::string path; + tl::optional readonly; +}; + +LLJS_FROM_OBJ(Root) +{ + LLJS_FROM(path); + LLJS_FROM_OPT(readonly); +} + +LLJS_TO_OBJ(Root) +{ + LLJS_TO(path); + LLJS_TO(readonly); +} + +struct Process { + util::str_vec args; + util::str_vec env; + std::string cwd; +}; + +inline void from_json(const nlohmann::json &j, Process &o) +{ + o.args = j.at("args").get(); + o.env = j.at("env").get(); + o.cwd = j.at("cwd").get(); +} + +inline void to_json(nlohmann::json &j, const Process &o) +{ + j["args"] = o.args; + j["env"] = o.env; + j["cwd"] = o.cwd; +} + +struct Mount { + enum Type { + Unknown, + Bind, + Proc, + Sysfs, + Devpts, + Mqueue, + Tmpfs, + Cgroup, + Cgroup2, + }; + std::string destination; + std::string type; + std::string source; + util::str_vec data; + + Type fsType; + uint32_t flags = 0u; +}; + +inline void from_json(const nlohmann::json &j, Mount &o) +{ + static std::map fsTypes = { + {"bind", Mount::Bind}, {"proc", Mount::Proc}, {"devpts", Mount::Devpts}, {"mqueue", Mount::Mqueue}, + {"tmpfs", Mount::Tmpfs}, {"sysfs", Mount::Sysfs}, {"cgroup", Mount::Cgroup}, {"cgroup2", Mount::Cgroup2}, + }; + + struct mountFlag { + bool clear; + uint32_t flag; + }; + + static std::map optionFlags = { + {"acl", {false, MS_POSIXACL}}, + {"async", {true, MS_SYNCHRONOUS}}, + {"atime", {true, MS_NOATIME}}, + {"bind", {false, MS_BIND}}, + {"defaults", {false, 0}}, + {"dev", {true, MS_NODEV}}, + {"diratime", {true, MS_NODIRATIME}}, + {"dirsync", {false, MS_DIRSYNC}}, + {"exec", {true, MS_NOEXEC}}, + {"iversion", {false, MS_I_VERSION}}, + {"lazytime", {false, MS_LAZYTIME}}, + {"loud", {true, MS_SILENT}}, + {"mand", {false, MS_MANDLOCK}}, + {"noacl", {true, MS_POSIXACL}}, + {"noatime", {false, MS_NOATIME}}, + {"nodev", {false, MS_NODEV}}, + {"nodiratime", {false, MS_NODIRATIME}}, + {"noexec", {false, MS_NOEXEC}}, + {"noiversion", {true, MS_I_VERSION}}, + {"nolazytime", {true, MS_LAZYTIME}}, + {"nomand", {true, MS_MANDLOCK}}, + {"norelatime", {true, MS_RELATIME}}, + {"nostrictatime", {true, MS_STRICTATIME}}, + {"nosuid", {false, MS_NOSUID}}, + // {"nosymfollow",{false, MS_NOSYMFOLLOW}}, // since kernel 5.10 + {"rbind", {false, MS_BIND | MS_REC}}, + {"relatime", {false, MS_RELATIME}}, + {"remount", {false, MS_REMOUNT}}, + {"ro", {false, MS_RDONLY}}, + {"rw", {true, MS_RDONLY}}, + {"silent", {false, MS_SILENT}}, + {"strictatime", {false, MS_STRICTATIME}}, + {"suid", {true, MS_NOSUID}}, + {"sync", {false, MS_SYNCHRONOUS}}, + // {"symfollow",{true, MS_NOSYMFOLLOW}}, // since kernel 5.10 + }; + + o.destination = j.at("destination").get(); + o.type = j.at("type").get(); + o.fsType = fsTypes.find(o.type)->second; + if (o.fsType == Mount::Bind) { + o.flags = MS_BIND; + } + o.source = j.at("source").get(); + o.data = {}; + + // Parse options to data and flags. + // FIXME: support "propagation flags" and "recursive mount attrs" + // https://github.com/opencontainers/runc/blob/c83abc503de7e8b3017276e92e7510064eee02a8/libcontainer/specconv/spec_linux.go#L958 + auto options = j.value("options", util::str_vec()); + for (auto const &opt : options) { + auto it = optionFlags.find(opt); + if (it != optionFlags.end()) { + if (it->second.clear) { + o.flags &= ~it->second.flag; + } else + o.flags |= it->second.flag; + } else { + o.data.push_back(opt); + } + } +} + +inline void to_json(nlohmann::json &j, const Mount &o) +{ + j["destination"] = o.destination; + j["source"] = o.source; + j["type"] = o.type; + j["options"] = o.data; // FIXME: this data is not original options, some of them have been prased to flags. +} + +struct Namespace { + int type; +}; + +static std::map namespaceType = { + {"pid", CLONE_NEWPID}, {"uts", CLONE_NEWUTS}, {"mount", CLONE_NEWNS}, {"cgroup", CLONE_NEWCGROUP}, + {"network", CLONE_NEWNET}, {"ipc", CLONE_NEWIPC}, {"user", CLONE_NEWUSER}, +}; + +inline void from_json(const nlohmann::json &j, Namespace &o) +{ + o.type = namespaceType.find(j.at("type").get())->second; +} + +inline void to_json(nlohmann::json &j, const Namespace &o) +{ + auto matchPair = std::find_if(std::begin(namespaceType), std::end(namespaceType), + [&](const std::pair &pair) { return pair.second == o.type; }); + j["type"] = matchPair->first; +} + +struct IDMap { + uint64_t containerID = 0u; + uint64_t hostID = 0u; + uint64_t size = 0u; +}; + +inline void from_json(const nlohmann::json &j, IDMap &o) +{ + o.hostID = j.value("hostID", 0); + o.containerID = j.value("containerID", 0); + o.size = j.value("size", 0); +} + +inline void to_json(nlohmann::json &j, const IDMap &o) +{ + j["hostID"] = o.hostID; + j["containerID"] = o.containerID; + j["size"] = o.size; +} + +typedef std::string SeccompAction; +typedef std::string SeccompArch; + +struct SyscallArg { + u_int index; // require + u_int64_t value; // require + u_int64_t valueTwo; // optional + std::string op; // require +}; + +inline void from_json(const nlohmann::json &j, SyscallArg &o) +{ + o.index = j.at("index").get(); + o.value = j.at("value").get(); + o.valueTwo = j.value("valueTwo", u_int64_t()); + o.op = j.at("op").get(); +} + +inline void to_json(nlohmann::json &j, const SyscallArg &o) +{ + j["index"] = o.index; + j["value"] = o.value; + j["valueTwo"] = o.valueTwo; + j["op"] = o.op; +} + +struct Syscall { + util::str_vec names; + SeccompAction action; + std::vector args; +}; + +inline void from_json(const nlohmann::json &j, Syscall &o) +{ + o.names = j.at("names").get(); + o.action = j.at("action").get(); + o.args = j.value("args", std::vector()); +} + +inline void to_json(nlohmann::json &j, const Syscall &o) +{ + j["names"] = o.names; + j["action"] = o.action; + j["args"] = o.args; +} + +struct Seccomp { + SeccompAction defaultAction = "INVALID_ACTION"; + std::vector architectures; + std::vector syscalls; +}; + +inline void from_json(const nlohmann::json &j, Seccomp &o) +{ + o.defaultAction = j.at("defaultAction").get(); + o.architectures = j.value("architectures", std::vector {}); + o.syscalls = j.value("syscalls", std::vector {}); +} + +inline void to_json(nlohmann::json &j, const Seccomp &o) +{ + j["defaultAction"] = o.defaultAction; + j["architectures"] = o.architectures; + j["syscalls"] = o.syscalls; +} + +// https://github.com/containers/crun/blob/main/crun.1.md#memory-controller +struct ResourceMemory { + int64_t limit = -1; + int64_t reservation = -1; + int64_t swap = -1; +}; + +inline void from_json(const nlohmann::json &j, ResourceMemory &o) +{ + o.limit = j.value("limit", -1); + o.reservation = j.value("reservation", -1); + o.swap = j.value("swap", -1); +} + +inline void to_json(nlohmann::json &j, const ResourceMemory &o) +{ + j["limit"] = o.limit; + j["reservation"] = o.reservation; + j["swap"] = o.swap; +} + +// https://github.com/containers/crun/blob/main/crun.1.md#cpu-controller +// support v1 and v2 with conversion +struct ResourceCPU { + u_int64_t shares = 1024; + int64_t quota = 100000; + u_int64_t period = 100000; + // int64_t realtimeRuntime; + // int64_t realtimePeriod; + // std::string cpus; + // std::string mems; +}; + +inline void from_json(const nlohmann::json &j, ResourceCPU &o) +{ + o.shares = j.value("shares", 1024); + o.quota = j.value("quota", 100000); + o.period = j.value("period", 100000); +} + +inline void to_json(nlohmann::json &j, const ResourceCPU &o) +{ + j["shares"] = o.shares; + j["quota"] = o.quota; + j["period"] = o.period; +} + +struct Resources { + ResourceMemory memory; + ResourceCPU cpu; +}; + +inline void from_json(const nlohmann::json &j, Resources &o) +{ + o.cpu = j.value("cpu", ResourceCPU()); + o.memory = j.value("memory", ResourceMemory()); +} + +inline void to_json(nlohmann::json &j, const Resources &o) +{ + j["cpu"] = o.cpu; + j["memory"] = o.memory; +} + +struct Linux { + std::vector namespaces; + std::vector uidMappings; + std::vector gidMappings; + tl::optional seccomp; + std::string cgroupsPath; + Resources resources; +}; + +inline void from_json(const nlohmann::json &j, Linux &o) +{ + o.namespaces = j.at("namespaces").get>(); + o.uidMappings = j.value("uidMappings", std::vector {}); + o.gidMappings = j.value("gidMappings", std::vector {}); + o.seccomp = optional(j, "seccomp"); + o.cgroupsPath = j.value("cgroupsPath", ""); + o.resources = j.value("resources", Resources()); +} + +inline void to_json(nlohmann::json &j, const Linux &o) +{ + j["namespaces"] = o.namespaces; + j["uidMappings"] = o.uidMappings; + j["gidMappings"] = o.gidMappings; + j["seccomp"] = o.seccomp; + j["cgroupsPath"] = o.cgroupsPath; + j["resources"] = o.resources; +} + +/* + "hooks": { + "prestart": [ + { + "path": "/usr/bin/fix-mounts", + "args": ["fix-mounts", "arg1", "arg2"], + "env": [ "key1=value1"] + }, + { + "path": "/usr/bin/setup-network" + } + ], + "poststart": [ + { + "path": "/usr/bin/notify-start", + "timeout": 5 + } + ], + "poststop": [ + { + "path": "/usr/sbin/cleanup.sh", + "args": ["cleanup.sh", "-f"] + } + ] + } + */ + +struct Hook { + std::string path; + tl::optional args; + tl::optional> env; +}; + +inline void from_json(const nlohmann::json &j, Hook &o) +{ + LLJS_FROM(path); + LLJS_FROM_OPT(args); + LLJS_FROM_OPT(env); +} + +inline void to_json(nlohmann::json &j, const Hook &o) +{ + j["path"] = o.path; + j["args"] = o.args; + j["env"] = o.env; +} + +struct Hooks { + tl::optional> prestart; + tl::optional> poststart; + tl::optional> poststop; +}; + +inline void from_json(const nlohmann::json &j, Hooks &o) +{ + LLJS_FROM_OPT(prestart); + LLJS_FROM_OPT(poststart); + LLJS_FROM_OPT(poststop); +} + +inline void to_json(nlohmann::json &j, const Hooks &o) +{ + j["poststop"] = o.poststop; + j["poststart"] = o.poststart; + j["prestart"] = o.prestart; +} + +struct AnnotationsOverlayfs { + std::string lower_parent; + std::string upper; + std::string workdir; + std::vector mounts; +}; + +LLJS_FROM_OBJ(AnnotationsOverlayfs) +{ + LLJS_FROM(lower_parent); + LLJS_FROM(upper); + LLJS_FROM(workdir); + LLJS_FROM(mounts); +} + +LLJS_TO_OBJ(AnnotationsOverlayfs) +{ + LLJS_TO(lower_parent); + LLJS_TO(upper); + LLJS_TO(workdir); + LLJS_TO(mounts); +} + +struct AnnotationsNativeRootfs { + std::vector mounts; +}; + +LLJS_FROM_OBJ(AnnotationsNativeRootfs) +{ + LLJS_FROM(mounts); +} + +LLJS_TO_OBJ(AnnotationsNativeRootfs) +{ + LLJS_TO(mounts); +} + +struct Annotations { + std::string container_root_path; + tl::optional overlayfs; + tl::optional native; +}; + +LLJS_FROM_OBJ(Annotations) +{ + LLJS_FROM(container_root_path); + LLJS_FROM_OPT(overlayfs); + LLJS_FROM_OPT(native); +} + +LLJS_TO_OBJ(Annotations) +{ + LLJS_TO(container_root_path); + LLJS_TO(overlayfs); + LLJS_TO(native); +} + +struct Runtime { + std::string version; + Root root; + Process process; + std::string hostname; + Linux linux; + tl::optional> mounts; + tl::optional hooks; + tl::optional annotations; +}; + +inline void from_json(const nlohmann::json &j, Runtime &o) +{ + o.version = j.at("ociVersion").get(); + o.hostname = j.at("hostname").get(); + LLJS_FROM(process); + o.mounts = optional(j, "mounts"); + LLJS_FROM(linux); + // maybe optional + LLJS_FROM(root); + o.hooks = optional(j, "hooks"); + LLJS_FROM_OPT(annotations); +} + +inline void to_json(nlohmann::json &j, const Runtime &o) +{ + j["ociVersion"] = o.version; + j["hostname"] = o.hostname; + j["process"] = o.process; + j["mounts"] = o.mounts; + j["linux"] = o.linux; + j["root"] = o.root; + j["hooks"] = o.hooks; + LLJS_TO(annotations); +} + +inline static Runtime fromFile(const std::string &filepath) +{ + return util::json::fromFile(filepath).get(); +} + +inline static Runtime fromString(const std::string &content) +{ + return util::json::fromByteArray(content).get(); +} + +} // namespace linglong + +#endif /* LINGLONG_BOX_SRC_UTIL_OCI_RUNTIME_H_ */ diff --git a/src/modules/util/platform.cpp b/src/modules/util/platform.cpp new file mode 100644 index 0000000..5831fb5 --- /dev/null +++ b/src/modules/util/platform.cpp @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2020-2021. Uniontech Software Ltd. All rights reserved. + * + * Author: Iceyer + * + * Maintainer: Iceyer + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include "platform.h" +#include "logger.h" +#include "debug/debug.h" + +#include +#include +#include +#include + +namespace linglong { + +const int kStackSize = (1024 * 1024); + +namespace util { + +int PlatformClone(int (*callback)(void *), int flags, void *arg, ...) +{ + char *stack; + char *stackTop; + + stack = reinterpret_cast( + mmap(nullptr, kStackSize, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_STACK, -1, 0)); + if (stack == MAP_FAILED) { + return EXIT_FAILURE; + } + + stackTop = stack + kStackSize; + + return clone(callback, stackTop, flags, arg); +} + +int Exec(const util::str_vec &args, tl::optional> env_list) +{ + auto targetArgc = args.size(); + const char *targetArgv[targetArgc + 1]; + for (decltype(targetArgc) i = 0; i < targetArgc; i++) { + targetArgv[i] = args[i].c_str(); + } + targetArgv[targetArgc] = nullptr; + + auto targetEnvc = env_list.has_value() ? env_list->size() : 0; + const char **targetEnvv = targetEnvc ? new const char *[targetEnvc + 1] : nullptr; + if (targetEnvv) { + for (decltype(targetEnvc) i = 0; i < targetEnvc; i++) { + targetEnvv[i] = env_list.value().at(i).c_str(); + } + targetEnvv[targetEnvc] = nullptr; + } + + logDbg() << "execve" << targetArgv[0] << " in pid:" << getpid(); + + int ret = execvpe(targetArgv[0], const_cast(targetArgv), const_cast(targetEnvv)); + + delete[] targetEnvv; + + return ret; +} + +} // namespace util +} // namespace linglong diff --git a/src/modules/util/platform.h b/src/modules/util/platform.h new file mode 100644 index 0000000..be3e0d5 --- /dev/null +++ b/src/modules/util/platform.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2020-2021. Uniontech Software Ltd. All rights reserved. + * + * Author: Iceyer + * + * Maintainer: Iceyer + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#ifndef LINGLONG_BOX_SRC_UTIL_PLATFORM_H_ +#define LINGLONG_BOX_SRC_UTIL_PLATFORM_H_ + +#include "common.h" +#include + +#define tl std + +namespace linglong { + +namespace util { + +int PlatformClone(int (*callback)(void *), int flags, void *arg, ...); + +int Exec(const util::str_vec &args, tl::optional> env_list); + +} // namespace util + +} // namespace linglong + +#endif /* LINGLONG_BOX_SRC_UTIL_PLATFORM_H_ */ diff --git a/src/modules/util/semaphore.cpp b/src/modules/util/semaphore.cpp new file mode 100644 index 0000000..c74bac0 --- /dev/null +++ b/src/modules/util/semaphore.cpp @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2020-2021. Uniontech Software Ltd. All rights reserved. + * + * Author: Iceyer + * + * Maintainer: Iceyer + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include "semaphore.h" + +#include + +#include "logger.h" + +namespace linglong { + +union semun { + int val; + struct semid_ds *buf; + ushort *array; +}; + +struct Semaphore::SemaphorePrivate { + explicit SemaphorePrivate(Semaphore *parent = nullptr) + : q_ptr(parent) + { + (void)q_ptr; + } + + struct sembuf sem_lock = {0, -1, SEM_UNDO}; + + struct sembuf sem_unlock = {0, 1, SEM_UNDO}; + + int sem_id = -1; + + Semaphore *q_ptr; +}; + +Semaphore::Semaphore(int key) + : dd_ptr(new SemaphorePrivate(this)) +{ + dd_ptr->sem_id = semget(key, 1, IPC_CREAT | 0666); + if (dd_ptr->sem_id < 0) { + logErr() << "semget failed" << util::RetErrString(dd_ptr->sem_id); + } +} + +Semaphore::~Semaphore() = default; + +int Semaphore::init() +{ + union semun sem_union = {0}; + sem_union.val = 0; + logDbg() << "semctl " << dd_ptr->sem_id; + if (semctl(dd_ptr->sem_id, 0, SETVAL, sem_union) == -1) { + logErr() << "semctl failed" << util::RetErrString(-1); + } + return 0; +} + +int Semaphore::passeren() +{ + return semop(dd_ptr->sem_id, &dd_ptr->sem_lock, 1); +} + +int Semaphore::vrijgeven() +{ + return semop(dd_ptr->sem_id, &dd_ptr->sem_unlock, 1); +} + +} // namespace linglong diff --git a/src/modules/util/semaphore.h b/src/modules/util/semaphore.h new file mode 100644 index 0000000..1345551 --- /dev/null +++ b/src/modules/util/semaphore.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2020-2021. Uniontech Software Ltd. All rights reserved. + * + * Author: Iceyer + * + * Maintainer: Iceyer + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#ifndef LINGLONG_BOX_SRC_UTIL_SEMAPHORE_H_ +#define LINGLONG_BOX_SRC_UTIL_SEMAPHORE_H_ + +#include + +namespace linglong { + +class Semaphore +{ +public: + explicit Semaphore(int key); + ~Semaphore(); + + int init(); + + //! passeren -1 to value + //! if value < 0, block + //! \return + int passeren(); + + //! passeren +1 to value + //! if value <= 0, release process in queue + //! \return + int vrijgeven(); + +private: + struct SemaphorePrivate; + std::unique_ptr dd_ptr; +}; + +} // namespace linglong + +#endif /* LINGLONG_BOX_SRC_UTIL_SEMAPHORE_H_ */ diff --git a/src/modules/util/util.h b/src/modules/util/util.h new file mode 100644 index 0000000..ed0a604 --- /dev/null +++ b/src/modules/util/util.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2020-2021. Uniontech Software Ltd. All rights reserved. + * + * Author: Iceyer + * + * Maintainer: Iceyer + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#ifndef LINGLONG_BOX_SRC_UTIL_UTIL_H_ +#define LINGLONG_BOX_SRC_UTIL_UTIL_H_ + +#include "macro.h" +#include "common.h" +#include "logger.h" +#include "semaphore.h" +#include "filesystem.h" +#include "json.h" + +#include + +namespace linglong { +namespace util { +namespace json { + +inline nlohmann::json fromByteArray(const std::string &content) +{ + return nlohmann::json::parse(content); +} + +inline nlohmann::json fromFile(const std::string &filepath) +{ + std::ifstream f(filepath); + std::string str((std::istreambuf_iterator(f)), std::istreambuf_iterator()); + auto j = fromByteArray(str); + return j; +} + +} // namespace json +} // namespace util +} // namespace linglong + +#endif /* LINGLONG_BOX_SRC_UTIL_UTIL_H_ */ diff --git a/src/service/CMakeLists.txt b/src/service/CMakeLists.txt new file mode 100644 index 0000000..201e392 --- /dev/null +++ b/src/service/CMakeLists.txt @@ -0,0 +1,55 @@ +set(CMAKE_INCLUDE_CURRENT_DIR ON) +set(CMAKE_AUTOMOC ON) +set(CMAKE_AUTORCC ON) + +find_package(Qt5 REQUIRED COMPONENTS Core DBus Concurrent) + +qt5_add_dbus_adaptor(ADAPTER_SOURCES + ../../DBus/org.desktopspec.ApplicationManager.xml + impl/application_manager.h + ApplicationManager) + +qt5_add_dbus_adaptor(ADAPTER_SOURCES + ../../DBus/org.desktopspec.Application.xml + impl/application.h + Application) + +qt5_add_dbus_adaptor(ADAPTER_SOURCES + ../../DBus/org.desktopspec.ApplicationInstance.xml + impl/application_instance.h + ApplicationInstance) +add_definitions(-DUSE_QT) + +set(SRCS ${PROJECT_BINARY_DIR}/src/define.h) + +set(SRCS + ./main.cpp + ./impl/application_manager.h + ./impl/application_manager.cpp + ./impl/application.h + ./impl/application.cpp + ./impl/application_instance.h + ./impl/application_instance.cpp + ../modules/applicationhelper/helper.h + ../modules/tools/desktop_deconstruction.hpp + ../modules/socket/server.h + ../modules/socket/server.cpp + ../modules/methods/basic.h + ../modules/methods/task.hpp + ../modules/methods/instance.hpp + ../modules/methods/quit.hpp + ../modules/methods/registe.hpp +) + +add_executable(deepin-application-manager + ${SRCS} + ${ADAPTER_SOURCES} +) + +target_link_libraries(deepin-application-manager + Qt5::Core + Qt5::DBus + Qt5::Concurrent +) + +install(TARGETS deepin-application-manager DESTINATION bin) diff --git a/src/service/impl/application.cpp b/src/service/impl/application.cpp new file mode 100644 index 0000000..e15b8a4 --- /dev/null +++ b/src/service/impl/application.cpp @@ -0,0 +1,168 @@ +#include "application.h" + +#include +#include +#include +#include +#include + +#include "../modules/applicationhelper/helper.h" +#include "../modules/tools/desktop_deconstruction.hpp" +#include "application_instance.h" + +class ApplicationPrivate { + Application *q_ptr = nullptr; + Q_DECLARE_PUBLIC(Application); + + QList> instances; + QSharedPointer helper; + QString m_prefix; + Application::Type m_type; + +public: + ApplicationPrivate(Application *parent) : q_ptr(parent) {} + + ~ApplicationPrivate() {} + + QStringList categories() const + { + return helper->categories(); + } + + QString icon() const + { + return helper->icon(); + } + + QString id() const + { + return helper->id(); + } + + QStringList mimetypes() const + { + return helper->mimetypes(); + } + + QString comment(const QString &locale) const + { + return helper->comment(locale); + } + + QString name(const QString &name) const + { + return helper->name(name); + } +}; + +Application::Application(const QString &prefix, Type type, QSharedPointer helper) : QObject(nullptr), dd_ptr(new ApplicationPrivate(this)) +{ + Q_D(Application); + + d->helper = helper; + d->m_type = type; + d->m_prefix = prefix; +} + +Application::~Application() {} + +QStringList Application::categories() const +{ + Q_D(const Application); + + return d->categories(); +} + +QString Application::icon() const +{ + Q_D(const Application); + + return d->icon(); +} + +QString Application::id() const +{ + Q_D(const Application); + + const QString id{ d->id() }; + return QString("/%1/%2/%3").arg(d->m_prefix).arg(d->m_type == Application::Type::System ? "system" : "user").arg(id); +} + +QList Application::instances() const +{ + Q_D(const Application); + + QList result; + + for (const auto &ins : d->instances) { + result << ins->path(); + } + + return result; +} + +QStringList Application::mimetypes() const +{ + Q_D(const Application); + + return d->mimetypes(); +} + +QString Application::Comment(const QString &locale) +{ + Q_D(const Application); + + return d->comment(locale); +} + +QString Application::Name(const QString &locale) +{ + Q_D(const Application); + + return d->name(locale); +} + +QDBusObjectPath Application::path() const +{ + return QDBusObjectPath(QString("/org/desktopspec/Application/%1").arg(QString(QCryptographicHash::hash(id().toUtf8(), QCryptographicHash::Md5).toHex()))); +} + +Application::Type Application::type() const +{ + Q_D(const Application); + + return d->m_type; +} + +QString Application::filePath() const +{ + Q_D(const Application); + + return d->helper->desktop(); +} + +QSharedPointer Application::createInstance() +{ + Q_D(Application); + + d->instances << QSharedPointer(new ApplicationInstance(this, d->helper)); + + connect(d->instances.last().get(), &ApplicationInstance::taskFinished, this, [=] { + for (auto it = d->instances.begin(); it != d->instances.end(); ++it) { + if ((*it).data() == sender()) { + d->instances.erase(it); + return; + } + } + qWarning() << "The instance should not be found!"; + }); + + return d->instances.last(); +} + +QString Application::prefix() const +{ + Q_D(const Application); + + return d->m_prefix; +} diff --git a/src/service/impl/application.h b/src/service/impl/application.h new file mode 100644 index 0000000..73ea62b --- /dev/null +++ b/src/service/impl/application.h @@ -0,0 +1,60 @@ +#ifndef A216803F_06DD_4F40_8FD1_5BAED85905BE +#define A216803F_06DD_4F40_8FD1_5BAED85905BE + +#include +#include +#include + +namespace modules { + namespace ApplicationHelper { + class Helper; + } +} + +class ApplicationPrivate; +class ApplicationInstance; +class Application : public QObject { + Q_OBJECT + QScopedPointer dd_ptr; + Q_DECLARE_PRIVATE_D(qGetPtrHelper(dd_ptr), Application) +public: + enum class Type { + System, + User, + }; + + Application(const QString& prefix, Type type, QSharedPointer helper); + ~Application() override; + +public: // PROPERTIES + Q_PROPERTY(QStringList categories READ categories) + QStringList categories() const; + + Q_PROPERTY(QString icon READ icon) + QString icon() const; + + Q_PROPERTY(QString id READ id) + QString id() const; + + Q_PROPERTY(QList instances READ instances) + QList instances() const; + + Q_PROPERTY(QStringList mimetypes READ mimetypes) + QStringList mimetypes() const; + + QDBusObjectPath path() const; + + QString prefix() const; + + Type type() const; + + QString filePath() const; + + QSharedPointer createInstance(); + +public Q_SLOTS: // METHODS + QString Comment(const QString &locale); + QString Name(const QString &locale); +}; + +#endif /* A216803F_06DD_4F40_8FD1_5BAED85905BE */ diff --git a/src/service/impl/application_instance.cpp b/src/service/impl/application_instance.cpp new file mode 100644 index 0000000..44980cd --- /dev/null +++ b/src/service/impl/application_instance.cpp @@ -0,0 +1,169 @@ +#include "application_instance.h" + +#include + +#include +#include +#include +#include +#include +#include + +#include "../../modules/applicationhelper/helper.h" +#include "application.h" +#include "applicationinstanceadaptor.h" + +#ifdef DEFINE_LOADER_PATH +#include "../../src/define.h" +#endif + +class ApplicationInstancePrivate { + ApplicationInstance* q_ptr = nullptr; + Q_DECLARE_PUBLIC(ApplicationInstance); + + Application* application; + ApplicationInstanceAdaptor* adapter; + QString m_path; + QSharedPointer helper; + QDateTime startupTime; + QString m_id; + +public: + ApplicationInstancePrivate(ApplicationInstance* parent) : q_ptr(parent) + { + startupTime = QDateTime::currentDateTime(); + m_id = QString(QCryptographicHash::hash(QUuid::createUuid().toByteArray(), QCryptographicHash::Md5).toHex()); + m_path = QString("/org/desktopspec/ApplicationInstance/%1").arg(m_id); + adapter = new ApplicationInstanceAdaptor(q_ptr); + } + + ~ApplicationInstancePrivate() + { + // disconnect dbus + QDBusConnection::sessionBus().unregisterObject(m_path); + } + + void run() + { +#ifdef DEFINE_LOADER_PATH + const QString task_hash{ QString("DAM_TASK_HASH=%1").arg(m_id) }; + const QString task_type{ "DAM_TASK_TYPE=freedesktop " }; + QProcess* p = new QProcess(q_ptr); + p->connect(p, static_cast(&QProcess::finished), p, [=] { + qInfo().noquote() << p->readAllStandardOutput(); + qWarning().noquote() << p->readAllStandardError(); + }); + p->connect(p, static_cast(&QProcess::finished), q_ptr, &ApplicationInstance::taskFinished); + p->connect(p, &QProcess::readyReadStandardOutput, p, [=] { qInfo() << p->readAllStandardOutput(); }); + p->connect(p, &QProcess::readyReadStandardError, p, [=] { qWarning() << p->readAllStandardError(); }); + p->setProgram(LOADER_PATH); + QProcessEnvironment env = QProcessEnvironment::systemEnvironment(); + env.insert("DAM_TASK_HASH", m_id); + env.insert("DAM_TASK_TYPE", "freedesktop"); + p->setEnvironment(env.toStringList()); + p->start(); + p->waitForStarted(); + if (p->state() == QProcess::ProcessState::NotRunning) { + emit q_ptr->taskFinished(p->exitCode()); + } +#else + QDBusInterface systemd("org.freedesktop.systemd1", "/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager"); + QDBusReply reply = systemd.call("StartUnit", QString("org.deskspec.application.instance@%1.service").arg(m_id), "replace-irreversibly"); + if (!reply.isValid()) { + qInfo() << reply.error(); + q_ptr->deleteLater(); + } +#endif + } + + void _exit() + { +#ifdef LOADER_PATH +#else + QDBusInterface systemd("org.freedesktop.systemd1", "/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager"); + qInfo() << systemd.call("StopUnit", QString("org.deskspec.application.instance@%1.service").arg(m_id), "replace-irreversibly"); +#endif + } + + void _kill() {} +}; + +ApplicationInstance::ApplicationInstance(Application* parent, QSharedPointer helper) : QObject(nullptr), dd_ptr(new ApplicationInstancePrivate(this)) +{ + Q_D(ApplicationInstance); + + d->application = parent; + d->helper = helper; + + QTimer::singleShot(0, this, [=] { + QDBusConnection::sessionBus().registerObject(d->m_path, "org.desktopspec.ApplicationInstance", this); + d->run(); + }); +} + +ApplicationInstance::~ApplicationInstance() +{ + Q_D(ApplicationInstance); + qDebug() << "instance quit " << d->helper->desktop(); +} + +QDBusObjectPath ApplicationInstance::id() const +{ + Q_D(const ApplicationInstance); + + return d->application->path(); +} + +QString ApplicationInstance::hash() const +{ + Q_D(const ApplicationInstance); + + return d->m_id; +} + +quint64 ApplicationInstance::startuptime() const +{ + Q_D(const ApplicationInstance); + + return d->startupTime.toSecsSinceEpoch(); +} + +QDBusObjectPath ApplicationInstance::path() const +{ + Q_D(const ApplicationInstance); + + return QDBusObjectPath(d->m_path); +} + +Methods::Task ApplicationInstance::taskInfo() const +{ + Q_D(const ApplicationInstance); + + Methods::Task task; + task.id = d->m_id.toStdString(); + task.runId = d->application->id().toStdString(); + task.date = QString::number(startuptime()).toStdString(); + + // TODO: debug to display environment + task.environments.insert({ "DISPLAY", ":0" }); + auto sysEnv = QProcessEnvironment::systemEnvironment(); + for (const auto& key : sysEnv.keys()) { + task.environments.insert({ key.toStdString(), sysEnv.value(key).toStdString() }); + } + + return task; +} + +void ApplicationInstance::Exit() +{ + Q_D(ApplicationInstance); + + return d->_exit(); +} + +void ApplicationInstance::Kill() +{ + Q_D(ApplicationInstance); + + return d->_kill(); +} diff --git a/src/service/impl/application_instance.h b/src/service/impl/application_instance.h new file mode 100644 index 0000000..64e13c8 --- /dev/null +++ b/src/service/impl/application_instance.h @@ -0,0 +1,44 @@ +#ifndef D6D05668_8A58_43AA_91C5_C6278643A1AF +#define D6D05668_8A58_43AA_91C5_C6278643A1AF + +#include +#include + +#include "../../modules/methods/task.hpp" + +namespace modules { +namespace ApplicationHelper { +class Helper; +} +} // namespace modules + +class Application; +class ApplicationInstancePrivate; +class ApplicationInstance : public QObject { + Q_OBJECT + QScopedPointer dd_ptr; + Q_DECLARE_PRIVATE_D(qGetPtrHelper(dd_ptr), ApplicationInstance) +public: + ApplicationInstance(Application* parent, QSharedPointer helper); + ~ApplicationInstance() override; + +public: // PROPERTIES + Q_PROPERTY(QDBusObjectPath id READ id) + QDBusObjectPath id() const; + + Q_PROPERTY(quint64 startuptime READ startuptime) + quint64 startuptime() const; + + QDBusObjectPath path() const; + QString hash() const; + Methods::Task taskInfo() const; + +Q_SIGNALS: + void taskFinished(int exitCode) const; + +public Q_SLOTS: // METHODS + void Exit(); + void Kill(); +}; + +#endif /* D6D05668_8A58_43AA_91C5_C6278643A1AF */ diff --git a/src/service/impl/application_manager.cpp b/src/service/impl/application_manager.cpp new file mode 100644 index 0000000..83eb2b8 --- /dev/null +++ b/src/service/impl/application_manager.cpp @@ -0,0 +1,195 @@ +#include "application_manager.h" + +#include + +#include +#include +#include +#include +#include + +#include "../../modules/methods/basic.h" +#include "../../modules/methods/instance.hpp" +#include "../../modules/methods/quit.hpp" +#include "../../modules/methods/registe.hpp" +#include "../../modules/methods/task.hpp" +#include "../../modules/socket/server.h" +#include "application.h" +#include "application_instance.h" +#include "applicationinstanceadaptor.h" + +class ApplicationManagerPrivate : public QObject { + Q_OBJECT + ApplicationManager *q_ptr = nullptr; + Q_DECLARE_PUBLIC(ApplicationManager); + + QList> applications; + Socket::Server server; + std::multimap> tasks; + +public: + ApplicationManagerPrivate(ApplicationManager *parent) : QObject(parent), q_ptr(parent) + { + const QString socketPath{ QString("/run/user/%1/deepin-application-manager.socket").arg(getuid()) }; + connect(&server, &Socket::Server::onReadyRead, this, &ApplicationManagerPrivate::recvClientData, Qt::QueuedConnection); + server.listen(socketPath.toStdString()); + } + ~ApplicationManagerPrivate() {} + +private: + void recvClientData(int socket, const std::vector &data) + { + std::string tmp; + for (char c : data) { + tmp += c; + } + using namespace nlohmann; + if (json::parse(tmp).is_null()) { + server.close(socket); + return; + } + Methods::Basic basic = json::parse(tmp); + do { + if (basic.type == "instance") { + Methods::Instance instance = nlohmann::json::parse(tmp); + auto find = tasks.find(instance.hash); + if (find != tasks.end()) { + nlohmann::json result = find->second->taskInfo(); + write(socket, result.dump()); + tasks.erase(find); + break; + } + } + if (basic.type == "quit") { + Methods::Quit quit = json::parse(tmp); + server.close(socket); + std::cout << "client quit" << std::endl; + break; + } + if (basic.type == "registe") { + Methods::Registe registe = nlohmann::json::parse(tmp); + Methods::Registe result; + result.state = false; + //std::lock_guard lock(task_mutex); + for (auto it = tasks.begin(); it != tasks.end(); ++it) { + result.state = true; + result.hash = it->first; + } + write(socket, json(result).dump()); + std::cout << "registe a new client" << std::endl; + break; + } + write(socket, json().dump()); + } while (false); + } + + void write(int socket, const std::vector &data) + { + std::vector tmp = data; + tmp.push_back('\0'); + server.write(socket, tmp); + } + void write(int socket, const std::string &data) + { + std::vector result; + std::copy(data.cbegin(), data.cend(), std::back_inserter(result)); + return write(socket, result); + } + void write(int socket, const char c) + { + return write(socket, std::vector(c)); + } +}; + +ApplicationManager::ApplicationManager(QObject *parent) : QObject(parent), dd_ptr(new ApplicationManagerPrivate(this)) {} + +ApplicationManager::~ApplicationManager() {} + +void ApplicationManager::addApplication(const QList> &list) +{ + Q_D(ApplicationManager); + + d->applications = list; +} + +QDBusObjectPath ApplicationManager::GetId(int pid) +{ + return {}; +} + +QDBusObjectPath ApplicationManager::GetInformation(const QString &id) +{ + Q_D(ApplicationManager); + + for (const QSharedPointer &app : d->applications) { + if (app->id() == id) { + return app->path(); + } + } + return {}; +} + +QList ApplicationManager::GetInstances(const QString &id) +{ + Q_D(const ApplicationManager); + + for (const auto &app : d->applications) { + if (app->id() == id) { + return app->instances(); + } + } + + return {}; +} + +QDBusObjectPath ApplicationManager::Run(const QString &id) +{ + Q_D(ApplicationManager); + + // 创建一个实例 + for (const QSharedPointer &app : d->applications) { + if (app->id() == id) { + // 创建任务所需的数据,并记录到任务队列,等待 loader 消耗 + QSharedPointer instance{ app->createInstance() }; + const std::string hash{ instance->hash().toStdString() }; + connect(instance.get(), &ApplicationInstance::taskFinished, this, [=] { + for (auto it = d->tasks.begin(); it != d->tasks.end(); ++it) { + if (it->first == hash) { + d->tasks.erase(it); + break; + } + } + }); + d->tasks.insert(std::make_pair(hash, instance)); + return instance->path(); + } + } + return {}; +} + +QList ApplicationManager::instances() const +{ + Q_D(const ApplicationManager); + + QList result; + + for (const auto &app : d->applications) { + result += app->instances(); + } + + return result; +} + +QList ApplicationManager::list() const +{ + Q_D(const ApplicationManager); + + QList result; + for (const QSharedPointer &app : d->applications) { + result << app->path(); + } + + return result; +} + +#include "application_manager.moc" diff --git a/src/service/impl/application_manager.h b/src/service/impl/application_manager.h new file mode 100644 index 0000000..0dfc7e0 --- /dev/null +++ b/src/service/impl/application_manager.h @@ -0,0 +1,43 @@ +#ifndef A2862DC7_5DA3_4129_9796_671D88015BED +#define A2862DC7_5DA3_4129_9796_671D88015BED + +#include +#include +#include + +class Application; +class ApplicationInstance; +class ApplicationManagerPrivate; +class ApplicationManager : public QObject { + Q_OBJECT + Q_PROPERTY(QList instances READ instances) + Q_PROPERTY(QList list READ list) + QScopedPointer dd_ptr; + Q_DECLARE_PRIVATE_D(qGetPtrHelper(dd_ptr), ApplicationManager) + + ApplicationManager(QObject *parent = nullptr); + +public: + ~ApplicationManager() override; + static ApplicationManager* Instance() { + static ApplicationManager manager; + return &manager; + } + + void addApplication(const QList> &list); + +signals: + void requestCreateInstance(const QSharedPointer instance); + +public: // PROPERTIES + QList instances() const; + QList list() const; + +public Q_SLOTS: // METHODS + QDBusObjectPath GetId(int pid); + QDBusObjectPath GetInformation(const QString &id); + QList GetInstances(const QString &id); + QDBusObjectPath Run(const QString &id); +}; + +#endif /* A2862DC7_5DA3_4129_9796_671D88015BED */ diff --git a/src/service/main.cpp b/src/service/main.cpp new file mode 100644 index 0000000..6b6e8b6 --- /dev/null +++ b/src/service/main.cpp @@ -0,0 +1,81 @@ +#include + +#include "impl/application_manager.h" +#include "impl/application.h" +#include "applicationmanageradaptor.h" +#include "applicationadaptor.h" +#include "../modules/applicationhelper/helper.h" + +#include +#include + +QFileInfoList scan(const QString &path) +{ + QDir dir(path); + dir.setFilter(QDir::Files); + dir.setNameFilters({ "*.desktop" }); + return dir.entryInfoList(); +} + +// 扫描系统目录 +// 扫描用户目录 +QList> scanFiles() +{ + QList> applications; + auto apps = scan("/usr/share/applications/"); + for (const QFileInfo &info : apps) { + applications << QSharedPointer(new Application( + "freedesktop", + Application::Type::System, + QSharedPointer(new modules::ApplicationHelper::Helper(info.filePath())) + )); + } + + struct passwd *user = getpwent(); + while (user) { + auto userApps = scan(QString("%1/.local/share/applications/").arg(user->pw_dir)); + for (const QFileInfo &info : userApps) { + applications << QSharedPointer(new Application( + "freedesktop", + Application::Type::System, + QSharedPointer(new modules::ApplicationHelper::Helper(info.filePath())) + )); + } + user = getpwent(); + } + endpwent(); + auto linglong = scan("/deepin/linglong/entries/share/applications/"); + for (const QFileInfo &info : linglong) { + applications << QSharedPointer(new Application( + "linglong", + Application::Type::System, + QSharedPointer(new modules::ApplicationHelper::Helper(info.filePath())) + )); + } + + return applications; +} + + +int main(int argc, char *argv[]) +{ + QCoreApplication app(argc, argv); + + new ApplicationManagerAdaptor(ApplicationManager::Instance()); + + QDBusConnection::sessionBus().registerService("org.desktopspec.Application"); + QDBusConnection::sessionBus().registerService("org.desktopspec.ApplicationManager"); + QDBusConnection::sessionBus().registerObject("/org/desktopspec/ApplicationManager", "org.desktopspec.ApplicationManager", ApplicationManager::Instance()); + + QList> apps{ scanFiles() }; + QList> appAdapters; + for (const QSharedPointer app : apps) { + QSharedPointer adapter = QSharedPointer(new ApplicationAdaptor(app.get())); + appAdapters << adapter; + QDBusConnection::sessionBus().registerObject(app->path().path(), "org.desktopspec.Application", app.get()); + } + + ApplicationManager::Instance()->addApplication(apps); + + return app.exec(); +}