diff options
| author | Dan Engelbrecht <[email protected]> | 2025-03-14 09:48:15 +0100 |
|---|---|---|
| committer | Dan Engelbrecht <[email protected]> | 2025-03-14 09:48:15 +0100 |
| commit | 18aeb8856dc168336955f0d2778cdc74c64a680f (patch) | |
| tree | 83932b76bb01d1a9e9b473bf5bbc36cfca1c2b5b /src/zenutil/service.cpp | |
| parent | Merge remote-tracking branch 'origin/main' into de/zen-service-command (diff) | |
| parent | Merge pull request #288 from ue-foundation/lm/zen-service-command (diff) | |
| download | zen-18aeb8856dc168336955f0d2778cdc74c64a680f.tar.xz zen-18aeb8856dc168336955f0d2778cdc74c64a680f.zip | |
Merge remote-tracking branch 'origin/de/zen-service-command' into de/zen-service-command
Diffstat (limited to 'src/zenutil/service.cpp')
| -rw-r--r-- | src/zenutil/service.cpp | 63 |
1 files changed, 48 insertions, 15 deletions
diff --git a/src/zenutil/service.cpp b/src/zenutil/service.cpp index 45874d1b5..ea7c2aae6 100644 --- a/src/zenutil/service.cpp +++ b/src/zenutil/service.cpp @@ -6,6 +6,7 @@ #include <zencore/process.h> #include <zencore/scopeguard.h> #include <zencore/zencore.h> +#include <string_view> #if ZEN_PLATFORM_WINDOWS # include <zencore/windows.h> @@ -23,6 +24,7 @@ # include <unistd.h> # include <sys/stat.h> +# include <regex> #endif namespace zen { @@ -258,7 +260,6 @@ namespace { #if ZEN_PLATFORM_MAC || ZEN_PLATFORM_LINUX - // TODO: Is this good enough to capture all output/errors/return codes? std::pair<int, std::string> ExecuteProgram(std::string_view Cmd) { std::string Data; @@ -289,10 +290,11 @@ namespace { int Status = pclose(Stream); if (Status < 0) { + ZEN_DEBUG("Command {} returned {}, errno {}", Command, Status, errno); return {Status, Data}; } uint64_t WaitMS = 100; - if (!WIFEXITED(Status)) + if (WIFEXITED(Status)) { Res = WEXITSTATUS(Status); } @@ -319,10 +321,9 @@ namespace { std::string BuildUnitFile(std::string_view ServiceName, const std::filesystem::path& ExecutablePath, std::string_view CommandLineOptions, - std::string_view AliasName) + std::string_view AliasName, + std::string_view UserName) { - // TODO: Revise to make sure the unit file is correct - // TODO: Do we need a separate config file or is that optional? return fmt::format( "[Unit]\n" "Description={}\n" @@ -333,17 +334,17 @@ namespace { "StartLimitIntervalSec=0\n" "\n" "[Service]\n" - "Type=simple\n" + "Type=notify\n" "Restart=always\n" "RestartSec=1\n" - "User=serviceuser\n" + "User={}\n" "ExecStart={} {}\n" - "Restart=always\n" "RuntimeDirectory={}\n" "[Install]\n" "Alias={}\n" "WantedBy=multi-user.target", ServiceName, + UserName, ExecutablePath, CommandLineOptions, ExecutablePath.parent_path(), @@ -881,12 +882,23 @@ StopService(std::string_view ServiceName) std::error_code InstallService(std::string_view ServiceName, const ServiceSpec& Spec) { - // TODO: Do we need to create a separate user for the service or is running as root OK? - const std::string UnitName = GetUnitName(ServiceName); const std::filesystem::path ServiceUnitPath = GetServiceUnitPath(UnitName); + std::string UserName = Spec.UserName; + + if (UserName == "") + { + std::pair<int, std::string> UserResult = ExecuteProgram("echo $SUDO_USER"); + if (UserResult.first != 0 || UserResult.second.empty()) + { + ZEN_ERROR("Unable to determine current user"); + return MakeErrorCode(UserResult.first); + } + + UserName = UserResult.second; + } - std::string UnitFile = BuildUnitFile(ServiceName, Spec.ExecutablePath, Spec.CommandLineOptions, UnitName); + std::string UnitFile = BuildUnitFile(ServiceName, Spec.ExecutablePath, Spec.CommandLineOptions, UnitName, UserName); ZEN_DEBUG("Writing systemd unit file to {}", ServiceUnitPath.string()); try { @@ -963,17 +975,38 @@ QueryInstalledService(std::string_view ServiceName, ServiceInfo& OutInfo) if (std::filesystem::is_regular_file(ServiceUnitPath)) { OutInfo.Status = ServiceStatus::Stopped; - // TODO: Read and parse unit file ? - std::pair<int, std::string> Res = ExecuteProgram(fmt::format("systemctl status {}", UnitName)); + std::pair<int, std::string> Res = ExecuteProgram(fmt::format("systemctl is-active --quiet {}", UnitName)); if (Res.first == 0) { - // TODO: What does status really return and what info can we use here to get the ServiceInfo complete? OutInfo.Status = ServiceStatus::Running; + + std::pair<int, std::string> ShowResult = ExecuteProgram(fmt::format("systemctl show -p ExecStart {}", UnitName)); + if (ShowResult.first == 0) + { + std::regex Regex(R"~(ExecStart=\{ path=(.*?) ; argv\[\]=(.*?) ;)~"); + std::smatch Match; + + if (std::regex_search(ShowResult.second, Match, Regex)) + { + std::string Executable = Match[1].str(); + std::string CommandLine = Match[2].str(); + OutInfo.Spec.ExecutablePath = Executable; + OutInfo.Spec.CommandLineOptions = CommandLine.substr(Executable.size(), CommandLine.size()); + } + else + { + ZEN_WARN("Failed to parse output of systemctl show: {}", ShowResult.second); + } + } + else + { + ZEN_WARN("Failed to read start info from systemctl: error code {}", ShowResult.first); + } } else { - ZEN_DEBUG("systemctl status failed with '{}"({}), Res.second, Res.first); + ZEN_DEBUG("systemctl status failed with '{}'({})", Res.second, Res.first); } } |