diff options
| author | Dan Engelbrecht <[email protected]> | 2024-08-28 11:42:35 +0200 |
|---|---|---|
| committer | Dan Engelbrecht <[email protected]> | 2025-01-08 10:01:23 +0100 |
| commit | d108042c4e654cb79cbb843630c8059b823d63b7 (patch) | |
| tree | cb91394285a27862642b7573548a7f63773c537a /src/zenutil/service.cpp | |
| parent | wip (diff) | |
| download | zen-d108042c4e654cb79cbb843630c8059b823d63b7.tar.xz zen-d108042c4e654cb79cbb843630c8059b823d63b7.zip | |
more WIP
Diffstat (limited to 'src/zenutil/service.cpp')
| -rw-r--r-- | src/zenutil/service.cpp | 931 |
1 files changed, 499 insertions, 432 deletions
diff --git a/src/zenutil/service.cpp b/src/zenutil/service.cpp index cf0c8ef50..cce154988 100644 --- a/src/zenutil/service.cpp +++ b/src/zenutil/service.cpp @@ -9,498 +9,565 @@ #if ZEN_PLATFORM_WINDOWS # include <zencore/windows.h> #endif +#if ZEN_PLATFORM_MAC +# include <zencore/filesystem.h> +#endif namespace zen { using namespace std::literals; +#if ZEN_PLATFORM_MAC + +namespace { + std::vector<std::string_view> SplitArguments(std::string_view Arguments) + { + bool IsQuote = false; + size_t Start = 0; + size_t Offset = 0; + std::vector<std::string_view> Result; + for (; Offset < Arguments.length(); Offset++) + { + switch (Arguments[Offset]) + { + case ' ': + if (IsQuote) + { + continue; + } + else if (Offset > Start) + { + Result.push_back(Arguments.substr(Start, Offset - Start)); + Start = Offset + 1; + } + break; + case '"': + if (IsQuote) + { + IsQuote = false; + if (Offset - Start > 1) + { + Result.push_back(Arguments.substr(Start + 1, Offset - (Start + 1))); + } + Start = Offset + 1; + } + else + { + IsQuote = true; + } + break; + case '=': + if (IsQuote) + { + continue; + } + else if (Offset > Start) + { + Result.push_back(Arguments.substr(Start, Offset - Start)); + Start = Offset + 1; + } + break; + default: + break; + } + } + ZEN_ASSERT(!IsQuote); + if (Offset > Start) + { + Result.push_back(Arguments.substr(Start, Offset - Start)); + } + return Result; + } + + // Needs special character escaping + void AppendEscaped(std::string_view String, StringBuilderBase& SB) + { + size_t Offset = 0; + while (Offset < String.length()) + { + size_t NextEscapeCharacter = String.find_first_of("\"'<>&", Offset); + if (NextEscapeCharacter == std::string_view::npos) + { + break; + } + if (NextEscapeCharacter > Offset) + { + SB.Append(String.substr(Offset, NextEscapeCharacter - Offset)); + } + switch (String[NextEscapeCharacter]) + { + case '"': + SB.Append("""); + break; + case '\'': + SB.Append("&apos"); + break; + case '<': + SB.Append("<"); + break; + case '>': + SB.Append(">"); + break; + case '&': + SB.Append("&"); + break; + default: + ZEN_ASSERT(false); + break; + } + Offset = NextEscapeCharacter + 1; + } + if (Offset == 0) + { + SB.Append(String); + } + else if (String.length() > Offset) + { + SB.Append(String.substr(Offset)); + } + } + + std::string BuildPlist(const std::filesystem::path& ExecutablePath, + std::string_view CommandLineOptions, + std::string_view ServiceName, + std::string_view /*ServiceDisplayName*/, + std::string_view /*ServiceDescription*/, + bool Debug) + { + std::vector<std::string_view> Arguments = SplitArguments(CommandLineOptions); + ExtendableStringBuilder<256> ProgramArguments; + for (const std::string_view Argument : Arguments) + { + ProgramArguments.Append("<string>\n"); + AppendEscaped(Argument, ProgramArguments); + ProgramArguments.Append("\n</string>\n"); + } + + return fmt::format( + "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + "<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n" + "<plist version=\"1.0\">\n" + " <dict>\n" + " <key>Label</key>\n" + " <string>{}</string>\n" // ServiceName + " <key>ProgramArguments</key>\n" + " <array>\n" + " <string>{}</string>\n" // Program name + " {}" // "<string>arg</string>\n" * number of arguments + " </array>\n" + " <key>KeepAlive</key>\n" + " <true/>\n" + " <key>StandardOutPath</key>\n" + " <string>/var/log/myjob.log</string>\n" + " <key>StandardErrorPath</key>\n" + " <string>/var/log/myjob.log</string>\n" + " <key>Debug</key>\n" + " <{}/>\n" + " </dict>\n" + "</plist>\n", + ServiceName, + ExecutablePath.filename().string(), + ProgramArguments.ToView(), + Debug ? "true"sv : "false"sv); + + // "<key>Sockets</key>" + // "<dict>" + // "<key>Listeners</key>" + // "<dict>" + // "<key>SockServiceName</key>" + // "<string>{}</string>" // Listen socket + // "<key>SockType</key>" + // "<string>tcp</string>" + // "<key>SockFamily</key>" + // "<string>IPv4</string>" + // "</dict>" + // "</dict>" + } +} // namespace + +#endif // ZEN_PLATFORM_MAC + std::string_view ToString(ServiceStatus Status) { - switch (Status) - { - case ServiceStatus::NotInstalled: - return "Not installed"sv; - case ServiceStatus::Starting: - return "Starting"sv; - case ServiceStatus::Running: - return "Running"sv; - case ServiceStatus::Stopping: - return "Stopping"sv; - case ServiceStatus::Stopped: - return "Stopped"sv; - case ServiceStatus::Pausing: - return "Pausing"sv; - case ServiceStatus::Paused: - return "Paused"sv; - case ServiceStatus::Resuming: - return "Resuming"sv; - default: - ZEN_ASSERT(false); - return ""sv; - } + switch (Status) + { + case ServiceStatus::NotInstalled: + return "Not installed"sv; + case ServiceStatus::Starting: + return "Starting"sv; + case ServiceStatus::Running: + return "Running"sv; + case ServiceStatus::Stopping: + return "Stopping"sv; + case ServiceStatus::Stopped: + return "Stopped"sv; + case ServiceStatus::Pausing: + return "Pausing"sv; + case ServiceStatus::Paused: + return "Paused"sv; + case ServiceStatus::Resuming: + return "Resuming"sv; + default: + ZEN_ASSERT(false); + return ""sv; + } } #if ZEN_PLATFORM_WINDOWS std::error_code InstallService(const std::filesystem::path& ExecutablePath, - std::string_view CommandLineOptions, - std::string_view ServiceName, - std::string_view ServiceDisplayName, - std::string_view ServiceDescription) + std::string_view CommandLineOptions, + std::string_view ServiceName, + std::string_view ServiceDisplayName, + std::string_view ServiceDescription) { - // Get a handle to the SCM database. - - SC_HANDLE schSCManager = OpenSCManager(NULL, // local computer - NULL, // ServicesActive database - SC_MANAGER_ALL_ACCESS); // full access rights - - if (NULL == schSCManager) - { - return MakeErrorCodeFromLastError(); - } - - auto _ = MakeGuard([schSCManager]() { CloseServiceHandle(schSCManager); }); - - // Create the service - - ExtendableWideStringBuilder<128> Name; - Utf8ToWide(ServiceName, Name); - - ExtendableWideStringBuilder<128> DisplayName; - Utf8ToWide(ServiceDisplayName, DisplayName); - - ExtendableWideStringBuilder<128> Path; - Path.Append(ExecutablePath.c_str()); - if (!CommandLineOptions.empty()) - { - Path.AppendAscii(" "); - Utf8ToWide(CommandLineOptions, Path); - } - - SC_HANDLE schService = CreateService(schSCManager, // SCM database - Name.c_str(), // name of service - DisplayName.c_str(), // service name to display - SERVICE_ALL_ACCESS, // desired access - SERVICE_WIN32_OWN_PROCESS, // service type - SERVICE_DEMAND_START, // start type - SERVICE_ERROR_NORMAL, // error control type - Path.c_str(), // path to service's binary - NULL, // no load ordering group - NULL, // no tag identifier - NULL, // no dependencies - NULL, // LocalSystem account - NULL); // no password - - if (schService == NULL) - { - return MakeErrorCodeFromLastError(); - } - - if (!ServiceDescription.empty()) - { - ExtendableWideStringBuilder<128> DescriptionBuilder; - Utf8ToWide(ServiceDescription, DescriptionBuilder); - - SERVICE_DESCRIPTION Description; - Description.lpDescription = const_cast<wchar_t*>(DescriptionBuilder.c_str()); - if (!ChangeServiceConfig2(schService, SERVICE_CONFIG_DESCRIPTION, &Description)) - { - return MakeErrorCodeFromLastError(); - } - } - - CloseServiceHandle(schService); - - return {}; + // Get a handle to the SCM database. + + SC_HANDLE schSCManager = OpenSCManager(NULL, // local computer + NULL, // ServicesActive database + SC_MANAGER_ALL_ACCESS); // full access rights + + if (NULL == schSCManager) + { + return MakeErrorCodeFromLastError(); + } + + auto _ = MakeGuard([schSCManager]() { CloseServiceHandle(schSCManager); }); + + // Create the service + + ExtendableWideStringBuilder<128> Name; + Utf8ToWide(ServiceName, Name); + + ExtendableWideStringBuilder<128> DisplayName; + Utf8ToWide(ServiceDisplayName, DisplayName); + + ExtendableWideStringBuilder<128> Path; + Path.Append(ExecutablePath.c_str()); + if (!CommandLineOptions.empty()) + { + Path.AppendAscii(" "); + Utf8ToWide(CommandLineOptions, Path); + } + + SC_HANDLE schService = CreateService(schSCManager, // SCM database + Name.c_str(), // name of service + DisplayName.c_str(), // service name to display + SERVICE_ALL_ACCESS, // desired access + SERVICE_WIN32_OWN_PROCESS, // service type + SERVICE_DEMAND_START, // start type + SERVICE_ERROR_NORMAL, // error control type + Path.c_str(), // path to service's binary + NULL, // no load ordering group + NULL, // no tag identifier + NULL, // no dependencies + NULL, // LocalSystem account + NULL); // no password + + if (schService == NULL) + { + return MakeErrorCodeFromLastError(); + } + + if (!ServiceDescription.empty()) + { + ExtendableWideStringBuilder<128> DescriptionBuilder; + Utf8ToWide(ServiceDescription, DescriptionBuilder); + + SERVICE_DESCRIPTION Description; + Description.lpDescription = const_cast<wchar_t*>(DescriptionBuilder.c_str()); + if (!ChangeServiceConfig2(schService, SERVICE_CONFIG_DESCRIPTION, &Description)) + { + return MakeErrorCodeFromLastError(); + } + } + + CloseServiceHandle(schService); + + return {}; } std::error_code UninstallService(std::string_view ServiceName) { - // Get a handle to the SCM database. - SC_HANDLE schSCManager = OpenSCManager(NULL, // local computer - NULL, // ServicesActive database - SC_MANAGER_ALL_ACCESS); // full access rights - - if (NULL == schSCManager) - { - return MakeErrorCodeFromLastError(); - } - - auto _ = MakeGuard([schSCManager]() { CloseServiceHandle(schSCManager); }); - - // Get a handle to the service. - - ExtendableWideStringBuilder<128> Name; - Utf8ToWide(ServiceName, Name); - - SC_HANDLE schService = OpenService(schSCManager, // SCM database - Name.c_str(), // name of service - DELETE); // need delete access - - if (schService == NULL) - { - DWORD Error = ::GetLastError(); - if (Error == ERROR_SERVICE_DOES_NOT_EXIST) - { - return {}; - } - return MakeErrorCode(Error); - } - auto __ = MakeGuard([schService]() { CloseServiceHandle(schService); }); - - // Delete the service. - - if (!DeleteService(schService)) - { - return MakeErrorCodeFromLastError(); - } - - return {}; + // Get a handle to the SCM database. + SC_HANDLE schSCManager = OpenSCManager(NULL, // local computer + NULL, // ServicesActive database + SC_MANAGER_ALL_ACCESS); // full access rights + + if (NULL == schSCManager) + { + return MakeErrorCodeFromLastError(); + } + + auto _ = MakeGuard([schSCManager]() { CloseServiceHandle(schSCManager); }); + + // Get a handle to the service. + + ExtendableWideStringBuilder<128> Name; + Utf8ToWide(ServiceName, Name); + + SC_HANDLE schService = OpenService(schSCManager, // SCM database + Name.c_str(), // name of service + DELETE); // need delete access + + if (schService == NULL) + { + DWORD Error = ::GetLastError(); + if (Error == ERROR_SERVICE_DOES_NOT_EXIST) + { + return {}; + } + return MakeErrorCode(Error); + } + auto __ = MakeGuard([schService]() { CloseServiceHandle(schService); }); + + // Delete the service. + + if (!DeleteService(schService)) + { + return MakeErrorCodeFromLastError(); + } + + return {}; } std::error_code QueryInstalledService(std::string_view ServiceName, ServiceInfo& OutInfo) { - // Get a handle to the SCM database. - SC_HANDLE schSCManager = OpenSCManager(NULL, // local computer - NULL, // ServicesActive database - SC_MANAGER_CONNECT); // standard access rights - - if (NULL == schSCManager) - { - return MakeErrorCodeFromLastError(); - } - - auto _ = MakeGuard([schSCManager]() { CloseServiceHandle(schSCManager); }); - - // Get a handle to the service. - - ExtendableWideStringBuilder<128> Name; - Utf8ToWide(ServiceName, Name); - - SC_HANDLE schService = OpenService(schSCManager, // SCM database - Name.c_str(), // name of service - SERVICE_QUERY_STATUS | SERVICE_QUERY_CONFIG); // need delete access - - if (schService == NULL) - { - DWORD Error = ::GetLastError(); - if (Error == ERROR_SERVICE_DOES_NOT_EXIST) - { - OutInfo.Status = ServiceStatus::NotInstalled; - return {}; - } - return MakeErrorCode(Error); - } - auto __ = MakeGuard([schService]() { CloseServiceHandle(schService); }); - - std::vector<std::uint8_t> Buffer(8192); - QUERY_SERVICE_CONFIG* ServiceConfig = reinterpret_cast<QUERY_SERVICE_CONFIG*>(Buffer.data()); - DWORD BytesNeeded = 0; - if (!QueryServiceConfig(schService, ServiceConfig, (DWORD)Buffer.size(), &BytesNeeded)) - { - return MakeErrorCodeFromLastError(); - } - - OutInfo.ExecutablePath = std::filesystem::path(ServiceConfig->lpBinaryPathName); - OutInfo.DisplayName = WideToUtf8(ServiceConfig->lpDisplayName); - - SERVICE_STATUS ServiceStatus; - if (!::QueryServiceStatus(schService, &ServiceStatus)) - { - return MakeErrorCodeFromLastError(); - } - - switch (ServiceStatus.dwCurrentState) - { - case SERVICE_STOPPED: - OutInfo.Status = ServiceStatus::Stopped; - break; - case SERVICE_START_PENDING: - OutInfo.Status = ServiceStatus::Starting; - break; - case SERVICE_STOP_PENDING: - OutInfo.Status = ServiceStatus::Stopping; - break; - case SERVICE_RUNNING: - OutInfo.Status = ServiceStatus::Running; - break; - case SERVICE_CONTINUE_PENDING: - OutInfo.Status = ServiceStatus::Resuming; - break; - case SERVICE_PAUSE_PENDING: - OutInfo.Status = ServiceStatus::Pausing; - break; - case SERVICE_PAUSED: - OutInfo.Status = ServiceStatus::Paused; - break; - default: - throw std::runtime_error(fmt::format("Unknown service status for '{}': {}", ServiceName, ServiceStatus.dwCurrentState)); - } - - if (!QueryServiceConfig2(schService, SERVICE_CONFIG_DESCRIPTION, Buffer.data(), (DWORD)Buffer.size(), &BytesNeeded)) - { - DWORD Error = ::GetLastError(); - if (Error == ERROR_INSUFFICIENT_BUFFER) - { - Buffer.resize((size_t)BytesNeeded); - if (!QueryServiceConfig2(schService, SERVICE_CONFIG_DESCRIPTION, Buffer.data(), (DWORD)Buffer.size(), &BytesNeeded)) - { - return MakeErrorCodeFromLastError(); - } - } - else - { - return MakeErrorCode(Error); - } - } - SERVICE_DESCRIPTION* Description = (SERVICE_DESCRIPTION*)Buffer.data(); - if (Description->lpDescription != NULL) - { - OutInfo.Description = WideToUtf8(std::wstring(Description->lpDescription)); - } - - return {}; + // Get a handle to the SCM database. + SC_HANDLE schSCManager = OpenSCManager(NULL, // local computer + NULL, // ServicesActive database + SC_MANAGER_CONNECT); // standard access rights + + if (NULL == schSCManager) + { + return MakeErrorCodeFromLastError(); + } + + auto _ = MakeGuard([schSCManager]() { CloseServiceHandle(schSCManager); }); + + // Get a handle to the service. + + ExtendableWideStringBuilder<128> Name; + Utf8ToWide(ServiceName, Name); + + SC_HANDLE schService = OpenService(schSCManager, // SCM database + Name.c_str(), // name of service + SERVICE_QUERY_STATUS | SERVICE_QUERY_CONFIG); // need delete access + + if (schService == NULL) + { + DWORD Error = ::GetLastError(); + if (Error == ERROR_SERVICE_DOES_NOT_EXIST) + { + OutInfo.Status = ServiceStatus::NotInstalled; + return {}; + } + return MakeErrorCode(Error); + } + auto __ = MakeGuard([schService]() { CloseServiceHandle(schService); }); + + std::vector<std::uint8_t> Buffer(8192); + QUERY_SERVICE_CONFIG* ServiceConfig = reinterpret_cast<QUERY_SERVICE_CONFIG*>(Buffer.data()); + DWORD BytesNeeded = 0; + if (!QueryServiceConfig(schService, ServiceConfig, (DWORD)Buffer.size(), &BytesNeeded)) + { + return MakeErrorCodeFromLastError(); + } + + OutInfo.ExecutablePath = std::filesystem::path(ServiceConfig->lpBinaryPathName); + OutInfo.DisplayName = WideToUtf8(ServiceConfig->lpDisplayName); + + SERVICE_STATUS ServiceStatus; + if (!::QueryServiceStatus(schService, &ServiceStatus)) + { + return MakeErrorCodeFromLastError(); + } + + switch (ServiceStatus.dwCurrentState) + { + case SERVICE_STOPPED: + OutInfo.Status = ServiceStatus::Stopped; + break; + case SERVICE_START_PENDING: + OutInfo.Status = ServiceStatus::Starting; + break; + case SERVICE_STOP_PENDING: + OutInfo.Status = ServiceStatus::Stopping; + break; + case SERVICE_RUNNING: + OutInfo.Status = ServiceStatus::Running; + break; + case SERVICE_CONTINUE_PENDING: + OutInfo.Status = ServiceStatus::Resuming; + break; + case SERVICE_PAUSE_PENDING: + OutInfo.Status = ServiceStatus::Pausing; + break; + case SERVICE_PAUSED: + OutInfo.Status = ServiceStatus::Paused; + break; + default: + throw std::runtime_error(fmt::format("Unknown service status for '{}': {}", ServiceName, ServiceStatus.dwCurrentState)); + } + + if (!QueryServiceConfig2(schService, SERVICE_CONFIG_DESCRIPTION, Buffer.data(), (DWORD)Buffer.size(), &BytesNeeded)) + { + DWORD Error = ::GetLastError(); + if (Error == ERROR_INSUFFICIENT_BUFFER) + { + Buffer.resize((size_t)BytesNeeded); + if (!QueryServiceConfig2(schService, SERVICE_CONFIG_DESCRIPTION, Buffer.data(), (DWORD)Buffer.size(), &BytesNeeded)) + { + return MakeErrorCodeFromLastError(); + } + } + else + { + return MakeErrorCode(Error); + } + } + SERVICE_DESCRIPTION* Description = (SERVICE_DESCRIPTION*)Buffer.data(); + if (Description->lpDescription != NULL) + { + OutInfo.Description = WideToUtf8(std::wstring(Description->lpDescription)); + } + + return {}; } std::error_code StartService(std::string_view ServiceName) { - // Get a handle to the SCM database. - SC_HANDLE schSCManager = OpenSCManager(NULL, // local computer - NULL, // ServicesActive database - SC_MANAGER_CONNECT); // default access rights - - if (NULL == schSCManager) - { - return MakeErrorCodeFromLastError(); - } - - auto _ = MakeGuard([schSCManager]() { CloseServiceHandle(schSCManager); }); - - // Get a handle to the service. - - ExtendableWideStringBuilder<128> Name; - Utf8ToWide(ServiceName, Name); - - SC_HANDLE schService = OpenService(schSCManager, // SCM database - Name.c_str(), // name of service - SERVICE_START); // need start access - - if (schService == NULL) - { - return MakeErrorCodeFromLastError(); - } - auto __ = MakeGuard([schService]() { CloseServiceHandle(schService); }); - - // Start the service. - - if (!::StartService(schService, 0, NULL)) - { - return MakeErrorCodeFromLastError(); - } - - return {}; + // Get a handle to the SCM database. + SC_HANDLE schSCManager = OpenSCManager(NULL, // local computer + NULL, // ServicesActive database + SC_MANAGER_CONNECT); // default access rights + + if (NULL == schSCManager) + { + return MakeErrorCodeFromLastError(); + } + + auto _ = MakeGuard([schSCManager]() { CloseServiceHandle(schSCManager); }); + + // Get a handle to the service. + + ExtendableWideStringBuilder<128> Name; + Utf8ToWide(ServiceName, Name); + + SC_HANDLE schService = OpenService(schSCManager, // SCM database + Name.c_str(), // name of service + SERVICE_START); // need start access + + if (schService == NULL) + { + return MakeErrorCodeFromLastError(); + } + auto __ = MakeGuard([schService]() { CloseServiceHandle(schService); }); + + // Start the service. + + if (!::StartService(schService, 0, NULL)) + { + return MakeErrorCodeFromLastError(); + } + + return {}; } std::error_code StopService(std::string_view ServiceName) { - // Get a handle to the SCM database. - SC_HANDLE schSCManager = OpenSCManager(NULL, // local computer - NULL, // ServicesActive database - SC_MANAGER_CONNECT); // default access rights - - if (NULL == schSCManager) - { - return MakeErrorCodeFromLastError(); - } - - auto _ = MakeGuard([schSCManager]() { CloseServiceHandle(schSCManager); }); - - // Get a handle to the service. - - ExtendableWideStringBuilder<128> Name; - Utf8ToWide(ServiceName, Name); - - SC_HANDLE schService = OpenService(schSCManager, // SCM database - Name.c_str(), // name of service - SERVICE_STOP); // need start access - - if (schService == NULL) - { - return MakeErrorCodeFromLastError(); - } - auto __ = MakeGuard([schService]() { CloseServiceHandle(schService); }); - - // Stop the service. - SERVICE_STATUS ServiceStatus; - if (!::ControlService(schService, SERVICE_CONTROL_STOP, &ServiceStatus)) - { - return MakeErrorCodeFromLastError(); - } - - return {}; + // Get a handle to the SCM database. + SC_HANDLE schSCManager = OpenSCManager(NULL, // local computer + NULL, // ServicesActive database + SC_MANAGER_CONNECT); // default access rights + + if (NULL == schSCManager) + { + return MakeErrorCodeFromLastError(); + } + + auto _ = MakeGuard([schSCManager]() { CloseServiceHandle(schSCManager); }); + + // Get a handle to the service. + + ExtendableWideStringBuilder<128> Name; + Utf8ToWide(ServiceName, Name); + + SC_HANDLE schService = OpenService(schSCManager, // SCM database + Name.c_str(), // name of service + SERVICE_STOP); // need start access + + if (schService == NULL) + { + return MakeErrorCodeFromLastError(); + } + auto __ = MakeGuard([schService]() { CloseServiceHandle(schService); }); + + // Stop the service. + SERVICE_STATUS ServiceStatus; + if (!::ControlService(schService, SERVICE_CONTROL_STOP, &ServiceStatus)) + { + return MakeErrorCodeFromLastError(); + } + + return {}; } #else std::error_code InstallService(const std::filesystem::path& ExecutablePath, - std::string_view CommandLineOptions, - std::string_view ServiceName, - std::string_view ServiceDisplayName, - std::string_view ServiceDescription) + std::string_view CommandLineOptions, + std::string_view ServiceName, + std::string_view ServiceDisplayName, + std::string_view ServiceDescription) { - ZEN_UNUSED(ExecutablePath, CommandLineOptions, ServiceName, ServiceDisplayName, ServiceDescription); - ZEN_NOT_IMPLEMENTED("InstallService"); - return {}; + std::filesystem::path SymLink = std::filesystem::path("/usr/local/libexec") / ExecutablePath.filename(); + if (symlink(ExecutablePath.c_str(), SymLink.c_str()) == -1) + { + return MakeErrorCodeFromLastError(); + } + std::string DaemonName = fmt::format("com.epicgames.unreal.{}", ServiceName); + std::string PList = BuildPlist(ExecutablePath, CommandLineOptions, DaemonName, ServiceDisplayName, ServiceDescription, true); + zen::WriteFile(std::filesystem::path("/Library/LaunchDaemons") / (DaemonName + ".plist"), IoBuffer(PList.data(), PList.size())); + return {}; } std::error_code UninstallService(std::string_view ServiceName) { - ZEN_UNUSED(ServiceName); - ZEN_NOT_IMPLEMENTED("UninstallService"); - return {}; + ZEN_UNUSED(ServiceName); + ZEN_NOT_IMPLEMENTED("UninstallService"); + return {}; } std::error_code QueryInstalledService(std::string_view ServiceName, ServiceInfo& OutStatus) { - ZEN_UNUSED(ServiceName, OutStatus); - ZEN_NOT_IMPLEMENTED("QueryInstalledService"); - return {}; + ZEN_UNUSED(ServiceName, OutStatus); + ZEN_NOT_IMPLEMENTED("QueryInstalledService"); + return {}; } std::error_code StartService(std::string_view ServiceName) { - ZEN_UNUSED(ServiceName); - ZEN_NOT_IMPLEMENTED("StartService"); - return {}; + ZEN_UNUSED(ServiceName); + ZEN_NOT_IMPLEMENTED("StartService"); + return {}; } std::error_code StopService(std::string_view ServiceName) { - ZEN_UNUSED(ServiceName); - ZEN_NOT_IMPLEMENTED("StopService"); - return {}; + ZEN_UNUSED(ServiceName); + ZEN_NOT_IMPLEMENTED("StopService"); + return {}; } -#endif // ZEN_PLATFORM_WINDOWS +#endif // ZEN_PLATFORM_WINDOWS -#if ZEN_PLATFORM_MAC - -std::error_code -InstallService(const std::filesystem::path& ExecutablePath, - std::string_view CommandLineOptions, - std::string_view ServiceName, - std::string_view ServiceDisplayName, - std::string_view ServiceDescription) -{ - const char* PlistTemplate = - "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" - "<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">" - "<plist version=\"1.0\">" - "<dict>" - "<key>Label</key>" - "<string>{}</string>" // ServiceName - "<key>ProgramArguments</key>" - "<array>" - "<string>{}</string>" // Program name - "{}" // "<string>arg</string>\n" * number of arguments - "</array>" - "<key>KeepAlive</key>" - "<true/>" -// "<key>Sockets</key>" -// "<dict>" -// "<key>Listeners</key>" -// "<dict>" -// "<key>SockServiceName</key>" -// "<string>{}</string>" // Listen socket -// "<key>SockType</key>" -// "<string>tcp</string>" -// "<key>SockFamily</key>" -// "<string>IPv4</string>" -// "</dict>" -// "</dict>" - "<key>StandardOutPath</key>" - "<string>/var/log/myjob.log</string>" - "<key>StandardErrorPath</key>" - "<string>/var/log/myjob.log</string>" - "<key>Debug</key>" - "<{}/>" - - "</dict>" - "</plist>"; - - std::string ExecutableName = ExecutablePath.filename(); - - const bool Debug = true; - - bool IsQuote = false; - size_t Start = 0; - ExtendableStringBuilder<256> CommandLineOptions; - for (size_t Offset = 0; Offset < CommandLineOptions.length(); Offset++) - { - switch(CommandLineOptions[Offset]) - { - case ' ': - if (IsQuote) - { - continue; - } - else if (Offset > Start) - { - CommandLineOptions.Append("<string>"); - CommandLineOptions.Append(CommandLineOptions.substr(Start, Offset - Start)); - CommandLineOptions.Append("</string>\n"); - Start = Offset + 1; - } - break; - case '"': - if (IsQuite) - { - IsQuote = false; - if (Offset - Start > 1) - { - CommandLineOptions.Append("<string>"); - CommandLineOptions.Append(CommandLineOptions.substr(Start + 1, (Offset - 1) - (Start + 1)); - CommandLineOptions.Append("</string>\n"); - } - Start = Offset + 1; - } - else{ - IsQuite = true; - } - break; - default: - break; - } - } - ZEN_ASSERT(!IsQuote); - if (Offset > Start) - { - CommandLineOptions.Append("<string>"); - CommandLineOptions.Append(CommandLineOptions.substr(Start, Offset - Start)); - CommandLineOptions.Append("</string>\n"); - } - - ForEachStrTok(CommandLineOptions, ' ', <#Fn &&Func#>) - - std::string PList = fmt::format(PlistTemplate, ServiceName,ExecutableName, CommandLineOptions,debug ? "true" : "false"); - - std::filesystem::path SymLink = std::filesystem::path("/usr/local/libexec") / ExecutableName; - if (symlink(ExecutablePath.c_str(), SymLink.c_str()) == -1) - { - return MakeErrorCodeFromLastError(); - } - return {}; -} - - -#endif // ZEN_PLATFORM_MAC } // namespace zen - |