aboutsummaryrefslogtreecommitdiff
path: root/src/zenutil/service.cpp
diff options
context:
space:
mode:
authorLiam Mitchell <[email protected]>2025-02-27 02:16:10 +0000
committerLiam Mitchell <[email protected]>2025-02-27 02:16:10 +0000
commitc49b0a053c5e28de1afa83600ebffd383766e38a (patch)
tree3b9e7880e09ff816edd1fc9f996015ed9e6ee522 /src/zenutil/service.cpp
parentLinux compilation fixes (diff)
downloadzen-c49b0a053c5e28de1afa83600ebffd383766e38a.tar.xz
zen-c49b0a053c5e28de1afa83600ebffd383766e38a.zip
Implementation of service commands for Linux.
Diffstat (limited to 'src/zenutil/service.cpp')
-rw-r--r--src/zenutil/service.cpp59
1 files changed, 46 insertions, 13 deletions
diff --git a/src/zenutil/service.cpp b/src/zenutil/service.cpp
index 8d6b399ca..c156b001c 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"
@@ -336,14 +337,14 @@ namespace {
"Type=simple\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,13 +975,34 @@ 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
{