diff options
| author | Dan Engelbrecht <[email protected]> | 2025-01-15 09:30:12 +0100 |
|---|---|---|
| committer | Dan Engelbrecht <[email protected]> | 2025-01-15 09:30:12 +0100 |
| commit | 531c59032bbc46bc1f7284859fa8ff8c8b5ede61 (patch) | |
| tree | c06583ef275904824a9eeae42951fa6a48ebae20 /src | |
| parent | clang format (diff) | |
| download | zen-531c59032bbc46bc1f7284859fa8ff8c8b5ede61.tar.xz zen-531c59032bbc46bc1f7284859fa8ff8c8b5ede61.zip | |
systemd unit file, incomplete
Diffstat (limited to 'src')
| -rw-r--r-- | src/zen/cmds/service_cmd.cpp | 2 | ||||
| -rw-r--r-- | src/zencore/process.cpp | 4 | ||||
| -rw-r--r-- | src/zencore/thread.cpp | 2 | ||||
| -rw-r--r-- | src/zenserver/main.cpp | 13 | ||||
| -rw-r--r-- | src/zenutil/service.cpp | 250 | ||||
| -rw-r--r-- | src/zenutil/zenserverprocess.cpp | 3 |
6 files changed, 156 insertions, 118 deletions
diff --git a/src/zen/cmds/service_cmd.cpp b/src/zen/cmds/service_cmd.cpp index 16a2940a8..5e02db813 100644 --- a/src/zen/cmds/service_cmd.cpp +++ b/src/zen/cmds/service_cmd.cpp @@ -98,7 +98,7 @@ namespace { #else // ZEN_PLATFORM_WINDOWS - bool IsElevated() { return true; /*geteuid() == 0;*/ } + bool IsElevated() { return geteuid() == 0; } #endif // ZEN_PLATFORM_WINDOWS diff --git a/src/zencore/process.cpp b/src/zencore/process.cpp index 3d6a67ada..8049130da 100644 --- a/src/zencore/process.cpp +++ b/src/zencore/process.cpp @@ -853,6 +853,10 @@ IsProcessRunning(int pid, std::error_code& OutEc) { return false; } + else if (Error == EPERM) + { + return true; // Running under a user we don't have access to, assume it is live + } else { OutEc = MakeErrorCode(Error); diff --git a/src/zencore/thread.cpp b/src/zencore/thread.cpp index ab7e6857a..fef5c28a4 100644 --- a/src/zencore/thread.cpp +++ b/src/zencore/thread.cpp @@ -483,7 +483,7 @@ NamedMutex::Create(std::string_view MutexName) ExtendableStringBuilder<64> Name; Name << "/tmp/" << MutexName; - int Inner = open(Name.c_str(), O_RDWR | O_CREAT | O_CLOEXEC, 0666); + int Inner = open(Name.c_str(), O_RDWR | O_CREAT | O_CLOEXEC, geteuid() == 0 ? 0766 : 0666); if (Inner < 0) { return false; diff --git a/src/zenserver/main.cpp b/src/zenserver/main.cpp index d5419d342..6bf369ca4 100644 --- a/src/zenserver/main.cpp +++ b/src/zenserver/main.cpp @@ -91,6 +91,7 @@ ZenEntryPoint::ZenEntryPoint(ZenServerOptions& ServerOptions) : m_ServerOptions( int ZenEntryPoint::Run() { + ZEN_INFO("ZenEntryPoint::Run()"); zen::SetCurrentThreadName("main"); #if ZEN_USE_SENTRY @@ -107,8 +108,11 @@ ZenEntryPoint::Run() try { // Mutual exclusion and synchronization + ZEN_INFO("ZenServerState ServerState"); ZenServerState ServerState; + ZEN_INFO("ServerState.Initialize()"); ServerState.Initialize(); + ZEN_INFO("ServerState.Sweep()"); ServerState.Sweep(); uint32_t AttachSponsorProcessRetriesLeft = 3; @@ -173,6 +177,8 @@ ZenEntryPoint::Run() } } + ZEN_INFO("Preparing lock file"); + std::error_code Ec; std::filesystem::path LockFilePath = m_ServerOptions.DataDir / ".lock"; @@ -186,6 +192,7 @@ ZenEntryPoint::Run() .ExecutablePath = GetRunningExecutablePath()}); }; + ZEN_INFO("m_LockFile.Create"); m_LockFile.Create(LockFilePath, MakeLockData(false), Ec); if (Ec) @@ -201,6 +208,7 @@ ZenEntryPoint::Run() } } + ZEN_INFO("InitializeServerLogging"); InitializeServerLogging(m_ServerOptions); ZEN_INFO("Command line: {}", m_ServerOptions.CommandLine); @@ -303,6 +311,11 @@ ZenEntryPoint::Run() ZEN_CRITICAL("Caught assert exception in main for process {}: {}", zen::GetCurrentProcessId(), AssertEx.FullDescription()); RequestApplicationExit(1); } + catch (const std::system_error& e) + { + ZEN_CRITICAL("Caught system error exception in main for process {}: {} ({})", zen::GetCurrentProcessId(), e.what(), e.code().value()); + RequestApplicationExit(1); + } catch (const std::exception& e) { ZEN_CRITICAL("Caught exception in main for process {}: {}", zen::GetCurrentProcessId(), e.what()); diff --git a/src/zenutil/service.cpp b/src/zenutil/service.cpp index 834d8b764..820edf565 100644 --- a/src/zenutil/service.cpp +++ b/src/zenutil/service.cpp @@ -256,111 +256,58 @@ namespace { #endif // ZEN_PLATFORM_MAC -#if ZEN_PLATFORM_MAC +#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; - const int max_buffer = 256; - char buffer[max_buffer]; + std::string Data; + const int BufferSize = 256; + char Buffer[BufferSize]; std::string Command(Cmd); Command.append(" 2>&1"); - FILE* stream = popen(Command.c_str(), "r"); - if (stream) + ZEN_DEBUG("Running: '{}'", Command); + + FILE* Stream = popen(Command.c_str(), "r"); + if (Stream) { - while (!feof(stream)) + while (!feof(Stream)) { - if (fgets(buffer, max_buffer, stream) != NULL) + if (fgets(Buffer, BufferSize, Stream) != NULL) { - data.append(buffer); + Data.append(Buffer); } } + while(!Data.empty() && isspace(Data[Data.length() - 1])) + { + Data.pop_back(); + } + int Res = -1; - int st = pclose(stream); - if (WIFEXITED(st)) - Res = WEXITSTATUS(st); - return {Res, data}; + int Status = pclose(Stream); + if (Status < 0) + { + return {Status, Data}; + } + uint64_t WaitMS = 100; + if (!WIFEXITED(Status)) + { + Res = WEXITSTATUS(Status); + } + if (Res != 0 && Res != (128+13)) + { + return {Res, Data}; + } + return {0, Data}; } return {errno, ""}; - -# if 0 - int in[2], out[2], n, pid; - char buf[255]; - - /* In a pipe, xx[0] is for reading, xx[1] is for writing */ - - if (pipe(in) < 0) - { - return {errno, ""}; - } - if (pipe(out) < 0) - { - close(in[0]); - close(in[1]); - return {errno, ""}; - } - - if ((pid=fork()) == 0) { - /* This is the child process */ - - /* Close stdin, stdout, stderr */ - close(0); - close(1); - close(2); - /* make our pipes, our new stdin,stdout and stderr */ - dup2(in[0],0); - dup2(out[1],1); - dup2(out[1],2); - - /* Close the other ends of the pipes that the parent will use, because if - * we leave these open in the child, the child/parent will not get an EOF - * when the parent/child closes their end of the pipe. - */ - close(in[1]); - close(out[0]); - - va_list args; - va_start(args, Executable); - va_end(args); - - /* Over-write the child process with the hexdump binary */ - execl(Executable, Executable, args); - } - - /* This is the parent process */ - /* Close the pipe ends that the child uses to read from / write to so - * the when we close the others, an EOF will be transmitted properly. - */ - close(in[0]); - - /* Because of the small amount of data, the child may block unless we - * close it's input stream. This sends an EOF to the child on it's - * stdin. - */ - close(in[1]); - - /* Read back any output */ - n = read(out[0], buf, 250); - if (n == 0) - { - n = read(out[1], buf, 250); - } - buf[n] = 0; - close(out[0]); - close(out[1]); - - std::string Output(buf); - return {0, Output}; -# endif // 0 } -#endif // ZEN_PLATFORM_MAC +#endif // ZEN_PLATFORM_MAC || ZEN_PLATFORM_LINUX #if ZEN_PLATFORM_LINUX - // const char* SystemInstallFolder = "/etc/systemd/system/"; - std::string GetUnitName(std::string_view ServiceName) { return fmt::format("com.epicgames.unreal.{}", ServiceName); } std::filesystem::path GetServiceUnitPath(const std::string& UnitName) @@ -374,10 +321,8 @@ namespace { std::string_view CommandLineOptions, std::string_view AliasName) { -# if 0 - ZEN_UNUSED(ServiceName, ExecutablePath, CommandLineOptions, AliasName); - return ""; -# else + // 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" @@ -396,13 +341,13 @@ namespace { "Restart=always\n" "RuntimeDirectory={}\n" "[Install]\n" - "Alias={}", + "Alias={}\n" + "WantedBy=multi-user.target", ServiceName, ExecutablePath, CommandLineOptions, ExecutablePath.parent_path(), AliasName); -# endif } #endif // ZEN_PLATFORM_LINUX @@ -440,8 +385,10 @@ ToString(ServiceStatus Status) std::error_code InstallService(std::string_view ServiceName, const ServiceSpec& Spec) { - // Get a handle to the SCM database. + // TODO: Is "LocalService account" the correct account? + // TODO: Is ther eother parameters/security settings that we need to set up properly? + // Get a handle to the SCM database. SC_HANDLE schSCManager = OpenSCManager(NULL, // local computer NULL, // ServicesActive database SC_MANAGER_ALL_ACCESS); // full access rights @@ -744,6 +691,7 @@ 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 the default service user OK? const std::string DaemonName = GetDaemonName(ServiceName); std::string PList = BuildPlist(ServiceName, Spec.ExecutablePath, Spec.CommandLineOptions, DaemonName, true); @@ -780,8 +728,6 @@ UninstallService(std::string_view ServiceName) std::error_code QueryInstalledService(std::string_view ServiceName, ServiceInfo& OutInfo) { - ZEN_UNUSED(ServiceName, OutInfo); - OutInfo.Status = ServiceStatus::NotInstalled; const std::string DaemonName = GetDaemonName(ServiceName); @@ -873,7 +819,7 @@ QueryInstalledService(std::string_view ServiceName, ServiceInfo& OutInfo) }); } { - std::pair<int, std::string> Res = ExecuteProgram(std::string("launchctl list ") + DaemonName); + std::pair<int, std::string> Res = ExecuteProgram(fmt::format("launchctl list {}", DaemonName)); if (Res.first == 0 && !Res.second.empty()) { ForEachStrTok(Res.second, '\n', [&](std::string_view Line) { @@ -901,12 +847,10 @@ QueryInstalledService(std::string_view ServiceName, ServiceInfo& OutInfo) std::error_code StartService(std::string_view ServiceName) { - ZEN_UNUSED(ServiceName); - const std::string DaemonName = GetDaemonName(ServiceName); const std::filesystem::path PListPath = GetPListPath(DaemonName); - std::pair<int, std::string> Res = ExecuteProgram(std::string("launchctl bootstrap system ") + PListPath.string()); + std::pair<int, std::string> Res = ExecuteProgram(fmt::format("launchctl bootstrap system {}", PListPath)); if (Res.first != 0) { return MakeErrorCode(Res.first); @@ -918,12 +862,10 @@ StartService(std::string_view ServiceName) std::error_code StopService(std::string_view ServiceName) { - ZEN_UNUSED(ServiceName); - const std::string DaemonName = GetDaemonName(ServiceName); const std::filesystem::path PListPath = GetPListPath(DaemonName); - std::pair<int, std::string> Res = ExecuteProgram(std::string("launchctl bootout system ") + PListPath.string()); + std::pair<int, std::string> Res = ExecuteProgram(fmt::format("launchctl bootout system ", PListPath.)); if (Res.first != 0) { return MakeErrorCode(Res.first); @@ -939,13 +881,13 @@ StopService(std::string_view ServiceName) std::error_code InstallService(std::string_view ServiceName, const ServiceSpec& Spec) { - ZEN_UNUSED(ServiceName, 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); - std::string UnitFile = BuildUnitFile(ServiceName, Spec.ExecutablePath, Spec.CommandLineOptions, UnitName); - const std::filesystem::path ServiceUnitPath = GetServiceUnitPath(UnitName); - ZEN_INFO("Writing systemd unit file to {}", ServiceUnitPath.string()); + + std::string UnitFile = BuildUnitFile(ServiceName, Spec.ExecutablePath, Spec.CommandLineOptions, UnitName); + ZEN_DEBUG("Writing systemd unit file to {}", ServiceUnitPath.string()); try { zen::WriteFile(ServiceUnitPath, IoBuffer(IoBuffer::Wrap, UnitFile.data(), UnitFile.size())); @@ -955,20 +897,57 @@ InstallService(std::string_view ServiceName, const ServiceSpec& Spec) return MakeErrorCode(Ex.code().value()); } - ZEN_INFO("Changing permissions to 644 for {}", ServiceUnitPath.string()); + ZEN_DEBUG("Changing permissions to 644 for {}", ServiceUnitPath.string()); if (chmod(ServiceUnitPath.string().c_str(), 0644) == -1) { return MakeErrorCodeFromLastError(); } + std::pair<int, std::string> Res = ExecuteProgram("systemctl daemon-reload"); + if (Res.first != 0 && Res.first != -1) + { + ZEN_ERROR("systemctl daemon-reload failed with {}: '{}'", Res.first, Res.second); + return MakeErrorCode(Res.first); + } + + Res = ExecuteProgram(fmt::format("systemctl enable {}", UnitName)); + if (Res.first != 0 && Res.first != -1) + { + ZEN_ERROR("systemctl enable failed with {}: '{}'", Res.first, Res.second); + return MakeErrorCode(Res.first); + } + return {}; } std::error_code UninstallService(std::string_view ServiceName) { - ZEN_UNUSED(ServiceName); - ZEN_NOT_IMPLEMENTED("linux service implementation incomplete"); + const std::string UnitName = GetUnitName(ServiceName); + const std::filesystem::path ServiceUnitPath = GetServiceUnitPath(UnitName); + + std::pair<int, std::string> Res = ExecuteProgram(fmt::format("systemctl disable {}", UnitName)); + if (Res.first != 0 && Res.first != -1) + { + ZEN_ERROR("systemctl disable failed with {}: '{}'", Res.first, Res.second); + return MakeErrorCode(Res.first); + } + + ZEN_DEBUG("Attempting to remove systemd unit file from {}", ServiceUnitPath.string()); + std::error_code Ec; + std::filesystem::remove(ServiceUnitPath, Ec); + if (Ec) + { + ZEN_ERROR("failed to remove {}: '{}'", ServiceUnitPath, Ec.message()); + return Ec; + } + + Res = ExecuteProgram("systemctl daemon-reload"); + if (Res.first != 0 && Res.first != -1) + { + ZEN_ERROR("systemctl daemon-reload failed with {}: '{}'", Res.first, Res.second); + return MakeErrorCode(Res.first); + } return {}; } @@ -976,8 +955,27 @@ UninstallService(std::string_view ServiceName) std::error_code QueryInstalledService(std::string_view ServiceName, ServiceInfo& OutInfo) { - ZEN_UNUSED(ServiceName, OutInfo); - ZEN_NOT_IMPLEMENTED("linux service implementation incomplete"); + const std::string UnitName = GetUnitName(ServiceName); + const std::filesystem::path ServiceUnitPath = GetServiceUnitPath(UnitName); + + OutInfo.Status = ServiceStatus::NotInstalled; + + 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)); + 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; + } + else + { + ZEN_DEBUG("systemctl status failed with '{}" ({}), Res.second, Res.first); + } + } return {}; } @@ -985,8 +983,20 @@ QueryInstalledService(std::string_view ServiceName, ServiceInfo& OutInfo) std::error_code StartService(std::string_view ServiceName) { - ZEN_UNUSED(ServiceName); - ZEN_NOT_IMPLEMENTED("linux service implementation incomplete"); + // TODO: Starting the service returns -1 from ExecuteProgram, so the service won't start + // TODO: Running start from command line gives no output but the service does not start - not sure what is wrong. + // Starting the same command line for the service using `sudo` *will* start the service sucessfully so I + // assume that the Unit file or some config is wrong/missing. + + const std::string UnitName = GetUnitName(ServiceName); + const std::filesystem::path ServiceUnitPath = GetServiceUnitPath(UnitName); + + std::pair<int, std::string> Res = ExecuteProgram(fmt::format("systemctl start {}", UnitName)); + if (Res.first != 0) + { + ZEN_ERROR("service start failed with {}: '{}'", Res.first, Res.second); + return MakeErrorCode(Res.first); + } return {}; } @@ -994,8 +1004,18 @@ StartService(std::string_view ServiceName) std::error_code StopService(std::string_view ServiceName) { - ZEN_UNUSED(ServiceName); - ZEN_NOT_IMPLEMENTED("linux service implementation incomplete"); + // TODO: Stopping the service returns -1 from ExecuteProgram, maybe this ie expected as I have yet to successfully start the service using systemctl start + + const std::string UnitName = GetUnitName(ServiceName); + const std::filesystem::path ServiceUnitPath = GetServiceUnitPath(UnitName); + + std::pair<int, std::string> Res = ExecuteProgram(fmt::format("systemctl stop {}", UnitName)); + if (Res.first != 0) + { + ZEN_ERROR("service stop failed with {}: '{}'", Res.first, Res.second); + return MakeErrorCode(Res.first); + } + return {}; } diff --git a/src/zenutil/zenserverprocess.cpp b/src/zenutil/zenserverprocess.cpp index 214737425..3b7bf5ad4 100644 --- a/src/zenutil/zenserverprocess.cpp +++ b/src/zenutil/zenserverprocess.cpp @@ -167,7 +167,8 @@ ZenServerState::Initialize() ThrowLastError("Could not map view of Zen server state"); } #else - int Fd = shm_open("/UnrealEngineZen", O_RDWR | O_CREAT | O_CLOEXEC, 0666); +ZEN_INFO("{}", S_IRUSR | S_IWUSR | S_IXUSR); + int Fd = shm_open("/UnrealEngineZen", O_RDWR | O_CREAT | O_CLOEXEC, geteuid() == 0 ? 0766 : 0666); if (Fd < 0) { ThrowLastError("Could not open a shared memory object"); |