diff --git a/apps/ll-cli/src/main.cpp b/apps/ll-cli/src/main.cpp index 6a3579c51..3928ab847 100644 --- a/apps/ll-cli/src/main.cpp +++ b/apps/ll-cli/src/main.cpp @@ -112,7 +112,7 @@ int lockCheck() noexcept return 0; } - qCritical() << "failed to open lock" << lock << ::strerror(errno); + qCritical() << "failed to open lock" << lock << errorString(errno); return -1; } diff --git a/apps/ll-driver-detect/src/main.cpp b/apps/ll-driver-detect/src/main.cpp index 39d91c429..258ebf00a 100644 --- a/apps/ll-driver-detect/src/main.cpp +++ b/apps/ll-driver-detect/src/main.cpp @@ -8,7 +8,7 @@ #include "driver_detection_config.h" #include "driver_detection_manager.h" #include "driver_detector.h" -#include "linglong/utils/command/cmd.h" +#include "linglong/utils/cmd.h" #include "linglong/utils/error/error.h" #include "linglong/utils/gettext.h" #include "linglong/utils/global/initialize.h" @@ -105,8 +105,7 @@ installDriverPackage(const std::vector &drivers) } // Execute ll-cli install command to install the package - auto ret = linglong::utils::command::Cmd("ll-cli").exec( - { "install", QString::fromStdString(driverInfo.packageName) }); + auto ret = linglong::utils::Cmd("ll-cli").exec({ "install", driverInfo.packageName }); if (!ret) { return LINGLONG_ERR("Installation command failed: " + ret.error().message()); } diff --git a/apps/ll-driver-detect/src/nvidia_driver_detector.cpp b/apps/ll-driver-detect/src/nvidia_driver_detector.cpp index a970661d3..3eb5f72e0 100644 --- a/apps/ll-driver-detect/src/nvidia_driver_detector.cpp +++ b/apps/ll-driver-detect/src/nvidia_driver_detector.cpp @@ -4,7 +4,7 @@ #include "nvidia_driver_detector.h" -#include "linglong/utils/command/cmd.h" +#include "linglong/utils/cmd.h" #include "linglong/utils/error/error.h" #include "linglong/utils/log/log.h" @@ -53,13 +53,12 @@ utils::error::Result NVIDIADriverDetector::checkPackageExists(const std::s { LINGLONG_TRACE("Check if NVIDIA driver package exists in repository"); // Execute ll-cli search command to check driver package existence - auto ret = linglong::utils::command::Cmd("ll-cli").exec( - { "search", QString::fromStdString(packageName) }); + auto ret = linglong::utils::Cmd("ll-cli").exec({ "search", packageName }); if (!ret) { return LINGLONG_ERR("Search command failed: " + ret.error().message()); } - if (!ret->contains(QString::fromStdString(packageName))) { + if (ret->find(packageName) == std::string::npos) { return LINGLONG_ERR("Driver package not found in linglong repo: " + packageName); } return LINGLONG_OK; @@ -72,8 +71,7 @@ NVIDIADriverDetector::checkPackageInstalled(const std::string &packageName) try { // First execute ll-cli info to get the package info - auto listResult = - linglong::utils::command::Cmd("ll-cli").exec({ "info", packageName.c_str() }); + auto listResult = linglong::utils::Cmd("ll-cli").exec({ "info", packageName }); if (!listResult) { LogD( diff --git a/libs/linglong/src/linglong/builder/linglong_builder.cpp b/libs/linglong/src/linglong/builder/linglong_builder.cpp index 9968470f3..f5615f5bf 100644 --- a/libs/linglong/src/linglong/builder/linglong_builder.cpp +++ b/libs/linglong/src/linglong/builder/linglong_builder.cpp @@ -20,7 +20,7 @@ #include "linglong/repo/config.h" #include "linglong/repo/ostree_repo.h" #include "linglong/runtime/container.h" -#include "linglong/utils/command/cmd.h" +#include "linglong/utils/cmd.h" #include "linglong/utils/error/error.h" #include "linglong/utils/file.h" #include "linglong/utils/global/initialize.h" @@ -985,11 +985,8 @@ utils::error::Result Builder::generateAppConf() noexcept scriptFile = dir->filePath("app-conf-generator"); QFile::copy(":/scripts/app-conf-generator", scriptFile); } - auto output = - utils::command::Cmd("bash").exec({ "-e", - scriptFile, - QString::fromStdString(project.package.id), - QString::fromStdString(buildOutput.string()) }); + auto output = utils::Cmd("bash").exec( + { "-e", scriptFile.toStdString(), project.package.id, buildOutput.string() }); if (!output) { return LINGLONG_ERR(output); } @@ -1700,8 +1697,8 @@ utils::error::Result Builder::extractLayer(const QString &layerPath, return LINGLONG_ERR(layerDir); } - auto output = - utils::command::Cmd("cp").exec({ "-r", layerDir->absolutePath(), destDir.absolutePath() }); + auto output = utils::Cmd("cp").exec( + { "-r", layerDir->absolutePath().toStdString(), destDir.absolutePath().toStdString() }); if (!output) { return LINGLONG_ERR(output); } diff --git a/libs/linglong/src/linglong/builder/source_fetcher.cpp b/libs/linglong/src/linglong/builder/source_fetcher.cpp index f068db185..924cbcca5 100644 --- a/libs/linglong/src/linglong/builder/source_fetcher.cpp +++ b/libs/linglong/src/linglong/builder/source_fetcher.cpp @@ -7,7 +7,6 @@ #include "source_fetcher.h" #include "configure.h" -#include "linglong/utils/command/cmd.h" #include "linglong/utils/error/error.h" #include "linglong/utils/global/initialize.h" #include "linglong/utils/log/log.h" @@ -55,15 +54,15 @@ auto SourceFetcher::fetch(QDir destination) noexcept -> utils::error::ResultsetEnv("GIT_SUBMODULES", source.submodules.value_or(true) ? "true" : "") - .exec({ - scriptFile, - destination.absoluteFilePath(getSourceName()), - QString::fromStdString(*source.url), - QString::fromStdString(source.kind == "git" ? *source.commit : *source.digest), - this->cacheDir.absolutePath(), - }); + if (source.kind == "git") { + m_cmd->setEnv("GIT_SUBMODULES", source.submodules.value_or(true) ? "true" : ""); + } + auto output = m_cmd->exec( + std::vector{ scriptFile.toStdString(), + destination.absoluteFilePath(getSourceName()).toStdString(), + *source.url, + source.kind == "git" ? *source.commit : *source.digest, + this->cacheDir.absolutePath().toStdString() }); if (!output.has_value()) { LogE("output error: {}", output.error()); return LINGLONG_ERR("stderr:", output); diff --git a/libs/linglong/src/linglong/builder/source_fetcher.h b/libs/linglong/src/linglong/builder/source_fetcher.h index 7036100f9..248c37d4c 100644 --- a/libs/linglong/src/linglong/builder/source_fetcher.h +++ b/libs/linglong/src/linglong/builder/source_fetcher.h @@ -8,7 +8,7 @@ #include "linglong/api/types/v1/BuilderConfig.hpp" #include "linglong/api/types/v1/BuilderProjectSource.hpp" -#include "linglong/utils/command/cmd.h" +#include "linglong/utils/cmd.h" #include "linglong/utils/error/error.h" #include @@ -25,13 +25,13 @@ class SourceFetcher auto fetch(QDir destination) noexcept -> utils::error::Result; - void setCommand(std::shared_ptr cmd) { this->m_cmd = cmd; } + void setCommand(std::shared_ptr cmd) { this->m_cmd = cmd; } private: QString getSourceName(); QDir cacheDir; api::types::v1::BuilderProjectSource source; - std::shared_ptr m_cmd = std::make_shared("sh"); + std::shared_ptr m_cmd = std::make_shared("sh"); }; } // namespace linglong::builder diff --git a/libs/linglong/src/linglong/cli/cli.cpp b/libs/linglong/src/linglong/cli/cli.cpp index a61f3d172..e3fc88b69 100644 --- a/libs/linglong/src/linglong/cli/cli.cpp +++ b/libs/linglong/src/linglong/cli/cli.cpp @@ -483,8 +483,7 @@ int Cli::run(const RunOptions &options) // placeholder file auto fd = ::open(pidFile.c_str(), O_WRONLY | O_CREAT | O_EXCL, mode); if (fd == -1) { - qCritical() - << QString{ "create file %1 error: %2" }.arg(pidFile.c_str()).arg(::strerror(errno)); + LogE("create file {} error: {}", pidFile, errorString(errno)); QCoreApplication::exit(-1); return -1; } @@ -1712,7 +1711,7 @@ int Cli::content(const ContentOptions &options) if (real != nullptr) { target = real; } else { - qCritical() << "resolve symlink " << file.c_str() << " error: " << ::strerror(errno); + LogE("resolve symlink {} error: {}", file, errorString(errno)); } } @@ -1992,7 +1991,7 @@ Cli::RequestDirectories(const api::types::v1::PackageInfoV2 &info) noexcept auto fd = ::shm_open(info.id.c_str(), O_RDWR | O_CREAT, 0600); if (fd < 0) { - return LINGLONG_ERR(QString{ "shm_open error:" } + ::strerror(errno)); + return LINGLONG_ERR("shm_open error:" + errorString(errno)); } auto closeFd = utils::finally::finally([fd] { ::close(fd); @@ -2017,7 +2016,7 @@ Cli::RequestDirectories(const api::types::v1::PackageInfoV2 &info) noexcept continue; } - return LINGLONG_ERR(QString{ "fcntl lock error: " } + ::strerror(errno)); + return LINGLONG_ERR("fcntl lock error: " + errorString(errno)); } if (!anotherRunning) { @@ -2027,7 +2026,7 @@ Cli::RequestDirectories(const api::types::v1::PackageInfoV2 &info) noexcept lock.l_type = F_UNLCK; ret = ::fcntl(fd, F_SETLK, &lock); if (ret == -1) { - return LINGLONG_ERR(QString{ "fcntl unlock error: " } + ::strerror(errno)); + return LINGLONG_ERR("fcntl unlock error: " + errorString(errno)); } return LINGLONG_OK; @@ -2037,11 +2036,11 @@ Cli::RequestDirectories(const api::types::v1::PackageInfoV2 &info) noexcept auto releaseResource = utils::finally::finally([&info, &lock, fd] { lock.l_type = F_UNLCK; if (::fcntl(fd, F_SETLK, &lock) == -1) { - qDebug() << "failed to unlock mem file:" << ::strerror(errno); + LogD("failed to unlock mem file: {}", errorString(errno)); } if (::shm_unlink(info.id.c_str()) == -1) { - qDebug() << "shm_unlink error:" << ::strerror(errno); + LogD("shm_unlink error: {}", errorString(errno)); } }); diff --git a/libs/linglong/src/linglong/package/layer_file.cpp b/libs/linglong/src/linglong/package/layer_file.cpp index fc367a4e1..b7c21a8a7 100644 --- a/libs/linglong/src/linglong/package/layer_file.cpp +++ b/libs/linglong/src/linglong/package/layer_file.cpp @@ -8,6 +8,8 @@ #include "linglong/api/types/v1/Generators.hpp" #include "linglong/api/types/v1/LayerInfo.hpp" +#include "linglong/utils/error/error.h" +#include "linglong/utils/log/formatter.h" #include "linglong/utils/serialize/json.h" #include @@ -31,7 +33,7 @@ utils::error::Result> LayerFile::New(const QString &pa LINGLONG_TRACE("install layer file from path") auto fd = ::open(path.toLocal8Bit(), O_RDONLY); if (fd < 0) { - return LINGLONG_ERR("failed to open " + path + ":" + ::strerror(errno)); + return LINGLONG_ERR(fmt::format("failed to open {}: {}", path, ::errorString(errno))); } return New(fd); diff --git a/libs/linglong/src/linglong/package/layer_packager.cpp b/libs/linglong/src/linglong/package/layer_packager.cpp index 6522e77a6..288e58246 100644 --- a/libs/linglong/src/linglong/package/layer_packager.cpp +++ b/libs/linglong/src/linglong/package/layer_packager.cpp @@ -8,8 +8,8 @@ #include "linglong/api/types/v1/Generators.hpp" #include "linglong/api/types/v1/LayerInfo.hpp" -#include "linglong/utils/command/cmd.h" -#include "linglong/utils/command/env.h" +#include "linglong/utils/cmd.h" +#include "linglong/utils/file.h" #include "linglong/utils/log/log.h" #include @@ -28,8 +28,7 @@ LayerPackager::LayerPackager() // maybe refactor on later auto ret = this->initWorkDir(); if (!ret) { - qCritical() << "init work dir failed"; - Q_ASSERT(false); + LogE("init work dir failed"); } } @@ -48,16 +47,15 @@ utils::error::Result LayerPackager::initWorkDir() // 优先使用环境变量LINGLONG_TMPDIR指定的目录,默认为/var/tmp,避免/tmp是tmpfs内存不足 auto uuid = QUuid::createUuid().toString(QUuid::Id128); auto dirName = "linglong-layer-workdir-" + uuid.toStdString(); - auto tmpDir = utils::command::getEnv("LINGLONG_TMPDIR"); - auto dirPath = std::filesystem::path(tmpDir.value_or("/var/tmp")) / dirName; + auto *tmpDir = std::getenv("LINGLONG_TMPDIR"); + auto dirPath = std::filesystem::path(tmpDir ? tmpDir : "/var/tmp") / dirName; auto ret = this->mkdirDir(dirPath); if (!ret.has_value()) { // 如果/var/tmp目录无权限创建,则使用临时目录 dirPath = std::filesystem::temp_directory_path() / dirName; ret = this->mkdirDir(dirPath); if (!ret) { - qCritical() << "failed to set work dir" << ret.error().message(); - Q_ASSERT(false); + LogE("failed to set work dir: {}", ret.error()); } } this->workDir = dirPath; @@ -84,16 +82,15 @@ utils::error::Result LayerPackager::mkdirDir(const std::string &path) noex LayerPackager::~LayerPackager() { if (this->isMounted) { - auto ret = utils::command::Cmd("fusermount") + auto ret = utils::Cmd("fusermount") .exec({ "-z", "-u", (this->workDir / "unpack").string().c_str() }); if (!ret) { - qWarning() << "failed to umount " << (this->workDir / "unpack").c_str() - << ", please umount it manually"; + LogW("failed to umount {}, please umount it manually", + (this->workDir / "unpack").string()); } } if (!std::filesystem::remove_all(this->workDir)) { LogE("failed to remove {}", this->workDir); - Q_ASSERT(false); } } @@ -151,23 +148,21 @@ LayerPackager::pack(const LayerDir &dir, const QString &layerFilePath) const // compress data with erofs const auto &compressedFilePath = this->workDir / "tmp.erofs"; - const auto &ignoreRegex = QString{ "--exclude-regex=minified*" }; // 使用-b统一指定block size为4096(2^12), 避免不同系统的兼容问题 // loongarch64默认使用(16384)2^14, 在x86和arm64不受支持, 会导致无法推包 - auto ret = utils::command::Cmd("mkfs.erofs") - .exec({ "-z" + compressor, - "-b4096", - compressedFilePath.string().c_str(), - ignoreRegex, - dir.absolutePath() }); + auto ret = utils::Cmd("mkfs.erofs") + .exec(std::vector{ "-z" + compressor.toStdString(), + "-b4096", + compressedFilePath.string(), + "--exclude-regex=minified*", + dir.absolutePath().toStdString() }); if (!ret) { return LINGLONG_ERR(ret); } - ret = utils::command::Cmd("sh").exec( - { "-c", QString("cat %1 >> %2").arg(compressedFilePath.string().c_str(), layerFilePath) }); - if (!ret) { - LINGLONG_ERR(ret); + auto res = utils::concatFile(compressedFilePath, layerFilePath.toStdString()); + if (!res) { + return LINGLONG_ERR(res); } auto result = LayerFile::New(layerFilePath); @@ -242,8 +237,10 @@ utils::error::Result LayerPackager::unpack(LayerFile &file) } fuseOffset = "0"; } - auto ret = utils::command::Cmd("erofsfuse") - .exec({ "--offset=" + fuseOffset, fdPath, unpackDir.absolutePath() }); + auto ret = utils::Cmd("erofsfuse") + .exec({ "--offset=" + fuseOffset.toStdString(), + fdPath.toStdString(), + unpackDir.absolutePath().toStdString() }); if (!ret) { return LINGLONG_ERR(ret); } @@ -252,15 +249,16 @@ utils::error::Result LayerPackager::unpack(LayerFile &file) } // 判断fsck.erofs命令是否存在,fsck.erofs是erofs-utils的命令,可用于解压erofs文件 // 在旧版本中fsck.erofs不支持offset参数,所以需要提前将erofs文件复制到临时目录 - auto erofsFscExistsRet = utils::command::Cmd("fsck.erofs").exists(); + auto erofsFscExistsRet = utils::Cmd("fsck.erofs").exists(); if (erofsFscExistsRet) { fdPath = (this->workDir / "layer.erofs").string().c_str(); auto ret = this->copyFile(file, fdPath.toStdString(), *offset); if (!ret) { return LINGLONG_ERR(ret); } - auto cmdRet = utils::command::Cmd("fsck.erofs") - .exec({ "--extract=" + unpackDir.absolutePath(), fdPath }); + auto cmdRet = + utils::Cmd("fsck.erofs") + .exec({ "--extract=" + unpackDir.absolutePath().toStdString(), fdPath.toStdString() }); if (!cmdRet) { return LINGLONG_ERR(cmdRet); } @@ -278,7 +276,7 @@ void LayerPackager::setCompressor(const QString &compressor) noexcept utils::error::Result LayerPackager::checkErofsFuseExists() const { - return utils::command::Cmd("erofsfuse").exists(); + return utils::Cmd("erofsfuse").exists(); } } // namespace linglong::package diff --git a/libs/linglong/src/linglong/package/layer_packager.h b/libs/linglong/src/linglong/package/layer_packager.h index 964062e4a..e672c39f6 100644 --- a/libs/linglong/src/linglong/package/layer_packager.h +++ b/libs/linglong/src/linglong/package/layer_packager.h @@ -10,6 +10,7 @@ #include "linglong/package/layer_file.h" #include "linglong/utils/error/error.h" +#include #include #include diff --git a/libs/linglong/src/linglong/package/uab_file.cpp b/libs/linglong/src/linglong/package/uab_file.cpp index 75209b72b..e1b41c700 100644 --- a/libs/linglong/src/linglong/package/uab_file.cpp +++ b/libs/linglong/src/linglong/package/uab_file.cpp @@ -5,10 +5,10 @@ #include "linglong/package/uab_file.h" #include "linglong/api/types/v1/Generators.hpp" -#include "linglong/utils/command/cmd.h" -#include "linglong/utils/command/env.h" +#include "linglong/utils/cmd.h" #include "linglong/utils/error/error.h" #include "linglong/utils/finally/finally.h" +#include "linglong/utils/log/log.h" #include @@ -56,18 +56,16 @@ utils::error::Result> UABFile::loadFromFile(int fd) noe UABFile::~UABFile() { if (!m_mountPoint.empty()) { - auto ret = utils::command::Cmd("fusermount").exec({ "-z", "-u", m_mountPoint.c_str() }); + auto ret = utils::Cmd("fusermount").exec({ "-z", "-u", m_mountPoint.c_str() }); if (!ret) { - qCritical() << "failed to umount " << m_mountPoint.c_str() - << ", please umount it manually"; + LogE("failed to umount {}, please umount it manually", m_mountPoint); } } if (!m_unpackPath.empty()) { std::error_code ec; std::filesystem::remove_all(std::filesystem::path(m_unpackPath).parent_path(), ec); if (ec) { - qCritical() << "failed to remove " << m_unpackPath.c_str() - << ", please remove it manually"; + LogE("failed to remove {}, please remove it manually", m_unpackPath); } } if (this->e) { @@ -216,8 +214,8 @@ utils::error::Result UABFile::unpack() noexcept auto dirName = "linglong-uab-" + QUuid::createUuid().toString(QUuid::Id128).toStdString(); // 优先使用/var/tmp目录,避免tmpfs内存不足 - auto tmpDir = utils::command::getEnv("LINGLONG_TMPDIR").value_or("/var/tmp"); - auto unpackPath = std::filesystem::path(tmpDir) / dirName / "unpack"; + auto *tmpDir = std::getenv("LINGLONG_TMPDIR"); + auto unpackPath = std::filesystem::path(tmpDir ? tmpDir : "/var/tmp") / dirName / "unpack"; auto ret = this->mkdirDir(unpackPath); if (!ret) { // 如果/var/tmp目录无权限创建,则使用临时目录 @@ -239,9 +237,10 @@ utils::error::Result UABFile::unpack() noexcept return LINGLONG_ERR(ret.error()); } } - auto ret = - utils::command::Cmd("erofsfuse") - .exec(QStringList{ QString{ "--offset=%1" }.arg(offset), uabFile, unpackPath.c_str() }); + auto ret = utils::Cmd("erofsfuse") + .exec(std::vector{ fmt::format("--offset={}", offset), + uabFile.toStdString(), + unpackPath.string() }); if (!ret) { return LINGLONG_ERR(ret.error()); } @@ -256,8 +255,9 @@ utils::error::Result UABFile::unpack() noexcept if (!ret) { return LINGLONG_ERR(ret.error()); } - auto cmdRet = utils::command::Cmd("fsck.erofs") - .exec({ "--extract=" + QString::fromStdString(unpackPath), uabFile }); + auto cmdRet = utils::Cmd("fsck.erofs") + .exec(std::vector{ "--extract=" + unpackPath.string(), + uabFile.toStdString() }); if (!cmdRet) { return LINGLONG_ERR(cmdRet); } @@ -278,7 +278,7 @@ utils::error::Result UABFile::extractSignData() noexcept auto signSection = getSectionHeader("linglong.bundle.sign"); if (!signSection) { - qInfo() << "couldn't get sign data:" << signSection.error().message() << "skip"; + LogI("couldn't get sign data: {} skip", signSection.error()); return {}; } @@ -321,7 +321,7 @@ utils::error::Result UABFile::extractSignData() noexcept std::error_code ec; if (!std::filesystem::remove(tarFile, ec) && ec) { - qWarning() << "remove" << tarFile.c_str() << "failed:" << ec.message().c_str(); + LogW("failed to remove {}: {}", tarFile.string(), ec.message()); } }); @@ -342,7 +342,7 @@ utils::error::Result UABFile::extractSignData() noexcept continue; } return LINGLONG_ERR( - QString{ "read from sign section error: %1" }.arg(::strerror(errno))); + fmt::format("read from sign section error: {}", ::errorString(errno))); } while (true) { @@ -353,7 +353,7 @@ utils::error::Result UABFile::extractSignData() noexcept continue; } return LINGLONG_ERR( - QString{ "write to sign.tar error: %1" }.arg(::strerror(errno))); + fmt::format("write to sign.tar error: {}", ::errorString(errno))); } if (writeBytes != readBytes) { @@ -366,17 +366,16 @@ utils::error::Result UABFile::extractSignData() noexcept } if (::fsync(tarFd) == -1) { - return LINGLONG_ERR(QString{ "fsync sign.tar error: %1" }.arg(::strerror(errno))); + return LINGLONG_ERR(fmt::format("fsync sign.tar error: {}", ::errorString(errno))); } if (::close(tarFd) == -1) { tarFd = -1; // no need to try twice - return LINGLONG_ERR(QString{ "failed to close tar: %1" }.arg(::strerror(errno))); + return LINGLONG_ERR(fmt::format("failed to close tar: {}", ::errorString(errno))); } tarFd = -1; - auto ret = utils::command::Cmd("tar").exec( - QStringList{ "-xf", tarFile.c_str(), "-C", destination.c_str() }); + auto ret = utils::Cmd("tar").exec({ "-xf", tarFile.string(), "-C", destination.string() }); if (!ret) { return LINGLONG_ERR(ret); } @@ -416,7 +415,7 @@ utils::error::Result UABFile::saveErofsToFile(const std::string &path) auto bytesRead = ::read(handle(), buf.data(), readBytes); if (bytesRead == -1) { return LINGLONG_ERR( - QString{ "read from bundle section error: %1" }.arg(::strerror(errno))); + fmt::format("read from bundle section error: {}", ::errorString(errno))); } ofs.write(buf.data(), bytesRead); if (ofs.fail()) { @@ -444,7 +443,7 @@ utils::error::Result UABFile::mkdirDir(const std::string &path) noexcept bool UABFile::checkCommandExists(const std::string &command) const { - return utils::command::Cmd(command.c_str()).exists(); + return utils::Cmd(command).exists(); } } // namespace linglong::package diff --git a/libs/linglong/src/linglong/package/uab_packager.cpp b/libs/linglong/src/linglong/package/uab_packager.cpp index 22d632c1d..84b253fa9 100644 --- a/libs/linglong/src/linglong/package/uab_packager.cpp +++ b/libs/linglong/src/linglong/package/uab_packager.cpp @@ -9,7 +9,7 @@ #include "linglong/api/types/v1/UabLayer.hpp" #include "linglong/api/types/v1/Version.hpp" #include "linglong/package/architecture.h" -#include "linglong/utils/command/cmd.h" +#include "linglong/utils/cmd.h" #include "linglong/utils/error/error.h" #include "linglong/utils/file.h" #include "linglong/utils/log/log.h" @@ -839,13 +839,13 @@ utils::error::Result UABPackager::packBundle(bool distributedOnly) noexcep } } else { // https://github.com/erofs/erofs-utils/blob/b526c0d7da46b14f1328594cf1d1b2401770f59b/README#L171-L183 - if (auto ret = utils::command::Cmd("mkfs.erofs") - .exec({ "-z" + compressor, + if (auto ret = utils::Cmd("mkfs.erofs") + .exec({ "-z" + compressor.toStdString(), "-Efragments,dedupe,ztailpacking", "-C1048576", "-b4096", // force 4096 block size, default is page size - bundleFile, - bundleDir.absolutePath() }); + bundleFile.toStdString(), + bundleDir.absolutePath().toStdString() }); !ret) { return LINGLONG_ERR(ret); } diff --git a/libs/linglong/src/linglong/package_manager/package_manager.cpp b/libs/linglong/src/linglong/package_manager/package_manager.cpp index ff57e2c0a..a4b928d70 100644 --- a/libs/linglong/src/linglong/package_manager/package_manager.cpp +++ b/libs/linglong/src/linglong/package_manager/package_manager.cpp @@ -252,16 +252,14 @@ PackageManager::getAllRunningContainers() noexcept LINGLONG_TRACE("lock whole repo") lockFd = ::open(repoLockPath, O_RDWR | O_CREAT, 0644); if (lockFd == -1) { - return LINGLONG_ERR(QStringLiteral("failed to create lock file %1: %2") - .arg(repoLockPath) - .arg(::strerror(errno))); + return LINGLONG_ERR( + fmt::format("failed to create lock file {}: {}", repoLockPath, errorString(errno))); } struct flock locker{ .l_type = F_WRLCK, .l_whence = SEEK_SET, .l_start = 0, .l_len = 0 }; if (::fcntl(lockFd, F_SETLK, &locker) == -1) { - return LINGLONG_ERR( - QStringLiteral("failed to lock %1: %2").arg(repoLockPath).arg(::strerror(errno))); + return LINGLONG_ERR(fmt::format("failed to lock {}: {}", repoLockPath, errorString(errno))); } return LINGLONG_OK; @@ -279,7 +277,7 @@ PackageManager::getAllRunningContainers() noexcept if (::fcntl(lockFd, F_SETLK, &unlocker)) { return LINGLONG_ERR( - QStringLiteral("failed to unlock %1: %2").arg(repoLockPath).arg(::strerror(errno))); + fmt::format("failed to unlock {}: {}", repoLockPath, errorString(errno))); } ::close(lockFd); diff --git a/libs/linglong/src/linglong/repo/ostree_repo.cpp b/libs/linglong/src/linglong/repo/ostree_repo.cpp index ec5e8272e..61112beb3 100644 --- a/libs/linglong/src/linglong/repo/ostree_repo.cpp +++ b/libs/linglong/src/linglong/repo/ostree_repo.cpp @@ -19,8 +19,8 @@ #include "linglong/package/reference.h" #include "linglong/package_manager/package_task.h" #include "linglong/repo/config.h" -#include "linglong/utils/command/cmd.h" -#include "linglong/utils/command/env.h" +#include "linglong/utils/cmd.h" +#include "linglong/utils/env.h" #include "linglong/utils/error/error.h" #include "linglong/utils/file.h" #include "linglong/utils/finally/finally.h" @@ -668,11 +668,8 @@ OSTreeRepo::OSTreeRepo(const QDir &path, api::types::v1::RepoConfigV2 cfg) noexc // To avoid glib start thread // set GIO_USE_VFS to local and GVFS_REMOTE_VOLUME_MONITOR_IGNORE to 1 - linglong::utils::command::EnvironmentVariableGuard gioGuard{ "GIO_USE_VFS", "local" }; - linglong::utils::command::EnvironmentVariableGuard gvfsGuard{ - "GVFS_REMOTE_VOLUME_MONITOR_IGNORE", - "1" - }; + utils::EnvironmentVariableGuard gioGuard{ "GIO_USE_VFS", "local" }; + utils::EnvironmentVariableGuard gvfsGuard{ "GVFS_REMOTE_VOLUME_MONITOR_IGNORE", "1" }; g_autoptr(GError) gErr = nullptr; g_autoptr(GFile) repoPath = nullptr; @@ -965,8 +962,8 @@ utils::error::Result OSTreeRepo::pushToRemote(const std::string &remoteRep const auto tarFileName = fmt::format("{}.tgz", reference.id); const QString tarFilePath = QDir::cleanPath(tmpDir.filePath(QString::fromStdString(tarFileName))); - QStringList args = { "-zcf", tarFilePath, "-C", layerDir->absolutePath(), "." }; - auto tarStdout = utils::command::Cmd("tar").exec(args); + auto tarStdout = utils::Cmd("tar").exec( + { "-zcf", tarFilePath.toStdString(), "-C", layerDir->absolutePath().toStdString(), "." }); if (!tarStdout) { return LINGLONG_ERR(tarStdout); } @@ -2138,45 +2135,48 @@ void OSTreeRepo::updateSharedInfo() noexcept auto mimeDataDir = QDir(this->repoDir.absoluteFilePath("entries/share/mime")); auto glibSchemasDir = QDir(this->repoDir.absoluteFilePath("entries/share/glib-2.0/schemas")); - QStringList desktopDirs; + std::vector desktopDirs; if (defaultApplicationDir.exists()) { - desktopDirs << defaultApplicationDir.absolutePath(); + desktopDirs.emplace_back(defaultApplicationDir.absolutePath().toStdString()); } if (defaultApplicationDir != customApplicationDir) { if (customApplicationDir.exists()) { - desktopDirs << customApplicationDir.absolutePath(); + desktopDirs.emplace_back(customApplicationDir.absolutePath().toStdString()); } else { - qWarning() << "warning: custom application dir " << customApplicationDir.absolutePath() - << " does not exist, ignoring"; + LogW("custom application dir {} does not exist, ignoring", + customApplicationDir.absolutePath().toStdString()); } } // 更新 desktop database if (!desktopDirs.empty()) { - auto ret = utils::command::Cmd("update-desktop-database").exec(desktopDirs); + auto ret = utils::Cmd("update-desktop-database").exec(desktopDirs); if (!ret) { - qWarning() << "warning: failed to update desktop database in " + desktopDirs.join(" ") - + ": " + QString::fromStdString(ret.error().message()); + LogW("failed to update desktop database in {}", + common::strings::join(desktopDirs, ' ')); } } // 更新 mime type database if (mimeDataDir.exists()) { - auto ret = utils::command::Cmd("update-mime-database").exec({ mimeDataDir.absolutePath() }); + auto ret = + utils::Cmd("update-mime-database").exec({ mimeDataDir.absolutePath().toStdString() }); if (!ret) { - qWarning() << "warning: failed to update mime type database in " - + mimeDataDir.absolutePath() + ": " + ret.error().message().c_str(); + LogW("failed to update mime type database in {}: {}", + mimeDataDir.absolutePath().toStdString(), + ret.error()); } } // 更新 glib-2.0/schemas if (glibSchemasDir.exists()) { auto ret = - utils::command::Cmd("glib-compile-schemas").exec({ glibSchemasDir.absolutePath() }); + utils::Cmd("glib-compile-schemas").exec({ glibSchemasDir.absolutePath().toStdString() }); if (!ret) { - qWarning() << "warning: failed to update schemas in " + glibSchemasDir.absolutePath() - + ": " + QString::fromStdString(ret.error().message()); + LogW("failed to update schemas in {}: {}", + glibSchemasDir.absolutePath().toStdString(), + ret.error()); } } } diff --git a/libs/linglong/tests/ll-tests/CMakeLists.txt b/libs/linglong/tests/ll-tests/CMakeLists.txt index 72aae05da..abbfab78c 100644 --- a/libs/linglong/tests/ll-tests/CMakeLists.txt +++ b/libs/linglong/tests/ll-tests/CMakeLists.txt @@ -18,7 +18,6 @@ pfl_add_executable( src/linglong/cli/cli_test.cpp src/linglong/common/strings_test.cpp src/linglong/common/xdg_test.cpp - src/linglong/mocks/command_mock.h src/linglong/mocks/layer_packager_mock.h src/linglong/mocks/linglong_builder_mock.h src/linglong/mocks/ostree_repo_mock.h @@ -46,7 +45,7 @@ pfl_add_executable( src/linglong/repo/ostree_repo_test.cpp src/linglong/runtime/run_context_test.cpp src/linglong/utils/bash_command_helper_test.cpp - src/linglong/utils/command_test.cpp + src/linglong/utils/cmd_test.cpp src/linglong/utils/error/error_test.cpp src/linglong/utils/file_test.cpp src/linglong/utils/filelock_test.cpp diff --git a/libs/linglong/tests/ll-tests/src/linglong/builder/source_fetcher_test.cpp b/libs/linglong/tests/ll-tests/src/linglong/builder/source_fetcher_test.cpp index 35cbe6f75..c4afd6a67 100644 --- a/libs/linglong/tests/ll-tests/src/linglong/builder/source_fetcher_test.cpp +++ b/libs/linglong/tests/ll-tests/src/linglong/builder/source_fetcher_test.cpp @@ -1,20 +1,43 @@ // SPDX-FileCopyrightText: 2024 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: LGPL-3.0-or-later +#include #include -#include "../mocks/command_mock.h" -#include "linglong/api/types/v1/Generators.hpp" #include "linglong/builder/source_fetcher.h" #include "linglong/utils/error/error.h" #include -#include using namespace linglong; -using namespace testing; namespace linglong::builder { + +using ::testing::_; +using ::testing::ElementsAre; +using ::testing::EndsWith; +using ::testing::Return; +using ::testing::ReturnRef; + +class MockCommand : public linglong::utils::Cmd +{ +public: + MockCommand(std::string command) + : Cmd(command) + { + } + + MOCK_METHOD(utils::error::Result, + exec, + (const std::vector &args), + (noexcept, override)); + MOCK_METHOD(bool, exists, (), (noexcept, override)); + MOCK_METHOD(Cmd &, + setEnv, + (const std::string &name, const std::string &value), + (noexcept, override)); +}; + class SourceFetcherTest : public ::testing::Test { protected: @@ -34,15 +57,13 @@ TEST_F(SourceFetcherTest, FetchFileSource) QDir cacheDir("/tmp/cache"); auto mockCmd = std::make_shared("mock"); - mockCmd->wrapExecFunc = [&](const QStringList &args) { - auto argsStr = args.join(" ").toStdString(); - EXPECT_TRUE(args[0].contains("fetch-file-source")) << argsStr; - EXPECT_EQ(args[1].toStdString(), "/tmp/dest/" + source.name.value()) << argsStr; - EXPECT_EQ(args[2].toStdString(), source.url) << argsStr; - EXPECT_EQ(args[3].toStdString(), source.digest) << argsStr; - EXPECT_EQ(args[4].toStdString(), "/tmp/cache") << argsStr; - return utils::error::Result("ok"); - }; + EXPECT_CALL(*mockCmd, + exec(ElementsAre(EndsWith("fetch-file-source"), + "/tmp/dest/" + source.name.value(), + source.url, + source.digest, + "/tmp/cache"))) + .WillOnce(Return("ok")); SourceFetcher fetcher(source, cacheDir); fetcher.setCommand(mockCmd); auto ret = fetcher.fetch(QDir("/tmp/dest")); @@ -62,15 +83,14 @@ TEST_F(SourceFetcherTest, FetchGitSource) QDir cacheDir("/tmp/cache"); auto mockCmd = std::make_shared("mock"); - mockCmd->wrapExecFunc = [&](const QStringList &args) { - auto argsStr = args.join(" ").toStdString(); - EXPECT_TRUE(args[0].contains("fetch-git-source")) << argsStr; - EXPECT_EQ(args[1].toStdString(), "/tmp/dest/" + source.name.value()) << argsStr; - EXPECT_EQ(args[2].toStdString(), source.url) << argsStr; - EXPECT_EQ(args[3].toStdString(), source.commit) << argsStr; - EXPECT_EQ(args[4], "/tmp/cache") << argsStr; - return utils::error::Result("ok"); - }; + EXPECT_CALL(*mockCmd, + exec(ElementsAre(EndsWith("fetch-git-source"), + "/tmp/dest/" + source.name.value(), + source.url, + source.commit, + "/tmp/cache"))) + .WillOnce(Return("ok")); + EXPECT_CALL(*mockCmd, setEnv("GIT_SUBMODULES", "true")).WillOnce(ReturnRef(*mockCmd)); SourceFetcher fetcher(source, cacheDir); fetcher.setCommand(mockCmd); auto ret = fetcher.fetch(QDir("/tmp/dest")); @@ -92,15 +112,8 @@ TEST_F(SourceFetcherTest, gitSubmodules) { QDir cacheDir("/tmp/cache"); auto mockCmd = std::make_shared("mock"); - mockCmd->wrapExecFunc = [&]([[maybe_unused]] const QStringList &args) { - return utils::error::Result("ok"); - }; - mockCmd->wrapSetEnvFunc = [&](const QString &name, - const QString &value) -> linglong::utils::command::Cmd & { - EXPECT_EQ(name, "GIT_SUBMODULES"); - EXPECT_EQ(value, "true"); - return *mockCmd; - }; + EXPECT_CALL(*mockCmd, exec(_)).WillOnce(Return("ok")); + EXPECT_CALL(*mockCmd, setEnv("GIT_SUBMODULES", "true")).WillOnce(ReturnRef(*mockCmd)); SourceFetcher fetcher(source, cacheDir); fetcher.setCommand(mockCmd); auto ret2 = fetcher.fetch(QDir("/tmp/dest")); @@ -111,14 +124,8 @@ TEST_F(SourceFetcherTest, gitSubmodules) { QDir cacheDir("/tmp/cache"); auto mockCmd = std::make_shared("mock"); - mockCmd->wrapExecFunc = [&]([[maybe_unused]] const QStringList &args) { - return utils::error::Result("ok"); - }; - mockCmd->wrapSetEnvFunc = [&](const QString &name, - const QString &value) -> linglong::utils::command::Cmd & { - EXPECT_TRUE(value.isEmpty()) << name.toStdString(); - return *mockCmd; - }; + EXPECT_CALL(*mockCmd, exec(_)).WillOnce(Return("ok")); + EXPECT_CALL(*mockCmd, setEnv("GIT_SUBMODULES", "")).WillOnce(ReturnRef(*mockCmd)); SourceFetcher fetcher(source, cacheDir); fetcher.setCommand(mockCmd); auto ret2 = fetcher.fetch(QDir("/tmp/dest")); @@ -170,6 +177,8 @@ TEST_F(SourceFetcherTest, FetchMissingRequiredField) // 预期:两种情况都返回错误结果,场景2的错误码为-2 TEST_F(SourceFetcherTest, FetchInvalidGitCommit) { + LINGLONG_TRACE("FetchInvalidGitCommit"); + api::types::v1::BuilderProjectSource source; source.kind = "git"; source.url = "https://example.com/repo.git"; @@ -184,10 +193,8 @@ TEST_F(SourceFetcherTest, FetchInvalidGitCommit) source.commit = "invalid_commit"; SourceFetcher fetcher(source, cacheDir); auto mockCmd = std::make_shared("mock"); - mockCmd->wrapExecFunc = [&]([[maybe_unused]] const QStringList &args) { - LINGLONG_TRACE("FetchInvalidGitCommit"); - return LINGLONG_ERR("Invalid commit", -2); - }; + EXPECT_CALL(*mockCmd, exec(_)).WillOnce(Return(LINGLONG_ERR("Invalid commit", -2))); + EXPECT_CALL(*mockCmd, setEnv("GIT_SUBMODULES", "true")).WillOnce(ReturnRef(*mockCmd)); fetcher.setCommand(mockCmd); auto ret = fetcher.fetch(QDir("/tmp/dest")); EXPECT_FALSE(ret.has_value()); @@ -200,6 +207,8 @@ TEST_F(SourceFetcherTest, FetchInvalidGitCommit) // 预期:两种情况都返回错误结果,场景2的错误码为-3 TEST_F(SourceFetcherTest, FetchInvalidFileDigest) { + LINGLONG_TRACE("FetchInvalidFileDigest"); + api::types::v1::BuilderProjectSource source; source.kind = "file"; source.url = "https://example.com/repo.git"; @@ -214,10 +223,7 @@ TEST_F(SourceFetcherTest, FetchInvalidFileDigest) source.digest = "invalid_digest"; SourceFetcher fetcher(source, cacheDir); auto mockCmd = std::make_shared("mock"); - mockCmd->wrapExecFunc = [&]([[maybe_unused]] const QStringList &args) { - LINGLONG_TRACE("FetchInvalidFileDigest"); - return LINGLONG_ERR("Invalid digest", -3); - }; + EXPECT_CALL(*mockCmd, exec(_)).WillOnce(Return(LINGLONG_ERR("Invalid digest", -3))); fetcher.setCommand(mockCmd); auto ret = fetcher.fetch(QDir("/tmp/dest")); EXPECT_FALSE(ret.has_value()); @@ -236,9 +242,7 @@ TEST_F(SourceFetcherTest, FetchNoSetName) QDir cacheDir("/tmp/cache"); auto mockCmd = std::make_shared("mock"); - mockCmd->wrapExecFunc = [&]([[maybe_unused]] const QStringList &args) { - return utils::error::Result("ok"); - }; + EXPECT_CALL(*mockCmd, exec(_)).WillOnce(Return("ok")); SourceFetcher fetcher(source, cacheDir); fetcher.setCommand(mockCmd); auto ret = fetcher.fetch(QDir("/tmp/dest")); diff --git a/libs/linglong/tests/ll-tests/src/linglong/common/xdg_test.cpp b/libs/linglong/tests/ll-tests/src/linglong/common/xdg_test.cpp index 1ff288862..105ced6ea 100644 --- a/libs/linglong/tests/ll-tests/src/linglong/common/xdg_test.cpp +++ b/libs/linglong/tests/ll-tests/src/linglong/common/xdg_test.cpp @@ -5,7 +5,7 @@ #include #include "linglong/common/xdg.h" -#include "linglong/utils/command/env.h" +#include "linglong/utils/env.h" #include @@ -42,13 +42,13 @@ TEST_F(XDGTest, GetXDGRuntimeDir) } { - linglong::utils::command::EnvironmentVariableGuard env("XDG_RUNTIME_DIR", "/tmp"); + linglong::utils::EnvironmentVariableGuard env("XDG_RUNTIME_DIR", "/tmp"); auto runtimeDir = linglong::common::xdg::getXDGRuntimeDir(); ASSERT_EQ(runtimeDir, "/tmp"); } { - linglong::utils::command::EnvironmentVariableGuard env("XDG_RUNTIME_DIR", ""); + linglong::utils::EnvironmentVariableGuard env("XDG_RUNTIME_DIR", ""); auto runtimeDir = linglong::common::xdg::getXDGRuntimeDir(); checkRuntimeDir(runtimeDir); } diff --git a/libs/linglong/tests/ll-tests/src/linglong/mocks/command_mock.h b/libs/linglong/tests/ll-tests/src/linglong/mocks/command_mock.h deleted file mode 100644 index 4bf9b632d..000000000 --- a/libs/linglong/tests/ll-tests/src/linglong/mocks/command_mock.h +++ /dev/null @@ -1,43 +0,0 @@ -// SPDX-FileCopyrightText: 2024 UnionTech Software Technology Co., Ltd. -// -// SPDX-License-Identifier: LGPL-3.0-or-later -#include - -#include "linglong/utils/command/cmd.h" -#include "linglong/utils/error/error.h" - -#include -#include -#include - -#include - -class MockCommand : public linglong::utils::command::Cmd -{ -public: - MockCommand(const QString &command) - : Cmd(command) - { - } - - // mock exec - std::function(const QStringList &)> wrapExecFunc; - - linglong::utils::error::Result exec(const QStringList &args) noexcept override - { - return wrapExecFunc ? wrapExecFunc(args) : Cmd::exec(args); - } - - // mock exists - std::function wrapExistsFunc; - - bool exists() noexcept override { return wrapExistsFunc ? wrapExistsFunc() : Cmd::exists(); } - - // mock setEnv - std::function wrapSetEnvFunc; - - Cmd &setEnv(const QString &name, const QString &value) noexcept override - { - return wrapSetEnvFunc ? wrapSetEnvFunc(name, value) : Cmd::setEnv(name, value); - } -}; diff --git a/libs/linglong/tests/ll-tests/src/linglong/package/layer_packager_test.cpp b/libs/linglong/tests/ll-tests/src/linglong/package/layer_packager_test.cpp index 41e3c0e50..c094b4784 100644 --- a/libs/linglong/tests/ll-tests/src/linglong/package/layer_packager_test.cpp +++ b/libs/linglong/tests/ll-tests/src/linglong/package/layer_packager_test.cpp @@ -8,10 +8,11 @@ #include "linglong/api/types/v1/Generators.hpp" #include "linglong/common/strings.h" #include "linglong/package/layer_packager.h" -#include "linglong/utils/command/cmd.h" +#include "linglong/utils/cmd.h" #include "linglong/utils/error/error.h" #include +#include #include #include @@ -113,7 +114,7 @@ TEST_F(LayerPackagerTest, LayerPackagerUnpackFuseOffset) TEST_F(LayerPackagerTest, LayerPackagerUnpackFuse) { { - auto ret = utils::command::Cmd("erofsfuse").exists(); + auto ret = utils::Cmd("erofsfuse").exists(); if (!ret) { #ifdef GTEST_SKIP GTEST_SKIP() << "Skipping this test."; diff --git a/libs/linglong/tests/ll-tests/src/linglong/package/uab_file_test.cpp b/libs/linglong/tests/ll-tests/src/linglong/package/uab_file_test.cpp index 403d8c73e..4d3963a8a 100644 --- a/libs/linglong/tests/ll-tests/src/linglong/package/uab_file_test.cpp +++ b/libs/linglong/tests/ll-tests/src/linglong/package/uab_file_test.cpp @@ -9,7 +9,7 @@ #include "linglong/common/strings.h" #include "linglong/package/uab_file.h" #include "linglong/package/uab_packager.h" -#include "linglong/utils/command/cmd.h" +#include "linglong/utils/cmd.h" #include #include @@ -47,8 +47,8 @@ class UabFileTest : public ::testing::Test std::ofstream tmpFile(helloFilePath); tmpFile << "Hello, World!"; tmpFile.close(); - auto ret = utils::command::Cmd("mkfs.erofs") - .exec({ bundleFile.c_str(), (testDir / "bundle").c_str() }); + auto ret = + utils::Cmd("mkfs.erofs").exec({ bundleFile.c_str(), (testDir / "bundle").c_str() }); ASSERT_TRUE(ret.has_value()) << "Failed to create erofs file" << ret.error().message(); auto ret2 = (*uab)->addSection("linglong.bundle", bundleFile); ASSERT_TRUE(ret2.has_value()) << ret2.error().message(); @@ -95,7 +95,7 @@ class UabFileTest : public ::testing::Test std::ofstream tmpFile(helloFilePath); tmpFile << "Hello, World!"; tmpFile.close(); - auto ret = utils::command::Cmd("tar").exec( + auto ret = utils::Cmd("tar").exec( { "-cvf", (testDir / "sign.tar").c_str(), "-C", signDir.c_str(), "." }); ASSERT_TRUE(ret.has_value()) << "Failed to create tar file"; auto ret2 = (*uab)->addSection("linglong.bundle.sign", testDir / "sign.tar"); @@ -141,7 +141,7 @@ TEST_F(UabFileTest, UnpackFuseOffset) TEST_F(UabFileTest, UnpackFuse) { { - auto ret = utils::command::Cmd("erofsfuse").exists(); + auto ret = utils::Cmd("erofsfuse").exists(); if (!ret) { #ifdef GTEST_SKIP GTEST_SKIP() << "Skipping this test."; diff --git a/libs/linglong/tests/ll-tests/src/linglong/utils/cmd_test.cpp b/libs/linglong/tests/ll-tests/src/linglong/utils/cmd_test.cpp new file mode 100644 index 000000000..cb4622aa3 --- /dev/null +++ b/libs/linglong/tests/ll-tests/src/linglong/utils/cmd_test.cpp @@ -0,0 +1,79 @@ +// SPDX-FileCopyrightText: 2024 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#include + +#include "linglong/utils/cmd.h" + +namespace { + +TEST(command, Exec) +{ + auto ret = linglong::utils::Cmd("echo").exec({ "-n", "hello" }); + EXPECT_TRUE(ret); + EXPECT_EQ(ret->size(), 5); + EXPECT_EQ(*ret, "hello"); + auto userId = linglong::utils::Cmd("id").exec({ "-u" }); + EXPECT_TRUE(userId.has_value()); + + userId = userId->substr(0, userId->find('\n')); + EXPECT_EQ(*userId, std::to_string(getuid())); + + // 测试command不存在时 + auto ret3 = linglong::utils::Cmd("nonexistent").exec(); + EXPECT_FALSE(ret3.has_value()); + + // 测试exec出错时 + auto ret4 = linglong::utils::Cmd("ls").exec({ "/linglong/nonexistent" }); + EXPECT_FALSE(ret4.has_value()); +} + +TEST(command, setEnv) +{ + linglong::utils::Cmd cmd("bash"); + // test set + cmd.setEnv("LINGLONG_TEST_SETENV", "OK"); + auto existsRef = cmd.exists(); + EXPECT_TRUE(existsRef); + // test unset + cmd.setEnv("PATH", ""); + auto ret = cmd.exec({ "-c", "export" }); + EXPECT_TRUE(ret.has_value()) << "failed to execute export command: " << ret.error().message(); + EXPECT_TRUE(ret->find("declare -x LINGLONG_TEST_SETENV=") != std::string::npos) << *ret; + EXPECT_FALSE(ret->find("declare -x PATH=\"") != std::string::npos) << *ret; +} + +TEST(command, toStdin) +{ + // Test writing to stdin using cat command + auto ret = linglong::utils::Cmd("cat").toStdin("Hello, World!").exec(); + + EXPECT_TRUE(ret.has_value()) << "failed to execute cat command: " << ret.error().message(); + EXPECT_EQ(*ret, "Hello, World!"); + + // Test with multiline input + auto ret2 = linglong::utils::Cmd("wc").toStdin("line1\nline2\nline3\n").exec({ "-l" }); + + EXPECT_TRUE(ret2.has_value()) << "failed to execute wc command: " << ret2.error().message(); + // wc -l should output " 3" for 3 lines + EXPECT_TRUE(ret2->find("3") != std::string::npos) << *ret2; + + // Test empty stdin + auto ret3 = linglong::utils::Cmd("cat").toStdin("").exec(); + + EXPECT_TRUE(ret3.has_value()) << "failed to execute cat with empty stdin: " + << ret3.error().message(); + EXPECT_TRUE(ret3->empty()) << "cat with empty stdin should return empty output"; + + // Test with data exceeding 1M + std::string large_data(1048576 + 1024, 'A'); + auto ret4 = linglong::utils::Cmd("wc").toStdin(large_data).exec({ "-c" }); + + EXPECT_TRUE(ret4.has_value()) << "failed to execute wc with large stdin: " + << ret4.error().message(); + EXPECT_TRUE(ret4->find("1049600") != std::string::npos) + << "wc -c should report 1049600 bytes, got: " << *ret4; +} + +} // namespace diff --git a/libs/linglong/tests/ll-tests/src/linglong/utils/error/error_test.cpp b/libs/linglong/tests/ll-tests/src/linglong/utils/error/error_test.cpp index 3e4fe0af4..c3688e6aa 100644 --- a/libs/linglong/tests/ll-tests/src/linglong/utils/error/error_test.cpp +++ b/libs/linglong/tests/ll-tests/src/linglong/utils/error/error_test.cpp @@ -7,7 +7,7 @@ #include #include "linglong/common/strings.h" -#include "linglong/utils/command/env.h" +#include "linglong/utils/env.h" #include "linglong/utils/error/error.h" #include "linglong/utils/log/log.h" @@ -16,7 +16,7 @@ using namespace linglong::utils::error; using namespace linglong::common; -using linglong::utils::command::EnvironmentVariableGuard; +using linglong::utils::EnvironmentVariableGuard; TEST(Error, New) { diff --git a/libs/linglong/tests/ll-tests/src/linglong/utils/file_test.cpp b/libs/linglong/tests/ll-tests/src/linglong/utils/file_test.cpp index 1b22554ea..68df92ba1 100644 --- a/libs/linglong/tests/ll-tests/src/linglong/utils/file_test.cpp +++ b/libs/linglong/tests/ll-tests/src/linglong/utils/file_test.cpp @@ -5,7 +5,6 @@ #include #include "linglong/utils/file.h" -#include "linglong/utils/global/initialize.h" #include #include @@ -323,3 +322,226 @@ TEST_F(FileTest, RelinkFile) EXPECT_EQ(fs::read_symlink(link), target); } } + +TEST_F(FileTest, WriteFile) +{ + // Test writing to a new file + fs::path test_file = dest_dir / "test_write.txt"; + std::string content = "Hello, World!"; + + auto result = linglong::utils::writeFile(test_file.string(), content); + ASSERT_TRUE(result.has_value()) << result.error().message(); + EXPECT_TRUE(fs::exists(test_file)); + + // Verify content was written correctly + std::ifstream ifs(test_file); + std::string read_content((std::istreambuf_iterator(ifs)), + (std::istreambuf_iterator())); + EXPECT_EQ(read_content, content); + + // Test overwriting an existing file + std::string new_content = "New content"; + result = linglong::utils::writeFile(test_file.string(), new_content); + ASSERT_TRUE(result.has_value()) << result.error().message(); + + std::ifstream ifs2(test_file); + std::string read_content2((std::istreambuf_iterator(ifs2)), + (std::istreambuf_iterator())); + EXPECT_EQ(read_content2, new_content); + + // Test writing empty content + fs::path empty_file = dest_dir / "empty.txt"; + result = linglong::utils::writeFile(empty_file.string(), "\0"); + ASSERT_TRUE(result.has_value()) << result.error().message(); + EXPECT_TRUE(fs::exists(empty_file)); + + std::ifstream ifs3(empty_file); + std::string read_content3((std::istreambuf_iterator(ifs3)), + (std::istreambuf_iterator())); + EXPECT_EQ(read_content3, ""); + + // Test writing to a file in a subdirectory that doesn't exist + fs::path subdir_file = dest_dir / "subdir" / "subfile.txt"; + result = linglong::utils::writeFile(subdir_file.string(), "subdir content"); + EXPECT_FALSE(result.has_value()); // Should fail because parent directory doesn't exist +} + +TEST_F(FileTest, ReadFile) +{ + // Test reading an existing file + fs::path test_file = dest_dir / "test_read.txt"; + std::string content = "Test content for reading"; + std::ofstream(test_file) << content; + + auto result = linglong::utils::readFile(test_file.string()); + ASSERT_TRUE(result.has_value()) << result.error().message(); + EXPECT_EQ(*result, content); + + // Test reading an empty file + fs::path empty_file = dest_dir / "empty_read.txt"; + auto _ = std::ofstream(empty_file); // Create empty file + + auto result_empty = linglong::utils::readFile(empty_file.string()); + ASSERT_TRUE(result_empty.has_value()) << result_empty.error().message(); + EXPECT_EQ(*result_empty, ""); + + // Test reading a file with special characters + fs::path special_file = dest_dir / "special.txt"; + std::string special_content = "Special chars: \n\t\r\"'\\"; + std::ofstream(special_file) << special_content; + + auto result_special = linglong::utils::readFile(special_file.string()); + ASSERT_TRUE(result_special.has_value()) << result_special.error().message(); + EXPECT_EQ(*result_special, special_content); + + // Test reading a non-existent file + fs::path non_existent = dest_dir / "non_existent.txt"; + auto result_non_existent = linglong::utils::readFile(non_existent.string()); + EXPECT_FALSE(result_non_existent.has_value()); + + // Test reading a file in a subdirectory + fs::path subdir = dest_dir / "subdir"; + fs::create_directories(subdir); + fs::path subdir_file = subdir / "subfile.txt"; + std::string subdir_content = "Content in subdirectory"; + std::ofstream(subdir_file) << subdir_content; + + auto result_subdir = linglong::utils::readFile(subdir_file.string()); + ASSERT_TRUE(result_subdir.has_value()) << result_subdir.error().message(); + EXPECT_EQ(*result_subdir, subdir_content); +} + +TEST_F(FileTest, WriteFileAndReadFile) +{ + // Test write and read operations together + fs::path test_file = dest_dir / "test_roundtrip.txt"; + std::string original_content = + "This is a test for write and read operations.\nMultiple lines.\nSpecial chars: 中文"; + + // Write the file + auto write_result = linglong::utils::writeFile(test_file.string(), original_content); + ASSERT_TRUE(write_result.has_value()) << write_result.error().message(); + + // Read the file back + auto read_result = linglong::utils::readFile(test_file.string()); + ASSERT_TRUE(read_result.has_value()) << read_result.error().message(); + + // Verify round-trip integrity + EXPECT_EQ(*read_result, original_content); + + // Test with binary content + fs::path binary_file = dest_dir / "test_binary.txt"; + std::string binary_content = "\x01\x02\x03\xFF\xFE\xFD"; + + auto write_binary = linglong::utils::writeFile(binary_file.string(), binary_content); + ASSERT_TRUE(write_binary.has_value()) << write_binary.error().message(); + + auto read_binary = linglong::utils::readFile(binary_file.string()); + ASSERT_TRUE(read_binary.has_value()) << read_binary.error().message(); + + EXPECT_EQ(*read_binary, binary_content); +} + +TEST_F(FileTest, ConcatFile) +{ + // Test concatenating to a new target file + fs::path source_file = dest_dir / "source.txt"; + fs::path target_file = dest_dir / "target.txt"; + std::string source_content = "Source content"; + std::string target_content = "Target content"; + + // Create source file + std::ofstream(source_file) << source_content; + + // Create target file with initial content + std::ofstream(target_file) << target_content; + + // Concatenate source to target + auto result = linglong::utils::concatFile(source_file, target_file); + ASSERT_TRUE(result.has_value()) << result.error().message(); + + // Verify concatenated content + auto read_result = linglong::utils::readFile(target_file.string()); + ASSERT_TRUE(read_result.has_value()) << read_result.error().message(); + EXPECT_EQ(*read_result, target_content + source_content); + + // Test concatenating to an empty target file + fs::path empty_target = dest_dir / "empty_target.txt"; + fs::path another_source = dest_dir / "another_source.txt"; + std::string another_content = "Another source content"; + + std::ofstream(another_source) << another_content; + + auto result_empty = linglong::utils::concatFile(another_source, empty_target); + ASSERT_TRUE(result_empty.has_value()) << result_empty.error().message(); + + auto read_empty = linglong::utils::readFile(empty_target.string()); + ASSERT_TRUE(read_empty.has_value()) << read_empty.error().message(); + EXPECT_EQ(*read_empty, another_content); + + // Test multiple concatenations + fs::path multi_target = dest_dir / "multi_target.txt"; + fs::path source1 = dest_dir / "source1.txt"; + fs::path source2 = dest_dir / "source2.txt"; + + std::ofstream(multi_target) << "Initial"; + std::ofstream(source1) << "First"; + std::ofstream(source2) << "Second"; + + auto result1 = linglong::utils::concatFile(source1, multi_target); + ASSERT_TRUE(result1.has_value()) << result1.error().message(); + + auto result2 = linglong::utils::concatFile(source2, multi_target); + ASSERT_TRUE(result2.has_value()) << result2.error().message(); + + auto read_multi = linglong::utils::readFile(multi_target.string()); + ASSERT_TRUE(read_multi.has_value()) << read_multi.error().message(); + EXPECT_EQ(*read_multi, "InitialFirstSecond"); +} + +TEST_F(FileTest, ConcatFile_ErrorCases) +{ + // Test concatenating from non-existent source file + fs::path non_existent_source = dest_dir / "non_existent.txt"; + fs::path target = dest_dir / "target.txt"; + std::ofstream(target) << "target"; + + auto result = linglong::utils::concatFile(non_existent_source, target); + EXPECT_FALSE(result.has_value()) << "Should fail when source file doesn't exist"; + + // Test concatenating when source and target are the same file + fs::path same_file = dest_dir / "same.txt"; + std::ofstream(same_file) << "same content"; + + auto result_same = linglong::utils::concatFile(same_file, same_file); + EXPECT_FALSE(result_same.has_value()) << "Should fail when source and target are the same file"; + + // Test concatenating binary content + fs::path binary_source = dest_dir / "binary_source.txt"; + fs::path binary_target = dest_dir / "binary_target.txt"; + + std::string binary_content = "\x01\x02\xFF\xFE\xFD"; + std::ofstream(binary_source, std::ios::binary) << binary_content; + std::ofstream(binary_target, std::ios::binary) << std::string("\x10\x20", 2); + + auto result_binary = linglong::utils::concatFile(binary_source, binary_target); + ASSERT_TRUE(result_binary.has_value()) << result_binary.error().message(); + + auto read_binary = linglong::utils::readFile(binary_target.string()); + ASSERT_TRUE(read_binary.has_value()) << read_binary.error().message(); + EXPECT_EQ(*read_binary, std::string("\x10\x20", 2) + binary_content); + + // Test concatenating empty source file + fs::path empty_source = dest_dir / "empty_source.txt"; + fs::path empty_target = dest_dir / "empty_target.txt"; + + auto _ = std::ofstream(empty_source); // Create empty file + std::ofstream(empty_target) << "target content"; + + auto result_empty_source = linglong::utils::concatFile(empty_source, empty_target); + ASSERT_TRUE(result_empty_source.has_value()) << result_empty_source.error().message(); + + auto read_empty_source = linglong::utils::readFile(empty_target.string()); + ASSERT_TRUE(read_empty_source.has_value()) << read_empty_source.error().message(); + EXPECT_EQ(*read_empty_source, "target content"); +} diff --git a/libs/utils/CMakeLists.txt b/libs/utils/CMakeLists.txt index 6e17622e2..9823c5a4f 100644 --- a/libs/utils/CMakeLists.txt +++ b/libs/utils/CMakeLists.txt @@ -10,12 +10,10 @@ pfl_add_library( SOURCES # find -regex '\.\/*.+\.[ch]\(pp\)?\(.in\)?' -type f -printf '%P\n'| sort src/linglong/utils/bash_command_helper.h - src/linglong/utils/command/cmd.cpp - src/linglong/utils/command/cmd.h - src/linglong/utils/command/env.cpp - src/linglong/utils/command/env.h - src/linglong/utils/command/ocppi-helper.cpp - src/linglong/utils/command/ocppi-helper.h + src/linglong/utils/cmd.cpp + src/linglong/utils/cmd.h + src/linglong/utils/env.cpp + src/linglong/utils/env.h src/linglong/utils/dbus/log.cpp src/linglong/utils/dbus/log.h src/linglong/utils/dbus/properties_forwarder.cpp diff --git a/libs/utils/src/linglong/utils/cmd.cpp b/libs/utils/src/linglong/utils/cmd.cpp new file mode 100644 index 000000000..fa0efd49f --- /dev/null +++ b/libs/utils/src/linglong/utils/cmd.cpp @@ -0,0 +1,320 @@ +/* + * SPDX-FileCopyrightText: 2023 UnionTech Software Technology Co., Ltd. + * + * SPDX-License-Identifier: LGPL-3.0-or-later + */ + +#include "cmd.h" + +#include "configure.h" +#include "linglong/common/strings.h" +#include "linglong/utils/finally/finally.h" +#include "linglong/utils/log/log.h" + +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace linglong::utils { + +Cmd::Cmd(std::string command) noexcept + : m_command(std::move(command)) +{ +} + +Cmd::~Cmd() = default; + +bool Cmd::exists() noexcept +{ + return !getCommandPath().empty(); +} + +std::filesystem::path Cmd::getCommandPath() +{ + // Check if command is an absolute path + std::error_code ec; + std::filesystem::path path{ m_command }; + if (path.is_absolute() && std::filesystem::exists(path, ec)) { + return path; + } + + // Search in PATH environment variable + std::vector pathDirs = { BINDIR }; + const char *pathEnv = std::getenv("PATH"); + if (pathEnv && pathEnv[0] != '\0') { + auto dirs = common::strings::split(pathEnv, ':', common::strings::splitOption::SkipEmpty); + pathDirs.insert(pathDirs.end(), dirs.begin(), dirs.end()); + } else { + pathDirs.insert(pathDirs.end(), { "/usr/local/bin", "/usr/bin", "/bin" }); + } + + for (const auto &pathDir : pathDirs) { + std::filesystem::path fullPath = std::filesystem::path{ pathDir } / m_command; + if (std::filesystem::exists(fullPath, ec) + && std::filesystem::is_regular_file(fullPath, ec)) { + return fullPath; + } + } + + return {}; +} + +utils::error::Result Cmd::exec(const std::vector &args) noexcept +{ + LINGLONG_TRACE(fmt::format("exec cmd: {} args: {}", m_command, fmt::join(args, " "))); + + auto commandPath = getCommandPath(); + if (commandPath.empty()) { + return LINGLONG_ERR(fmt::format("command not found: {}", m_command)); + } + + int stdoutPipe[2]; + if (pipe(stdoutPipe) == -1) { + return LINGLONG_ERR(fmt::format("pipe error: {}", errorString(errno))); + } + auto stdoutPipeCloser = utils::finally::finally([stdoutPipe]() { + close(stdoutPipe[0]); + close(stdoutPipe[1]); + }); + + int stdinPipe[2]; + if (pipe(stdinPipe) == -1) { + return LINGLONG_ERR(fmt::format("pipe error: {}", errorString(errno))); + } + auto stdinPipeCloser = utils::finally::finally([stdinPipe]() { + close(stdinPipe[0]); + close(stdinPipe[1]); + }); + + // Pre-allocate environment variables before fork to avoid memory allocation issues + std::vector envStrings; + std::vector envp; + + // Copy current environment + for (char **env = environ; *env != nullptr; ++env) { + envStrings.emplace_back(*env); + } + + // Add or override environment variables + for (const auto &[name, value] : m_envs) { + if (value.empty()) { + // Remove the environment variable if value is empty + envStrings.erase(std::remove_if(envStrings.begin(), + envStrings.end(), + [&name](const std::string &env) { + return env.size() > name.size() + && env.substr(0, name.size()) == name + && env[name.size()] == '='; + }), + envStrings.end()); + } else { + // Add or update environment variable + std::string envVar = name + "=" + value; + bool found = false; + for (auto &env : envStrings) { + if (env.size() > name.size() && env.substr(0, name.size()) == name + && env[name.size()] == '=') { + env = envVar; + found = true; + break; + } + } + if (!found) { + envStrings.push_back(envVar); + } + } + } + + // Build envp array for execvpe + envp.reserve(envStrings.size() + 1); + for (auto &env : envStrings) { + envp.push_back(const_cast(env.c_str())); + } + envp.push_back(nullptr); + + LogD("execute {} with args [{}]", commandPath, fmt::join(args, ", ")); + + pid_t pid = fork(); + if (pid == -1) { + return LINGLONG_ERR(fmt::format("fork error: {}", errorString(errno))); + } + + // child process + if (pid == 0) { + // Redirect stdout and stdin + close(stdoutPipe[0]); + close(stdinPipe[1]); + if (dup2(stdoutPipe[1], STDOUT_FILENO) == -1 || dup2(stdinPipe[0], STDIN_FILENO) == -1) { + exit(1); + } + close(stdoutPipe[1]); + close(stdinPipe[0]); + + std::vector argv; + auto filename = commandPath.filename(); + argv.push_back(const_cast(filename.c_str())); + for (const auto &arg : args) { + argv.push_back(const_cast(arg.c_str())); + } + argv.push_back(nullptr); + + execvpe(commandPath.c_str(), argv.data(), envp.data()); + + std::cout << "execvpe failed: " << errorString(errno) << std::endl; + + _exit(1); + } + + // parent process + close(stdoutPipe[1]); + close(stdinPipe[0]); + + auto setNonBlock = [](int fd) { + int flags = fcntl(fd, F_GETFL, 0); + if (flags != -1) { + return fcntl(fd, F_SETFL, flags | O_NONBLOCK) != -1; + } + return false; + }; + if (!setNonBlock(stdoutPipe[0]) || !setNonBlock(stdinPipe[1])) { + return LINGLONG_ERR(fmt::format("set non block error: {}", errorString(errno))); + } + + int epfd = epoll_create1(O_CLOEXEC); + if (epfd == -1) { + return LINGLONG_ERR(fmt::format("epoll_create error: {}", errorString(errno))); + } + auto epfdCloser = utils::finally::finally([epfd]() { + close(epfd); + }); + + struct epoll_event ev; + int activeFds = 0; + + ev.events = EPOLLIN; + ev.data.fd = stdoutPipe[0]; + if (epoll_ctl(epfd, EPOLL_CTL_ADD, stdoutPipe[0], &ev) == -1) { + return LINGLONG_ERR(fmt::format("epoll_ctl stdout error: {}", errorString(errno))); + } + activeFds++; + + size_t writtenBytes = 0; + if (!m_stdinContent.empty()) { + ev.events = EPOLLOUT; + ev.data.fd = stdinPipe[1]; + if (epoll_ctl(epfd, EPOLL_CTL_ADD, stdinPipe[1], &ev) == -1) { + return LINGLONG_ERR(fmt::format("epoll_ctl stdin error: {}", errorString(errno))); + } + activeFds++; + } else { + // If no input, close write end immediately to send EOF to child + close(stdinPipe[1]); + } + + std::string output; + std::vector buffer(4096); + const int MAX_EVENTS = 2; + struct epoll_event events[MAX_EVENTS]; + + while (activeFds > 0) { + int nfds = epoll_wait(epfd, events, MAX_EVENTS, -1); + if (nfds == -1) { + if (errno == EINTR) + continue; + return LINGLONG_ERR(fmt::format("epoll_wait error: {}", errorString(errno))); + } + + for (int i = 0; i < nfds; ++i) { + int fd = events[i].data.fd; + if (fd == stdoutPipe[0]) { + while (true) { + ssize_t n = read(fd, buffer.data(), buffer.size()); + if (n > 0) { + output.append(buffer.data(), static_cast(n)); + continue; + } + + if (n == -1 && errno == EINTR) { + continue; + } + + if ((n == -1 && errno != EAGAIN && errno != EWOULDBLOCK) || n == 0) { + // error or EOF, stop monitoring this FD + epoll_ctl(epfd, EPOLL_CTL_DEL, fd, nullptr); + activeFds--; + } + break; + } + } else if (fd == stdinPipe[1]) { + if (writtenBytes < m_stdinContent.size()) { + ssize_t n = write(fd, + m_stdinContent.data() + writtenBytes, + m_stdinContent.size() - writtenBytes); + if (n > 0) { + writtenBytes += n; + } else if (n == -1) { + if (errno == EINTR) { + continue; + } + + if (errno != EAGAIN && errno != EWOULDBLOCK) { + epoll_ctl(epfd, EPOLL_CTL_DEL, fd, nullptr); + activeFds--; + } + } + } + + if (writtenBytes >= m_stdinContent.size()) { + epoll_ctl(epfd, EPOLL_CTL_DEL, fd, nullptr); + close(fd); + activeFds--; + } + } + } + } + + int status; + while (waitpid(pid, &status, 0) == -1) { + if (errno == EINTR) + continue; + return LINGLONG_ERR(fmt::format("waitpid error: {}", errorString(errno))); + } + + if (WIFEXITED(status)) { + int exitCode = WEXITSTATUS(status); + if (exitCode == 0) { + return output; + } else { + return LINGLONG_ERR( + fmt::format("command execute failed with exit code {}: {}", exitCode, output)); + } + } else if (WIFSIGNALED(status)) { + return LINGLONG_ERR(fmt::format("command killed by signal: {}", WTERMSIG(status))); + } + + return LINGLONG_ERR("command exited abnormally"); +} + +Cmd &Cmd::setEnv(const std::string &name, const std::string &value) noexcept +{ + // Store the environment variable (empty value means unset) + m_envs[name] = value; + return *this; +} + +Cmd &Cmd::toStdin(std::string content) noexcept +{ + m_stdinContent = std::move(content); + return *this; +} + +} // namespace linglong::utils diff --git a/libs/utils/src/linglong/utils/cmd.h b/libs/utils/src/linglong/utils/cmd.h new file mode 100644 index 000000000..1da3bc079 --- /dev/null +++ b/libs/utils/src/linglong/utils/cmd.h @@ -0,0 +1,40 @@ +/* + * SPDX-FileCopyrightText: 2023 UnionTech Software Technology Co., Ltd. + * + * SPDX-License-Identifier: LGPL-3.0-or-later + */ + +#pragma once + +#include "linglong/utils/error/error.h" + +#include +#include +#include +#include + +namespace linglong::utils { + +// Executes a command from the standard system PATH +// blocking until the child process exits, and the stdout of the child process is returned +class Cmd +{ +public: + explicit Cmd(std::string command) noexcept; + ~Cmd(); + + virtual bool exists() noexcept; + virtual utils::error::Result + exec(const std::vector &args = {}) noexcept; + virtual Cmd &setEnv(const std::string &name, const std::string &value) noexcept; + virtual Cmd &toStdin(std::string content) noexcept; + +private: + std::filesystem::path getCommandPath(); + + std::string m_command; + std::map m_envs; + std::string m_stdinContent; +}; + +} // namespace linglong::utils diff --git a/libs/utils/src/linglong/utils/command/cmd.cpp b/libs/utils/src/linglong/utils/command/cmd.cpp deleted file mode 100644 index 3627f1025..000000000 --- a/libs/utils/src/linglong/utils/command/cmd.cpp +++ /dev/null @@ -1,81 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2023 UnionTech Software Technology Co., Ltd. - * - * SPDX-License-Identifier: LGPL-3.0-or-later - */ - -#include "cmd.h" - -#include "linglong/utils/log/log.h" - -#include - -#include -#include - -#include - -namespace linglong::utils::command { - -Cmd::Cmd(QString command) noexcept - : m_command(std::move(command)) -{ -} - -Cmd::~Cmd() = default; - -linglong::utils::error::Result Cmd::exec() noexcept -{ - return exec({}); -} - -linglong::utils::error::Result Cmd::exec(const QStringList &args) noexcept -{ - LINGLONG_TRACE( - fmt::format("exec {} {}", m_command.toStdString(), args.join(" ").toStdString())); - LogD("exec {} {}", m_command, args); - QProcess process; - process.setProgram(m_command); - if (!m_envs.isEmpty()) { - // Merge system and custom environment variables - auto envs = QProcessEnvironment::systemEnvironment(); - for (auto it = m_envs.cbegin(); it != m_envs.cend(); ++it) { - const auto &key = it.key(); - const auto &value = it.value(); - if (value.isEmpty()) { - envs.remove(key); - } else { - envs.insert(key, value); - } - } - process.setProcessEnvironment(envs); - } - process.setArguments(args); - process.setProcessChannelMode(QProcess::MergedChannels); - process.start(); - - if (!process.waitForFinished(-1)) { - return LINGLONG_ERR(process.errorString(), process.error()); - } - - if (process.exitCode() != 0) { - return LINGLONG_ERR(process.readAllStandardOutput().toStdString(), process.exitCode()); - } - - return process.readAllStandardOutput(); -} - -bool Cmd::exists() noexcept -{ - auto ret = QStandardPaths::findExecutable(m_command); - return !ret.isEmpty(); -} - -// set environment, if value is empty, remove the environment -Cmd &Cmd::setEnv(const QString &name, const QString &value) noexcept -{ - m_envs[name] = value; - return *this; -} - -} // namespace linglong::utils::command diff --git a/libs/utils/src/linglong/utils/command/cmd.h b/libs/utils/src/linglong/utils/command/cmd.h deleted file mode 100644 index 2ce631205..000000000 --- a/libs/utils/src/linglong/utils/command/cmd.h +++ /dev/null @@ -1,33 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2023 UnionTech Software Technology Co., Ltd. - * - * SPDX-License-Identifier: LGPL-3.0-or-later - */ - -#pragma once - -#include "linglong/utils/error/error.h" - -#include -#include -#include - -namespace linglong::utils::command { - -class Cmd -{ -public: - explicit Cmd(QString command) noexcept; - virtual ~Cmd(); - - virtual bool exists() noexcept; - virtual linglong::utils::error::Result exec() noexcept; - virtual linglong::utils::error::Result exec(const QStringList &args) noexcept; - virtual Cmd &setEnv(const QString &name, const QString &value) noexcept; - -private: - QString m_command; - QMap m_envs; -}; - -} // namespace linglong::utils::command diff --git a/libs/utils/src/linglong/utils/command/env.cpp b/libs/utils/src/linglong/utils/command/env.cpp deleted file mode 100644 index b34348cf3..000000000 --- a/libs/utils/src/linglong/utils/command/env.cpp +++ /dev/null @@ -1,35 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2023 UnionTech Software Technology Co., Ltd. - * - * SPDX-License-Identifier: LGPL-3.0-or-later - */ - -#include "env.h" - -#include - -namespace linglong::utils::command { - -QStringList getUserEnv(const QStringList &filters) -{ - auto sysEnv = QProcessEnvironment::systemEnvironment(); - auto ret = QProcessEnvironment(); - for (const auto &filter : filters) { - auto v = sysEnv.value(filter, ""); - if (!v.isEmpty()) { - ret.insert(filter, v); - } - } - return ret.toStringList(); -} - -std::optional getEnv(const std::string &variableName) -{ - auto value = std::getenv(variableName.c_str()); - if (value == nullptr) { - return std::nullopt; - } - return std::string(value); -} - -} // namespace linglong::utils::command diff --git a/libs/utils/src/linglong/utils/command/env.h b/libs/utils/src/linglong/utils/command/env.h deleted file mode 100644 index 052ad121e..000000000 --- a/libs/utils/src/linglong/utils/command/env.h +++ /dev/null @@ -1,80 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2023 UnionTech Software Technology Co., Ltd. - * - * SPDX-License-Identifier: LGPL-3.0-or-later - */ - -#pragma once - -#include "linglong/utils/error/error.h" - -#include - -#include - -namespace linglong::utils::command { - -QStringList getUserEnv(const QStringList &filters); - -std::optional getEnv(const std::string &variableName); - -class EnvironmentVariableGuard -{ -public: - /** - * @brief Constructs the guard, saving the original value and setting the new one. - * @param variableName The name of the environment variable to manage. - * @param newValue The new value to set for the environment variable. - */ - EnvironmentVariableGuard(std::string variableName, const std::string &newValue) - : m_variableName(std::move(variableName)) - , m_originalValue(getOriginalValue()) - { - if (::setenv(m_variableName.c_str(), newValue.c_str(), 1) != 0) { - qCritical() << "Failed to set environment variable" - << QString::fromStdString(m_variableName) << "Error:" << errno - << strerror(errno); - } - } - - ~EnvironmentVariableGuard() { restoreOriginalValue(); } - - EnvironmentVariableGuard(const EnvironmentVariableGuard &) = delete; - EnvironmentVariableGuard &operator=(const EnvironmentVariableGuard &) = delete; - EnvironmentVariableGuard(EnvironmentVariableGuard &&) = delete; - EnvironmentVariableGuard &operator=(EnvironmentVariableGuard &&) = delete; - -private: - std::optional getOriginalValue() const - { - const char *original = std::getenv(m_variableName.c_str()); - if (original != nullptr) { - return std::string(original); - } - return std::nullopt; - } - - void restoreOriginalValue() - { - if (m_originalValue) { - // Restore the original value - if (::setenv(m_variableName.c_str(), m_originalValue->c_str(), 1) != 0) { - qCritical() << "Failed to restore environment variable" - << QString::fromStdString(m_variableName) << "Error:" << errno - << strerror(errno); - } - } else { - // Unset the variable if it didn't exist originally - if (::unsetenv(m_variableName.c_str()) != 0) { - qCritical() << "Failed to unset environment variable" - << QString::fromStdString(m_variableName) << "Error:" << errno - << strerror(errno); - } - } - } - - std::string m_variableName; - std::optional m_originalValue; -}; - -} // namespace linglong::utils::command diff --git a/libs/utils/src/linglong/utils/command/ocppi-helper.cpp b/libs/utils/src/linglong/utils/command/ocppi-helper.cpp deleted file mode 100644 index accda380a..000000000 --- a/libs/utils/src/linglong/utils/command/ocppi-helper.cpp +++ /dev/null @@ -1,54 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2023 UnionTech Software Technology Co., Ltd. - * - * SPDX-License-Identifier: LGPL-3.0-or-later - */ - -#include "linglong/utils/command/ocppi-helper.h" - -namespace linglong::utils::command { - -// 添加一个mount到config -void AddMount(ocppi::runtime::config::types::Config &config, - const QString &source, - const QString &destination, - const QStringList &options, - const QString &bind) -{ - ocppi::runtime::config::types::Mount m; - m.type = bind.toStdString(); - m.source = source.toStdString(); - m.destination = destination.toStdString(); - - std::vector opts; - for (const auto &opt : options) { - opts.push_back(opt.toStdString()); - } - m.options = opts; - - if (config.mounts.has_value()) { - config.mounts->push_back(m); - } else { - config.mounts = { m }; - } -} - -std::map AnnotationMap{ - { AnnotationKey::MountRootfsComments, "dev.linglong.mount.rootfs.comments" }, -}; - -// 添加一个annotation到config -void AddAnnotation(ocppi::runtime::config::types::Config &config, - const AnnotationKey &key, - const QString &val) -{ - - std::pair a = { AnnotationMap[key], val.toStdString() }; - if (config.annotations.has_value()) { - config.annotations->insert(a); - } else { - config.annotations = { a }; - } -} - -} // namespace linglong::utils::command diff --git a/libs/utils/src/linglong/utils/command/ocppi-helper.h b/libs/utils/src/linglong/utils/command/ocppi-helper.h deleted file mode 100644 index 327fb1bcc..000000000 --- a/libs/utils/src/linglong/utils/command/ocppi-helper.h +++ /dev/null @@ -1,26 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2023 UnionTech Software Technology Co., Ltd. - * - * SPDX-License-Identifier: LGPL-3.0-or-later - */ - -#pragma once - -#include "ocppi/runtime/config/types/Config.hpp" - -#include - -namespace linglong::utils::command { - -enum class AnnotationKey { MountRootfsComments, MountRuntimeComments }; - -void AddMount(ocppi::runtime::config::types::Config &config, - const QString &src, - const QString &dist, - const QStringList &options, - const QString &bind = "bind"); -void AddAnnotation(ocppi::runtime::config::types::Config &config, - const AnnotationKey &key, - const QString &val); - -} // namespace linglong::utils::command diff --git a/libs/utils/src/linglong/utils/env.cpp b/libs/utils/src/linglong/utils/env.cpp new file mode 100644 index 000000000..6c470ef29 --- /dev/null +++ b/libs/utils/src/linglong/utils/env.cpp @@ -0,0 +1,54 @@ +/* + * SPDX-FileCopyrightText: 2023 UnionTech Software Technology Co., Ltd. + * + * SPDX-License-Identifier: LGPL-3.0-or-later + */ + +#include "env.h" + +#include "linglong/utils/log/log.h" + +namespace linglong::utils { + +EnvironmentVariableGuard::EnvironmentVariableGuard(std::string variableName, + const std::string &newValue) + : m_variableName(std::move(variableName)) + , m_originalValue(getOriginalValue()) +{ + if (::setenv(m_variableName.c_str(), newValue.c_str(), 1) != 0) { + LogE("Failed to set environment variable {}: {}", m_variableName, errorString(errno)); + } +} + +EnvironmentVariableGuard::~EnvironmentVariableGuard() +{ + restoreOriginalValue(); +} + +std::optional EnvironmentVariableGuard::getOriginalValue() const +{ + const char *original = std::getenv(m_variableName.c_str()); + if (original != nullptr) { + return std::string(original); + } + return std::nullopt; +} + +void EnvironmentVariableGuard::restoreOriginalValue() +{ + if (m_originalValue) { + // Restore the original value + if (::setenv(m_variableName.c_str(), m_originalValue->c_str(), 1) != 0) { + LogE("Failed to restore environment variable {}: {}", + m_variableName, + errorString(errno)); + } + } else { + // Unset the variable if it didn't exist originally + if (::unsetenv(m_variableName.c_str()) != 0) { + LogE("Failed to unset environment variable {}: {}", m_variableName, errorString(errno)); + } + } +} + +} // namespace linglong::utils diff --git a/libs/utils/src/linglong/utils/env.h b/libs/utils/src/linglong/utils/env.h new file mode 100644 index 000000000..6b2ad87bf --- /dev/null +++ b/libs/utils/src/linglong/utils/env.h @@ -0,0 +1,39 @@ +/* + * SPDX-FileCopyrightText: 2023 UnionTech Software Technology Co., Ltd. + * + * SPDX-License-Identifier: LGPL-3.0-or-later + */ + +#pragma once + +#include "linglong/utils/error/error.h" + +#include + +namespace linglong::utils { + +class EnvironmentVariableGuard +{ +public: + /** + * @brief Constructs the guard, saving the original value and setting the new one. + * @param variableName The name of the environment variable to manage. + * @param newValue The new value to set for the environment variable. + */ + EnvironmentVariableGuard(std::string variableName, const std::string &newValue); + ~EnvironmentVariableGuard(); + + EnvironmentVariableGuard(const EnvironmentVariableGuard &) = delete; + EnvironmentVariableGuard &operator=(const EnvironmentVariableGuard &) = delete; + EnvironmentVariableGuard(EnvironmentVariableGuard &&) = delete; + EnvironmentVariableGuard &operator=(EnvironmentVariableGuard &&) = delete; + +private: + std::optional getOriginalValue() const; + void restoreOriginalValue(); + + std::string m_variableName; + std::optional m_originalValue; +}; + +} // namespace linglong::utils diff --git a/libs/utils/src/linglong/utils/error/error.cpp b/libs/utils/src/linglong/utils/error/error.cpp index dbfff4039..2854fdbc9 100644 --- a/libs/utils/src/linglong/utils/error/error.cpp +++ b/libs/utils/src/linglong/utils/error/error.cpp @@ -5,3 +5,8 @@ */ #include "linglong/utils/error/error.h" + +std::string errorString(int err) +{ + return std::system_category().message(err); +} diff --git a/libs/utils/src/linglong/utils/error/error.h b/libs/utils/src/linglong/utils/error/error.h index 47404e207..6e5031a30 100644 --- a/libs/utils/src/linglong/utils/error/error.h +++ b/libs/utils/src/linglong/utils/error/error.h @@ -475,3 +475,5 @@ inline QDebug operator<<(QDebug debug, const std::string &str) debug.noquote().nospace() << QString::fromStdString(str); return debug; } + +std::string errorString(int err); diff --git a/libs/utils/src/linglong/utils/file.cpp b/libs/utils/src/linglong/utils/file.cpp index 648f6fa61..d3a8526d7 100644 --- a/libs/utils/src/linglong/utils/file.cpp +++ b/libs/utils/src/linglong/utils/file.cpp @@ -18,7 +18,7 @@ namespace linglong::utils { -linglong::utils::error::Result readFile(std::string filepath) +linglong::utils::error::Result readFile(const std::filesystem::path &filepath) { LINGLONG_TRACE(fmt::format("read file {}", filepath)); std::error_code ec; @@ -31,43 +31,63 @@ linglong::utils::error::Result readFile(std::string filepath) } std::ifstream in{ filepath }; if (!in.is_open()) { - auto msg = std::string("open file:") + std::strerror(errno); + auto msg = std::string("open file:") + errorString(errno); return LINGLONG_ERR(msg.c_str()); } std::stringstream buffer; buffer << in.rdbuf(); - if (in.fail()) { - auto msg = std::string("read file: ") + std::strerror(errno); + if (buffer.bad()) { + auto msg = std::string("read file: ") + errorString(errno); return LINGLONG_ERR(msg.c_str()); } return buffer.str(); } -linglong::utils::error::Result writeFile(const std::string &filepath, +linglong::utils::error::Result writeFile(const std::filesystem::path &filepath, const std::string &content) { LINGLONG_TRACE(fmt::format("write file {}", filepath)); - std::error_code ec; - auto exists = std::filesystem::exists(filepath, ec); - if (ec) { - return LINGLONG_ERR("check file", ec); - } - if (exists) { - std::filesystem::remove(filepath, ec); - if (ec) { - return LINGLONG_ERR("remove file", ec); - } - } std::ofstream out{ filepath }; if (!out.is_open()) { - auto msg = std::string("open file:") + std::strerror(errno); - return LINGLONG_ERR(msg.c_str()); + return LINGLONG_ERR( + fmt::format("failed to open file {}: {}", filepath, errorString(errno))); } out << content; - if (out.fail()) { - auto msg = std::string("write file: ") + std::strerror(errno); - return LINGLONG_ERR(msg.c_str()); + if (out.bad()) { + return LINGLONG_ERR(fmt::format("failed to write file {}", errorString(errno))); + } + return LINGLONG_OK; +} + +linglong::utils::error::Result concatFile(const std::filesystem::path &source, + const std::filesystem::path &target) +{ + LINGLONG_TRACE(fmt::format("concat file {} to {}", source, target)); + + std::error_code ec; + if (!std::filesystem::exists(source, ec)) { + return LINGLONG_ERR("source file not exists", ec); + } + + if (std::filesystem::exists(target, ec) && std::filesystem::equivalent(source, target, ec)) { + return LINGLONG_ERR("source and target are the same file", ec); + } + + std::ifstream ifs(source, std::ios::binary); + if (!ifs) { + return LINGLONG_ERR(fmt::format("failed to open source {}", source)); + } + + std::ofstream ofs(target, std::ios::binary | std::ios::app); + if (!ofs) { + return LINGLONG_ERR(fmt::format("failed to open target {}", target)); + } + + ofs << ifs.rdbuf(); + if (ofs.bad()) { + return LINGLONG_ERR( + fmt::format("failed to write target {} {}", target, errorString(errno))); } return LINGLONG_OK; } @@ -93,7 +113,7 @@ calculateDirectorySize(const std::filesystem::path &dir) noexcept if (::lstat64(path.c_str(), &st) == -1) { return LINGLONG_ERR( - QString{ "failed to get symlink size: %1" }.arg(::strerror(errno))); + fmt::format("failed to get symlink size: {}", errorString(errno))); } size += st.st_size; @@ -110,7 +130,7 @@ calculateDirectorySize(const std::filesystem::path &dir) noexcept if (::stat64(path.c_str(), &st) == -1) { return LINGLONG_ERR( - QString{ "failed to get directory size: %1" }.arg(::strerror(errno))); + fmt::format("failed to get directory size: {}", errorString(errno))); } size += st.st_size; continue; diff --git a/libs/utils/src/linglong/utils/file.h b/libs/utils/src/linglong/utils/file.h index e651e06a1..a333e3807 100644 --- a/libs/utils/src/linglong/utils/file.h +++ b/libs/utils/src/linglong/utils/file.h @@ -10,11 +10,14 @@ namespace linglong::utils { -linglong::utils::error::Result readFile(std::string filepath); +linglong::utils::error::Result readFile(const std::filesystem::path &filepath); -linglong::utils::error::Result writeFile(const std::string &filepath, +linglong::utils::error::Result writeFile(const std::filesystem::path &filepath, const std::string &content); +linglong::utils::error::Result concatFile(const std::filesystem::path &source, + const std::filesystem::path &target); + linglong::utils::error::Result calculateDirectorySize(const std::filesystem::path &dir) noexcept; diff --git a/libs/utils/src/linglong/utils/filelock.cpp b/libs/utils/src/linglong/utils/filelock.cpp index c1f4c1c4c..464938ae1 100644 --- a/libs/utils/src/linglong/utils/filelock.cpp +++ b/libs/utils/src/linglong/utils/filelock.cpp @@ -52,7 +52,7 @@ utils::error::Result FileLock::create(std::filesystem::path path, auto fd = ::open(abs_path.c_str(), flags, default_file_mode); if (fd < 0) { - return LINGLONG_ERR(fmt::format("open file failed: {}", ::strerror(errno))); + return LINGLONG_ERR(fmt::format("open file failed: {}", errorString(errno))); } locked_paths[abs_path] = true; @@ -71,7 +71,7 @@ FileLock::~FileLock() noexcept } if (fd > 0 && ::close(fd) < 0) { - LogW("close file failed: {}", ::strerror(errno)); + LogW("close file failed: {}", errorString(errno)); } fd = -1; @@ -168,7 +168,7 @@ utils::error::Result FileLock::lock(LockType type) noexcept continue; } - return LINGLONG_ERR(fmt::format("failed to lock file {}: {}", path, ::strerror(errno))); + return LINGLONG_ERR(fmt::format("failed to lock file {}: {}", path, errorString(errno))); } } @@ -211,7 +211,7 @@ utils::error::Result FileLock::tryLock(LockType type) noexcept return false; } - return LINGLONG_ERR(fmt::format("failed to lock file {}: {}", path, ::strerror(errno))); + return LINGLONG_ERR(fmt::format("failed to lock file {}: {}", path, errorString(errno))); } } @@ -242,7 +242,7 @@ utils::error::Result FileLock::unlock() noexcept if (errno == EINTR) { continue; } - return LINGLONG_ERR(fmt::format("failed to unlock file {}: {}", path, ::strerror(errno))); + return LINGLONG_ERR(fmt::format("failed to unlock file {}: {}", path, errorString(errno))); } } @@ -275,7 +275,7 @@ utils::error::Result FileLock::relock(LockType new_type) noexcept continue; } - return LINGLONG_ERR(fmt::format("failed to relock file {}: {}", path, ::strerror(errno))); + return LINGLONG_ERR(fmt::format("failed to relock file {}: {}", path, errorString(errno))); } } diff --git a/libs/utils/src/linglong/utils/hooks.cpp b/libs/utils/src/linglong/utils/hooks.cpp index ce4f82122..8b719021a 100644 --- a/libs/utils/src/linglong/utils/hooks.cpp +++ b/libs/utils/src/linglong/utils/hooks.cpp @@ -7,8 +7,9 @@ #include "hooks.h" #include "configure.h" -#include "linglong/utils/command/env.h" +#include "linglong/utils/env.h" #include "linglong/utils/error/error.h" +#include "linglong/utils/log/log.h" #include @@ -51,11 +52,11 @@ utils::error::Result executeHookCommands( { LINGLONG_TRACE("Executing command"); - std::vector> envVarGuards; + std::vector> envVarGuards; envVarGuards.reserve(envVars.size()); for (const auto &pair : envVars) { envVarGuards.emplace_back( - std::make_unique(pair.first, pair.second)); + std::make_unique(pair.first, pair.second)); } for (const auto &command_raw : commands) { @@ -66,7 +67,7 @@ utils::error::Result executeHookCommands( if (ret == -1) { return LINGLONG_ERR(fmt::format("Failed to execute command: '{}'. System error: {}.", fullCommand, - strerror(errno))); + errorString(errno))); } if (!WIFEXITED(ret)) { @@ -101,8 +102,7 @@ utils::error::Result InstallHookManager::parseInstallHooks() if (!std::filesystem::is_regular_file(entry.status(ec))) { if (ec) { - qWarning() << "Failed to get status for" << entry.path().c_str() << ":" - << QString::fromStdString(ec.message()); + LogE("Failed to get status for {}: {}", entry.path().c_str(), ec.message()); } continue; } @@ -157,7 +157,7 @@ utils::error::Result InstallHookManager::executeInstallHooks(int fd) noexc if (size == -1) { return LINGLONG_ERR( - QString{ "Failed to read file link for fd %1: %2" }.arg(fd).arg(strerror(errno))); + fmt::format("Failed to read file link for fd {}: {}", fd, errorString(errno))); } pathBuf[size] = '\0'; diff --git a/libs/utils/src/linglong/utils/namespace.cpp b/libs/utils/src/linglong/utils/namespace.cpp index b434f1952..475bddbd9 100644 --- a/libs/utils/src/linglong/utils/namespace.cpp +++ b/libs/utils/src/linglong/utils/namespace.cpp @@ -4,7 +4,7 @@ #include "namespace.h" -#include "linglong/utils/command/cmd.h" +#include "linglong/utils/cmd.h" #include "linglong/utils/finally/finally.h" #include "linglong/utils/log/log.h" @@ -135,7 +135,7 @@ utils::error::Result runInNamespace(int argc, char **argv) continue; } - LogW("failed to write data to sync socket: {}", ::strerror(errno)); + LogW("failed to write data to sync socket: {}", errorString(errno)); return -1; } @@ -147,7 +147,7 @@ utils::error::Result runInNamespace(int argc, char **argv) continue; } - LogW("failed to read data from sync socket: {}", ::strerror(errno)); + LogW("failed to read data from sync socket: {}", errorString(errno)); return -1; } @@ -158,13 +158,13 @@ utils::error::Result runInNamespace(int argc, char **argv) execvp(runInNamespaceArgs->argv[0], runInNamespaceArgs->argv); - LogE("execvp failed: {}", ::strerror(errno)); + LogE("execvp failed: {}", errorString(errno)); return -1; }; std::array pair{}; if (socketpair(AF_UNIX, SOCK_SEQPACKET | SOCK_CLOEXEC, 0, pair.data()) == -1) { - return LINGLONG_ERR(fmt::format("socketpair failed: {}", ::strerror(errno))); + return LINGLONG_ERR(fmt::format("socketpair failed: {}", errorString(errno))); } auto closeSocket = linglong::utils::finally::finally([&pair]() { @@ -186,7 +186,7 @@ utils::error::Result runInNamespace(int argc, char **argv) 0); if (addr == MAP_FAILED) { return LINGLONG_ERR( - fmt::format("failed to create stack for child process: {}", strerror(errno))); + fmt::format("failed to create stack for child process: {}", errorString(errno))); } auto recycle = linglong::utils::finally::finally([addr]() { @@ -202,7 +202,7 @@ utils::error::Result runInNamespace(int argc, char **argv) pair[1] = -1; if (pid < 0) { - return LINGLONG_ERR(fmt::format("clone failed: {}", strerror(errno))); + return LINGLONG_ERR(fmt::format("clone failed: {}", errorString(errno))); } LogD("waiting child {}", pid); @@ -213,7 +213,7 @@ utils::error::Result runInNamespace(int argc, char **argv) continue; } - return LINGLONG_ERR(fmt::format("read failed: {}", ::strerror(errno))); + return LINGLONG_ERR(fmt::format("read failed: {}", errorString(errno))); } auto mappingTool = [](bool isUid, pid_t pid) -> utils::error::Result { @@ -227,14 +227,14 @@ utils::error::Result runInNamespace(int argc, char **argv) return LINGLONG_ERR("failed to get subuid range", ranges.error()); } - utils::command::Cmd cmd(isUid ? "newuidmap" : "newgidmap"); + utils::Cmd cmd(isUid ? "newuidmap" : "newgidmap"); unsigned long containerID = 0; - QStringList args = { QString::number(pid), - QString::number(containerID), - QString::number(id), - "1" }; + auto args = std::vector{ std::to_string(pid), + std::to_string(containerID), + std::to_string(id), + "1" }; for (const auto &range : *ranges) { - args << QString::number(containerID + 1) << range.subuid.c_str() << range.count.c_str(); + args.insert(args.end(), { std::to_string(containerID + 1), range.subuid, range.count }); try { containerID += std::stoul(range.count); } catch (const std::invalid_argument &e) { @@ -276,7 +276,7 @@ utils::error::Result runInNamespace(int argc, char **argv) continue; } - return LINGLONG_ERR(fmt::format("waitpid failed {}", strerror(errno))); + return LINGLONG_ERR(fmt::format("waitpid failed {}", errorString(errno))); } if (WIFEXITED(status)) { diff --git a/libs/utils/src/linglong/utils/overlayfs.cpp b/libs/utils/src/linglong/utils/overlayfs.cpp index 3452c314a..20664ae86 100644 --- a/libs/utils/src/linglong/utils/overlayfs.cpp +++ b/libs/utils/src/linglong/utils/overlayfs.cpp @@ -4,12 +4,9 @@ #include "overlayfs.h" -#include "linglong/utils/command/cmd.h" +#include "linglong/utils/cmd.h" #include "linglong/utils/log/log.h" -#include -#include - #include #include @@ -28,11 +25,9 @@ OverlayFS::OverlayFS(std::filesystem::path lowerdir, OverlayFS::~OverlayFS() { - auto res = utils::command::Cmd("fusermount") - .exec({ "-z", "-u", QString::fromStdString(merged_.string()) }); + auto res = utils::Cmd("fusermount").exec({ "-z", "-u", merged_.string() }); if (!res) { - qWarning() << QString("failed to umount %1 ").arg(QString::fromStdString(merged_.string())) - << res.error(); + LogW("failed to umount {}: {}", merged_.string(), res.error()); } } @@ -55,19 +50,18 @@ bool OverlayFS::mount() } // TODO: check mountpoint whether already mounted - auto ret = utils::command::Cmd("fusermount") - .exec({ "-z", "-u", QString::fromStdString(merged_.string()) }); + auto ret = utils::Cmd("fusermount").exec({ "-z", "-u", merged_.string() }); if (!ret) { LogD("failed to set lazy umount {}", ret.error()); } - ret = utils::command::Cmd("fuse-overlayfs") - .exec({ "-o", - QString("lowerdir=%1,upperdir=%2,workdir=%3") - .arg((lowerdir_.c_str()), (upperdir_.c_str()), (workdir_.c_str())), - QString::fromStdString(merged_.string()) }); + ret = + utils::Cmd("fuse-overlayfs") + .exec({ "-o", + fmt::format("lowerdir={},upperdir={},workdir={}", lowerdir_, upperdir_, workdir_), + merged_.string() }); if (!ret) { - qWarning() << "failed to mount " << ret.error(); + LogW("failed to mount {}", ret.error()); } return !!ret; @@ -75,11 +69,9 @@ bool OverlayFS::mount() void OverlayFS::unmount(bool clean) { - auto res = utils::command::Cmd("fusermount") - .exec({ "-z", "-u", QString::fromStdString(merged_.string()) }); + auto res = utils::Cmd("fusermount").exec({ "-z", "-u", merged_.string() }); if (!res) { - qWarning() << QString("failed to umount %1 ").arg(QString::fromStdString(merged_.string())) - << res.error(); + LogW("failed to umount {}: {}", merged_.string(), res.error()); } if (clean) {