feat: 初始代码

AM初始代码,迁移自gitlabwh

Log:
Task: https://pms.uniontech.com/task-view-108539.html
Influence:
Change-Id: I6096f97e5d68d13796ff5dc51d9858c0f40264a0
This commit is contained in:
tanfeng 2022-03-30 17:56:27 +08:00
parent 9414feb1f7
commit 0b22bb3adf
54 changed files with 3607 additions and 0 deletions

9
CMakeLists.txt Normal file
View 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)

View 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>

View 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>

View 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
View File

@ -0,0 +1,7 @@
## Build Dep
* Qt
* Core
* Network
* DBus
* [nlohmann/json](https://github.com/nlohmann/json)

5
debian/changelog vendored Normal file
View 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
View 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
View 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
View 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
View File

@ -0,0 +1 @@
3.0 (quilt)

1
misc/CMakeLists.txt Normal file
View File

@ -0,0 +1 @@
add_subdirectory(systemd)

View 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/)

View 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

View 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
View 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
View 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
View 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
View 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;
}

View 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 */

View 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_

View 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 */

View 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_

View 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 &registe)
{
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 &registe)
{
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_

View 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 */

View 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

View 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 */

View 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"

View 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 */

View 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
View File

@ -0,0 +1,11 @@
#ifndef QUERY_H
#define QUERY_H
#include <QStringList>
namespace Tools {
}
#endif // QUERY_H

View 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
View 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_ */

View 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

View 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_ */

View 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

View 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
View 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_ */

View 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
View 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
View 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_ */

View 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_ */

View 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

View 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_ */

View 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

View 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
View 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_ */

View 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)

View 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;
}

View 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 */

View 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();
}

View 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 */

View 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"

View 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
View 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();
}