diff --git a/.github/workflows/backup-to-gitlab.yml b/.github/workflows/backup-to-gitlab.yml new file mode 100644 index 000000000..eb7461b6c --- /dev/null +++ b/.github/workflows/backup-to-gitlab.yml @@ -0,0 +1,12 @@ +name: backup to gitlab +on: [push] + +concurrency: + group: ${{ github.workflow }} + cancel-in-progress: true + +jobs: + backup-to-gitlabwh: + uses: deepin-community/.github/.github/workflows/backup-to-gitlabwh.yml@release + secrets: + BRIDGETOKEN: ${{ secrets.BRIDGETOKEN }} diff --git a/.github/workflows/call-auto-tag.yml b/.github/workflows/call-auto-tag.yml deleted file mode 100644 index 5018e5beb..000000000 --- a/.github/workflows/call-auto-tag.yml +++ /dev/null @@ -1,16 +0,0 @@ -name: auto tag - -on: - pull_request_target: - types: [opened, synchronize, closed] - paths: - - "debian/changelog" - -concurrency: - group: ${{ github.workflow }}-pull/${{ github.event.number }} - cancel-in-progress: true - -jobs: - auto_tag: - uses: linuxdeepin/.github/.github/workflows/auto-tag.yml@master - secrets: inherit diff --git a/.github/workflows/call-build-tag.yml b/.github/workflows/call-build-tag.yml new file mode 100644 index 000000000..d0a591ae3 --- /dev/null +++ b/.github/workflows/call-build-tag.yml @@ -0,0 +1,14 @@ +name: tag build +on: + push: + tags: "*" + +concurrency: + group: ${{ github.workflow }} + cancel-in-progress: true + +jobs: + build: + uses: deepin-community/.github/.github/workflows/build-tag.yml@master + secrets: + BridgeToken: ${{ secrets.BridgeToken }} diff --git a/.github/workflows/call-chatOps.yml b/.github/workflows/call-chatOps.yml new file mode 100644 index 000000000..83ef38cc2 --- /dev/null +++ b/.github/workflows/call-chatOps.yml @@ -0,0 +1,9 @@ +name: chatOps +on: + issue_comment: + types: [created] + +jobs: + chatopt: + uses: deepin-community/.github/.github/workflows/chatOps.yml@master + secrets: inherit diff --git a/.github/workflows/call-commitlint.yml b/.github/workflows/call-commitlint.yml deleted file mode 100644 index 69f5b2dc5..000000000 --- a/.github/workflows/call-commitlint.yml +++ /dev/null @@ -1,11 +0,0 @@ -name: Call commitlint -on: - pull_request_target: - -concurrency: - group: ${{ github.workflow }}-pull/${{ github.event.number }} - cancel-in-progress: true - -jobs: - check_job: - uses: linuxdeepin/.github/.github/workflows/commitlint.yml@master diff --git a/.github/workflows/call-license-check.yml b/.github/workflows/call-license-check.yml deleted file mode 100644 index 347d55602..000000000 --- a/.github/workflows/call-license-check.yml +++ /dev/null @@ -1,16 +0,0 @@ -name: Call License and README Check -on: - pull_request_target: - types: [opened, synchronize, reopened] - -permissions: - pull-requests: write - contents: read - -concurrency: - group: ${{ github.workflow }}-pull/${{ github.event.number }} - cancel-in-progress: true - -jobs: - license-check: - uses: linuxdeepin/.github/.github/workflows/license-check.yml@master diff --git a/.github/workflows/cppcheck.yml b/.github/workflows/cppcheck.yml deleted file mode 100644 index e808a89b1..000000000 --- a/.github/workflows/cppcheck.yml +++ /dev/null @@ -1,26 +0,0 @@ -name: cppcheck -on: - pull_request_target: - paths-ignore: - - ".github/workflows/**" - -concurrency: - group: ${{ github.workflow }}-pull/${{ github.event.number }} - cancel-in-progress: true - -jobs: - cppchceck: - name: cppcheck - runs-on: ubuntu-latest - steps: - - run: export - - uses: actions/checkout@v3 - with: - ref: ${{ github.event.pull_request.head.sha }} - persist-credentials: false - - uses: linuxdeepin/action-cppcheck@main - with: - github_token: ${{ secrets.GITHUB_TOKEN }} - repository: ${{ github.repository }} - pull_request_id: ${{ github.event.pull_request.number }} - allow_approve: false diff --git a/CMakeLists.txt b/CMakeLists.txt index 8b003bb39..ed452ee44 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -24,8 +24,8 @@ set(UAB_SPECIAL_INSTALL OFF CACHE BOOL "enable special installation of UAB") -if (${UAB_SPECIAL_INSTALL}) - add_definitions(-DUAB_SPECIAL_INSTALL) +if(${UAB_SPECIAL_INSTALL}) + add_definitions(-DUAB_SPECIAL_INSTALL) endif() set(ENABLE_UAB @@ -187,6 +187,19 @@ add_definitions("-DQT_NO_KEYWORDS") # NOTE(black_desk): Enable Qt logging with context. add_definitions("-DQT_MESSAGELOGCONTEXT") +# FIXME: can not start container since the kernel does not support the +# CLONE_NEWUSER feature in the chroot environment, reference: +# https://man7.org/linux/man-pages/man2/unshare.2.html. so we skip font cache +# generator by LINGLONG_FONT_CACHE_GENERATOR, it can be removed when the above +# problem is solved. +set(ENABLE_FONT_CACHE_GENERATOR + OFF + CACHE BOOL "enable font cache generator") + +if(ENABLE_FONT_CACHE_GENERATOR) + add_definitions(-DLINGLONG_FONT_CACHE_GENERATOR) +endif() + # We need to support both Qt5 and Qt6, and according to the Qt documentation, we # should be using versioned targets and functions. These varible must be set # before find_package() diff --git a/apps/ll-box/CMakeLists.txt b/apps/ll-box/CMakeLists.txt index e8d78522d..af358dbf8 100644 --- a/apps/ll-box/CMakeLists.txt +++ b/apps/ll-box/CMakeLists.txt @@ -12,6 +12,7 @@ if(${STATIC_BOX}) endif() pkg_search_module(SECCOMP REQUIRED IMPORTED_TARGET libseccomp) +pkg_search_module(CAP REQUIRED IMPORTED_TARGET libcap) pfl_add_executable( SOURCES @@ -51,6 +52,7 @@ pfl_add_executable( nlohmann_json::nlohmann_json linglong::ocppi PkgConfig::SECCOMP + PkgConfig::CAP COMPILE_OPTIONS PRIVATE -DJSON_USE_IMPLICIT_CONVERSIONS=0) diff --git a/apps/ll-box/src/container/container.cpp b/apps/ll-box/src/container/container.cpp index 6b0e1a177..810a04719 100644 --- a/apps/ll-box/src/container/container.cpp +++ b/apps/ll-box/src/container/container.cpp @@ -9,13 +9,11 @@ #include "container/helper.h" #include "container/mount/filesystem_driver.h" #include "container/mount/host_mount.h" -#include "container/seccomp.h" -#include "util/debug/debug.h" #include "util/filesystem.h" #include "util/logger.h" #include "util/platform.h" -#include "util/semaphore.h" +#include #include #include #include @@ -217,6 +215,82 @@ static bool parse_wstatus(const int &wstatus, std::string &info) } } +int setCapabilities(const Capabilities &capabilities) +{ + auto get_cap_list = [](const util::str_vec &cap_str_vec) -> std::vector { + std::vector cap_list; + for (const auto &cap : cap_str_vec) { + if (auto it = capsMap.find(cap); it != capsMap.end()) { + cap_list.push_back(it->second); + } + } + + return cap_list; + }; + + std::unique_ptr<_cap_struct, decltype(&cap_free)> caps(cap_get_proc(), cap_free); + if (caps == nullptr) { + logErr() << "call cap_get_proc failed" << util::RetErrString(0); + return 1; + } + + if (!capabilities.effective.empty() + && cap_set_flag(caps.get(), + CAP_EFFECTIVE, + capabilities.effective.size(), + get_cap_list(capabilities.effective).data(), + CAP_SET) + == -1) { + logErr() << "cap_set_flag CAP_EFFECTIVE" << util::RetErrString(-1); + return -1; + } + + if (!capabilities.permitted.empty() + && cap_set_flag(caps.get(), + CAP_PERMITTED, + capabilities.permitted.size(), + get_cap_list(capabilities.permitted).data(), + CAP_SET) + == -1) { + logErr() << "cap_set_flag CAP_PERMITTED" << util::RetErrString(-1); + return -1; + } + + if (!capabilities.inheritable.empty() + && cap_set_flag(caps.get(), + CAP_INHERITABLE, + capabilities.inheritable.size(), + get_cap_list(capabilities.inheritable).data(), + CAP_SET) + == -1) { + logErr() << "cap_set_flag CAP_INHERITABLE" << util::RetErrString(-1); + return -1; + } + + // apply the modified capabilities to the process + if (cap_set_proc(caps.get()) == -1) { + logErr() << "cap_set_proc" << util::RetErrString(-1); + return -1; + } + + for (auto cap : get_cap_list(capabilities.ambient)) { + if (prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE, cap, 0, 0) == -1) { + logErr() << "prctl PR_CAP_AMBIENT PR_CAP_AMBIENT_RAISE" << cap + << util::RetErrString(-1); + return -1; + } + } + + for (auto cap : get_cap_list(capabilities.bounding)) { + if (prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_LOWER, cap, 0, 0) == -1) { + logErr() << "prctl PR_CAP_AMBIENT PR_CAP_AMBIENT_LOWER" << util::RetErrString(-1); + return -1; + } + } + + return 0; +} + struct ContainerPrivate { ContainerPrivate(Runtime r, const std::string &bundle, Container * /*unused*/) @@ -583,12 +657,22 @@ int NonePrivilegeProc(void *arg) idMap.size = 1; linux.gidMappings.push_back(idMap); - if (auto ret = ConfigUserNamespace(linux, 0); ret != 0) { - return ret; + int ret{ -1 }; + if (containerPrivate.runtime.process.capabilities) { + ret = setCapabilities(containerPrivate.runtime.process.capabilities.value()); + if (ret == -1) { + logErr() << "setCapabilities failed"; + return -1; + } } - auto ret = mount("proc", "/proc", "proc", 0, nullptr); - if (0 != ret) { + if (ret = ConfigUserNamespace(linux, 0); ret != 0) { + logErr() << "ConfigUserNamespace failed"; + return -1; + } + + ret = mount("proc", "/proc", "proc", 0, nullptr); + if (ret == -1) { logErr() << "mount proc failed" << util::RetErrString(ret); return -1; } diff --git a/apps/ll-box/src/main.cpp b/apps/ll-box/src/main.cpp index 0c7302965..555d7e3d5 100644 --- a/apps/ll-box/src/main.cpp +++ b/apps/ll-box/src/main.cpp @@ -676,6 +676,52 @@ Overload(Ts...) -> Overload; int main(int argc, char **argv) { + // detecting some kernel features at runtime to reporting errors friendly + std::error_code ec; + std::filesystem::path feature{ "/proc/sys/kernel/unprivileged_userns_clone" }; + + auto check = [](const std::filesystem::path &setting, int expected) { + // We assume that the fact that a file does not exist or that an error occurs during the + // detection process does not mean that the feature is disabled. + std::error_code ec; + if (!std::filesystem::exists(setting, ec)) { + return true; + } + + std::ifstream stream{ setting }; + if (!stream.is_open()) { + return true; + } + + std::string content; + std::getline(stream, content); + + try { + return std::stoi(content) == expected; + } catch (std::exception &e) { + logWan() << "ignore exception" << e.what() << "and continue"; // NOLINT + return true; + } + }; + + // for debian: + // https://salsa.debian.org/kernel-team/linux/-/blob/debian/latest/debian/patches/debian/add-sysctl-to-disallow-unprivileged-CLONE_NEWUSER-by-default.patch + if (!check("/proc/sys/kernel/unprivileged_userns_clone", 1)) { + logErr() << "unprivileged_userns_clone is not enabled"; + return EPERM; + } + + // for ubuntu: + // https://gitlab.com/apparmor/apparmor/-/wikis/unprivileged_userns_restriction#disabling-unprivileged-user-namespaces + if (!check("/proc/sys/kernel/apparmor_restrict_unprivileged_unconfined", 0)) { + logErr() << "apparmor_restrict_unprivileged_unconfined is not disabled"; + return EPERM; + } + if (!check("/proc/sys/kernel/apparmor_restrict_unprivileged_userns", 0)) { + logErr() << "apparmor_restrict_unprivileged_userns is not disabled"; + return EPERM; + } + if (argc == 1) { logErr() << "please specify a command"; return -1; diff --git a/apps/ll-box/src/util/debug/debug.cpp b/apps/ll-box/src/util/debug/debug.cpp index d84aad588..f517bdad5 100644 --- a/apps/ll-box/src/util/debug/debug.cpp +++ b/apps/ll-box/src/util/debug/debug.cpp @@ -8,6 +8,8 @@ #include "util/logger.h" +#include + #include #include #include @@ -19,6 +21,35 @@ namespace linglong { #define DUMP_DBG(func, line) /*NOLINT*/ \ (linglong::util::Logger(linglong::util::Logger::Debug, func, line)) +void DumpCap() +{ + cap_flag_value_t cap_value; + cap_value_t cap; + + logDbg() << "start -----------"; + + // Get the current process capabilities + std::unique_ptr<_cap_struct, decltype(cap_free) *> caps(cap_get_proc(), cap_free); + + // Iterate through all capabilities + for (cap = 0; cap <= CAP_LAST_CAP; cap++) { + if (cap_get_flag(caps.get(), cap, CAP_EFFECTIVE, &cap_value) == -1) { + logDbg() << "cap_get_flag failed"; + continue; + } + + const char *cap_name = cap_to_name(cap); + if (cap_name == nullptr) { + logDbg() << "Unknown capability: " << cap; + continue; + } + + logDbg() << cap_name << ": " << (cap_value == CAP_SET ? "yes" : "no"); + } + + logDbg() << "end -----------"; +} + void DumpIDMap() { logDbg() << "DumpIDMap Start -----------"; diff --git a/apps/ll-box/src/util/debug/debug.h b/apps/ll-box/src/util/debug/debug.h index 9f19afba6..3cbe2db58 100644 --- a/apps/ll-box/src/util/debug/debug.h +++ b/apps/ll-box/src/util/debug/debug.h @@ -14,6 +14,8 @@ namespace linglong { #define DUMP_FILE_INFO(path) DumpFileInfo1(path, __FUNCTION__, __LINE__) +void DumpCap(); + void DumpIDMap(); void DumpUidGidGroup(); diff --git a/apps/ll-box/src/util/oci_runtime.h b/apps/ll-box/src/util/oci_runtime.h index e56a1024e..0f6cd7c1e 100644 --- a/apps/ll-box/src/util/oci_runtime.h +++ b/apps/ll-box/src/util/oci_runtime.h @@ -8,6 +8,7 @@ #include "util.h" +#include #include // Compatible with linux kernel which is under 5.10 @@ -52,11 +53,80 @@ LLJS_TO_OBJ(Root) LLJS_TO(readonly); } +const static std::map capsMap{ + { "CAP_CHOWN", CAP_CHOWN }, + { "CAP_DAC_OVERRIDE", CAP_DAC_OVERRIDE }, + { "CAP_DAC_READ_SEARCH", CAP_DAC_READ_SEARCH }, + { "CAP_FOWNER", CAP_FOWNER }, + { "CAP_FSETID", CAP_FSETID }, + { "CAP_KILL", CAP_KILL }, + { "CAP_SETGID", CAP_SETGID }, + { "CAP_SETUID", CAP_SETUID }, + { "CAP_SETPCAP", CAP_SETPCAP }, + { "CAP_LINUX_IMMUTABLE", CAP_LINUX_IMMUTABLE }, + { "CAP_NET_BIND_SERVICE", CAP_NET_BIND_SERVICE }, + { "CAP_NET_BROADCAST", CAP_NET_BROADCAST }, + { "CAP_NET_ADMIN", CAP_NET_ADMIN }, + { "CAP_NET_RAW", CAP_NET_RAW }, + { "CAP_IPC_LOCK", CAP_IPC_LOCK }, + { "CAP_IPC_OWNER", CAP_IPC_OWNER }, + { "CAP_SYS_MODULE", CAP_SYS_MODULE }, + { "CAP_SYS_RAWIO", CAP_SYS_RAWIO }, + { "CAP_SYS_CHROOT", CAP_SYS_CHROOT }, + { "CAP_SYS_PTRACE", CAP_SYS_PTRACE }, + { "CAP_SYS_PACCT", CAP_SYS_PACCT }, + { "CAP_SYS_ADMIN", CAP_SYS_ADMIN }, + { "CAP_SYS_BOOT", CAP_SYS_BOOT }, + { "CAP_SYS_NICE", CAP_SYS_NICE }, + { "CAP_SYS_RESOURCE", CAP_SYS_RESOURCE }, + { "CAP_SYS_TIME", CAP_SYS_TIME }, + { "CAP_SYS_TTY_CONFIG", CAP_SYS_TTY_CONFIG }, + { "CAP_MKNOD", CAP_MKNOD }, + { "CAP_LEASE", CAP_LEASE }, + { "CAP_AUDIT_WRITE", CAP_AUDIT_WRITE }, + { "CAP_AUDIT_CONTROL", CAP_AUDIT_CONTROL }, + { "CAP_SETFCAP", CAP_SETFCAP }, + { "CAP_MAC_OVERRIDE", CAP_MAC_OVERRIDE }, + { "CAP_MAC_ADMIN", CAP_MAC_ADMIN }, + { "CAP_SYSLOG", CAP_SYSLOG }, + { "CAP_WAKE_ALARM", CAP_WAKE_ALARM }, + { "CAP_BLOCK_SUSPEND", CAP_BLOCK_SUSPEND }, + { "CAP_AUDIT_READ", CAP_AUDIT_READ } +}; + +struct Capabilities +{ + util::str_vec bounding; + util::str_vec permitted; + util::str_vec inheritable; + util::str_vec effective; + util::str_vec ambient; +}; + +inline void from_json(const nlohmann::json &j, Capabilities &o) +{ + o.bounding = j.at("bounding").get(); + o.permitted = j.at("permitted").get(); + o.inheritable = j.at("inheritable").get(); + o.effective = j.at("effective").get(); + o.ambient = j.at("ambient").get(); +} + +inline void to_json(nlohmann::json &j, const Capabilities &o) +{ + j["bounding"] = o.bounding; + j["permitted"] = o.permitted; + j["inheritable"] = o.inheritable; + j["effective"] = o.effective; + j["ambient"] = o.ambient; +} + struct Process { util::str_vec args; util::str_vec env; std::string cwd; + std::optional capabilities; }; inline void from_json(const nlohmann::json &j, Process &o) @@ -64,6 +134,9 @@ inline void from_json(const nlohmann::json &j, Process &o) o.args = j.at("args").get(); o.env = j.at("env").get(); o.cwd = j.at("cwd").get(); + if (j.find("capabilities") != j.end()) { + o.capabilities = j.at("capabilities").get(); + } } inline void to_json(nlohmann::json &j, const Process &o) @@ -71,6 +144,9 @@ inline void to_json(nlohmann::json &j, const Process &o) j["args"] = o.args; j["env"] = o.env; j["cwd"] = o.cwd; + if (o.capabilities) { + j["capabilities"] = o.capabilities; + } } struct Mount diff --git a/apps/ll-builder/src/main.cpp b/apps/ll-builder/src/main.cpp index 2df52b8a4..e28aceffd 100644 --- a/apps/ll-builder/src/main.cpp +++ b/apps/ll-builder/src/main.cpp @@ -329,7 +329,7 @@ You can report bugs to the linyaps team under this project: https://github.com/O commandParser.add_subcommand("import-dir", _("Import linyaps layer dir to build repo")) ->group(hiddenGroup); buildImportDir->usage(_("Usage: ll-builder import-dir PATH")); - buildImportDir->add_option("PATH", layerDir, _("layer dir path")) + buildImportDir->add_option("PATH", layerDir, _("Layer dir path")) ->type_name("PATH") ->required(); diff --git a/apps/ll-dialog/src/main.cpp b/apps/ll-dialog/src/main.cpp index f1312d4a1..546ae48c0 100644 --- a/apps/ll-dialog/src/main.cpp +++ b/apps/ll-dialog/src/main.cpp @@ -2,9 +2,9 @@ // // SPDX-License-Identifier: LGPL-3.0-or-later +#include "cache_dialog.h" #include "linglong/utils/configure.h" #include "permissionDialog.h" -#include "cache_dialog.h" #include "tl/expected.hpp" #include @@ -159,6 +159,8 @@ int showPermissionDialog() int main(int argc, char *argv[]) { + bindtextdomain(PACKAGE_LOCALE_DOMAIN, PACKAGE_LOCALE_DIR); + textdomain(PACKAGE_LOCALE_DOMAIN); #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) QApplication::setAttribute(Qt::ApplicationAttribute::AA_UseHighDpiPixmaps); QApplication::setAttribute(Qt::ApplicationAttribute::AA_EnableHighDpiScaling); diff --git a/apps/ll-package-manager/src/main.cpp b/apps/ll-package-manager/src/main.cpp index 26137f8a6..f50da7939 100644 --- a/apps/ll-package-manager/src/main.cpp +++ b/apps/ll-package-manager/src/main.cpp @@ -11,7 +11,6 @@ #include "linglong/repo/ostree_repo.h" #include "linglong/utils/configure.h" #include "linglong/utils/dbus/register.h" -#include "linglong/utils/file.h" #include "linglong/utils/global/initialize.h" #include "ocppi/cli/CLI.hpp" #include "ocppi/cli/crun/Crun.hpp" @@ -51,23 +50,9 @@ void withDBusDaemon(ocppi::cli::CLI &cli) } auto *ostreeRepo = new linglong::repo::OSTreeRepo(repoRoot, *config, *clientFactory); ostreeRepo->setParent(QCoreApplication::instance()); - { - auto exportVersion = repoRoot.absoluteFilePath("entries/.version").toStdString(); - auto data = linglong::utils::readFile(exportVersion); - if (data && data == LINGLONG_EXPORT_VERSION) { - qDebug() << exportVersion.c_str() << data->c_str(); - qDebug() << "skip export entry, already exported"; - } else { - auto ret = ostreeRepo->exportAllEntries(); - if (!ret.has_value()) { - qCritical() << "failed to export entries:" << ret.error(); - } else { - ret = linglong::utils::writeFile(exportVersion, LINGLONG_EXPORT_VERSION); - if (!ret.has_value()) { - qCritical() << "failed to write export version:" << ret.error(); - } - } - } + auto result = ostreeRepo->fixExportAllEntries(); + if (!result.has_value()) { + qCritical() << result.error().message(); } auto *containerBuilder = new linglong::runtime::ContainerBuilder(cli); @@ -78,7 +63,7 @@ void withDBusDaemon(ocppi::cli::CLI &cli) *containerBuilder, QCoreApplication::instance()); new linglong::adaptors::package_manger::PackageManager1(packageManager); - auto result = registerDBusObject(conn, "/org/deepin/linglong/PackageManager1", packageManager); + result = registerDBusObject(conn, "/org/deepin/linglong/PackageManager1", packageManager); if (!result.has_value()) { qCritical().noquote() << "Launching failed:" << Qt::endl << result.error().message(); QCoreApplication::exit(-1); @@ -126,6 +111,10 @@ void withoutDBusDaemon(ocppi::cli::CLI &cli) auto *ostreeRepo = new linglong::repo::OSTreeRepo(repoRoot, *config, *clientFactory); ostreeRepo->setParent(QCoreApplication::instance()); + auto result = ostreeRepo->fixExportAllEntries(); + if (!result.has_value()) { + qCritical() << result.error().message(); + } auto *containerBuilder = new linglong::runtime::ContainerBuilder(cli); containerBuilder->setParent(QCoreApplication::instance()); diff --git a/debian/changelog b/debian/changelog index cf95109f9..5ea051d44 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,9 @@ +linglong (1.7.5-1) unstable; urgency=medium + + * release 1.7.5 + + -- dengbo Wed, 25 Dec 2024 17:11:45 +0800 + linglong (1.5.6-1) unstable; urgency=medium * fix: layer size error when export layer repeatedly diff --git a/debian/control b/debian/control index a6ef7f604..c1bd042ca 100644 --- a/debian/control +++ b/debian/control @@ -31,7 +31,8 @@ Build-Depends: cmake, qtbase5-dev, qtbase5-private-dev, systemd, - zlib1g-dev + zlib1g-dev, + libcap-dev Standards-Version: 4.1.3 Homepage: http://www.deepin.org diff --git a/debian/deepin/workflows.yml b/debian/deepin/workflows.yml new file mode 100644 index 000000000..c6c2ebf8f --- /dev/null +++ b/debian/deepin/workflows.yml @@ -0,0 +1,36 @@ +test_build: + steps: + - link_package: + source_project: deepin:Develop:main + source_package: %{SCM_REPOSITORY_NAME} + target_project: deepin:CI + + - configure_repositories: + project: deepin:CI + repositories: + - name: deepin_develop + paths: + - target_project: deepin:CI + target_repository: deepin_develop + architectures: + - x86_64 + - aarch64 + + filters: + event: pull_request + +tag_build: + steps: + - trigger_services: + project: deepin:Unstable:main + package: %{SCM_REPOSITORY_NAME} + filters: + event: tag_push + +commit_build: + steps: + - trigger_services: + project: deepin:Develop:main + package: %{SCM_REPOSITORY_NAME} + filters: + event: push diff --git a/debian/linglong-bin.install b/debian/linglong-bin.install index b6adbc015..581c6572c 100644 --- a/debian/linglong-bin.install +++ b/debian/linglong-bin.install @@ -5,6 +5,7 @@ usr/bin/ll-cli usr/bin/llpkg usr/lib/linglong/* usr/lib/systemd/system-environment-generators/61-linglong +usr/lib/systemd/user-generators/linglong-user-systemd-generator usr/lib/systemd/system/org.deepin.linglong.PackageManager.service lib/systemd/system/ usr/lib/systemd/user/linglong-session-helper.service usr/lib/sysusers.d/linglong.conf diff --git a/libs/linglong/src/linglong/builder/linglong_builder.cpp b/libs/linglong/src/linglong/builder/linglong_builder.cpp index 9ed3d13d3..4518d3cdf 100644 --- a/libs/linglong/src/linglong/builder/linglong_builder.cpp +++ b/libs/linglong/src/linglong/builder/linglong_builder.cpp @@ -730,6 +730,10 @@ set -e // initialize the cache dir QDir appCache = this->workingDir.absoluteFilePath("linglong/cache"); + if (!appCache.mkpath(".")) { + return LINGLONG_ERR("make path " + appCache.absolutePath() + ": failed."); + } +#ifdef LINGLONG_FONT_CACHE_GENERATOR QDir appFontCache = appCache.absoluteFilePath("fontconfig"); if (!appFontCache.mkpath(".")) { return LINGLONG_ERR("make path " + appFontCache.absolutePath() + ": failed."); @@ -738,6 +742,7 @@ set -e if (!appFonts.mkpath(".")) { return LINGLONG_ERR("make path " + appFonts.absolutePath() + ": failed."); } +#endif // write ld.so.conf QFile ldsoconf = appCache.absoluteFilePath("ld.so.conf"); if (!ldsoconf.open(QIODevice::WriteOnly)) { @@ -754,6 +759,7 @@ include /opt/apps/@id@/files/etc/ld.so.conf)"; ldsoconf.write(ldRawConf.toUtf8()); // must be closed here, this conf will be used later. ldsoconf.close(); +#ifdef LINGLONG_FONT_CACHE_GENERATOR // write fonts.conf QFile fontsConf = appFonts.absoluteFilePath("fonts.conf"); if (!fontsConf.open(QIODevice::WriteOnly)) { @@ -767,6 +773,7 @@ include /opt/apps/@id@/files/etc/ld.so.conf)"; )"; fontsRawConf.replace("@id@", QString::fromStdString(this->project.package.id)); fontsConf.write(fontsRawConf.toUtf8()); +#endif opts.mounts.push_back({ .destination = "/run/linglong/cache", @@ -1557,6 +1564,7 @@ utils::error::Result Builder::run(const QStringList &modules, .source = this->workingDir.absoluteFilePath("linglong/cache").toStdString(), .type = "bind", }); +#ifdef LINGLONG_FONT_CACHE_GENERATOR // mount font cache applicationMounts.push_back(ocppi::runtime::config::types::Mount{ .destination = "/var/cache/fontconfig", @@ -1564,24 +1572,27 @@ utils::error::Result Builder::run(const QStringList &modules, .source = this->workingDir.absoluteFilePath("linglong/cache/fontconfig").toStdString(), .type = "bind", }); +#endif std::vector generateCache{}; std::vector ldconfigCmd = { "/sbin/ldconfig", "-C", "/run/linglong/cache/ld.so.cache" }; - std::vector fontconfiCmd = { "fc-cache", "-f" }; generateCache.push_back(ocppi::runtime::config::types::Hook{ .args = std::move(ldconfigCmd), .env = {}, .path = "/sbin/ldconfig", .timeout = {}, }); +#ifdef LINGLONG_FONT_CACHE_GENERATOR + std::vector fontconfiCmd = { "fc-cache", "-f" }; generateCache.push_back(ocppi::runtime::config::types::Hook{ .args = std::move(fontconfiCmd), .env = {}, .path = "/bin/fc-cache", .timeout = {}, }); +#endif options.hooks.startContainer = std::move(generateCache); options.mounts = std::move(applicationMounts); diff --git a/libs/linglong/src/linglong/cli/cli.cpp b/libs/linglong/src/linglong/cli/cli.cpp index 6df683929..8e325195f 100644 --- a/libs/linglong/src/linglong/cli/cli.cpp +++ b/libs/linglong/src/linglong/cli/cli.cpp @@ -6,6 +6,7 @@ #include "linglong/cli/cli.h" +#include "linglong/api/dbus/v1/dbus_peer.h" #include "linglong/api/types/v1/InteractionReply.hpp" #include "linglong/api/types/v1/InteractionRequest.hpp" #include "linglong/api/types/v1/PackageManager1InstallParameters.hpp" @@ -22,6 +23,7 @@ #include "linglong/package/layer_file.h" #include "linglong/runtime/container_builder.h" #include "linglong/utils/configure.h" +#include "linglong/utils/gettext.h" #include "linglong/utils/error/error.h" #include "linglong/utils/finally/finally.h" #include "linglong/utils/serialize/json.h" @@ -49,6 +51,9 @@ using namespace linglong::utils::error; +static const std::string permissionNotifyMsg = + _("Permission deny, please check whether you are running as root."); + namespace { static std::vector getAutoModuleList() noexcept @@ -256,7 +261,7 @@ void Cli::interaction(QDBusObjectPath object_path, int messageID, QVariantMap ad if (dbusReply.isError()) { if (dbusReply.error().type() == QDBusError::AccessDenied) { this->notifier->notify(api::types::v1::InteractionRequest{ - .summary = "Permission deny, please check whether you are running as root." }); + .summary = permissionNotifyMsg }); return; } @@ -663,12 +668,14 @@ int Cli::run() .type = "bind", }); +#ifdef LINGLONG_FONT_CACHE_GENERATOR applicationMounts.push_back(ocppi::runtime::config::types::Mount{ .destination = "/var/cache/fontconfig", .options = nlohmann::json::array({ "rbind", "ro" }), .source = *appCache + "/fontconfig", .type = "bind", }); +#endif auto container = this->containerBuilder.create({ .appID = curAppRef->id, @@ -983,7 +990,7 @@ int Cli::installFromFile(const QFileInfo &fileInfo, const api::types::v1::Common if (pendingReply.isError()) { if (pendingReply.error().type() == QDBusError::AccessDenied) { this->notifier->notify(api::types::v1::InteractionRequest{ - .summary = "Permission deny, please check whether you are running as root." }); + .summary = permissionNotifyMsg }); return -1; } auto err = LINGLONG_ERRV(pendingReply.error().message()); @@ -1032,6 +1039,7 @@ int Cli::installFromFile(const QFileInfo &fileInfo, const api::types::v1::Common return -1; } + updateAM(); return this->lastState == linglong::api::types::v1::State::Succeed ? 0 : -1; } @@ -1105,7 +1113,7 @@ int Cli::install() if (pendingReply.isError()) { if (pendingReply.error().type() == QDBusError::AccessDenied) { this->notifier->notify(api::types::v1::InteractionRequest{ - .summary = "Permission deny, please check whether you are running as root." }); + .summary = permissionNotifyMsg }); return -1; } @@ -1149,6 +1157,7 @@ int Cli::install() } loop.exec(); + updateAM(); return this->lastState == linglong::api::types::v1::State::Succeed ? 0 : -1; } @@ -1212,7 +1221,7 @@ int Cli::upgrade() if (pendingReply.isError()) { if (pendingReply.error().type() == QDBusError::AccessDenied) { this->notifier->notify(api::types::v1::InteractionRequest{ - .summary = "Permission deny, please check whether you are running as root." }); + .summary = permissionNotifyMsg }); return -1; } @@ -1258,6 +1267,7 @@ int Cli::upgrade() } loop.exec(); + updateAM(); if (this->lastState != linglong::api::types::v1::State::Succeed) { return -1; } @@ -1279,7 +1289,7 @@ int Cli::search() if (pendingReply.isError()) { if (pendingReply.error().type() == QDBusError::AccessDenied) { this->notifier->notify(api::types::v1::InteractionRequest{ - .summary = "Permission deny, please check whether you are running as root." }); + .summary = permissionNotifyMsg }); return -1; } @@ -1394,7 +1404,7 @@ int Cli::prune() if (pendingReply.isError()) { if (pendingReply.error().type() == QDBusError::AccessDenied) { this->notifier->notify(api::types::v1::InteractionRequest{ - .summary = "Permission deny, please check whether you are running as root." }); + .summary = permissionNotifyMsg }); return -1; } @@ -1483,7 +1493,7 @@ int Cli::uninstall() if (pendingReply.isError()) { if (pendingReply.error().type() == QDBusError::AccessDenied) { this->notifier->notify(api::types::v1::InteractionRequest{ - .summary = "Permission deny, please check whether you are running as root." }); + .summary = permissionNotifyMsg }); return -1; } @@ -1536,6 +1546,7 @@ int Cli::uninstall() } loop.exec(); + updateAM(); return this->lastState == linglong::api::types::v1::State::Succeed ? 0 : -1; } @@ -1680,7 +1691,7 @@ int Cli::repo(CLI::App *app) if (this->pkgMan.lastError().isValid()) { if (this->pkgMan.lastError().type() == QDBusError::AccessDenied) { this->notifier->notify(api::types::v1::InteractionRequest{ - .summary = "Permission deny, please check whether you are running as root." }); + .summary = permissionNotifyMsg }); return -1; } @@ -1803,7 +1814,7 @@ int Cli::setRepoConfig(const QVariantMap &config) if (this->pkgMan.lastError().isValid()) { if (this->pkgMan.lastError().type() == QDBusError::AccessDenied) { this->notifier->notify(api::types::v1::InteractionRequest{ - .summary = "Permission deny, please check whether you are running as root." }); + .summary = permissionNotifyMsg }); return -1; } @@ -2087,6 +2098,9 @@ Cli::RequestDirectories(const api::types::v1::PackageInfoV2 &info) noexcept { LINGLONG_TRACE("request directories"); + // TODO: skip request directories for now + return LINGLONG_OK; + auto userHome = qgetenv("HOME").toStdString(); if (userHome.empty()) { return LINGLONG_ERR("HOME is not set, skip request directories"); @@ -2395,10 +2409,7 @@ Cli::ensureCache(const package::Reference &ref, if (ret != 0) { this->notifier->notify(api::types::v1::InteractionRequest{ .summary = - "The cache generation failed, please uninstall and reinstall the application." }); - } else { - this->notifier->notify( - api::types::v1::InteractionRequest{ .summary = "The cache has been regenerated." }); + _("The cache generation failed, please uninstall and reinstall the application.") }); } process.close(); @@ -2409,4 +2420,25 @@ Cli::ensureCache(const package::Reference &ref, return appCache; } + +void Cli::updateAM() noexcept +{ + // NOTE: make sure AM refresh the cache of desktop + if ((QSysInfo::productType() == "Deepin" || QSysInfo::productType() == "deepin") + && this->lastState == linglong::api::types::v1::State::Succeed) { + QDBusConnection conn = QDBusConnection::systemBus(); + if (!conn.isConnected()) { + qWarning() << "Failed to connect to the system bus"; + } + + auto peer = linglong::api::dbus::v1::DBusPeer("org.desktopspec.ApplicationUpdateNotifier1", + "/org/desktopspec/ApplicationUpdateNotifier1", + conn); + auto reply = peer.Ping(); + reply.waitForFinished(); + if (!reply.isValid()) { + qWarning() << "Failed to ping org.desktopspec.ApplicationUpdateNotifier1" << reply.error(); + } + } +} } // namespace linglong::cli diff --git a/libs/linglong/src/linglong/cli/cli.h b/libs/linglong/src/linglong/cli/cli.h index a721cec1a..c7ec7e438 100644 --- a/libs/linglong/src/linglong/cli/cli.h +++ b/libs/linglong/src/linglong/cli/cli.h @@ -99,6 +99,7 @@ class Cli : public QObject ensureCache(const package::Reference &ref, const api::types::v1::RepositoryCacheLayersItem &appLayerItem) noexcept; QDBusReply authorization(); + void updateAM() noexcept; private Q_SLOTS: // maybe use in the future diff --git a/libs/linglong/src/linglong/package_manager/package_manager.cpp b/libs/linglong/src/linglong/package_manager/package_manager.cpp index 47c09df15..1ed23f5ca 100644 --- a/libs/linglong/src/linglong/package_manager/package_manager.cpp +++ b/libs/linglong/src/linglong/package_manager/package_manager.cpp @@ -334,7 +334,7 @@ PackageManager::removeAfterInstall(const package::Reference &oldRef, << tmp.message(); } if (module == "binary" || module == "runtime") { - auto ret = this->generateCache(oldRef); + auto ret = this->tryGenerateCache(oldRef); if (!ret) { qCritical() << ret.error().message(); } @@ -691,7 +691,7 @@ QVariantMap PackageManager::installFromLayer(const QDBusUnixFileDescriptor &fd, return; } - auto generateCacheRet = this->generateCache(*newRef); + auto generateCacheRet = this->tryGenerateCache(*newRef); if (!generateCacheRet) { taskRef.reportError(std::move(generateCacheRet).error()); return; @@ -712,7 +712,7 @@ QVariantMap PackageManager::installFromLayer(const QDBusUnixFileDescriptor &fd, return; } - auto generateCacheRet = this->generateCache(*newRef); + auto generateCacheRet = this->tryGenerateCache(*newRef); if (!generateCacheRet) { taskRef.reportError(std::move(generateCacheRet).error()); return; @@ -1027,7 +1027,7 @@ QVariantMap PackageManager::installFromUAB(const QDBusUnixFileDescriptor &fd, } this->repo.exportReference(newAppRef); - auto result = this->generateCache(newAppRef); + auto result = this->tryGenerateCache(newAppRef); if (!result) { taskRef.updateState(linglong::api::types::v1::State::Failed, "Failed to generate some cache.\n" + result.error().message()); @@ -1335,7 +1335,7 @@ void PackageManager::Install(PackageTask &taskContext, } else { this->repo.exportReference(newRef); } - auto result = this->generateCache(newRef); + auto result = this->tryGenerateCache(newRef); if (!result) { taskContext.updateState(linglong::api::types::v1::State::Failed, "Failed to generate some cache.\n" + result.error().message()); @@ -1570,7 +1570,7 @@ void PackageManager::UninstallRef(PackageTask &taskContext, << ref.toString(); } if (module == "binary" || module == "runtime") { - auto ret = this->generateCache(ref); + auto ret = this->tryGenerateCache(ref); if (!ret) { qCritical() << ret.error().message(); } @@ -1785,7 +1785,7 @@ void PackageManager::Update(PackageTask &taskContext, return; } - auto result = this->generateCache(newRef); + auto result = this->tryGenerateCache(newRef); if (!result) { taskContext.updateState(linglong::api::types::v1::State::Failed, "Failed to generate some cache.\n" + result.error().message()); @@ -2158,19 +2158,22 @@ utils::error::Result PackageManager::generateCache(const package::Referenc } const auto appCache = std::filesystem::path(LINGLONG_ROOT) / "cache" / layerItem->commit; - const auto appFontCache = appCache / "fontconfig"; const std::string appCacheDest = "/run/linglong/cache"; const std::string generatorDest = "/run/linglong/generator"; const std::string ldGenerator = generatorDest + "/ld-cache-generator"; - const std::string fontGenerator = generatorDest + "/font-cache-generator"; utils::Transaction transaction; + +#ifdef LINGLONG_FONT_CACHE_GENERATOR + const auto appFontCache = appCache / "fontconfig"; + const std::string fontGenerator = generatorDest + "/font-cache-generator"; +#endif std::error_code ec; - if (!std::filesystem::exists(appFontCache, ec)) { + if (!std::filesystem::exists(appCache, ec)) { if (ec) { return LINGLONG_ERR(QString::fromStdString(ec.message())); } - if (!std::filesystem::create_directories(appFontCache, ec)) { + if (!std::filesystem::create_directories(appCache, ec)) { return LINGLONG_ERR(QString::fromStdString(ec.message())); } } @@ -2191,6 +2194,7 @@ utils::error::Result PackageManager::generateCache(const package::Referenc .source = appCache, .type = "bind", }); +#ifdef LINGLONG_FONT_CACHE_GENERATOR // bind mount font cache applicationMounts.push_back(ocppi::runtime::config::types::Mount{ .destination = "/var/cache/fontconfig", @@ -2198,6 +2202,7 @@ utils::error::Result PackageManager::generateCache(const package::Referenc .source = appFontCache, .type = "bind", }); +#endif // bind mount generator applicationMounts.push_back(ocppi::runtime::config::types::Mount{ .destination = generatorDest, @@ -2254,9 +2259,13 @@ utils::error::Result PackageManager::generateCache(const package::Referenc // font-cache-generator [cacheRoot] [id] const std::string ldGenerateCmd = ldGenerator + " " + appCacheDest + " " + ref.id.toStdString() + " " + currentArch->getTriplet().toStdString(); +#ifdef LINGLONG_FONT_CACHE_GENERATOR const std::string fontGenerateCmd = fontGenerator + " " + appCacheDest + " " + ref.id.toStdString(); process.args = std::vector{ "bash", "-c", ldGenerateCmd + ";" + fontGenerateCmd }; +#endif + + process.args = std::vector{ "bash", "-c", ldGenerateCmd }; // Note: XDG_RUNTIME_DIR is not set in PM, the ll-box will finally fallback to /run/ll-box. // But PM has no write permission in that place, so we should specific the root path. @@ -2275,6 +2284,18 @@ utils::error::Result PackageManager::generateCache(const package::Referenc return LINGLONG_OK; } +// we should allow cache generation to fail, skip it when an error occurs by. the function can be +// removed later when the kernel clone new_user problem is solved. +utils::error::Result PackageManager::tryGenerateCache(const package::Reference &ref) noexcept +{ + auto ret = generateCache(ref); + if (!ret) { + qWarning() << "failed to generate cache" << ret.error(); + } + + return LINGLONG_OK; +} + utils::error::Result PackageManager::removeCache(const package::Reference &ref) noexcept { LINGLONG_TRACE("remove the cache of " + ref.toString()); diff --git a/libs/linglong/src/linglong/package_manager/package_manager.h b/libs/linglong/src/linglong/package_manager/package_manager.h index a08e8fad0..192baff4d 100644 --- a/libs/linglong/src/linglong/package_manager/package_manager.h +++ b/libs/linglong/src/linglong/package_manager/package_manager.h @@ -146,6 +146,7 @@ public utils::error::Result Prune(std::vector &removedInfo) noexcept; utils::error::Result generateCache(const package::Reference &ref) noexcept; + utils::error::Result tryGenerateCache(const package::Reference &ref) noexcept; utils::error::Result removeCache(const package::Reference &ref) noexcept; linglong::repo::OSTreeRepo &repo; // NOLINT PackageTaskQueue tasks; diff --git a/libs/linglong/src/linglong/package_manager/package_task.cpp b/libs/linglong/src/linglong/package_manager/package_task.cpp index 76ba0653a..9bb3efe11 100644 --- a/libs/linglong/src/linglong/package_manager/package_task.cpp +++ b/libs/linglong/src/linglong/package_manager/package_task.cpp @@ -176,37 +176,25 @@ void PackageTask::reportError(linglong::utils::error::Error &&err) noexcept void PackageTask::Cancel() noexcept { - if (g_cancellable_is_cancelled(m_cancelFlag) == TRUE) { + if (m_state == static_cast(linglong::api::types::v1::State::Canceled)) { return; } - qInfo() << "task " << taskID() << "has been canceled by user"; - g_cancellable_cancel(m_cancelFlag); - const auto &id = taskID(); - auto oldState = state(); + qInfo() << "task " << id << "has been canceled by user"; updateState(linglong::api::types::v1::State::Canceled, QString{ "task %1 has been canceled by user" }.arg(id)); - if (oldState == linglong::api::types::v1::State::Queued) { - auto *ptr = parent(); - if (ptr == nullptr) { // temporary task - return; - } - Q_EMIT qobject_cast(ptr)->taskDone(id); + if (m_cancelFlag == nullptr || g_cancellable_is_cancelled(m_cancelFlag) == TRUE) { + return; } + + g_cancellable_cancel(m_cancelFlag); } -utils::error::Result PackageTask::run() noexcept +void PackageTask::run() noexcept { - LINGLONG_TRACE("run task"); - if (m_state != static_cast(linglong::api::types::v1::State::Queued)) { - qInfo() << "task" << taskID() << " is not in queued state" << static_cast(state()); - return LINGLONG_ERR("task is not in queued state"); - } - m_job(*this); - return LINGLONG_OK; } PackageTaskQueue::PackageTaskQueue(QObject *parent) @@ -221,17 +209,13 @@ PackageTaskQueue::PackageTaskQueue(QObject *parent) return; } - if (m_taskQueue.size() > 1) { + auto &task = m_taskQueue.front(); + if (task.state() != linglong::api::types::v1::State::Queued) { qDebug() << "other task is running, wait for it done"; return; } - auto &task = m_taskQueue.front(); - auto ret = task.run(); - if (!ret) { - qWarning() << ret.error(); - } - + task.run(); Q_EMIT taskDone(task.taskID()); }, Qt::QueuedConnection); @@ -250,7 +234,7 @@ PackageTaskQueue::PackageTaskQueue(QObject *parent) // if queued task is done, only remove it from queue // otherwise, remove it and start next task - bool isQueuedDone = task == m_taskQueue.begin(); + bool isQueuedDone = task->state() == linglong::api::types::v1::State::Queued; Q_EMIT qobject_cast(this->parent()) ->TaskRemoved(QDBusObjectPath{ task->taskObjectPath() }, static_cast(task->state()), @@ -259,7 +243,7 @@ PackageTaskQueue::PackageTaskQueue(QObject *parent) task->getPercentage()); m_taskQueue.erase(task); - if (isQueuedDone) { + if (!isQueuedDone) { Q_EMIT startTask(); } }); diff --git a/libs/linglong/src/linglong/package_manager/package_task.h b/libs/linglong/src/linglong/package_manager/package_task.h index 49b6a7af8..cc1693f5f 100644 --- a/libs/linglong/src/linglong/package_manager/package_task.h +++ b/libs/linglong/src/linglong/package_manager/package_task.h @@ -99,7 +99,7 @@ class PackageTask : public QObject, protected QDBusContext auto cancellable() noexcept { return m_cancelFlag; } - utils::error::Result run() noexcept; + void run() noexcept; [[nodiscard]] double getPercentage() const noexcept { @@ -171,28 +171,7 @@ class PackageTaskQueue : public QObject LINGLONG_TRACE("add new task"); static_assert(std::is_invocable_r_v, "mismatch function signature"); - auto exist = - std::any_of(m_taskQueue.begin(), m_taskQueue.end(), [&refs](const PackageTask &task) { - QStringList intersection; - std::set_intersection(refs.begin(), - refs.end(), - task.m_refs.begin(), - task.m_refs.end(), - std::back_inserter(intersection)); - if (intersection.empty()) { - return false; - } - - for (const auto &ref : intersection) { - qWarning() << "ref " << ref << " is operating by task " << task.taskID(); - } - - return true; - }); - - if (exist) { - return LINGLONG_ERR("ref conflict"); - } + auto &ref = m_taskQueue.emplace_back(conn, refs, std::forward(job), this); Q_EMIT taskAdded(); diff --git a/libs/linglong/src/linglong/repo/ostree_repo.cpp b/libs/linglong/src/linglong/repo/ostree_repo.cpp index 4ffa93902..11ee13a42 100644 --- a/libs/linglong/src/linglong/repo/ostree_repo.cpp +++ b/libs/linglong/src/linglong/repo/ostree_repo.cpp @@ -18,6 +18,7 @@ #include "linglong/repo/config.h" #include "linglong/utils/command/env.h" #include "linglong/utils/error/error.h" +#include "linglong/utils/file.h" #include "linglong/utils/finally/finally.h" #include "linglong/utils/gkeyfile_wrapper.h" #include "linglong/utils/packageinfo_handler.h" @@ -49,6 +50,7 @@ #include #include #include +#include #include #include #include @@ -1505,7 +1507,7 @@ void OSTreeRepo::unexportReference(const package::Reference &ref) noexcept return; } - QDir entriesDir = this->repoDir.absoluteFilePath("entries/share"); + QDir entriesDir = this->repoDir.absoluteFilePath("entries"); QDirIterator it(entriesDir.absolutePath(), QDir::AllEntries | QDir::NoDot | QDir::NoDotDot | QDir::System, QDirIterator::Subdirectories); @@ -1538,7 +1540,7 @@ void OSTreeRepo::unexportReference(const package::Reference &ref) noexcept void OSTreeRepo::exportReference(const package::Reference &ref) noexcept { - auto entriesDir = QDir(this->repoDir.absoluteFilePath("entries/share")); + auto entriesDir = QDir(this->repoDir.absoluteFilePath("entries")); if (!entriesDir.exists()) { entriesDir.mkpath("."); } @@ -1549,7 +1551,7 @@ void OSTreeRepo::exportReference(const package::Reference &ref) noexcept Q_ASSERT(false); return; } - auto ret = exportEntries(entriesDir, *item); + auto ret = exportEntries(entriesDir.absolutePath().toStdString(), *item); if (!ret.has_value()) { qCritical() << QString("Failed to export %1:").arg(ref.toString()) << ret.error().message(); Q_ASSERT(false); @@ -1558,104 +1560,232 @@ void OSTreeRepo::exportReference(const package::Reference &ref) noexcept this->updateSharedInfo(); } -utils::error::Result OSTreeRepo::exportEntries( - const QDir &entriesDir, const api::types::v1::RepositoryCacheLayersItem &item) noexcept +// 递归源目录所有文件,并在目标目录创建软链接,max_depth 控制递归深度以避免环形链接导致的无限递归 +utils::error::Result OSTreeRepo::exportDir(const std::string &appID, + const std::filesystem::path &source, + const std::filesystem::path &destination, + const int &max_depth) { - LINGLONG_TRACE(QString("export %1").arg(item.info.id.c_str())); - auto layerDir = getMergedModuleDir(item); - if (!layerDir.has_value()) { - return LINGLONG_ERR("get layer dir", layerDir); - } - auto layerEntriesDir = QDir(layerDir->absoluteFilePath("entries/share")); - if (!layerEntriesDir.exists()) { - qCritical() << QString("Failed to export %1:").arg(item.info.id.c_str()) << layerEntriesDir - << "not exists."; + LINGLONG_TRACE(QString("export %1").arg(source.c_str())); + if (max_depth <= 0) { + qWarning() << "ttl reached, skipping export for" << source.c_str(); return LINGLONG_OK; } - const QStringList exportPaths = { - "applications", // Copy desktop files - "mime", // Copy MIME Type files - "icons", // Icons - "dbus-1", // D-Bus service files - "gnome-shell", // Search providers - "appdata", // Copy appdata/metainfo files (legacy path) - "metainfo", // Copy appdata/metainfo files - "plugins", // Copy plugins conf,The configuration files provided by some applications - // maybe used by the host dde-file-manager. - "systemd", // copy systemd service files - "deepin-manual", // copy deepin-manual files - "deepin-elf-verify" // for uab signature - }; - - for (const auto &path : exportPaths) { - QDir exportDir = layerEntriesDir.absoluteFilePath(path); - if (!exportDir.exists()) { + std::error_code ec; + // 检查源目录是否存在 + auto exists = std::filesystem::exists(source, ec); + if (ec) { + return LINGLONG_ERR("check source", ec); + } + if (!exists) { + return LINGLONG_ERR("source directory does not exist"); + } + auto is_directory = std::filesystem::is_directory(source, ec); + if (ec) { + return LINGLONG_ERR("check source", ec); + } + if (!is_directory) { + return LINGLONG_ERR("source is not a directory"); + } + // 检查目标目录是否存在,如果不存在则创建 + exists = std::filesystem::exists(destination, ec); + if (ec) { + return LINGLONG_ERR(QString("Failed to check file existence: ") + destination.c_str(), ec); + } + // 如果目标非目录,则删除它并重新创建 + if (exists && !std::filesystem::is_directory(destination, ec)) { + std::filesystem::remove(destination, ec); + if (ec) { + return LINGLONG_ERR(QString("Failed to remove file: ") + destination.c_str(), ec); + } + // 标记目标不存在 + exists = false; + } + if (!exists) { + std::filesystem::create_directories(destination, ec); + if (ec) { + return LINGLONG_ERR(QString("Failed to create directory: ") + destination.c_str(), ec); + } + } + auto iterator = std::filesystem::directory_iterator(source, ec); + if (ec) { + return LINGLONG_ERR("list directory: " + source.string(), ec); + } + // 遍历源目录中的所有文件和子目录 + for (const auto &entry : iterator) { + const auto &source_path = entry.path(); + const auto &target_path = destination / source_path.filename(); + // 跳过无效的软链接 + exists = std::filesystem::exists(source_path, ec); + if (ec) { + return LINGLONG_ERR("check source existence" + source_path.string(), ec); + } + if (!exists) { continue; } - QDirIterator it(exportDir.absolutePath(), - QDir::AllEntries | QDir::NoDotAndDotDot | QDir::System | QDir::Hidden, - QDirIterator::Subdirectories | QDirIterator::FollowSymlinks); - while (it.hasNext()) { - it.next(); - const auto info = it.fileInfo(); - if (info.isDir()) { - continue; + // 如果是文件,创建符号链接 + auto is_regular_file = std::filesystem::is_regular_file(source_path, ec); + if (ec) { + return LINGLONG_ERR("check file type: " + source_path.string(), ec); + } + if (is_regular_file) { + exists = std::filesystem::exists(target_path, ec); + if (ec) { + return LINGLONG_ERR("check file existence", ec); } - // Check if the target file of the symbolic link exists. - if (info.isSymLink() && !std::filesystem::exists(info.symLinkTarget().toStdString())) { - qCritical() << "Invalid symlink: " << info.filePath(); - continue; + if (exists) { + std::filesystem::remove(target_path, ec); + if (ec) { + return LINGLONG_ERR("remove file failed", ec); + } } - auto ret = IniLikeFileRewrite(info, QString::fromStdString(item.info.id)); - if (!ret) { - qCritical() << ret.error().message(); - } + { + auto info = QFileInfo(target_path.c_str()); + if ((info.path().contains("share/applications") && info.suffix() == "desktop") + || (info.path().contains("share/dbus-1") && info.suffix() == "service") + || (info.path().contains("share/systemd/user") && info.suffix() == "service") + || (info.path().contains("share/applications/context-menus"))) { + // We should not modify the files of the checked application directly, but + // should copy them to root entries directory and then modify. + std::filesystem::copy(source_path, target_path, ec); + if (ec) { + return LINGLONG_ERR("copy file failed: " + target_path.string(), ec); + } - const auto parentDirForLinkPath = - layerEntriesDir.relativeFilePath(it.fileInfo().dir().absolutePath()); + exists = std::filesystem::exists(target_path, ec); + if (ec) { + return LINGLONG_ERR("check file exists", ec); + } - if (!entriesDir.mkpath(parentDirForLinkPath)) { - qCritical() << "Failed to mkpath" - << entriesDir.absoluteFilePath(parentDirForLinkPath); - } - auto from = std::filesystem::path{ - entriesDir.absoluteFilePath(parentDirForLinkPath).toStdString() - } / it.fileName().toStdString(); - - QDir parentDir(entriesDir.absoluteFilePath(parentDirForLinkPath)); - auto to = std::filesystem::path{ - parentDir.relativeFilePath(info.absoluteFilePath()).toStdString() - }; - - std::error_code ec; - auto status = std::filesystem::symlink_status(from, ec); - if (ec && ec.value() != static_cast(std::errc::no_such_file_or_directory)) { - qCritical() << "symlink_status" << from.c_str() - << "error:" << QString::fromStdString(ec.message()); - continue; - } + if (!exists) { + qWarning() << "failed to copy file: " << source_path.c_str(); + continue; + } - if (std::filesystem::exists(status)) { - qInfo() << from.c_str() << "symlink already exists, try to remove it"; - if (!std::filesystem::remove(from, ec)) { - qCritical() << "remove" << from.c_str() - << "error:" << QString::fromStdString(ec.message()); - continue; + auto ret = IniLikeFileRewrite(QFileInfo(target_path.c_str()), appID.c_str()); + if (ret) { + continue; + } } } - std::filesystem::create_symlink(to, from, ec); + std::filesystem::create_symlink(source_path, target_path, ec); if (ec) { - qCritical().nospace() - << "Failed to create link " << from.c_str() << " -> " << to.c_str() << " : " - << QString::fromStdString(ec.message()); - continue; + return LINGLONG_ERR("create symlink failed: " + target_path.string(), ec); + } + continue; + } + // 如果是目录,进行递归导出 + is_directory = std::filesystem::is_directory(source_path, ec); + if (ec) { + return LINGLONG_ERR("check file type", ec); + } + if (is_directory) { + auto ret = this->exportDir(appID, source_path, target_path, max_depth - 1); + if (!ret.has_value()) { + return ret; + } + continue; + } + // 其他情况,报错 + qWarning() << "invalid file: " << source_path.c_str(); + } + return LINGLONG_OK; +} + +utils::error::Result +OSTreeRepo::exportEntries(const std::filesystem::path &rootEntriesDir, + const api::types::v1::RepositoryCacheLayersItem &item) noexcept +{ + LINGLONG_TRACE(QString("export %1").arg(item.info.id.c_str())); + auto layerDir = getMergedModuleDir(item); + if (!layerDir.has_value()) { + return LINGLONG_ERR("get layer dir", layerDir); + } + std::error_code ec; + // 检查目录是否存在 + std::filesystem::path appEntriesDir = layerDir->absoluteFilePath("entries").toStdString(); + auto exists = std::filesystem::exists(appEntriesDir, ec); + if (ec) { + return LINGLONG_ERR("check appEntriesDir exists", ec); + } + if (!exists) { + qCritical() << QString("Failed to export %1:").arg(item.info.id.c_str()) + << appEntriesDir.c_str() << "not exists."; + return LINGLONG_OK; + } + std::vector exportPaths = { + "share/applications", // Copy desktop files + "share/mime", // Copy MIME Type files + "share/icons", // Icons + "share/dbus-1", // D-Bus service files + "share/gnome-shell", // Search providers + "share/appdata", // Copy appdata/metainfo files (legacy path) + "share/metainfo", // Copy appdata/metainfo files + "share/plugins", // Copy plugins conf,The configuration files provided by some applications + // maybe used by the host dde-file-manager. + "share/deepin-manual", // copy deepin-manual files + "share/deepin-elf-verify", // for uab signature + + }; + // 如果存在lib/systemd目录,则导出lib/systemd,否则导出share/systemd + exists = std::filesystem::exists(appEntriesDir / "lib/systemd", ec); + if (ec) { + return LINGLONG_ERR("Failed to check the existence of lib/systemd directory: {}", ec); + } + if (exists) { + exportPaths.push_back("lib/systemd"); + } else { + exportPaths.push_back("share/systemd"); + } + // 导出应用entries目录下的所有文件到玲珑仓库的entries目录下 + for (const auto &path : exportPaths) { + auto source = appEntriesDir / path; + auto destination = rootEntriesDir / path; + // 将 share/systemd 目录下的文件导出到 lib/systemd 目录下 + if (path == "share/systemd") { + destination = rootEntriesDir / "lib/systemd"; + } + // 检查源目录是否存在,跳过不存在的目录 + exists = std::filesystem::exists(source, ec); + if (ec) { + return LINGLONG_ERR(QString("Failed to check file existence: ") + source.c_str(), ec); + } + if (!exists) { + continue; + } + auto ret = this->exportDir(item.info.id, source, destination, 10); + if (!ret.has_value()) { + return ret; + } + } + return LINGLONG_OK; +} + +utils::error::Result OSTreeRepo::fixExportAllEntries() noexcept +{ + auto exportVersion = repoDir.absoluteFilePath("entries/.version").toStdString(); + auto data = linglong::utils::readFile(exportVersion); + if (data && data == LINGLONG_EXPORT_VERSION) { + qDebug() << exportVersion.c_str() << data->c_str(); + qDebug() << "skip export entry, already exported"; + } else { + auto ret = exportAllEntries(); + if (!ret.has_value()) { + qCritical() << "failed to export entries:" << ret.error(); + return ret; + } else { + ret = linglong::utils::writeFile(exportVersion, LINGLONG_EXPORT_VERSION); + if (!ret.has_value()) { + qCritical() << "failed to write export version:" << ret.error(); + return ret; } } } + return LINGLONG_OK; } @@ -1663,20 +1793,14 @@ utils::error::Result OSTreeRepo::exportAllEntries() noexcept { LINGLONG_TRACE("export all entries"); std::error_code ec; - // 创建新的share目录 + // 创建一个新的entries目录,使用UUID作为名称 auto id = QUuid::createUuid().toString(QUuid::Id128); - auto entriesDir = QDir(this->repoDir.absoluteFilePath("entries/share_new_" + id)); - if (entriesDir.exists()) { - std::filesystem::remove_all(entriesDir.absolutePath().toStdString(), ec); - if (ec) { - return LINGLONG_ERR("clean temp share directory", ec); - } - } - std::filesystem::create_directory(entriesDir.absolutePath().toStdString(), ec); + std::filesystem::path entriesDir = this->repoDir.filePath("entries_new_" + id).toStdString(); + std::filesystem::create_directory(entriesDir, ec); if (ec) { return LINGLONG_ERR("create temp share directory", ec); } - // 导出所有layer + // 导出所有layer到新entries目录 auto items = this->cache->queryExistingLayerItem(); for (const auto &item : items) { if (item.info.kind != "app") { @@ -1687,27 +1811,29 @@ utils::error::Result OSTreeRepo::exportAllEntries() noexcept return ret; } } - // 用新的share目录替换旧的 - std::filesystem::path workdir = repoDir.absoluteFilePath("entries").toStdString(); - - if (!std::filesystem::exists(workdir / "share")) { - std::filesystem::rename(entriesDir.dirName().toStdString(), workdir / "share", ec); + // 用新的entries目录替换旧的 + std::filesystem::path workdir = repoDir.absolutePath().toStdString(); + auto existsOldEntries = std::filesystem::exists(workdir / "entries", ec); + if (ec) { + return LINGLONG_ERR("check entries directory", ec); + } + if (!existsOldEntries) { + std::filesystem::rename(entriesDir, workdir / "entries", ec); if (ec) { - return LINGLONG_ERR("create new share symlink", ec); + return LINGLONG_ERR("rename new entries directory", ec); } } else { - auto oldshare = "share_old_" + QUuid::createUuid().toString(QUuid::Id128).toStdString(); - std::filesystem::rename(workdir / "share", workdir / oldshare, ec); + auto id = QUuid::createUuid().toString(QUuid::Id128).toStdString(); + auto oldEntriesDir = workdir / ("entries_old_" + id); + std::filesystem::rename(workdir / "entries", oldEntriesDir, ec); if (ec) { return LINGLONG_ERR("rename old share directory", ec); } - std::filesystem::rename(workdir / entriesDir.dirName().toStdString(), - workdir / "share", - ec); + std::filesystem::rename(entriesDir, workdir / "entries", ec); if (ec) { return LINGLONG_ERR("create new share symlink", ec); } - std::filesystem::remove_all(workdir / oldshare, ec); + std::filesystem::remove_all(oldEntriesDir, ec); if (ec) { return LINGLONG_ERR("remove old share directory", ec); } @@ -2219,7 +2345,7 @@ OSTreeRepo::listLocalBy(const linglong::repo::repoCacheQuery &query) const noexc QString getOriginRawExec(const QString &execArgs, const QString &id) { // Note: These strings have appeared in the app-conf-generator.sh of linglong-builder. - // We need to remove them. + // We need to remove them. const QString oldExec = "--exec "; const QString newExec = "-- "; @@ -2234,8 +2360,7 @@ QString getOriginRawExec(const QString &execArgs, const QString &id) return execArgs.mid(index + newExec.length()); } - qCritical() << "'-- ' or '--exec ' is not exist in" << execArgs - << ", return an empty string"; + qCritical() << "'-- ' or '--exec ' is not exist in" << execArgs << ", return an empty string"; return ""; } @@ -2243,7 +2368,7 @@ QString buildDesktopExec(QString origin, const QString &appID) noexcept { auto newExec = QString{ "%1 run %2 " }.arg(LINGLONG_CLIENT_PATH, appID); - if(origin.isEmpty()) { + if (origin.isEmpty()) { Q_ASSERT(false); return newExec; } @@ -2275,8 +2400,7 @@ QString buildDesktopExec(QString origin, const QString &appID) noexcept [[fallthrough]]; case 'F': { origin.insert(next - origin.begin(), '%'); - auto tmp = - QString{ "--file %%1 -- %2" }.arg(std::move(code), std::move(origin)); + auto tmp = QString{ "--file %%1 -- %2" }.arg(std::move(code), std::move(origin)); newExec.append(tmp); return newExec; } @@ -2284,8 +2408,7 @@ QString buildDesktopExec(QString origin, const QString &appID) noexcept [[fallthrough]]; case 'U': { origin.insert(next - origin.begin(), '%'); - auto tmp = - QString{ "--url %%1 -- %2" }.arg(std::move(code), std::move(origin)); + auto tmp = QString{ "--url %%1 -- %2" }.arg(std::move(code), std::move(origin)); newExec.append(tmp); return newExec; } diff --git a/libs/linglong/src/linglong/repo/ostree_repo.h b/libs/linglong/src/linglong/repo/ostree_repo.h index 35bb5b2aa..a7a40bafe 100644 --- a/libs/linglong/src/linglong/repo/ostree_repo.h +++ b/libs/linglong/src/linglong/repo/ostree_repo.h @@ -90,8 +90,6 @@ class OSTreeRepo : public QObject utils::error::Result prune(); void removeDanglingXDGIntergation() noexcept; - // exportEntries will clear the entries/share and export all applications to the entries/share - utils::error::Result exportAllEntries() noexcept; // exportReference should be called when LayerDir of ref is existed in local repo void exportReference(const package::Reference &ref) noexcept; // unexportReference should be called when LayerDir of ref is existed in local repo @@ -121,6 +119,7 @@ class OSTreeRepo : public QObject getLayerItem(const package::Reference &ref, std::string module = "binary", const std::optional &subRef = std::nullopt) const noexcept; + utils::error::Result fixExportAllEntries() noexcept; private: api::types::v1::RepoConfig cfg; @@ -154,10 +153,18 @@ class OSTreeRepo : public QObject [[nodiscard]] utils::error::Result getMergedModuleDir(const api::types::v1::RepositoryCacheLayersItem &layer, bool fallbackLayerDir = true) const noexcept; - utils::error::Result exportEntries( - const QDir &entriesDir, const api::types::v1::RepositoryCacheLayersItem &item) noexcept; + utils::error::Result + exportEntries(const std::filesystem::path &rootEntriesDir, + const api::types::v1::RepositoryCacheLayersItem &item) noexcept; static utils::error::Result IniLikeFileRewrite(const QFileInfo &info, const QString &id) noexcept; + + utils::error::Result exportDir(const std::string &appID, + const std::filesystem::path &source, + const std::filesystem::path &destination, + const int &max_depth); + // exportEntries will clear the entries/share and export all applications to the entries/share + utils::error::Result exportAllEntries() noexcept; }; } // namespace linglong::repo diff --git a/libs/linglong/src/linglong/runtime/container.cpp b/libs/linglong/src/linglong/runtime/container.cpp index f75eacf40..d59848863 100644 --- a/libs/linglong/src/linglong/runtime/container.cpp +++ b/libs/linglong/src/linglong/runtime/container.cpp @@ -153,7 +153,7 @@ utils::error::Result Container::run(const ocppi::runtime::config::types::P } ofs << "include /run/linglong/cache/ld.so.conf" << std::endl; } - +#ifdef LINGLONG_FONT_CACHE_GENERATOR { std::ofstream ofs(bundle.absoluteFilePath("conf.d/99-linglong.conf").toStdString()); Q_ASSERT(ofs.is_open()); @@ -167,6 +167,7 @@ utils::error::Result Container::run(const ocppi::runtime::config::types::P << std::endl; ofs << "" << std::endl; } +#endif this->cfg.mounts->push_back(ocppi::runtime::config::types::Mount{ .destination = "/etc/ld.so.conf.d/zz_deepin-linglong-app.conf", @@ -176,7 +177,7 @@ utils::error::Result Container::run(const ocppi::runtime::config::types::P .type = "bind", .uidMappings = {}, }); - +#ifdef LINGLONG_FONT_CACHE_GENERATOR this->cfg.mounts->push_back(ocppi::runtime::config::types::Mount{ .destination = "/etc/fonts/conf.d", .gidMappings = {}, @@ -185,6 +186,7 @@ utils::error::Result Container::run(const ocppi::runtime::config::types::P .type = "bind", .uidMappings = {}, }); +#endif nlohmann::json json = this->cfg; diff --git a/libs/oci-cfg-generators/src/linglong/oci-cfg-generators/30_user_home.cpp b/libs/oci-cfg-generators/src/linglong/oci-cfg-generators/30_user_home.cpp index de77897e5..507ced93a 100644 --- a/libs/oci-cfg-generators/src/linglong/oci-cfg-generators/30_user_home.cpp +++ b/libs/oci-cfg-generators/src/linglong/oci-cfg-generators/30_user_home.cpp @@ -286,36 +286,30 @@ bool UserHome::generate(ocppi::runtime::config::types::Config &config) const noe return false; } - auto permissions = linglong::api::types::v1::ApplicationConfigurationPermissions{}; + linglong::api::types::v1::ApplicationConfigurationPermissions permissions; auto configFile = privateAppDir / "permissions.json"; - if (!std::filesystem::exists(configFile, ec)) { - if (ec) { - std::cerr << "failed to get status of " << configFile.c_str() << ": " << ec.message() - << std::endl; + if (std::filesystem::exists(configFile, ec)) { + auto input = std::ifstream(configFile); + if (!input.is_open()) { + std::cerr << "couldn't open config file " << configFile.c_str() << std::endl; return false; } - // no permission config, do nothing - process.env = std::move(env); - config.process = std::move(process); - config.mounts = std::move(mounts); - return true; - } - - auto input = std::ifstream(configFile); - if (!input.is_open()) { - std::cerr << "couldn't open config file " << configFile.c_str() << std::endl; - return false; + try { + auto content = nlohmann::json::parse(input); + permissions = + content.get(); + } catch (nlohmann::json::parse_error &e) { + std::cerr << "deserialize error:" << e.what() << std::endl; + return false; + } catch (std::exception &e) { + std::cerr << "unknown exception:" << e.what() << std::endl; + return false; + } } - - try { - auto content = nlohmann::json::parse(input); - permissions = content.get(); - } catch (nlohmann::json::parse_error &e) { - std::cerr << "deserialize error:" << e.what() << std::endl; - return false; - } catch (std::exception &e) { - std::cerr << "unknown exception:" << e.what() << std::endl; + if (ec) { + std::cerr << "failed to get status of " << configFile.c_str() << ": " << ec.message() + << std::endl; return false; } @@ -341,7 +335,10 @@ bool UserHome::generate(ocppi::runtime::config::types::Config &config) const noe } return ret; }); - blacklist.erase(it); + + if (it != blacklist.end()) { + blacklist.erase(it); + } for (const auto &relative : blacklist) { if (!mountDir(privateAppDir / relative, cognitiveHomeDir / relative)) { diff --git a/libs/oci-cfg-generators/src/linglong/oci-cfg-generators/90_legacy.cpp b/libs/oci-cfg-generators/src/linglong/oci-cfg-generators/90_legacy.cpp index eea52d52b..024543721 100644 --- a/libs/oci-cfg-generators/src/linglong/oci-cfg-generators/90_legacy.cpp +++ b/libs/oci-cfg-generators/src/linglong/oci-cfg-generators/90_legacy.cpp @@ -47,7 +47,12 @@ bool Legacy::generate(ocppi::runtime::config::types::Config &config) const noexc { "/etc/machine-id", "/etc/machine-id" }, { "/etc/ssl/certs", "/run/host/etc/ssl/certs" }, { "/etc/ssl/certs", "/etc/ssl/certs" }, - { "/usr/local/share/fonts", "/usr/local/share/fonts" }, + { "/var/cache/fontconfig", "/run/host/appearance/fonts-cache" }, + // FIXME: app can not display normally due to missing cjk font cache file,so we need bind + // /var/cache/fontconfig to container. this is just a temporary solution,need to be removed + // when font cache solution implemented + { "/var/cache/fontconfig", "/var/cache/fontconfig" }, + { "/usr/share/fonts", "/usr/share/fonts" }, { "/usr/lib/locale/", "/usr/lib/locale/" }, { "/usr/share/themes", "/usr/share/themes" }, { "/usr/share/icons", "/usr/share/icons" }, diff --git a/libs/utils/src/linglong/utils/error/error.h b/libs/utils/src/linglong/utils/error/error.h index 9b18c6124..f78347726 100644 --- a/libs/utils/src/linglong/utils/error/error.h +++ b/libs/utils/src/linglong/utils/error/error.h @@ -20,6 +20,7 @@ #include #include +#include #include namespace linglong::utils::error { @@ -38,11 +39,9 @@ class Error [[nodiscard]] auto message() const { return pImpl->message(); } - static auto Err(const char *file, - int line, - const QString &trace_msg, - const QString &msg, - int code = -1) -> Error + static auto + Err(const char *file, int line, const QString &trace_msg, const QString &msg, int code = -1) + -> Error { return Error(std::make_unique(file, line, @@ -67,8 +66,8 @@ class Error nullptr)); } - static auto - Err(const char *file, int line, const QString &trace_msg, const QFile &qfile) -> Error + static auto Err(const char *file, int line, const QString &trace_msg, const QFile &qfile) + -> Error { return Error(std::make_unique(file, line, @@ -79,11 +78,9 @@ class Error nullptr)); } - static auto Err(const char *file, - int line, - const QString &trace_msg, - std::exception_ptr err, - int code = -1) -> Error + static auto + Err(const char *file, int line, const QString &trace_msg, std::exception_ptr err, int code = -1) + -> Error { QString what = trace_msg + ": "; try { @@ -118,8 +115,8 @@ class Error std::make_unique(file, line, "default", code, what, nullptr)); } - static auto - Err(const char *file, int line, const QString &trace_msg, const std::exception &e) -> Error + static auto Err(const char *file, int line, const QString &trace_msg, const std::exception &e) + -> Error { return Error(std::make_unique(file, line, @@ -156,6 +153,15 @@ class Error return Err(file, line, trace_msg, new_msg); } + static auto Err(const char *file, + int line, + const QString &trace_msg, + const char *msg, + const std::system_error &e) -> Error + { + return Err(file, line, trace_msg, msg, e, e.code().value()); + } + static auto Err(const char *file, int line, const QString &trace_msg, @@ -165,6 +171,15 @@ class Error return Err(file, line, trace_msg, msg, e, e.code().value()); } + static auto Err(const char *file, + int line, + const QString &trace_msg, + const std::string &msg, + const std::system_error &e) -> Error + { + return Err(file, line, trace_msg, msg.c_str(), e, e.code().value()); + } + template static auto Err(const char *file, int line, @@ -198,11 +213,9 @@ class Error std::move(cause.error().pImpl))); } - static auto Err(const char *file, - int line, - const QString &trace_msg, - const QString &msg, - Error &&cause) -> Error + static auto + Err(const char *file, int line, const QString &trace_msg, const QString &msg, Error &&cause) + -> Error { return Error(std::make_unique(file, line, diff --git a/misc/CMakeLists.txt b/misc/CMakeLists.txt index 75c2ac35b..2caa5c196 100644 --- a/misc/CMakeLists.txt +++ b/misc/CMakeLists.txt @@ -38,6 +38,7 @@ configure_files( lib/linglong/container/README.md lib/linglong/generate-xdg-data-dirs.sh lib/systemd/system-environment-generators/61-linglong + lib/systemd/user-generators/linglong-user-systemd-generator lib/systemd/system/org.deepin.linglong.PackageManager.service lib/systemd/system-preset/91-linglong.preset lib/systemd/user/linglong-session-helper.service @@ -149,6 +150,11 @@ install( ${CMAKE_CURRENT_BINARY_DIR}/lib/systemd/system-environment-generators/61-linglong DESTINATION ${SYSTEMD_SYSTEM_GENERATOR_PATH}) +install( + PROGRAMS + ${CMAKE_CURRENT_BINARY_DIR}/lib/systemd/user-generators/linglong-user-systemd-generator + DESTINATION ${CMAKE_INSTALL_PREFIX}/lib/systemd/user-generators) + # sysuser set(SYSTEMD_SYSUSERSDIR ${CMAKE_INSTALL_PREFIX}/lib/sysusers.d) diff --git a/misc/lib/systemd/user-generators/linglong-user-systemd-generator b/misc/lib/systemd/user-generators/linglong-user-systemd-generator new file mode 100755 index 000000000..f398a2216 --- /dev/null +++ b/misc/lib/systemd/user-generators/linglong-user-systemd-generator @@ -0,0 +1,9 @@ +#!/usr/bin/env sh + +# SPDX-FileCopyrightText: 2023 UnionTech Software Technology Co., Ltd. +# +# SPDX-License-Identifier: LGPL-3.0-or-later + +# https://www.freedesktop.org/software/systemd/man/latest/systemd.generator.html +late="$3" +cp -rs @LINGLONG_ROOT@/entries/lib/systemd/user/* "$late/" diff --git a/po/POTFILES.in b/po/POTFILES.in index 7aec559f1..41b8024c4 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -2,7 +2,7 @@ [encoding: UTF-8] # ll-cli source files - +libs/linglong/src/linglong/cli/cli.cpp apps/ll-cli/src/main.cpp # ll-builder source files diff --git a/po/en_GB.po b/po/en_GB.po index 7914611e0..aa01667aa 100644 --- a/po/en_GB.po +++ b/po/en_GB.po @@ -8,8 +8,8 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2024-12-11 11:01+0800\n" -"PO-Revision-Date: 2024-11-23 17:05+0800\n" +"POT-Creation-Date: 2025-01-07 17:11+0800\n" +"PO-Revision-Date: 2025-01-07 17:11+0800\n" "Last-Translator: deepiner, 2024\n" "Language-Team: LANGUAGE \n" "Language: en_GB\n" @@ -17,7 +17,17 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -#: ../apps/ll-cli/src/main.cpp:185 +#: ../libs/linglong/src/linglong/cli/cli.cpp:55 +msgid "Permission deny, please check whether you are running as root." +msgstr "Permission deny, please check whether you are running as root." + +#: ../libs/linglong/src/linglong/cli/cli.cpp:2412 +msgid "" +"The cache generation failed, please uninstall and reinstall the application." +msgstr "" +"The cache generation failed, please uninstall and reinstall the application." + +#: ../apps/ll-cli/src/main.cpp:179 msgid "" "linyaps CLI\n" "A CLI program to run application and manage application and runtime\n" @@ -628,7 +638,19 @@ msgstr "Usage: ll-builder import [OPTIONS] LAYER" msgid "Layer file path" msgstr "Layer file path" -#: ../apps/ll-builder/src/main.cpp:328 +#: ../apps/ll-builder/src/main.cpp:330 +msgid "Import linyaps layer dir to build repo" +msgstr "Import linyaps layer dir to build repo" + +#: ../apps/ll-builder/src/main.cpp:332 +msgid "Usage: ll-builder import-dir PATH" +msgstr "Usage: ll-builder import-dir PATH" + +#: ../apps/ll-builder/src/main.cpp:333 +msgid "Layer dir path" +msgstr "Layer dir path" + +#: ../apps/ll-builder/src/main.cpp:339 msgid "Extract linyaps layer to dir" msgstr "Extract linyaps layer to dir" @@ -694,6 +716,3 @@ msgstr "Linglong Package Manager" #: ../apps/ll-dialog/src/cache_dialog.cpp:52 msgid "is starting" msgstr "is starting" - -#~ msgid "Migrate repository data" -#~ msgstr "Migrate repository data" diff --git a/po/en_US.po b/po/en_US.po index 06f03d622..aa01667aa 100644 --- a/po/en_US.po +++ b/po/en_US.po @@ -8,16 +8,26 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2024-12-11 11:01+0800\n" -"PO-Revision-Date: 2024-11-21 17:58+0800\n" +"POT-Creation-Date: 2025-01-07 17:11+0800\n" +"PO-Revision-Date: 2025-01-07 17:11+0800\n" "Last-Translator: deepiner, 2024\n" "Language-Team: LANGUAGE \n" -"Language: en_US\n" +"Language: en_GB\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -#: ../apps/ll-cli/src/main.cpp:185 +#: ../libs/linglong/src/linglong/cli/cli.cpp:55 +msgid "Permission deny, please check whether you are running as root." +msgstr "Permission deny, please check whether you are running as root." + +#: ../libs/linglong/src/linglong/cli/cli.cpp:2412 +msgid "" +"The cache generation failed, please uninstall and reinstall the application." +msgstr "" +"The cache generation failed, please uninstall and reinstall the application." + +#: ../apps/ll-cli/src/main.cpp:179 msgid "" "linyaps CLI\n" "A CLI program to run application and manage application and runtime\n" @@ -628,7 +638,19 @@ msgstr "Usage: ll-builder import [OPTIONS] LAYER" msgid "Layer file path" msgstr "Layer file path" -#: ../apps/ll-builder/src/main.cpp:328 +#: ../apps/ll-builder/src/main.cpp:330 +msgid "Import linyaps layer dir to build repo" +msgstr "Import linyaps layer dir to build repo" + +#: ../apps/ll-builder/src/main.cpp:332 +msgid "Usage: ll-builder import-dir PATH" +msgstr "Usage: ll-builder import-dir PATH" + +#: ../apps/ll-builder/src/main.cpp:333 +msgid "Layer dir path" +msgstr "Layer dir path" + +#: ../apps/ll-builder/src/main.cpp:339 msgid "Extract linyaps layer to dir" msgstr "Extract linyaps layer to dir" @@ -694,6 +716,3 @@ msgstr "Linglong Package Manager" #: ../apps/ll-dialog/src/cache_dialog.cpp:52 msgid "is starting" msgstr "is starting" - -#~ msgid "Migrate repository data" -#~ msgstr "Migrate repository data" diff --git a/po/es.po b/po/es.po index 7833de1b3..b9fafbbd8 100644 --- a/po/es.po +++ b/po/es.po @@ -8,8 +8,8 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2024-12-11 11:01+0800\n" -"PO-Revision-Date: 2024-11-21 18:10+0800\n" +"POT-Creation-Date: 2025-01-07 17:11+0800\n" +"PO-Revision-Date: 2025-01-07 17:11+0800\n" "Last-Translator: deepiner, 2024\n" "Language-Team: LANGUAGE \n" "Language: es\n" @@ -17,7 +17,18 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -#: ../apps/ll-cli/src/main.cpp:185 +#: ../libs/linglong/src/linglong/cli/cli.cpp:55 +msgid "Permission deny, please check whether you are running as root." +msgstr "Permiso denegado, por favor verifica si estás ejecutando como root." + +#: ../libs/linglong/src/linglong/cli/cli.cpp:2412 +msgid "" +"The cache generation failed, please uninstall and reinstall the application." +msgstr "" +"La generación de la caché falló, por favor desinstala y reinstala la " +"aplicación." + +#: ../apps/ll-cli/src/main.cpp:179 msgid "" "linyaps CLI\n" "A CLI program to run application and manage application and runtime\n" @@ -642,7 +653,19 @@ msgstr "Uso: ll-builder import [OPCIONES] CAPA" msgid "Layer file path" msgstr "Ruta del archivo de la capa" -#: ../apps/ll-builder/src/main.cpp:328 +#: ../apps/ll-builder/src/main.cpp:330 +msgid "Import linyaps layer dir to build repo" +msgstr "Importar directorio de capa linyaps al repositorio de compilación" + +#: ../apps/ll-builder/src/main.cpp:332 +msgid "Usage: ll-builder import-dir PATH" +msgstr "Uso: ll-builder import-dir PATH" + +#: ../apps/ll-builder/src/main.cpp:333 +msgid "Layer dir path" +msgstr "Ruta del directorio de capa" + +#: ../apps/ll-builder/src/main.cpp:339 msgid "Extract linyaps layer to dir" msgstr "Extraer la capa de linyaps al directorio" @@ -708,6 +731,3 @@ msgstr "Administrador de paquetes Linglong" #: ../apps/ll-dialog/src/cache_dialog.cpp:52 msgid "is starting" msgstr "A partir de" - -#~ msgid "Migrate repository data" -#~ msgstr "Migrate datos del repositorio" diff --git a/po/linyaps.pot b/po/linyaps.pot index f6b7015ee..3bf487c75 100644 --- a/po/linyaps.pot +++ b/po/linyaps.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2024-12-11 11:01+0800\n" +"POT-Creation-Date: 2025-01-07 17:11+0800\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -17,7 +17,16 @@ msgstr "" "Content-Type: text/plain; charset=CHARSET\n" "Content-Transfer-Encoding: 8bit\n" -#: ../apps/ll-cli/src/main.cpp:185 +#: ../libs/linglong/src/linglong/cli/cli.cpp:55 +msgid "Permission deny, please check whether you are running as root." +msgstr "" + +#: ../libs/linglong/src/linglong/cli/cli.cpp:2412 +msgid "" +"The cache generation failed, please uninstall and reinstall the application." +msgstr "" + +#: ../apps/ll-cli/src/main.cpp:179 msgid "" "linyaps CLI\n" "A CLI program to run application and manage application and runtime\n" @@ -561,7 +570,19 @@ msgstr "" msgid "Layer file path" msgstr "" -#: ../apps/ll-builder/src/main.cpp:328 +#: ../apps/ll-builder/src/main.cpp:330 +msgid "Import linyaps layer dir to build repo" +msgstr "" + +#: ../apps/ll-builder/src/main.cpp:332 +msgid "Usage: ll-builder import-dir PATH" +msgstr "" + +#: ../apps/ll-builder/src/main.cpp:333 +msgid "Layer dir path" +msgstr "" + +#: ../apps/ll-builder/src/main.cpp:339 msgid "Extract linyaps layer to dir" msgstr "" diff --git a/po/zh_CN.po b/po/zh_CN.po index 7cf6c9ed5..561ae8ca2 100644 --- a/po/zh_CN.po +++ b/po/zh_CN.po @@ -8,8 +8,8 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2024-12-11 11:01+0800\n" -"PO-Revision-Date: 2024-11-21 18:08+0800\n" +"POT-Creation-Date: 2025-01-07 17:11+0800\n" +"PO-Revision-Date: 2025-01-07 17:11+0800\n" "Last-Translator: deepiner, 2024\n" "Language-Team: LANGUAGE \n" "Language: zh_CN\n" @@ -17,7 +17,16 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -#: ../apps/ll-cli/src/main.cpp:185 +#: ../libs/linglong/src/linglong/cli/cli.cpp:55 +msgid "Permission deny, please check whether you are running as root." +msgstr "权限被拒绝,请检查是否以 root 身份运行。" + +#: ../libs/linglong/src/linglong/cli/cli.cpp:2412 +msgid "" +"The cache generation failed, please uninstall and reinstall the application." +msgstr "缓存生成失败,请卸载并重新安装该应用程序。" + +#: ../apps/ll-cli/src/main.cpp:179 msgid "" "linyaps CLI\n" "A CLI program to run application and manage application and runtime\n" @@ -616,7 +625,19 @@ msgstr "用法: ll-builder import [选项] LAYER文件" msgid "Layer file path" msgstr "layer文件路径" -#: ../apps/ll-builder/src/main.cpp:328 +#: ../apps/ll-builder/src/main.cpp:330 +msgid "Import linyaps layer dir to build repo" +msgstr "导入如意玲珑layer目录到构建仓库" + +#: ../apps/ll-builder/src/main.cpp:332 +msgid "Usage: ll-builder import-dir PATH" +msgstr "用法: ll-builder import-dir PATH" + +#: ../apps/ll-builder/src/main.cpp:333 +msgid "Layer dir path" +msgstr "layer目录路径" + +#: ../apps/ll-builder/src/main.cpp:339 msgid "Extract linyaps layer to dir" msgstr "将如意玲珑layer文件解压到目录" @@ -682,6 +703,3 @@ msgstr "玲珑软件包管理器" #: ../apps/ll-dialog/src/cache_dialog.cpp:52 msgid "is starting" msgstr "正在启动" - -#~ msgid "Migrate repository data" -#~ msgstr "迁移仓库数据" diff --git a/rpm/linglong.spec b/rpm/linglong.spec index a8b59117c..3baae2d19 100644 --- a/rpm/linglong.spec +++ b/rpm/linglong.spec @@ -11,7 +11,7 @@ BuildRequires: qt5-qtbase-devel qt5-qtbase-private-devel BuildRequires: glib2-devel nlohmann-json-devel ostree-devel yaml-cpp-devel BuildRequires: systemd-devel gtest-devel libseccomp-devel elfutils-libelf-devel BuildRequires: glibc-static libstdc++-static -BuildRequires: libcurl-devel openssl-devel +BuildRequires: libcurl-devel openssl-devel libcap-devel BuildRequires: gtest-devel gmock-devel Requires: linglong-bin = %{version}-%{release} @@ -85,6 +85,7 @@ cd build %{_prefix}/lib/systemd/system-preset/*.preset %{_prefix}/lib/systemd/user/* %{_prefix}/lib/systemd/system-environment-generators/* +%{_prefix}/lib/systemd/user-generators/* %{_libexecdir}/%{name}/ll-package-manager %{_libexecdir}/%{name}/ll-session-helper %{_libexecdir}/%{name}/ld-cache-generator