feat: 初始代码
AM初始代码,迁移自gitlabwh Log: Task: https://pms.uniontech.com/task-view-108539.html Influence: Change-Id: I6096f97e5d68d13796ff5dc51d9858c0f40264a0
This commit is contained in:
parent
9414feb1f7
commit
0b22bb3adf
9
CMakeLists.txt
Normal file
9
CMakeLists.txt
Normal file
@ -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)
|
15
DBus/org.desktopspec.Application.xml
Normal file
15
DBus/org.desktopspec.Application.xml
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
<interface name='org.desktopspec.Application'>
|
||||||
|
<method name='Name'>
|
||||||
|
<arg type='s' name='locale' direction='in' />
|
||||||
|
<arg type='s' name='value' direction='out' />
|
||||||
|
</method>
|
||||||
|
<method name='Comment'>
|
||||||
|
<arg type='s' name='comment' direction='in' />
|
||||||
|
<arg type='s' name='value' direction='out' />
|
||||||
|
</method>
|
||||||
|
<property access='read' type='as' name='categories' />
|
||||||
|
<property access='read' type='as' name='mimetypes' />
|
||||||
|
<property access='read' type='s' name='id' />
|
||||||
|
<property access="read" type='s' name='icon' />
|
||||||
|
<property access='read' type='ao' name='instances' />
|
||||||
|
</interface>
|
8
DBus/org.desktopspec.ApplicationInstance.xml
Normal file
8
DBus/org.desktopspec.ApplicationInstance.xml
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
<interface name='org.desktopspec.ApplicationInstance'>
|
||||||
|
<method name='Exit'>
|
||||||
|
</method>
|
||||||
|
<method name='Kill'>
|
||||||
|
</method>
|
||||||
|
<property access='read' type='t(116)' name='startuptime' />
|
||||||
|
<property access='read' type='o' name='id' />
|
||||||
|
</interface>
|
20
DBus/org.desktopspec.ApplicationManager.xml
Normal file
20
DBus/org.desktopspec.ApplicationManager.xml
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
<interface name='org.desktopspec.ApplicationManager'>
|
||||||
|
<method name='Run'>
|
||||||
|
<arg type='s' name='id' direction='in'/>
|
||||||
|
<arg type='o' name='instance' direction='out'/>
|
||||||
|
</method>
|
||||||
|
<method name='GetInformation'>
|
||||||
|
<arg type='s' name='id' direction='in' />
|
||||||
|
<arg type='o' name='info' direction='out' />
|
||||||
|
</method>
|
||||||
|
<method name='GetId'>
|
||||||
|
<arg type='i' name='pid' direction='in' />
|
||||||
|
<arg type='o' name='instance' direction='out' />
|
||||||
|
</method>
|
||||||
|
<method name='GetInstances'>
|
||||||
|
<arg type='s' name='id' direction='in' />
|
||||||
|
<arg type='ao' name='instances' direction='out' />
|
||||||
|
</method>
|
||||||
|
<property access='read' type='ao' name='list' />
|
||||||
|
<property access='read' type='ao' name='instances' />
|
||||||
|
</interface>
|
7
README.md
Normal file
7
README.md
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
## Build Dep
|
||||||
|
|
||||||
|
* Qt
|
||||||
|
* Core
|
||||||
|
* Network
|
||||||
|
* DBus
|
||||||
|
* [nlohmann/json](https://github.com/nlohmann/json)
|
5
debian/changelog
vendored
Normal file
5
debian/changelog
vendored
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
dde-application-manager (1.0.0-1) UNRELEASED; urgency=medium
|
||||||
|
|
||||||
|
* Initial release
|
||||||
|
|
||||||
|
-- <tanfeng@uniontech.com> Wed, 30 Mar 2022 11:49:45 +0800
|
23
debian/control
vendored
Normal file
23
debian/control
vendored
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
Source: dde-application-manager
|
||||||
|
Section: devel
|
||||||
|
Priority: optional
|
||||||
|
Maintainer: Deepin Packages Builder <packages@deepin.com>
|
||||||
|
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.
|
21
debian/copyright
vendored
Normal file
21
debian/copyright
vendored
Normal file
@ -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 <http://www.gnu.org/licenses/>
|
||||||
|
.
|
||||||
|
On Debian systems, the complete text of the GNU General
|
||||||
|
Public License version 3 can be found in "/usr/share/common-licenses/GPL-3".
|
6
debian/rules
vendored
Executable file
6
debian/rules
vendored
Executable file
@ -0,0 +1,6 @@
|
|||||||
|
#!/usr/bin/make -f
|
||||||
|
|
||||||
|
include /usr/share/dpkg/default.mk
|
||||||
|
|
||||||
|
%:
|
||||||
|
dh $@ --buildsystem=cmake
|
1
debian/source/format
vendored
Normal file
1
debian/source/format
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
3.0 (quilt)
|
1
misc/CMakeLists.txt
Normal file
1
misc/CMakeLists.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
add_subdirectory(systemd)
|
6
misc/systemd/CMakeLists.txt
Normal file
6
misc/systemd/CMakeLists.txt
Normal file
@ -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/)
|
8
misc/systemd/org.deskspec.application.instance@.service
Normal file
8
misc/systemd/org.deskspec.application.instance@.service
Normal file
@ -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
|
7
misc/systemd/org.deskspec.application.manager.service
Normal file
7
misc/systemd/org.deskspec.application.manager.service
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=Deepin Application Manager service
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=DBus
|
||||||
|
BusName=org.deskspec.ApplicationManager
|
||||||
|
ExecStart=/usr/bin/deepin-application-service
|
15
src/CMakeLists.txt
Normal file
15
src/CMakeLists.txt
Normal file
@ -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")
|
6
src/define.h.in
Normal file
6
src/define.h.in
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
#ifndef DEFINE_H_IN
|
||||||
|
#define DEFINE_H_IN
|
||||||
|
|
||||||
|
#define LOADER_PATH "@LOADER_PATH@"
|
||||||
|
|
||||||
|
#endif // DEFINE_H_IN
|
39
src/loader/CMakeLists.txt
Normal file
39
src/loader/CMakeLists.txt
Normal file
@ -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)
|
252
src/loader/main.cpp
Normal file
252
src/loader/main.cpp
Normal file
@ -0,0 +1,252 @@
|
|||||||
|
#include <getopt.h>
|
||||||
|
#include <grp.h>
|
||||||
|
#include <pwd.h>
|
||||||
|
#include <sched.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/prctl.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/un.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include <filesystem>
|
||||||
|
#include <iostream>
|
||||||
|
#include <thread>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#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<std::string> 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<std::string>("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<std::string, std::string>& pair) -> std::string { return pair.first + "=" + pair.second; });
|
||||||
|
|
||||||
|
std::istringstream stream(dd.value<std::string>("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<void*>(&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;
|
||||||
|
}
|
79
src/modules/applicationhelper/helper.h
Normal file
79
src/modules/applicationhelper/helper.h
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
#ifndef A7B4B7B1_0422_4EC9_8441_778273A85F9C
|
||||||
|
#define A7B4B7B1_0422_4EC9_8441_778273A85F9C
|
||||||
|
|
||||||
|
#include "../tools/desktop_deconstruction.hpp"
|
||||||
|
|
||||||
|
#include <QString>
|
||||||
|
|
||||||
|
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 <typename T>
|
||||||
|
T value(const QString &key) const
|
||||||
|
{
|
||||||
|
QSettings settings = DesktopDeconstruction(m_file);
|
||||||
|
settings.beginGroup("Desktop Entry");
|
||||||
|
return settings.value(key).value<T>();
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList categories() const
|
||||||
|
{
|
||||||
|
QStringList result;
|
||||||
|
QStringList tmp{ value<QString>("Categories").split(";") };
|
||||||
|
for (auto t : tmp) {
|
||||||
|
if (t.isEmpty()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
result << t;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString icon() const
|
||||||
|
{
|
||||||
|
return value<QString>("Icon");
|
||||||
|
}
|
||||||
|
|
||||||
|
QString id() const
|
||||||
|
{
|
||||||
|
return m_file.split("/").last().split(".").first();
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList mimetypes() const
|
||||||
|
{
|
||||||
|
QStringList result;
|
||||||
|
QStringList tmp{ value<QString>("MimeType").split(";") };
|
||||||
|
for (auto t : tmp) {
|
||||||
|
if (t.isEmpty()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
result << t;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString comment(const QString &locale) const
|
||||||
|
{
|
||||||
|
return value<QString>(QString("Comment[%1]").arg(locale));
|
||||||
|
}
|
||||||
|
|
||||||
|
QString name(const QString &name) const
|
||||||
|
{
|
||||||
|
return value<QString>(QString("Name[%1]").arg(name));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} // namespace ApplicationHelper
|
||||||
|
} // namespace modules
|
||||||
|
|
||||||
|
#endif /* A7B4B7B1_0422_4EC9_8441_778273A85F9C */
|
18
src/modules/methods/basic.h
Normal file
18
src/modules/methods/basic.h
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
#ifndef BASIC_H_
|
||||||
|
#define BASIC_H_
|
||||||
|
|
||||||
|
#include <nlohmann/json.hpp>
|
||||||
|
|
||||||
|
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_
|
25
src/modules/methods/instance.hpp
Normal file
25
src/modules/methods/instance.hpp
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
#ifndef C664E26D_6517_412B_950F_07E20963349E
|
||||||
|
#define C664E26D_6517_412B_950F_07E20963349E
|
||||||
|
|
||||||
|
#include <nlohmann/json.hpp>
|
||||||
|
|
||||||
|
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 */
|
27
src/modules/methods/quit.hpp
Normal file
27
src/modules/methods/quit.hpp
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
#ifndef QUIT_H_
|
||||||
|
#define QUIT_H_
|
||||||
|
|
||||||
|
#include <nlohmann/json.hpp>
|
||||||
|
|
||||||
|
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_
|
29
src/modules/methods/registe.hpp
Normal file
29
src/modules/methods/registe.hpp
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
#ifndef REGISTER_H_
|
||||||
|
#define REGISTER_H_
|
||||||
|
|
||||||
|
#include <nlohmann/json.hpp>
|
||||||
|
|
||||||
|
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_
|
36
src/modules/methods/task.hpp
Normal file
36
src/modules/methods/task.hpp
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
#ifndef B0B88BD6_CF1E_4E87_926A_E6DBE6B9B19C
|
||||||
|
#define B0B88BD6_CF1E_4E87_926A_E6DBE6B9B19C
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
#include <nlohmann/json.hpp>
|
||||||
|
#include <utility>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace Methods {
|
||||||
|
struct Task {
|
||||||
|
std::string id;
|
||||||
|
std::string runId;
|
||||||
|
std::string type{ "task" };
|
||||||
|
std::string date;
|
||||||
|
std::vector<std::string> arguments;
|
||||||
|
std::multimap<std::string, std::string> 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 */
|
124
src/modules/socket/client.cpp
Normal file
124
src/modules/socket/client.cpp
Normal file
@ -0,0 +1,124 @@
|
|||||||
|
#include "client.h"
|
||||||
|
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <sys/un.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
|
namespace Socket {
|
||||||
|
struct ClientPrivate {
|
||||||
|
Client* q_ptr;
|
||||||
|
int socket_fd;
|
||||||
|
std::function<void(const std::vector<char>&)> 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<char> 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<void(const std::vector<char>&)> func)
|
||||||
|
{
|
||||||
|
d_ptr->readFunc = func;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Client::waitForFinished()
|
||||||
|
{
|
||||||
|
d_ptr->readThread->join();
|
||||||
|
}
|
||||||
|
} // namespace Socket
|
25
src/modules/socket/client.h
Normal file
25
src/modules/socket/client.h
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
#ifndef B1D5EB4F_7645_4BDA_87D6_6B80A4910014
|
||||||
|
#define B1D5EB4F_7645_4BDA_87D6_6B80A4910014
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
#include <memory>
|
||||||
|
#include <nlohmann/json.hpp>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace Socket {
|
||||||
|
class ClientPrivate;
|
||||||
|
class Client {
|
||||||
|
std::unique_ptr<ClientPrivate> 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<void(const std::vector<char>&)> func);
|
||||||
|
void waitForFinished();
|
||||||
|
};
|
||||||
|
} // namespace Socket
|
||||||
|
|
||||||
|
#endif /* B1D5EB4F_7645_4BDA_87D6_6B80A4910014 */
|
147
src/modules/socket/server.cpp
Normal file
147
src/modules/socket/server.cpp
Normal file
@ -0,0 +1,147 @@
|
|||||||
|
#include "server.h"
|
||||||
|
|
||||||
|
#include <qnamespace.h>
|
||||||
|
#include <qobject.h>
|
||||||
|
#include <qobjectdefs.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <sys/un.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include <QThread>
|
||||||
|
#include <QtConcurrent/QtConcurrent>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
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<char> 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<char> &data)
|
||||||
|
{
|
||||||
|
::write(socket, data.data(), data.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
void closeClient(int socket)
|
||||||
|
{
|
||||||
|
::close(socket);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Server::Server() : QObject(nullptr), d_ptr(new ServerPrivate(this))
|
||||||
|
{
|
||||||
|
qRegisterMetaType<std::vector<char>>("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<char> &data)
|
||||||
|
{
|
||||||
|
d_ptr->write(socket, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Server::close(int socket)
|
||||||
|
{
|
||||||
|
d_ptr->closeClient(socket);
|
||||||
|
}
|
||||||
|
} // namespace Socket
|
||||||
|
|
||||||
|
#include "server.moc"
|
27
src/modules/socket/server.h
Normal file
27
src/modules/socket/server.h
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
#ifndef F358257E_94E5_4A6C_91A8_4B6E57999E7B
|
||||||
|
#define F358257E_94E5_4A6C_91A8_4B6E57999E7B
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
#include <QObject>
|
||||||
|
|
||||||
|
namespace Socket {
|
||||||
|
class ServerPrivate;
|
||||||
|
class Server : public QObject {
|
||||||
|
Q_OBJECT
|
||||||
|
std::unique_ptr<ServerPrivate> d_ptr;
|
||||||
|
|
||||||
|
public:
|
||||||
|
Server();
|
||||||
|
~Server();
|
||||||
|
bool listen(const std::string& host);
|
||||||
|
void write(int socket, const std::vector<char>& data);
|
||||||
|
void close(int socket);
|
||||||
|
Q_SIGNALS:
|
||||||
|
void onReadyRead(int socket, const std::vector<char>& data) const;
|
||||||
|
};
|
||||||
|
} // namespace Socket
|
||||||
|
|
||||||
|
#endif /* F358257E_94E5_4A6C_91A8_4B6E57999E7B */
|
157
src/modules/tools/desktop_deconstruction.hpp
Normal file
157
src/modules/tools/desktop_deconstruction.hpp
Normal file
@ -0,0 +1,157 @@
|
|||||||
|
#ifndef BB9B5BB3_BEAF_4D25_B4F6_55273B263973
|
||||||
|
#define BB9B5BB3_BEAF_4D25_B4F6_55273B263973
|
||||||
|
|
||||||
|
#ifdef USE_QT
|
||||||
|
|
||||||
|
#include <QSettings>
|
||||||
|
#include <QTextStream>
|
||||||
|
|
||||||
|
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 <algorithm>
|
||||||
|
#include <any>
|
||||||
|
#include <fstream>
|
||||||
|
#include <iostream>
|
||||||
|
#include <memory>
|
||||||
|
#include <regex>
|
||||||
|
#include <string>
|
||||||
|
#include <utility>
|
||||||
|
#include <variant>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
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<std::pair<Key, Value>> pairs;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::vector<std::shared_ptr<Entry>> _parse()
|
||||||
|
{
|
||||||
|
std::ifstream file(m_path);
|
||||||
|
if (!file) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string line;
|
||||||
|
std::regex group_regex("\\[(.*?)\\]");
|
||||||
|
std::vector<std::shared_ptr<struct Entry>> entrys;
|
||||||
|
std::shared_ptr<Entry> 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<Entry>();
|
||||||
|
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<std::string> listKeys()
|
||||||
|
{
|
||||||
|
std::vector<std::shared_ptr<Entry>> entrys{ _parse() };
|
||||||
|
std::vector<std::string> result;
|
||||||
|
for (const std::shared_ptr<Entry> entry : entrys) {
|
||||||
|
for (const auto pair : entry.get()->pairs) {
|
||||||
|
result.push_back(pair.first);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
T value(const std::string key)
|
||||||
|
{
|
||||||
|
std::vector<std::shared_ptr<Entry>> entrys{ _parse() };
|
||||||
|
for (const std::shared_ptr<Entry> entry : entrys) {
|
||||||
|
if (entry.get()->name == "[" + m_group + "]") {
|
||||||
|
for (const auto pair : entry.get()->pairs) {
|
||||||
|
if (pair.first == key) {
|
||||||
|
return std::any_cast<T>(pair.second);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* BB9B5BB3_BEAF_4D25_B4F6_55273B263973 */
|
11
src/modules/tools/query.h
Normal file
11
src/modules/tools/query.h
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
#ifndef QUERY_H
|
||||||
|
#define QUERY_H
|
||||||
|
|
||||||
|
#include <QStringList>
|
||||||
|
|
||||||
|
namespace Tools {
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // QUERY_H
|
50
src/modules/util/common.cpp
Normal file
50
src/modules/util/common.cpp
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2021-2021. Uniontech Software Ltd. All rights reserved.
|
||||||
|
*
|
||||||
|
* Author: Iceyer <me@iceyer.net>
|
||||||
|
*
|
||||||
|
* Maintainer: Iceyer <me@iceyer.net>
|
||||||
|
*
|
||||||
|
* 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
|
64
src/modules/util/common.h
Normal file
64
src/modules/util/common.h
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2020-2021. Uniontech Software Ltd. All rights reserved.
|
||||||
|
*
|
||||||
|
* Author: Iceyer <me@iceyer.net>
|
||||||
|
*
|
||||||
|
* Maintainer: Iceyer <me@iceyer.net>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef LINGLONG_BOX_SRC_UTIL_COMMON_H_
|
||||||
|
#define LINGLONG_BOX_SRC_UTIL_COMMON_H_
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <cstring>
|
||||||
|
#include <cstdarg>
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <iostream>
|
||||||
|
#include <iterator>
|
||||||
|
|
||||||
|
namespace linglong {
|
||||||
|
namespace util {
|
||||||
|
|
||||||
|
typedef std::vector<std::string> 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<char[]> 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<typename T>
|
||||||
|
std::ostream &operator<<(std::ostream &out, const std::vector<T> &v)
|
||||||
|
{
|
||||||
|
if (!v.empty()) {
|
||||||
|
out << '[';
|
||||||
|
std::copy(v.begin(), v.end(), std::ostream_iterator<T>(out, ", "));
|
||||||
|
out << "\b\b]";
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* LINGLONG_BOX_SRC_UTIL_COMMON_H_ */
|
126
src/modules/util/debug/debug.cpp
Normal file
126
src/modules/util/debug/debug.cpp
Normal file
@ -0,0 +1,126 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2020-2021. Uniontech Software Ltd. All rights reserved.
|
||||||
|
*
|
||||||
|
* Author: Iceyer <me@iceyer.net>
|
||||||
|
*
|
||||||
|
* Maintainer: Iceyer <me@iceyer.net>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <pwd.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <grp.h>
|
||||||
|
#include <dirent.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
|
||||||
|
#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<char *>(__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
|
34
src/modules/util/debug/debug.h
Normal file
34
src/modules/util/debug/debug.h
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2020-2021. Uniontech Software Ltd. All rights reserved.
|
||||||
|
*
|
||||||
|
* Author: Iceyer <me@iceyer.net>
|
||||||
|
*
|
||||||
|
* Maintainer: Iceyer <me@iceyer.net>
|
||||||
|
*
|
||||||
|
* 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 <string>
|
||||||
|
|
||||||
|
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_ */
|
182
src/modules/util/filesystem.cpp
Normal file
182
src/modules/util/filesystem.cpp
Normal file
@ -0,0 +1,182 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2020-2021. Uniontech Software Ltd. All rights reserved.
|
||||||
|
*
|
||||||
|
* Author: Iceyer <me@iceyer.net>
|
||||||
|
*
|
||||||
|
* Maintainer: Iceyer <me@iceyer.net>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <string>
|
||||||
|
#include <climits>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#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
|
151
src/modules/util/filesystem.h
Normal file
151
src/modules/util/filesystem.h
Normal file
@ -0,0 +1,151 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2020-2021. Uniontech Software Ltd. All rights reserved.
|
||||||
|
*
|
||||||
|
* Author: Iceyer <me@iceyer.net>
|
||||||
|
*
|
||||||
|
* Maintainer: Iceyer <me@iceyer.net>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef LINGLONG_BOX_SRC_UTIL_FILESYSTEM_H_
|
||||||
|
#define LINGLONG_BOX_SRC_UTIL_FILESYSTEM_H_
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <ostream>
|
||||||
|
#include <system_error>
|
||||||
|
|
||||||
|
#include "common.h"
|
||||||
|
|
||||||
|
namespace linglong {
|
||||||
|
namespace util {
|
||||||
|
namespace fs {
|
||||||
|
|
||||||
|
class path : public std::basic_string<char>
|
||||||
|
{
|
||||||
|
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<std::string> 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_ */
|
58
src/modules/util/json.h
Normal file
58
src/modules/util/json.h
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2020-2021. Uniontech Software Ltd. All rights reserved.
|
||||||
|
*
|
||||||
|
* Author: Iceyer <me@iceyer.net>
|
||||||
|
*
|
||||||
|
* Maintainer: Iceyer <me@iceyer.net>
|
||||||
|
*
|
||||||
|
* 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 <nlohmann/json.hpp>
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
|
#define tl std
|
||||||
|
|
||||||
|
namespace nlohmann {
|
||||||
|
|
||||||
|
template<class J, class T>
|
||||||
|
inline void from_json(const J &j, tl::optional<T> &v)
|
||||||
|
{
|
||||||
|
if (j.is_null()) {
|
||||||
|
v = tl::nullopt;
|
||||||
|
} else {
|
||||||
|
v = j.template get<T>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class J, class T>
|
||||||
|
inline void to_json(J &j, const tl::optional<T> &o)
|
||||||
|
{
|
||||||
|
if (o.has_value()) {
|
||||||
|
j = o.value();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace nlohmann
|
||||||
|
|
||||||
|
namespace linglong {
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
tl::optional<T> optional(const nlohmann::json &j, const char *key)
|
||||||
|
{
|
||||||
|
tl::optional<T> o;
|
||||||
|
auto iter = j.template find(key);
|
||||||
|
if (iter != j.end()) {
|
||||||
|
o = iter->template get<tl::optional<T>>();
|
||||||
|
}
|
||||||
|
return o;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace linglong
|
||||||
|
|
||||||
|
#endif /* LINGLONG_BOX_SRC_UTIL_JSON_H_ */
|
42
src/modules/util/logger.cpp
Normal file
42
src/modules/util/logger.cpp
Normal file
@ -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
|
107
src/modules/util/logger.h
Normal file
107
src/modules/util/logger.h
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2020-2021. Uniontech Software Ltd. All rights reserved.
|
||||||
|
*
|
||||||
|
* Author: Iceyer <me@iceyer.net>
|
||||||
|
*
|
||||||
|
* Maintainer: Iceyer <me@iceyer.net>
|
||||||
|
*
|
||||||
|
* 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 <unistd.h>
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <sstream>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cstring>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
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<class T>
|
||||||
|
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_ */
|
19
src/modules/util/macro.h
Normal file
19
src/modules/util/macro.h
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2020-2021. Uniontech Software Ltd. All rights reserved.
|
||||||
|
*
|
||||||
|
* Author: Iceyer <me@iceyer.net>
|
||||||
|
*
|
||||||
|
* Maintainer: Iceyer <me@iceyer.net>
|
||||||
|
*
|
||||||
|
* 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_ */
|
544
src/modules/util/oci_runtime.h
Normal file
544
src/modules/util/oci_runtime.h
Normal file
@ -0,0 +1,544 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2020-2021. Uniontech Software Ltd. All rights reserved.
|
||||||
|
*
|
||||||
|
* Author: Iceyer <me@iceyer.net>
|
||||||
|
*
|
||||||
|
* Maintainer: Iceyer <me@iceyer.net>
|
||||||
|
*
|
||||||
|
* 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 <sys/mount.h>
|
||||||
|
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
namespace linglong {
|
||||||
|
|
||||||
|
#define LLJS_FROM(KEY) (o.KEY = j.at(#KEY).get<decltype(o.KEY)>())
|
||||||
|
#define LLJS_FROM_OPT(KEY) (o.KEY = optional<decltype(o.KEY)::value_type>(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<bool> 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<util::str_vec>();
|
||||||
|
o.env = j.at("env").get<util::str_vec>();
|
||||||
|
o.cwd = j.at("cwd").get<std::string>();
|
||||||
|
}
|
||||||
|
|
||||||
|
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<std::string, Mount::Type> 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<std::string, mountFlag> 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<std::string>();
|
||||||
|
o.type = j.at("type").get<std::string>();
|
||||||
|
o.fsType = fsTypes.find(o.type)->second;
|
||||||
|
if (o.fsType == Mount::Bind) {
|
||||||
|
o.flags = MS_BIND;
|
||||||
|
}
|
||||||
|
o.source = j.at("source").get<std::string>();
|
||||||
|
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<std::string, int> 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<std::string>())->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<std::string, int> &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<u_int>();
|
||||||
|
o.value = j.at("value").get<u_int64_t>();
|
||||||
|
o.valueTwo = j.value("valueTwo", u_int64_t());
|
||||||
|
o.op = j.at("op").get<std::string>();
|
||||||
|
}
|
||||||
|
|
||||||
|
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<SyscallArg> args;
|
||||||
|
};
|
||||||
|
|
||||||
|
inline void from_json(const nlohmann::json &j, Syscall &o)
|
||||||
|
{
|
||||||
|
o.names = j.at("names").get<util::str_vec>();
|
||||||
|
o.action = j.at("action").get<SeccompAction>();
|
||||||
|
o.args = j.value("args", std::vector<SyscallArg>());
|
||||||
|
}
|
||||||
|
|
||||||
|
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<SeccompArch> architectures;
|
||||||
|
std::vector<Syscall> syscalls;
|
||||||
|
};
|
||||||
|
|
||||||
|
inline void from_json(const nlohmann::json &j, Seccomp &o)
|
||||||
|
{
|
||||||
|
o.defaultAction = j.at("defaultAction").get<std::string>();
|
||||||
|
o.architectures = j.value("architectures", std::vector<SeccompArch> {});
|
||||||
|
o.syscalls = j.value("syscalls", std::vector<Syscall> {});
|
||||||
|
}
|
||||||
|
|
||||||
|
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<Namespace> namespaces;
|
||||||
|
std::vector<IDMap> uidMappings;
|
||||||
|
std::vector<IDMap> gidMappings;
|
||||||
|
tl::optional<Seccomp> seccomp;
|
||||||
|
std::string cgroupsPath;
|
||||||
|
Resources resources;
|
||||||
|
};
|
||||||
|
|
||||||
|
inline void from_json(const nlohmann::json &j, Linux &o)
|
||||||
|
{
|
||||||
|
o.namespaces = j.at("namespaces").get<std::vector<Namespace>>();
|
||||||
|
o.uidMappings = j.value("uidMappings", std::vector<IDMap> {});
|
||||||
|
o.gidMappings = j.value("gidMappings", std::vector<IDMap> {});
|
||||||
|
o.seccomp = optional<decltype(o.seccomp)::value_type>(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<util::str_vec> args;
|
||||||
|
tl::optional<std::vector<std::string>> 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<std::vector<Hook>> prestart;
|
||||||
|
tl::optional<std::vector<Hook>> poststart;
|
||||||
|
tl::optional<std::vector<Hook>> 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<Mount> 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<Mount> mounts;
|
||||||
|
};
|
||||||
|
|
||||||
|
LLJS_FROM_OBJ(AnnotationsNativeRootfs)
|
||||||
|
{
|
||||||
|
LLJS_FROM(mounts);
|
||||||
|
}
|
||||||
|
|
||||||
|
LLJS_TO_OBJ(AnnotationsNativeRootfs)
|
||||||
|
{
|
||||||
|
LLJS_TO(mounts);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Annotations {
|
||||||
|
std::string container_root_path;
|
||||||
|
tl::optional<AnnotationsOverlayfs> overlayfs;
|
||||||
|
tl::optional<AnnotationsNativeRootfs> 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<std::vector<Mount>> mounts;
|
||||||
|
tl::optional<Hooks> hooks;
|
||||||
|
tl::optional<Annotations> annotations;
|
||||||
|
};
|
||||||
|
|
||||||
|
inline void from_json(const nlohmann::json &j, Runtime &o)
|
||||||
|
{
|
||||||
|
o.version = j.at("ociVersion").get<std::string>();
|
||||||
|
o.hostname = j.at("hostname").get<std::string>();
|
||||||
|
LLJS_FROM(process);
|
||||||
|
o.mounts = optional<decltype(o.mounts)::value_type>(j, "mounts");
|
||||||
|
LLJS_FROM(linux);
|
||||||
|
// maybe optional
|
||||||
|
LLJS_FROM(root);
|
||||||
|
o.hooks = optional<decltype(o.hooks)::value_type>(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<Runtime>();
|
||||||
|
}
|
||||||
|
|
||||||
|
inline static Runtime fromString(const std::string &content)
|
||||||
|
{
|
||||||
|
return util::json::fromByteArray(content).get<Runtime>();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace linglong
|
||||||
|
|
||||||
|
#endif /* LINGLONG_BOX_SRC_UTIL_OCI_RUNTIME_H_ */
|
70
src/modules/util/platform.cpp
Normal file
70
src/modules/util/platform.cpp
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2020-2021. Uniontech Software Ltd. All rights reserved.
|
||||||
|
*
|
||||||
|
* Author: Iceyer <me@iceyer.net>
|
||||||
|
*
|
||||||
|
* Maintainer: Iceyer <me@iceyer.net>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "platform.h"
|
||||||
|
#include "logger.h"
|
||||||
|
#include "debug/debug.h"
|
||||||
|
|
||||||
|
#include <sched.h>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <sys/mman.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
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<char *>(
|
||||||
|
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<std::vector<std::string>> 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<char **>(targetArgv), const_cast<char **>(targetEnvv));
|
||||||
|
|
||||||
|
delete[] targetEnvv;
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace util
|
||||||
|
} // namespace linglong
|
31
src/modules/util/platform.h
Normal file
31
src/modules/util/platform.h
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2020-2021. Uniontech Software Ltd. All rights reserved.
|
||||||
|
*
|
||||||
|
* Author: Iceyer <me@iceyer.net>
|
||||||
|
*
|
||||||
|
* Maintainer: Iceyer <me@iceyer.net>
|
||||||
|
*
|
||||||
|
* 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 <optional>
|
||||||
|
|
||||||
|
#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<std::vector<std::string>> env_list);
|
||||||
|
|
||||||
|
} // namespace util
|
||||||
|
|
||||||
|
} // namespace linglong
|
||||||
|
|
||||||
|
#endif /* LINGLONG_BOX_SRC_UTIL_PLATFORM_H_ */
|
73
src/modules/util/semaphore.cpp
Normal file
73
src/modules/util/semaphore.cpp
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2020-2021. Uniontech Software Ltd. All rights reserved.
|
||||||
|
*
|
||||||
|
* Author: Iceyer <me@iceyer.net>
|
||||||
|
*
|
||||||
|
* Maintainer: Iceyer <me@iceyer.net>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "semaphore.h"
|
||||||
|
|
||||||
|
#include <sys/sem.h>
|
||||||
|
|
||||||
|
#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
|
43
src/modules/util/semaphore.h
Normal file
43
src/modules/util/semaphore.h
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2020-2021. Uniontech Software Ltd. All rights reserved.
|
||||||
|
*
|
||||||
|
* Author: Iceyer <me@iceyer.net>
|
||||||
|
*
|
||||||
|
* Maintainer: Iceyer <me@iceyer.net>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef LINGLONG_BOX_SRC_UTIL_SEMAPHORE_H_
|
||||||
|
#define LINGLONG_BOX_SRC_UTIL_SEMAPHORE_H_
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
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<SemaphorePrivate> dd_ptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace linglong
|
||||||
|
|
||||||
|
#endif /* LINGLONG_BOX_SRC_UTIL_SEMAPHORE_H_ */
|
44
src/modules/util/util.h
Normal file
44
src/modules/util/util.h
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2020-2021. Uniontech Software Ltd. All rights reserved.
|
||||||
|
*
|
||||||
|
* Author: Iceyer <me@iceyer.net>
|
||||||
|
*
|
||||||
|
* Maintainer: Iceyer <me@iceyer.net>
|
||||||
|
*
|
||||||
|
* 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 <fstream>
|
||||||
|
|
||||||
|
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<char>(f)), std::istreambuf_iterator<char>());
|
||||||
|
auto j = fromByteArray(str);
|
||||||
|
return j;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace json
|
||||||
|
} // namespace util
|
||||||
|
} // namespace linglong
|
||||||
|
|
||||||
|
#endif /* LINGLONG_BOX_SRC_UTIL_UTIL_H_ */
|
55
src/service/CMakeLists.txt
Normal file
55
src/service/CMakeLists.txt
Normal file
@ -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)
|
168
src/service/impl/application.cpp
Normal file
168
src/service/impl/application.cpp
Normal file
@ -0,0 +1,168 @@
|
|||||||
|
#include "application.h"
|
||||||
|
|
||||||
|
#include <QCryptographicHash>
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QSettings>
|
||||||
|
#include <QThread>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
#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<QSharedPointer<ApplicationInstance>> instances;
|
||||||
|
QSharedPointer<modules::ApplicationHelper::Helper> 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<modules::ApplicationHelper::Helper> 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<QDBusObjectPath> Application::instances() const
|
||||||
|
{
|
||||||
|
Q_D(const Application);
|
||||||
|
|
||||||
|
QList<QDBusObjectPath> 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<ApplicationInstance> Application::createInstance()
|
||||||
|
{
|
||||||
|
Q_D(Application);
|
||||||
|
|
||||||
|
d->instances << QSharedPointer<ApplicationInstance>(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;
|
||||||
|
}
|
60
src/service/impl/application.h
Normal file
60
src/service/impl/application.h
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
#ifndef A216803F_06DD_4F40_8FD1_5BAED85905BE
|
||||||
|
#define A216803F_06DD_4F40_8FD1_5BAED85905BE
|
||||||
|
|
||||||
|
#include <QSharedPointer>
|
||||||
|
#include <QObject>
|
||||||
|
#include <QDBusObjectPath>
|
||||||
|
|
||||||
|
namespace modules {
|
||||||
|
namespace ApplicationHelper {
|
||||||
|
class Helper;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ApplicationPrivate;
|
||||||
|
class ApplicationInstance;
|
||||||
|
class Application : public QObject {
|
||||||
|
Q_OBJECT
|
||||||
|
QScopedPointer<ApplicationPrivate> dd_ptr;
|
||||||
|
Q_DECLARE_PRIVATE_D(qGetPtrHelper(dd_ptr), Application)
|
||||||
|
public:
|
||||||
|
enum class Type {
|
||||||
|
System,
|
||||||
|
User,
|
||||||
|
};
|
||||||
|
|
||||||
|
Application(const QString& prefix, Type type, QSharedPointer<modules::ApplicationHelper::Helper> 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<QDBusObjectPath> instances READ instances)
|
||||||
|
QList<QDBusObjectPath> instances() const;
|
||||||
|
|
||||||
|
Q_PROPERTY(QStringList mimetypes READ mimetypes)
|
||||||
|
QStringList mimetypes() const;
|
||||||
|
|
||||||
|
QDBusObjectPath path() const;
|
||||||
|
|
||||||
|
QString prefix() const;
|
||||||
|
|
||||||
|
Type type() const;
|
||||||
|
|
||||||
|
QString filePath() const;
|
||||||
|
|
||||||
|
QSharedPointer<ApplicationInstance> createInstance();
|
||||||
|
|
||||||
|
public Q_SLOTS: // METHODS
|
||||||
|
QString Comment(const QString &locale);
|
||||||
|
QString Name(const QString &locale);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* A216803F_06DD_4F40_8FD1_5BAED85905BE */
|
169
src/service/impl/application_instance.cpp
Normal file
169
src/service/impl/application_instance.cpp
Normal file
@ -0,0 +1,169 @@
|
|||||||
|
#include "application_instance.h"
|
||||||
|
|
||||||
|
#include <qdatetime.h>
|
||||||
|
|
||||||
|
#include <QCryptographicHash>
|
||||||
|
#include <QDateTime>
|
||||||
|
#include <QProcess>
|
||||||
|
#include <QTimer>
|
||||||
|
#include <QUuid>
|
||||||
|
#include <QtConcurrent/QtConcurrent>
|
||||||
|
|
||||||
|
#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<modules::ApplicationHelper::Helper> 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<void (QProcess::*)(int, QProcess::ExitStatus)>(&QProcess::finished), p, [=] {
|
||||||
|
qInfo().noquote() << p->readAllStandardOutput();
|
||||||
|
qWarning().noquote() << p->readAllStandardError();
|
||||||
|
});
|
||||||
|
p->connect(p, static_cast<void (QProcess::*)(int, QProcess::ExitStatus)>(&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<void> 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<modules::ApplicationHelper::Helper> 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();
|
||||||
|
}
|
44
src/service/impl/application_instance.h
Normal file
44
src/service/impl/application_instance.h
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
#ifndef D6D05668_8A58_43AA_91C5_C6278643A1AF
|
||||||
|
#define D6D05668_8A58_43AA_91C5_C6278643A1AF
|
||||||
|
|
||||||
|
#include <QDBusObjectPath>
|
||||||
|
#include <QObject>
|
||||||
|
|
||||||
|
#include "../../modules/methods/task.hpp"
|
||||||
|
|
||||||
|
namespace modules {
|
||||||
|
namespace ApplicationHelper {
|
||||||
|
class Helper;
|
||||||
|
}
|
||||||
|
} // namespace modules
|
||||||
|
|
||||||
|
class Application;
|
||||||
|
class ApplicationInstancePrivate;
|
||||||
|
class ApplicationInstance : public QObject {
|
||||||
|
Q_OBJECT
|
||||||
|
QScopedPointer<ApplicationInstancePrivate> dd_ptr;
|
||||||
|
Q_DECLARE_PRIVATE_D(qGetPtrHelper(dd_ptr), ApplicationInstance)
|
||||||
|
public:
|
||||||
|
ApplicationInstance(Application* parent, QSharedPointer<modules::ApplicationHelper::Helper> 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 */
|
195
src/service/impl/application_manager.cpp
Normal file
195
src/service/impl/application_manager.cpp
Normal file
@ -0,0 +1,195 @@
|
|||||||
|
#include "application_manager.h"
|
||||||
|
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <map>
|
||||||
|
#include <mutex>
|
||||||
|
#include <nlohmann/json.hpp>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
|
#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<QSharedPointer<Application>> applications;
|
||||||
|
Socket::Server server;
|
||||||
|
std::multimap<std::string, QSharedPointer<ApplicationInstance>> 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<char> &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<std::mutex> 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<char> &data)
|
||||||
|
{
|
||||||
|
std::vector<char> tmp = data;
|
||||||
|
tmp.push_back('\0');
|
||||||
|
server.write(socket, tmp);
|
||||||
|
}
|
||||||
|
void write(int socket, const std::string &data)
|
||||||
|
{
|
||||||
|
std::vector<char> 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<char>(c));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
ApplicationManager::ApplicationManager(QObject *parent) : QObject(parent), dd_ptr(new ApplicationManagerPrivate(this)) {}
|
||||||
|
|
||||||
|
ApplicationManager::~ApplicationManager() {}
|
||||||
|
|
||||||
|
void ApplicationManager::addApplication(const QList<QSharedPointer<Application>> &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<Application> &app : d->applications) {
|
||||||
|
if (app->id() == id) {
|
||||||
|
return app->path();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
QList<QDBusObjectPath> 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<Application> &app : d->applications) {
|
||||||
|
if (app->id() == id) {
|
||||||
|
// 创建任务所需的数据,并记录到任务队列,等待 loader 消耗
|
||||||
|
QSharedPointer<ApplicationInstance> 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<QDBusObjectPath> ApplicationManager::instances() const
|
||||||
|
{
|
||||||
|
Q_D(const ApplicationManager);
|
||||||
|
|
||||||
|
QList<QDBusObjectPath> result;
|
||||||
|
|
||||||
|
for (const auto &app : d->applications) {
|
||||||
|
result += app->instances();
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
QList<QDBusObjectPath> ApplicationManager::list() const
|
||||||
|
{
|
||||||
|
Q_D(const ApplicationManager);
|
||||||
|
|
||||||
|
QList<QDBusObjectPath> result;
|
||||||
|
for (const QSharedPointer<Application> &app : d->applications) {
|
||||||
|
result << app->path();
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
#include "application_manager.moc"
|
43
src/service/impl/application_manager.h
Normal file
43
src/service/impl/application_manager.h
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
#ifndef A2862DC7_5DA3_4129_9796_671D88015BED
|
||||||
|
#define A2862DC7_5DA3_4129_9796_671D88015BED
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QDBusObjectPath>
|
||||||
|
#include <QList>
|
||||||
|
|
||||||
|
class Application;
|
||||||
|
class ApplicationInstance;
|
||||||
|
class ApplicationManagerPrivate;
|
||||||
|
class ApplicationManager : public QObject {
|
||||||
|
Q_OBJECT
|
||||||
|
Q_PROPERTY(QList<QDBusObjectPath> instances READ instances)
|
||||||
|
Q_PROPERTY(QList<QDBusObjectPath> list READ list)
|
||||||
|
QScopedPointer<ApplicationManagerPrivate> 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<QSharedPointer<Application>> &list);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void requestCreateInstance(const QSharedPointer<ApplicationInstance> instance);
|
||||||
|
|
||||||
|
public: // PROPERTIES
|
||||||
|
QList<QDBusObjectPath> instances() const;
|
||||||
|
QList<QDBusObjectPath> list() const;
|
||||||
|
|
||||||
|
public Q_SLOTS: // METHODS
|
||||||
|
QDBusObjectPath GetId(int pid);
|
||||||
|
QDBusObjectPath GetInformation(const QString &id);
|
||||||
|
QList<QDBusObjectPath> GetInstances(const QString &id);
|
||||||
|
QDBusObjectPath Run(const QString &id);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* A2862DC7_5DA3_4129_9796_671D88015BED */
|
81
src/service/main.cpp
Normal file
81
src/service/main.cpp
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
#include <QCoreApplication>
|
||||||
|
|
||||||
|
#include "impl/application_manager.h"
|
||||||
|
#include "impl/application.h"
|
||||||
|
#include "applicationmanageradaptor.h"
|
||||||
|
#include "applicationadaptor.h"
|
||||||
|
#include "../modules/applicationhelper/helper.h"
|
||||||
|
|
||||||
|
#include <QDir>
|
||||||
|
#include <pwd.h>
|
||||||
|
|
||||||
|
QFileInfoList scan(const QString &path)
|
||||||
|
{
|
||||||
|
QDir dir(path);
|
||||||
|
dir.setFilter(QDir::Files);
|
||||||
|
dir.setNameFilters({ "*.desktop" });
|
||||||
|
return dir.entryInfoList();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 扫描系统目录
|
||||||
|
// 扫描用户目录
|
||||||
|
QList<QSharedPointer<Application>> scanFiles()
|
||||||
|
{
|
||||||
|
QList<QSharedPointer<Application>> applications;
|
||||||
|
auto apps = scan("/usr/share/applications/");
|
||||||
|
for (const QFileInfo &info : apps) {
|
||||||
|
applications << QSharedPointer<Application>(new Application(
|
||||||
|
"freedesktop",
|
||||||
|
Application::Type::System,
|
||||||
|
QSharedPointer<modules::ApplicationHelper::Helper>(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<Application>(new Application(
|
||||||
|
"freedesktop",
|
||||||
|
Application::Type::System,
|
||||||
|
QSharedPointer<modules::ApplicationHelper::Helper>(new modules::ApplicationHelper::Helper(info.filePath()))
|
||||||
|
));
|
||||||
|
}
|
||||||
|
user = getpwent();
|
||||||
|
}
|
||||||
|
endpwent();
|
||||||
|
auto linglong = scan("/deepin/linglong/entries/share/applications/");
|
||||||
|
for (const QFileInfo &info : linglong) {
|
||||||
|
applications << QSharedPointer<Application>(new Application(
|
||||||
|
"linglong",
|
||||||
|
Application::Type::System,
|
||||||
|
QSharedPointer<modules::ApplicationHelper::Helper>(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<QSharedPointer<Application>> apps{ scanFiles() };
|
||||||
|
QList<QSharedPointer<ApplicationAdaptor>> appAdapters;
|
||||||
|
for (const QSharedPointer<Application> app : apps) {
|
||||||
|
QSharedPointer<ApplicationAdaptor> adapter = QSharedPointer<ApplicationAdaptor>(new ApplicationAdaptor(app.get()));
|
||||||
|
appAdapters << adapter;
|
||||||
|
QDBusConnection::sessionBus().registerObject(app->path().path(), "org.desktopspec.Application", app.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
ApplicationManager::Instance()->addApplication(apps);
|
||||||
|
|
||||||
|
return app.exec();
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user