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