aboutsummaryrefslogtreecommitdiff
path: root/src/zenutil/service.cpp
diff options
context:
space:
mode:
authorDan Engelbrecht <[email protected]>2025-03-14 09:48:15 +0100
committerDan Engelbrecht <[email protected]>2025-03-14 09:48:15 +0100
commit18aeb8856dc168336955f0d2778cdc74c64a680f (patch)
tree83932b76bb01d1a9e9b473bf5bbc36cfca1c2b5b /src/zenutil/service.cpp
parentMerge remote-tracking branch 'origin/main' into de/zen-service-command (diff)
parentMerge pull request #288 from ue-foundation/lm/zen-service-command (diff)
downloadzen-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.cpp63
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);
}
}