aboutsummaryrefslogtreecommitdiff
path: root/src/zenutil/service.cpp
diff options
context:
space:
mode:
authorDan Engelbrecht <[email protected]>2024-08-28 10:47:49 +0200
committerDan Engelbrecht <[email protected]>2024-09-18 10:43:36 +0200
commit8a2f7c0c7863f587113cdb77f51d622dbcb737aa (patch)
tree12b4ebc99992d4e3f30e6403a81e8927d37df8f0 /src/zenutil/service.cpp
parentzen `service` command (diff)
downloadzen-8a2f7c0c7863f587113cdb77f51d622dbcb737aa.tar.xz
zen-8a2f7c0c7863f587113cdb77f51d622dbcb737aa.zip
wip
Diffstat (limited to 'src/zenutil/service.cpp')
-rw-r--r--src/zenutil/service.cpp753
1 files changed, 433 insertions, 320 deletions
diff --git a/src/zenutil/service.cpp b/src/zenutil/service.cpp
index 840fe3104..cf0c8ef50 100644
--- a/src/zenutil/service.cpp
+++ b/src/zenutil/service.cpp
@@ -16,378 +16,491 @@ using namespace std::literals;
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 {};
+ ZEN_UNUSED(ExecutablePath, CommandLineOptions, ServiceName, ServiceDisplayName, ServiceDescription);
+ ZEN_NOT_IMPLEMENTED("InstallService");
+ 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
+#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
+