aboutsummaryrefslogtreecommitdiff
path: root/src/zenutil/service.cpp
diff options
context:
space:
mode:
authorDan Engelbrecht <[email protected]>2024-08-28 11:42:35 +0200
committerDan Engelbrecht <[email protected]>2025-01-08 10:01:23 +0100
commitd108042c4e654cb79cbb843630c8059b823d63b7 (patch)
treecb91394285a27862642b7573548a7f63773c537a /src/zenutil/service.cpp
parentwip (diff)
downloadzen-d108042c4e654cb79cbb843630c8059b823d63b7.tar.xz
zen-d108042c4e654cb79cbb843630c8059b823d63b7.zip
more WIP
Diffstat (limited to 'src/zenutil/service.cpp')
-rw-r--r--src/zenutil/service.cpp931
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("&quot");
+ break;
+ case '\'':
+ SB.Append("&apos");
+ break;
+ case '<':
+ SB.Append("&lt");
+ break;
+ case '>':
+ SB.Append("&gt");
+ break;
+ case '&':
+ SB.Append("&amp");
+ 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
-