From 1b9b121650d5852bc5781f1f7db149411c9d673c Mon Sep 17 00:00:00 2001 From: Dan Engelbrecht Date: Tue, 27 Aug 2024 17:09:55 +0200 Subject: zen `service` command --- src/zen/cmds/service_cmd.cpp | 308 ++++++++++++++++++++++++++ src/zen/cmds/service_cmd.h | 48 +++++ src/zen/zen.cpp | 3 + src/zenutil/include/zenutil/service.h | 38 ++++ src/zenutil/service.cpp | 393 ++++++++++++++++++++++++++++++++++ 5 files changed, 790 insertions(+) create mode 100644 src/zen/cmds/service_cmd.cpp create mode 100644 src/zen/cmds/service_cmd.h create mode 100644 src/zenutil/include/zenutil/service.h create mode 100644 src/zenutil/service.cpp diff --git a/src/zen/cmds/service_cmd.cpp b/src/zen/cmds/service_cmd.cpp new file mode 100644 index 000000000..a93bac37c --- /dev/null +++ b/src/zen/cmds/service_cmd.cpp @@ -0,0 +1,308 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +#include "service_cmd.h" + +#include +#include +#include +#include +#include + +#if ZEN_PLATFORM_WINDOWS +# include +# include +#endif + +ZEN_THIRD_PARTY_INCLUDES_START +#include +ZEN_THIRD_PARTY_INCLUDES_END + +#include + +namespace zen { + +////////////////////////////////////////////////////////////////////////// +#if ZEN_PLATFORM_WINDOWS +BOOL +WinIsElevated() +{ + BOOL fRet = FALSE; + HANDLE hToken = NULL; + if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken)) + { + TOKEN_ELEVATION Elevation; + DWORD cbSize = sizeof(TOKEN_ELEVATION); + if (GetTokenInformation(hToken, TokenElevation, &Elevation, sizeof(Elevation), &cbSize)) + { + fRet = Elevation.TokenIsElevated; + } + } + if (hToken) + { + CloseHandle(hToken); + } + return fRet; +} + +int +WinRelaunchElevated() +{ + TCHAR CurrentDir[4096]; + GetCurrentDirectory(4096, CurrentDir); + + ExtendableWideStringBuilder<256> Parameters; + std::filesystem::path ExecutablePath = GetRunningExecutablePath(); + std::wstring CommandLine(GetCommandLine()); + std::string::size_type ExtraLength = CommandLine[0] == '\"' ? 2 : 0; + std::wstring CommandArguments = CommandLine.substr(ExecutablePath.string().length() + ExtraLength + 1); + + ZEN_CONSOLE("Attempting to run '{} {}' elevated...", ExecutablePath, WideToUtf8(CommandArguments)); + + SHELLEXECUTEINFO shExInfo = {0}; + shExInfo.cbSize = sizeof(shExInfo); + shExInfo.fMask = SEE_MASK_NOCLOSEPROCESS | SEE_MASK_NO_CONSOLE; + shExInfo.hwnd = 0; + shExInfo.lpVerb = TEXT("runas"); // Operation to perform + shExInfo.lpFile = ExecutablePath.c_str(); // Application to start + shExInfo.lpParameters = CommandArguments.c_str(); // Additional parameters + shExInfo.lpDirectory = CurrentDir; + shExInfo.nShow = SW_HIDE; + shExInfo.hInstApp = 0; + + DWORD ReturnCode = 1; + if (ShellExecuteEx(&shExInfo)) + { + WaitForSingleObject(shExInfo.hProcess, INFINITE); + GetExitCodeProcess(shExInfo.hProcess, &ReturnCode); + CloseHandle(shExInfo.hProcess); + if (ReturnCode == 0) + { + ZEN_CONSOLE("Elevated execution completed successfully."); + } + else + { + ZEN_CONSOLE("Elevated execution completed unsuccessfully, return code: '{}'.", ReturnCode); + } + } + else + { + ZEN_CONSOLE("Failed to elevated, operation did not complete."); + } + return (int)ReturnCode; +} + +#endif + +ServiceCommand::ServiceCommand() +{ + m_Options.add_option("", + "v", + "verb", + fmt::format("Verb for service - {}, {}, {}, {}, {}. Use '--' ", + m_StatusOptions.program(), + m_InstallOptions.program(), + m_UninstallOptions.program(), + m_StartOptions.program(), + m_StopOptions.program()), + cxxopts::value(m_Verb), + ""); + m_Options.parse_positional({"verb"}); + m_Options.positional_help("verb"); + + m_StatusOptions.add_options()("h,help", "Print help"); + m_StatusOptions.add_option("", + "n", + "name", + fmt::format("Service name, defaults to \"{}\"", m_ServiceName), + cxxopts::value(m_ServiceName), + ""); + m_StatusOptions.parse_positional({"name"}); + m_StatusOptions.positional_help("name"); + + m_InstallOptions.add_options()("h,help", "Print help"); + m_InstallOptions.add_option("", "s", "executable", "Path to server executable", cxxopts::value(m_ServerExecutable), ""); + m_InstallOptions.add_option("", + "n", + "name", + fmt::format("Service name, defaults to \"{}\"", m_ServiceName), + cxxopts::value(m_ServiceName), + ""); + m_InstallOptions.add_option("", + "d", + "display-name", + fmt::format("Service display, defaults to \"{}\"", m_ServiceDisplayName), + cxxopts::value(m_ServiceDisplayName), + ""); + m_InstallOptions.add_option("", + "", + "description", + fmt::format("Service description", m_ServiceDescription), + cxxopts::value(m_ServiceDescription), + ""); + m_InstallOptions.parse_positional({"executable", "name", "display-name"}); + m_InstallOptions.positional_help("executable name display-name"); + + m_UninstallOptions.add_options()("h,help", "Print help"); + m_UninstallOptions.add_option("", + "n", + "name", + fmt::format("Service name, defaults to \"{}\"", m_ServiceName), + cxxopts::value(m_ServiceName), + ""); + m_UninstallOptions.parse_positional({"name"}); + m_UninstallOptions.positional_help("name"); + + m_StartOptions.add_options()("h,help", "Print help"); + m_StartOptions.add_option("", + "n", + "name", + fmt::format("Service name, defaults to \"{}\"", m_ServiceName), + cxxopts::value(m_ServiceName), + ""); + m_StartOptions.parse_positional({"name"}); + m_StartOptions.positional_help("name"); + + m_StopOptions.add_options()("h,help", "Print help"); + m_StopOptions.add_option("", + "n", + "name", + fmt::format("Service name, defaults to \"{}\"", m_ServiceName), + cxxopts::value(m_ServiceName), + ""); + m_StopOptions.parse_positional({"name"}); + m_StopOptions.positional_help("name"); +} + +ServiceCommand::~ServiceCommand() = default; + +int +ServiceCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) +{ + ZEN_UNUSED(GlobalOptions); + + using namespace std::literals; + + std::vector SubCommandArguments; + cxxopts::Options* SubOption = nullptr; + int ParentCommandArgCount = GetSubCommand(m_Options, argc, argv, m_SubCommands, SubOption, SubCommandArguments); + if (!ParseOptions(ParentCommandArgCount, argv)) + { + return 0; + } + + if (SubOption == nullptr) + { + throw zen::OptionParseException("command verb is missing"); + } + + if (!ParseOptions(*SubOption, gsl::narrow(SubCommandArguments.size()), SubCommandArguments.data())) + { + return 0; + } + + if (SubOption == &m_StatusOptions) + { + ServiceInfo Info; + std::error_code Ec = QueryInstalledService(m_ServiceName, Info); + if (Ec) + { + ZEN_CONSOLE("Can't get information about service '{}'. Reason: '{}'", m_ServiceName, Ec.message()); + return 1; + } + ZEN_CONSOLE( + "Service '{}':\n" + " Status: {}\n" + " Executable: '{}'\n" + " Display Name: '{}'\n" + " Description: '{}'", + m_ServiceName, + ToString(Info.Status), + Info.ExecutablePath, + Info.DisplayName, + Info.Description); + } + + if (SubOption == &m_InstallOptions) + { +#if ZEN_PLATFORM_WINDOWS + if (!WinIsElevated()) + { + return WinRelaunchElevated(); + } +#endif // ZEN_PLATFORM_WINDOWS + if (m_ServerExecutable.empty()) + { + std::filesystem::path ExePath = zen::GetRunningExecutablePath(); + ExePath.replace_filename("zenserver" + ExePath.extension().string()); + m_ServerExecutable = ExePath; + } + m_ServerExecutable = std::filesystem::absolute(m_ServerExecutable); + + std::error_code Ec = InstallService(m_ServerExecutable, + GlobalOptions.PassthroughCommandLine, + m_ServiceName, + m_ServiceDisplayName, + m_ServiceDescription); + if (Ec) + { + ZEN_CONSOLE("Failed to install service '{}' using '{}' . Reason: '{}'", m_ServiceName, m_ServerExecutable, Ec.message()); + return 1; + } + ZEN_CONSOLE("Installed service '{}' using '{}' successfully", m_ServiceName, m_ServerExecutable); + } + + if (SubOption == &m_UninstallOptions) + { +#if ZEN_PLATFORM_WINDOWS + if (!WinIsElevated()) + { + return WinRelaunchElevated(); + } +#endif // ZEN_PLATFORM_WINDOWS + std::error_code Ec = UninstallService(m_ServiceName); + if (Ec) + { + ZEN_CONSOLE("Failed to uninstall service '{}'. Reason: '{}'", m_ServiceName, Ec.message()); + return 1; + } + ZEN_CONSOLE("Uninstalled service {} successfully", m_ServiceName); + } + + if (SubOption == &m_StartOptions) + { +#if ZEN_PLATFORM_WINDOWS + if (!WinIsElevated()) + { + return WinRelaunchElevated(); + } +#endif // ZEN_PLATFORM_WINDOWS + std::error_code Ec = StartService(m_ServiceName); + if (Ec) + { + ZEN_CONSOLE("Failed to start service '{}'. Reason: '{}'", m_ServiceName, Ec.message()); + return 1; + } + ZEN_CONSOLE("Started service '{}' successfully", m_ServiceName); + } + + if (SubOption == &m_StopOptions) + { +#if ZEN_PLATFORM_WINDOWS + if (!WinIsElevated()) + { + return WinRelaunchElevated(); + } +#endif // ZEN_PLATFORM_WINDOWS + std::error_code Ec = StopService(m_ServiceName); + if (Ec) + { + ZEN_CONSOLE("Failed to stop service '{}'. Reason: '{}'", m_ServiceName, Ec.message()); + return 1; + } + ZEN_CONSOLE("Stopped service '{}' successfully", m_ServiceName); + } + + return 0; +} + +} // namespace zen diff --git a/src/zen/cmds/service_cmd.h b/src/zen/cmds/service_cmd.h new file mode 100644 index 000000000..d61748421 --- /dev/null +++ b/src/zen/cmds/service_cmd.h @@ -0,0 +1,48 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +#pragma once + +#include "../zen.h" + +#include + +namespace zen { + +class ServiceCommand : public ZenCmdBase +{ +public: + static constexpr char Name[] = "service"; + static constexpr char Description[] = "Manage zenserver as a service - status, install, uninstall, start and stop"; + + ServiceCommand(); + ~ServiceCommand(); + + virtual int Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) override; + virtual cxxopts::Options& Options() override { return m_Options; } + +private: + cxxopts::Options m_Options{Name, Description}; + + std::string m_Verb; // create, info, remove + + std::string m_ServiceName = "Unreal Zen Storage"; + + cxxopts::Options m_StatusOptions{"status", "Show information about an installed zenserver service"}; + + cxxopts::Options m_InstallOptions{ + "install", + "Install zenserver as a service. Arguments following \" -- \" will be added as parameters to the installed service."}; + std::filesystem::path m_ServerExecutable; + std::string m_ServiceDisplayName = "Unreal Zen Storage Service"; + std::string m_ServiceDescription; + + cxxopts::Options m_UninstallOptions{"uninstall", "Uninstall zenserver as a service"}; + + cxxopts::Options m_StartOptions{"start", "Start an installed zenserver service"}; + + cxxopts::Options m_StopOptions{"stop", "Start an installed zenserver service"}; + + cxxopts::Options* m_SubCommands[5] = {&m_StatusOptions, &m_InstallOptions, &m_UninstallOptions, &m_StartOptions, &m_StopOptions}; +}; + +} // namespace zen diff --git a/src/zen/zen.cpp b/src/zen/zen.cpp index 79338f6f3..039588728 100644 --- a/src/zen/zen.cpp +++ b/src/zen/zen.cpp @@ -16,6 +16,7 @@ #include "cmds/rpcreplay_cmd.h" #include "cmds/run_cmd.h" #include "cmds/serve_cmd.h" +#include "cmds/service_cmd.h" #include "cmds/status_cmd.h" #include "cmds/top_cmd.h" #include "cmds/trace_cmd.h" @@ -332,6 +333,7 @@ main(int argc, char** argv) VfsCommand VfsCmd; WorkspaceCommand WorkspaceCmd; WorkspaceShareCommand WorkspaceShareCmd; + ServiceCommand ServiceCmd; const struct CommandInfo { @@ -385,6 +387,7 @@ main(int argc, char** argv) {"flush", &FlushCmd, "Flush storage"}, {WorkspaceCommand::Name, &WorkspaceCmd, WorkspaceCommand::Description}, {WorkspaceShareCommand::Name, &WorkspaceShareCmd, WorkspaceShareCommand::Description}, + {ServiceCommand::Name, &ServiceCmd, ServiceCommand::Description}, // clang-format on }; diff --git a/src/zenutil/include/zenutil/service.h b/src/zenutil/include/zenutil/service.h new file mode 100644 index 000000000..79be16052 --- /dev/null +++ b/src/zenutil/include/zenutil/service.h @@ -0,0 +1,38 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +#pragma once + +#include + +namespace zen { +enum class ServiceStatus +{ + NotInstalled, + Starting, + Running, + Stopping, + Stopped, + Pausing, + Paused, + Resuming +}; + +std::string_view ToString(ServiceStatus Status); + +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::error_code UninstallService(std::string_view ServiceName); +struct ServiceInfo +{ + ServiceStatus Status; + std::filesystem::path ExecutablePath; + std::string DisplayName; + std::string Description; +}; +std::error_code QueryInstalledService(std::string_view ServiceName, ServiceInfo& OutStatus); +std::error_code StartService(std::string_view ServiceName); +std::error_code StopService(std::string_view ServiceName); +} // namespace zen diff --git a/src/zenutil/service.cpp b/src/zenutil/service.cpp new file mode 100644 index 000000000..840fe3104 --- /dev/null +++ b/src/zenutil/service.cpp @@ -0,0 +1,393 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +#include + +#include +#include +#include + +#if ZEN_PLATFORM_WINDOWS +# include +#endif + +namespace zen { +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; + } +} + +#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) +{ + // 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(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 {}; +} + +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 Buffer(8192); + QUERY_SERVICE_CONFIG* ServiceConfig = reinterpret_cast(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 {}; +} + +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 {}; +} +#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) +{ + 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 {}; +} + +std::error_code +QueryInstalledService(std::string_view ServiceName, ServiceInfo& OutStatus) +{ + 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 {}; +} + +std::error_code +StopService(std::string_view ServiceName) +{ + ZEN_UNUSED(ServiceName); + ZEN_NOT_IMPLEMENTED("StopService"); + return {}; +} + +#endif +} // namespace zen -- cgit v1.2.3 From 8a2f7c0c7863f587113cdb77f51d622dbcb737aa Mon Sep 17 00:00:00 2001 From: Dan Engelbrecht Date: Wed, 28 Aug 2024 10:47:49 +0200 Subject: wip --- src/zenutil/service.cpp | 753 ++++++++++++++++++++++++++++-------------------- 1 file 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(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(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 Buffer(8192); - QUERY_SERVICE_CONFIG* ServiceConfig = reinterpret_cast(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 Buffer(8192); + QUERY_SERVICE_CONFIG* ServiceConfig = reinterpret_cast(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 = + "" + "" + "" + "" + "Label" + "{}" // ServiceName + "ProgramArguments" + "" + "{}" // Program name + "{}" // "arg\n" * number of arguments + "" + "KeepAlive" + "" +// "Sockets" +// "" +// "Listeners" +// "" +// "SockServiceName" +// "{}" // Listen socket +// "SockType" +// "tcp" +// "SockFamily" +// "IPv4" +// "" +// "" + "StandardOutPath" + "/var/log/myjob.log" + "StandardErrorPath" + "/var/log/myjob.log" + "Debug" + "<{}/>" + + "" + ""; + + 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(""); + CommandLineOptions.Append(CommandLineOptions.substr(Start, Offset - Start)); + CommandLineOptions.Append("\n"); + Start = Offset + 1; + } + break; + case '"': + if (IsQuite) + { + IsQuote = false; + if (Offset - Start > 1) + { + CommandLineOptions.Append(""); + CommandLineOptions.Append(CommandLineOptions.substr(Start + 1, (Offset - 1) - (Start + 1)); + CommandLineOptions.Append("\n"); + } + Start = Offset + 1; + } + else{ + IsQuite = true; + } + break; + default: + break; + } + } + ZEN_ASSERT(!IsQuote); + if (Offset > Start) + { + CommandLineOptions.Append(""); + CommandLineOptions.Append(CommandLineOptions.substr(Start, Offset - Start)); + CommandLineOptions.Append("\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 + -- cgit v1.2.3 From a49f1b9cf5432b1528127427b0b911ce26ce5448 Mon Sep 17 00:00:00 2001 From: Dan Engelbrecht Date: Wed, 28 Aug 2024 11:42:35 +0200 Subject: more WIP --- src/zenutil/service.cpp | 931 ++++++++++++++++++++++++++---------------------- 1 file 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 #endif +#if ZEN_PLATFORM_MAC +# include +#endif namespace zen { using namespace std::literals; +#if ZEN_PLATFORM_MAC + +namespace { + std::vector SplitArguments(std::string_view Arguments) + { + bool IsQuote = false; + size_t Start = 0; + size_t Offset = 0; + std::vector 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 Arguments = SplitArguments(CommandLineOptions); + ExtendableStringBuilder<256> ProgramArguments; + for (const std::string_view Argument : Arguments) + { + ProgramArguments.Append("\n"); + AppendEscaped(Argument, ProgramArguments); + ProgramArguments.Append("\n\n"); + } + + return fmt::format( + "\n" + "\n" + "\n" + " \n" + " Label\n" + " {}\n" // ServiceName + " ProgramArguments\n" + " \n" + " {}\n" // Program name + " {}" // "arg\n" * number of arguments + " \n" + " KeepAlive\n" + " \n" + " StandardOutPath\n" + " /var/log/myjob.log\n" + " StandardErrorPath\n" + " /var/log/myjob.log\n" + " Debug\n" + " <{}/>\n" + " \n" + "\n", + ServiceName, + ExecutablePath.filename().string(), + ProgramArguments.ToView(), + Debug ? "true"sv : "false"sv); + + // "Sockets" + // "" + // "Listeners" + // "" + // "SockServiceName" + // "{}" // Listen socket + // "SockType" + // "tcp" + // "SockFamily" + // "IPv4" + // "" + // "" + } +} // 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(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(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 Buffer(8192); - QUERY_SERVICE_CONFIG* ServiceConfig = reinterpret_cast(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 Buffer(8192); + QUERY_SERVICE_CONFIG* ServiceConfig = reinterpret_cast(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 = - "" - "" - "" - "" - "Label" - "{}" // ServiceName - "ProgramArguments" - "" - "{}" // Program name - "{}" // "arg\n" * number of arguments - "" - "KeepAlive" - "" -// "Sockets" -// "" -// "Listeners" -// "" -// "SockServiceName" -// "{}" // Listen socket -// "SockType" -// "tcp" -// "SockFamily" -// "IPv4" -// "" -// "" - "StandardOutPath" - "/var/log/myjob.log" - "StandardErrorPath" - "/var/log/myjob.log" - "Debug" - "<{}/>" - - "" - ""; - - 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(""); - CommandLineOptions.Append(CommandLineOptions.substr(Start, Offset - Start)); - CommandLineOptions.Append("\n"); - Start = Offset + 1; - } - break; - case '"': - if (IsQuite) - { - IsQuote = false; - if (Offset - Start > 1) - { - CommandLineOptions.Append(""); - CommandLineOptions.Append(CommandLineOptions.substr(Start + 1, (Offset - 1) - (Start + 1)); - CommandLineOptions.Append("\n"); - } - Start = Offset + 1; - } - else{ - IsQuite = true; - } - break; - default: - break; - } - } - ZEN_ASSERT(!IsQuote); - if (Offset > Start) - { - CommandLineOptions.Append(""); - CommandLineOptions.Append(CommandLineOptions.substr(Start, Offset - Start)); - CommandLineOptions.Append("\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 - -- cgit v1.2.3 From f8398bea9f3b5e7ccd1ba4134fa75976da8b7545 Mon Sep 17 00:00:00 2001 From: Dan Engelbrecht Date: Wed, 28 Aug 2024 12:55:33 +0200 Subject: launchd plist install --- .DS_Store | Bin 0 -> 6148 bytes src/zen/cmds/service_cmd.h | 2 +- src/zenstore/cache/cachedisklayer.cpp | 4 ++-- src/zenutil/service.cpp | 19 +++++++++++++++---- 4 files changed, 18 insertions(+), 7 deletions(-) create mode 100644 .DS_Store diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 000000000..038340bdc Binary files /dev/null and b/.DS_Store differ diff --git a/src/zen/cmds/service_cmd.h b/src/zen/cmds/service_cmd.h index d61748421..15273e952 100644 --- a/src/zen/cmds/service_cmd.h +++ b/src/zen/cmds/service_cmd.h @@ -25,7 +25,7 @@ private: std::string m_Verb; // create, info, remove - std::string m_ServiceName = "Unreal Zen Storage"; + std::string m_ServiceName = "ZenServer"; cxxopts::Options m_StatusOptions{"status", "Show information about an installed zenserver service"}; diff --git a/src/zenstore/cache/cachedisklayer.cpp b/src/zenstore/cache/cachedisklayer.cpp index 110acba9e..21ccf2ec8 100644 --- a/src/zenstore/cache/cachedisklayer.cpp +++ b/src/zenstore/cache/cachedisklayer.cpp @@ -532,8 +532,8 @@ BucketManifestSerializer::ReadSidecarFile(RwLock::ExclusiveLockScope& B auto InvalidGuard = MakeGuard([&] { ZEN_WARN("skipping invalid sidecar file '{}'", SidecarPath); }); if (FileSize < sizeof(BucketMetaHeader)) - { - ZEN_WARN("Failed to read sidecar file '{}'. Minimum size {} expected, actual size: ", + { + ZEN_WARN("Failed to read sidecar file '{}'. Minimum size {} expected, actual size: ", SidecarPath, sizeof(BucketMetaHeader), FileSize); diff --git a/src/zenutil/service.cpp b/src/zenutil/service.cpp index cce154988..8ac35d969 100644 --- a/src/zenutil/service.cpp +++ b/src/zenutil/service.cpp @@ -11,6 +11,9 @@ #endif #if ZEN_PLATFORM_MAC # include + +# include +# include #endif namespace zen { @@ -137,9 +140,9 @@ namespace { ExtendableStringBuilder<256> ProgramArguments; for (const std::string_view Argument : Arguments) { - ProgramArguments.Append("\n"); + ProgramArguments.Append(" "); AppendEscaped(Argument, ProgramArguments); - ProgramArguments.Append("\n\n"); + ProgramArguments.Append("\n"); } return fmt::format( @@ -152,7 +155,7 @@ namespace { " ProgramArguments\n" " \n" " {}\n" // Program name - " {}" // "arg\n" * number of arguments + "{}" // "arg\n" * number of arguments " \n" " KeepAlive\n" " \n" @@ -526,13 +529,21 @@ InstallService(const std::filesystem::path& ExecutablePath, std::string_view ServiceDescription) { std::filesystem::path SymLink = std::filesystem::path("/usr/local/libexec") / ExecutablePath.filename(); + ZEN_INFO("Attempting synlink from {} to {}", ExecutablePath.string(), SymLink.string()); 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())); + std::filesystem::path PListPath = std::filesystem::path("/Library/LaunchDaemons") / (DaemonName + ".plist"); + ZEN_INFO("Writing launchd plist to {}", PListPath.string()); + zen::WriteFile(PListPath, IoBuffer(IoBuffer::Wrap, PList.data(), PList.size())); + ZEN_INFO("Changing permissions to 0555 for {}", PListPath.string()); + if (chmod(PListPath.c_str(), 0555) == -1) + { + return MakeErrorCodeFromLastError(); + } return {}; } -- cgit v1.2.3 From bb7bc2a3c26807aa810c3e1a9007c0ffc7fdfbe4 Mon Sep 17 00:00:00 2001 From: Dan Engelbrecht Date: Wed, 28 Aug 2024 12:56:18 +0200 Subject: clang format --- src/zenstore/cache/cachedisklayer.cpp | 4 ++-- src/zenutil/service.cpp | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/zenstore/cache/cachedisklayer.cpp b/src/zenstore/cache/cachedisklayer.cpp index 21ccf2ec8..110acba9e 100644 --- a/src/zenstore/cache/cachedisklayer.cpp +++ b/src/zenstore/cache/cachedisklayer.cpp @@ -532,8 +532,8 @@ BucketManifestSerializer::ReadSidecarFile(RwLock::ExclusiveLockScope& B auto InvalidGuard = MakeGuard([&] { ZEN_WARN("skipping invalid sidecar file '{}'", SidecarPath); }); if (FileSize < sizeof(BucketMetaHeader)) - { - ZEN_WARN("Failed to read sidecar file '{}'. Minimum size {} expected, actual size: ", + { + ZEN_WARN("Failed to read sidecar file '{}'. Minimum size {} expected, actual size: ", SidecarPath, sizeof(BucketMetaHeader), FileSize); diff --git a/src/zenutil/service.cpp b/src/zenutil/service.cpp index 8ac35d969..637974c13 100644 --- a/src/zenutil/service.cpp +++ b/src/zenutil/service.cpp @@ -155,7 +155,7 @@ namespace { " ProgramArguments\n" " \n" " {}\n" // Program name - "{}" // "arg\n" * number of arguments + "{}" // "arg\n" * number of arguments " \n" " KeepAlive\n" " \n" @@ -534,8 +534,8 @@ InstallService(const std::filesystem::path& ExecutablePath, { return MakeErrorCodeFromLastError(); } - std::string DaemonName = fmt::format("com.epicgames.unreal.{}", ServiceName); - std::string PList = BuildPlist(ExecutablePath, CommandLineOptions, DaemonName, ServiceDisplayName, ServiceDescription, true); + std::string DaemonName = fmt::format("com.epicgames.unreal.{}", ServiceName); + std::string PList = BuildPlist(ExecutablePath, CommandLineOptions, DaemonName, ServiceDisplayName, ServiceDescription, true); std::filesystem::path PListPath = std::filesystem::path("/Library/LaunchDaemons") / (DaemonName + ".plist"); ZEN_INFO("Writing launchd plist to {}", PListPath.string()); zen::WriteFile(PListPath, IoBuffer(IoBuffer::Wrap, PList.data(), PList.size())); -- cgit v1.2.3 From 5ad925a8523ce6cd06e43e00ba44611f95d741f8 Mon Sep 17 00:00:00 2001 From: Dan Engelbrecht Date: Wed, 28 Aug 2024 14:40:03 +0200 Subject: Add ServiceSpec struct --- src/zen/cmds/service_cmd.cpp | 26 ++++++------ src/zenutil/include/zenutil/service.h | 30 ++++++++------ src/zenutil/service.cpp | 74 +++++++++++++++++++++++------------ 3 files changed, 79 insertions(+), 51 deletions(-) diff --git a/src/zen/cmds/service_cmd.cpp b/src/zen/cmds/service_cmd.cpp index a93bac37c..51b41f2a0 100644 --- a/src/zen/cmds/service_cmd.cpp +++ b/src/zen/cmds/service_cmd.cpp @@ -211,15 +211,17 @@ ServiceCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) } ZEN_CONSOLE( "Service '{}':\n" - " Status: {}\n" - " Executable: '{}'\n" - " Display Name: '{}'\n" - " Description: '{}'", + " Status: {}\n" + " Executable: {}\n" + " CommandLineOptions: {}\n" + " Display Name: {}\n" + " Description: {}", m_ServiceName, ToString(Info.Status), - Info.ExecutablePath, - Info.DisplayName, - Info.Description); + Info.Spec.ExecutablePath, + Info.Spec.CommandLineOptions, + Info.Spec.DisplayName, + Info.Spec.Description); } if (SubOption == &m_InstallOptions) @@ -238,11 +240,11 @@ ServiceCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) } m_ServerExecutable = std::filesystem::absolute(m_ServerExecutable); - std::error_code Ec = InstallService(m_ServerExecutable, - GlobalOptions.PassthroughCommandLine, - m_ServiceName, - m_ServiceDisplayName, - m_ServiceDescription); + std::error_code Ec = InstallService(m_ServiceName, + ServiceSpec{.ExecutablePath = m_ServerExecutable, + .CommandLineOptions = GlobalOptions.PassthroughCommandLine, + .DisplayName = m_ServiceDisplayName, + .Description = m_ServiceDescription}); if (Ec) { ZEN_CONSOLE("Failed to install service '{}' using '{}' . Reason: '{}'", m_ServiceName, m_ServerExecutable, Ec.message()); diff --git a/src/zenutil/include/zenutil/service.h b/src/zenutil/include/zenutil/service.h index 79be16052..cd769b67b 100644 --- a/src/zenutil/include/zenutil/service.h +++ b/src/zenutil/include/zenutil/service.h @@ -5,6 +5,15 @@ #include namespace zen { + +struct ServiceSpec +{ + std::filesystem::path ExecutablePath; + std::string CommandLineOptions; + std::string DisplayName; + std::string Description; +}; + enum class ServiceStatus { NotInstalled, @@ -17,22 +26,17 @@ enum class ServiceStatus Resuming }; -std::string_view ToString(ServiceStatus Status); - -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::error_code UninstallService(std::string_view ServiceName); struct ServiceInfo { - ServiceStatus Status; - std::filesystem::path ExecutablePath; - std::string DisplayName; - std::string Description; + ServiceStatus Status; + ServiceSpec Spec; }; -std::error_code QueryInstalledService(std::string_view ServiceName, ServiceInfo& OutStatus); + +std::string_view ToString(ServiceStatus Status); + +std::error_code InstallService(std::string_view ServiceName, const ServiceSpec& Spec); +std::error_code UninstallService(std::string_view ServiceName); +std::error_code QueryInstalledService(std::string_view ServiceName, ServiceInfo& OutInfo); std::error_code StartService(std::string_view ServiceName); std::error_code StopService(std::string_view ServiceName); } // namespace zen diff --git a/src/zenutil/service.cpp b/src/zenutil/service.cpp index 637974c13..870a47df5 100644 --- a/src/zenutil/service.cpp +++ b/src/zenutil/service.cpp @@ -219,11 +219,7 @@ ToString(ServiceStatus Status) #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) +InstallService(std::string_view ServiceName, const ServiceSpec& Spec) { // Get a handle to the SCM database. @@ -244,14 +240,14 @@ InstallService(const std::filesystem::path& ExecutablePath, Utf8ToWide(ServiceName, Name); ExtendableWideStringBuilder<128> DisplayName; - Utf8ToWide(ServiceDisplayName, DisplayName); + Utf8ToWide(Spec.DisplayName, DisplayName); ExtendableWideStringBuilder<128> Path; - Path.Append(ExecutablePath.c_str()); - if (!CommandLineOptions.empty()) + Path.Append(Spec.ExecutablePath.c_str()); + if (!Spec.CommandLineOptions.empty()) { Path.AppendAscii(" "); - Utf8ToWide(CommandLineOptions, Path); + Utf8ToWide(Spec.CommandLineOptions, Path); } SC_HANDLE schService = CreateService(schSCManager, // SCM database @@ -273,10 +269,10 @@ InstallService(const std::filesystem::path& ExecutablePath, return MakeErrorCodeFromLastError(); } - if (!ServiceDescription.empty()) + if (!Spec.Description.empty()) { ExtendableWideStringBuilder<128> DescriptionBuilder; - Utf8ToWide(ServiceDescription, DescriptionBuilder); + Utf8ToWide(Spec.Description, DescriptionBuilder); SERVICE_DESCRIPTION Description; Description.lpDescription = const_cast(DescriptionBuilder.c_str()); @@ -380,8 +376,38 @@ QueryInstalledService(std::string_view ServiceName, ServiceInfo& OutInfo) return MakeErrorCodeFromLastError(); } - OutInfo.ExecutablePath = std::filesystem::path(ServiceConfig->lpBinaryPathName); - OutInfo.DisplayName = WideToUtf8(ServiceConfig->lpDisplayName); + std::wstring BinaryWithArguments(ServiceConfig->lpBinaryPathName); + if (BinaryWithArguments.size()) + { + if (BinaryWithArguments[0] == '"') + { + std::wstring::size_type ExecutableEnd = BinaryWithArguments.find('"', 1); + if (ExecutableEnd == std::wstring::npos) + { + OutInfo.Spec.ExecutablePath = BinaryWithArguments; + } + else + { + OutInfo.Spec.ExecutablePath = BinaryWithArguments.substr(0, ExecutableEnd + 1); + OutInfo.Spec.CommandLineOptions = + WideToUtf8(BinaryWithArguments.substr(ExecutableEnd + 1 + BinaryWithArguments[ExecutableEnd + 1] == ' ' ? 1 : 0)); + } + } + else + { + std::wstring::size_type ExecutableEnd = BinaryWithArguments.find(' ', 1); + if (ExecutableEnd == std::wstring::npos) + { + OutInfo.Spec.ExecutablePath = BinaryWithArguments; + } + else + { + OutInfo.Spec.ExecutablePath = BinaryWithArguments.substr(0, ExecutableEnd); + OutInfo.Spec.CommandLineOptions = WideToUtf8(BinaryWithArguments.substr(ExecutableEnd + 1)); + } + } + } + OutInfo.Spec.DisplayName = WideToUtf8(ServiceConfig->lpDisplayName); SERVICE_STATUS ServiceStatus; if (!::QueryServiceStatus(schService, &ServiceStatus)) @@ -435,7 +461,7 @@ QueryInstalledService(std::string_view ServiceName, ServiceInfo& OutInfo) SERVICE_DESCRIPTION* Description = (SERVICE_DESCRIPTION*)Buffer.data(); if (Description->lpDescription != NULL) { - OutInfo.Description = WideToUtf8(std::wstring(Description->lpDescription)); + OutInfo.Spec.Description = WideToUtf8(std::wstring(Description->lpDescription)); } return {}; @@ -522,20 +548,16 @@ StopService(std::string_view ServiceName) } #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) +InstallService(std::string_view ServiceName, const ServiceSpec& Spec) { - std::filesystem::path SymLink = std::filesystem::path("/usr/local/libexec") / ExecutablePath.filename(); - ZEN_INFO("Attempting synlink from {} to {}", ExecutablePath.string(), SymLink.string()); - if (symlink(ExecutablePath.c_str(), SymLink.c_str()) == -1) + std::filesystem::path SymLink = std::filesystem::path("/usr/local/libexec") / Spec.ExecutablePath.filename(); + ZEN_INFO("Attempting symlink from {} to {}", Spec.ExecutablePath.string(), SymLink.string()); + if (symlink(Spec.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); + std::string DaemonName = fmt::format("com.epicgames.unreal.{}", ServiceName); + std::string PList = BuildPlist(Spec.ExecutablePath, Spec.CommandLineOptions, DaemonName, Spec.DisplayName, Spec.Description, true); std::filesystem::path PListPath = std::filesystem::path("/Library/LaunchDaemons") / (DaemonName + ".plist"); ZEN_INFO("Writing launchd plist to {}", PListPath.string()); zen::WriteFile(PListPath, IoBuffer(IoBuffer::Wrap, PList.data(), PList.size())); @@ -556,9 +578,9 @@ UninstallService(std::string_view ServiceName) } std::error_code -QueryInstalledService(std::string_view ServiceName, ServiceInfo& OutStatus) +QueryInstalledService(std::string_view ServiceName, ServiceInfo& OutInfo) { - ZEN_UNUSED(ServiceName, OutStatus); + ZEN_UNUSED(ServiceName, OutInfo); ZEN_NOT_IMPLEMENTED("QueryInstalledService"); return {}; } -- cgit v1.2.3 From 368fb049a4e66b8a6dc61938558d0865e8f88c66 Mon Sep 17 00:00:00 2001 From: Dan Engelbrecht Date: Wed, 18 Sep 2024 10:43:26 +0200 Subject: wip --- src/zenutil/service.cpp | 142 ++++++++++++++++++++++++++++++++++++------------ 1 file changed, 108 insertions(+), 34 deletions(-) diff --git a/src/zenutil/service.cpp b/src/zenutil/service.cpp index 870a47df5..1637b9394 100644 --- a/src/zenutil/service.cpp +++ b/src/zenutil/service.cpp @@ -129,9 +129,10 @@ namespace { } } - std::string BuildPlist(const std::filesystem::path& ExecutablePath, + std::string BuildPlist(std::string_view ServiceName, + const std::filesystem::path& ExecutablePath, std::string_view CommandLineOptions, - std::string_view ServiceName, + std::string_view DaemonName, std::string_view /*ServiceDisplayName*/, std::string_view /*ServiceDescription*/, bool Debug) @@ -140,7 +141,7 @@ namespace { ExtendableStringBuilder<256> ProgramArguments; for (const std::string_view Argument : Arguments) { - ProgramArguments.Append(" "); + ProgramArguments.Append(" "); AppendEscaped(Argument, ProgramArguments); ProgramArguments.Append("\n"); } @@ -149,27 +150,38 @@ namespace { "\n" "\n" "\n" - " \n" - " Label\n" - " {}\n" // ServiceName - " ProgramArguments\n" - " \n" - " {}\n" // Program name + "\n" + " Label\n" + " {}\n" // DaemonName + " \n" + " ProgramArguments\n" + " \n" + " {}\n" // Program name "{}" // "arg\n" * number of arguments - " \n" - " KeepAlive\n" - " \n" - " StandardOutPath\n" - " /var/log/myjob.log\n" - " StandardErrorPath\n" - " /var/log/myjob.log\n" - " Debug\n" - " <{}/>\n" - " \n" + " \n" + " \n" + " RunAtLoad\n" + " \n" + " \n" +// " KeepAlive\n" +// " \n" +// " \n" + " StandardOutPath\n" + " /var/log/{}.log\n" + " \n" + " StandardErrorPath\n" + " /var/log/{}.err.log\n" + " \n" + " Debug\n" + " <{}/>\n" + " \n" + "\n" "\n", - ServiceName, - ExecutablePath.filename().string(), + DaemonName, + ExecutablePath.string(), ProgramArguments.ToView(), + ServiceName, + ServiceName, Debug ? "true"sv : "false"sv); // "Sockets" @@ -547,25 +559,73 @@ StopService(std::string_view ServiceName) return {}; } #else + +#if 0 +static int CopyFile(std::filesystem::path source, std::filesystem::path dest) +{ + + int childExitStatus; + pid_t pid; + int status; + + pid = fork(); + + if (pid == 0) { /* child */ + execl("/bin/cp", "/bin/cp", source.string().c_str(), dest.string().c_str(), (char *)0); + return 0; + } + else if (pid < 0) { + return -1; + } + else { + /* parent - wait for child - this has all error handling, you + * could just call wait() as long as you are only expecting to + * have one child process at a time. + */ + pid_t ws = waitpid( pid, &childExitStatus, WNOHANG); + if (ws == -1) + { + return -1; + } + + if( WIFEXITED(childExitStatus)) /* exit code in childExitStatus */ + { + status = WEXITSTATUS(childExitStatus); /* zero is normal exit */ + return status; + } + else if (WIFSIGNALED(childExitStatus)) /* killed */ + { + return -1; + } + else if (WIFSTOPPED(childExitStatus)) /* stopped */ + { + return -1; + } + return -1; + } +} +#endif + std::error_code InstallService(std::string_view ServiceName, const ServiceSpec& Spec) { - std::filesystem::path SymLink = std::filesystem::path("/usr/local/libexec") / Spec.ExecutablePath.filename(); - ZEN_INFO("Attempting symlink from {} to {}", Spec.ExecutablePath.string(), SymLink.string()); - if (symlink(Spec.ExecutablePath.c_str(), SymLink.c_str()) == -1) - { - return MakeErrorCodeFromLastError(); - } +// std::filesystem::path ServicePath = std::filesystem::path("/usr/local/libexec") / ServiceName; +// ZEN_INFO("Attempting to copy service from {} to {}", Spec.ExecutablePath.string(), ServicePath.string()); +// if (false && CopyFile(Spec.ExecutablePath, ServicePath) == -1) +//// if (symlink(Spec.ExecutablePath.c_str(), ServicePath.c_str()) == -1) +// { +// return MakeErrorCodeFromLastError(); +// } std::string DaemonName = fmt::format("com.epicgames.unreal.{}", ServiceName); - std::string PList = BuildPlist(Spec.ExecutablePath, Spec.CommandLineOptions, DaemonName, Spec.DisplayName, Spec.Description, true); - std::filesystem::path PListPath = std::filesystem::path("/Library/LaunchDaemons") / (DaemonName + ".plist"); + std::string PList = BuildPlist(ServiceName, Spec.ExecutablePath, Spec.CommandLineOptions, DaemonName, Spec.DisplayName, Spec.Description, true); + std::filesystem::path PListPath = std::filesystem::path("/Users/dan.engelbrecht/Library/LaunchAgents") / (DaemonName + ".plist"); ZEN_INFO("Writing launchd plist to {}", PListPath.string()); zen::WriteFile(PListPath, IoBuffer(IoBuffer::Wrap, PList.data(), PList.size())); ZEN_INFO("Changing permissions to 0555 for {}", PListPath.string()); - if (chmod(PListPath.c_str(), 0555) == -1) - { - return MakeErrorCodeFromLastError(); - } +// if (chmod(PListPath.string().c_str(), 0555) == -1) +// { +// return MakeErrorCodeFromLastError(); +// } return {}; } @@ -573,7 +633,21 @@ std::error_code UninstallService(std::string_view ServiceName) { ZEN_UNUSED(ServiceName); - ZEN_NOT_IMPLEMENTED("UninstallService"); + + std::filesystem::path ServicePath = std::filesystem::path("/usr/local/libexec") / ServiceName; + ZEN_INFO("Attempting to remove service from {}", ServicePath.string()); + if (unlink(ServicePath.string().c_str()) == -1) + { + return MakeErrorCodeFromLastError();; + } + std::string DaemonName = fmt::format("com.epicgames.unreal.{}", ServiceName); + std::filesystem::path PListPath = std::filesystem::path("/Library/LaunchDaemons") / (DaemonName + ".plist"); + ZEN_INFO("Attempting to remove launchd plist from {}", PListPath.string()); + if (unlink(ServicePath.string().c_str()) == -1) + { + return MakeErrorCodeFromLastError();; + } + return {}; } -- cgit v1.2.3 From 1747a513dc9dccd5a60e76daebd59cfba4a536d9 Mon Sep 17 00:00:00 2001 From: Dan Engelbrecht Date: Tue, 27 Aug 2024 17:09:55 +0200 Subject: zen `service` command --- src/zen/cmds/service_cmd.cpp | 308 ++++++++++++++++++++++++++ src/zen/cmds/service_cmd.h | 48 +++++ src/zen/zen.cpp | 3 + src/zenutil/include/zenutil/service.h | 38 ++++ src/zenutil/service.cpp | 393 ++++++++++++++++++++++++++++++++++ 5 files changed, 790 insertions(+) create mode 100644 src/zen/cmds/service_cmd.cpp create mode 100644 src/zen/cmds/service_cmd.h create mode 100644 src/zenutil/include/zenutil/service.h create mode 100644 src/zenutil/service.cpp diff --git a/src/zen/cmds/service_cmd.cpp b/src/zen/cmds/service_cmd.cpp new file mode 100644 index 000000000..a93bac37c --- /dev/null +++ b/src/zen/cmds/service_cmd.cpp @@ -0,0 +1,308 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +#include "service_cmd.h" + +#include +#include +#include +#include +#include + +#if ZEN_PLATFORM_WINDOWS +# include +# include +#endif + +ZEN_THIRD_PARTY_INCLUDES_START +#include +ZEN_THIRD_PARTY_INCLUDES_END + +#include + +namespace zen { + +////////////////////////////////////////////////////////////////////////// +#if ZEN_PLATFORM_WINDOWS +BOOL +WinIsElevated() +{ + BOOL fRet = FALSE; + HANDLE hToken = NULL; + if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken)) + { + TOKEN_ELEVATION Elevation; + DWORD cbSize = sizeof(TOKEN_ELEVATION); + if (GetTokenInformation(hToken, TokenElevation, &Elevation, sizeof(Elevation), &cbSize)) + { + fRet = Elevation.TokenIsElevated; + } + } + if (hToken) + { + CloseHandle(hToken); + } + return fRet; +} + +int +WinRelaunchElevated() +{ + TCHAR CurrentDir[4096]; + GetCurrentDirectory(4096, CurrentDir); + + ExtendableWideStringBuilder<256> Parameters; + std::filesystem::path ExecutablePath = GetRunningExecutablePath(); + std::wstring CommandLine(GetCommandLine()); + std::string::size_type ExtraLength = CommandLine[0] == '\"' ? 2 : 0; + std::wstring CommandArguments = CommandLine.substr(ExecutablePath.string().length() + ExtraLength + 1); + + ZEN_CONSOLE("Attempting to run '{} {}' elevated...", ExecutablePath, WideToUtf8(CommandArguments)); + + SHELLEXECUTEINFO shExInfo = {0}; + shExInfo.cbSize = sizeof(shExInfo); + shExInfo.fMask = SEE_MASK_NOCLOSEPROCESS | SEE_MASK_NO_CONSOLE; + shExInfo.hwnd = 0; + shExInfo.lpVerb = TEXT("runas"); // Operation to perform + shExInfo.lpFile = ExecutablePath.c_str(); // Application to start + shExInfo.lpParameters = CommandArguments.c_str(); // Additional parameters + shExInfo.lpDirectory = CurrentDir; + shExInfo.nShow = SW_HIDE; + shExInfo.hInstApp = 0; + + DWORD ReturnCode = 1; + if (ShellExecuteEx(&shExInfo)) + { + WaitForSingleObject(shExInfo.hProcess, INFINITE); + GetExitCodeProcess(shExInfo.hProcess, &ReturnCode); + CloseHandle(shExInfo.hProcess); + if (ReturnCode == 0) + { + ZEN_CONSOLE("Elevated execution completed successfully."); + } + else + { + ZEN_CONSOLE("Elevated execution completed unsuccessfully, return code: '{}'.", ReturnCode); + } + } + else + { + ZEN_CONSOLE("Failed to elevated, operation did not complete."); + } + return (int)ReturnCode; +} + +#endif + +ServiceCommand::ServiceCommand() +{ + m_Options.add_option("", + "v", + "verb", + fmt::format("Verb for service - {}, {}, {}, {}, {}. Use '--' ", + m_StatusOptions.program(), + m_InstallOptions.program(), + m_UninstallOptions.program(), + m_StartOptions.program(), + m_StopOptions.program()), + cxxopts::value(m_Verb), + ""); + m_Options.parse_positional({"verb"}); + m_Options.positional_help("verb"); + + m_StatusOptions.add_options()("h,help", "Print help"); + m_StatusOptions.add_option("", + "n", + "name", + fmt::format("Service name, defaults to \"{}\"", m_ServiceName), + cxxopts::value(m_ServiceName), + ""); + m_StatusOptions.parse_positional({"name"}); + m_StatusOptions.positional_help("name"); + + m_InstallOptions.add_options()("h,help", "Print help"); + m_InstallOptions.add_option("", "s", "executable", "Path to server executable", cxxopts::value(m_ServerExecutable), ""); + m_InstallOptions.add_option("", + "n", + "name", + fmt::format("Service name, defaults to \"{}\"", m_ServiceName), + cxxopts::value(m_ServiceName), + ""); + m_InstallOptions.add_option("", + "d", + "display-name", + fmt::format("Service display, defaults to \"{}\"", m_ServiceDisplayName), + cxxopts::value(m_ServiceDisplayName), + ""); + m_InstallOptions.add_option("", + "", + "description", + fmt::format("Service description", m_ServiceDescription), + cxxopts::value(m_ServiceDescription), + ""); + m_InstallOptions.parse_positional({"executable", "name", "display-name"}); + m_InstallOptions.positional_help("executable name display-name"); + + m_UninstallOptions.add_options()("h,help", "Print help"); + m_UninstallOptions.add_option("", + "n", + "name", + fmt::format("Service name, defaults to \"{}\"", m_ServiceName), + cxxopts::value(m_ServiceName), + ""); + m_UninstallOptions.parse_positional({"name"}); + m_UninstallOptions.positional_help("name"); + + m_StartOptions.add_options()("h,help", "Print help"); + m_StartOptions.add_option("", + "n", + "name", + fmt::format("Service name, defaults to \"{}\"", m_ServiceName), + cxxopts::value(m_ServiceName), + ""); + m_StartOptions.parse_positional({"name"}); + m_StartOptions.positional_help("name"); + + m_StopOptions.add_options()("h,help", "Print help"); + m_StopOptions.add_option("", + "n", + "name", + fmt::format("Service name, defaults to \"{}\"", m_ServiceName), + cxxopts::value(m_ServiceName), + ""); + m_StopOptions.parse_positional({"name"}); + m_StopOptions.positional_help("name"); +} + +ServiceCommand::~ServiceCommand() = default; + +int +ServiceCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) +{ + ZEN_UNUSED(GlobalOptions); + + using namespace std::literals; + + std::vector SubCommandArguments; + cxxopts::Options* SubOption = nullptr; + int ParentCommandArgCount = GetSubCommand(m_Options, argc, argv, m_SubCommands, SubOption, SubCommandArguments); + if (!ParseOptions(ParentCommandArgCount, argv)) + { + return 0; + } + + if (SubOption == nullptr) + { + throw zen::OptionParseException("command verb is missing"); + } + + if (!ParseOptions(*SubOption, gsl::narrow(SubCommandArguments.size()), SubCommandArguments.data())) + { + return 0; + } + + if (SubOption == &m_StatusOptions) + { + ServiceInfo Info; + std::error_code Ec = QueryInstalledService(m_ServiceName, Info); + if (Ec) + { + ZEN_CONSOLE("Can't get information about service '{}'. Reason: '{}'", m_ServiceName, Ec.message()); + return 1; + } + ZEN_CONSOLE( + "Service '{}':\n" + " Status: {}\n" + " Executable: '{}'\n" + " Display Name: '{}'\n" + " Description: '{}'", + m_ServiceName, + ToString(Info.Status), + Info.ExecutablePath, + Info.DisplayName, + Info.Description); + } + + if (SubOption == &m_InstallOptions) + { +#if ZEN_PLATFORM_WINDOWS + if (!WinIsElevated()) + { + return WinRelaunchElevated(); + } +#endif // ZEN_PLATFORM_WINDOWS + if (m_ServerExecutable.empty()) + { + std::filesystem::path ExePath = zen::GetRunningExecutablePath(); + ExePath.replace_filename("zenserver" + ExePath.extension().string()); + m_ServerExecutable = ExePath; + } + m_ServerExecutable = std::filesystem::absolute(m_ServerExecutable); + + std::error_code Ec = InstallService(m_ServerExecutable, + GlobalOptions.PassthroughCommandLine, + m_ServiceName, + m_ServiceDisplayName, + m_ServiceDescription); + if (Ec) + { + ZEN_CONSOLE("Failed to install service '{}' using '{}' . Reason: '{}'", m_ServiceName, m_ServerExecutable, Ec.message()); + return 1; + } + ZEN_CONSOLE("Installed service '{}' using '{}' successfully", m_ServiceName, m_ServerExecutable); + } + + if (SubOption == &m_UninstallOptions) + { +#if ZEN_PLATFORM_WINDOWS + if (!WinIsElevated()) + { + return WinRelaunchElevated(); + } +#endif // ZEN_PLATFORM_WINDOWS + std::error_code Ec = UninstallService(m_ServiceName); + if (Ec) + { + ZEN_CONSOLE("Failed to uninstall service '{}'. Reason: '{}'", m_ServiceName, Ec.message()); + return 1; + } + ZEN_CONSOLE("Uninstalled service {} successfully", m_ServiceName); + } + + if (SubOption == &m_StartOptions) + { +#if ZEN_PLATFORM_WINDOWS + if (!WinIsElevated()) + { + return WinRelaunchElevated(); + } +#endif // ZEN_PLATFORM_WINDOWS + std::error_code Ec = StartService(m_ServiceName); + if (Ec) + { + ZEN_CONSOLE("Failed to start service '{}'. Reason: '{}'", m_ServiceName, Ec.message()); + return 1; + } + ZEN_CONSOLE("Started service '{}' successfully", m_ServiceName); + } + + if (SubOption == &m_StopOptions) + { +#if ZEN_PLATFORM_WINDOWS + if (!WinIsElevated()) + { + return WinRelaunchElevated(); + } +#endif // ZEN_PLATFORM_WINDOWS + std::error_code Ec = StopService(m_ServiceName); + if (Ec) + { + ZEN_CONSOLE("Failed to stop service '{}'. Reason: '{}'", m_ServiceName, Ec.message()); + return 1; + } + ZEN_CONSOLE("Stopped service '{}' successfully", m_ServiceName); + } + + return 0; +} + +} // namespace zen diff --git a/src/zen/cmds/service_cmd.h b/src/zen/cmds/service_cmd.h new file mode 100644 index 000000000..d61748421 --- /dev/null +++ b/src/zen/cmds/service_cmd.h @@ -0,0 +1,48 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +#pragma once + +#include "../zen.h" + +#include + +namespace zen { + +class ServiceCommand : public ZenCmdBase +{ +public: + static constexpr char Name[] = "service"; + static constexpr char Description[] = "Manage zenserver as a service - status, install, uninstall, start and stop"; + + ServiceCommand(); + ~ServiceCommand(); + + virtual int Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) override; + virtual cxxopts::Options& Options() override { return m_Options; } + +private: + cxxopts::Options m_Options{Name, Description}; + + std::string m_Verb; // create, info, remove + + std::string m_ServiceName = "Unreal Zen Storage"; + + cxxopts::Options m_StatusOptions{"status", "Show information about an installed zenserver service"}; + + cxxopts::Options m_InstallOptions{ + "install", + "Install zenserver as a service. Arguments following \" -- \" will be added as parameters to the installed service."}; + std::filesystem::path m_ServerExecutable; + std::string m_ServiceDisplayName = "Unreal Zen Storage Service"; + std::string m_ServiceDescription; + + cxxopts::Options m_UninstallOptions{"uninstall", "Uninstall zenserver as a service"}; + + cxxopts::Options m_StartOptions{"start", "Start an installed zenserver service"}; + + cxxopts::Options m_StopOptions{"stop", "Start an installed zenserver service"}; + + cxxopts::Options* m_SubCommands[5] = {&m_StatusOptions, &m_InstallOptions, &m_UninstallOptions, &m_StartOptions, &m_StopOptions}; +}; + +} // namespace zen diff --git a/src/zen/zen.cpp b/src/zen/zen.cpp index fd58b024a..f64bc96a8 100644 --- a/src/zen/zen.cpp +++ b/src/zen/zen.cpp @@ -16,6 +16,7 @@ #include "cmds/rpcreplay_cmd.h" #include "cmds/run_cmd.h" #include "cmds/serve_cmd.h" +#include "cmds/service_cmd.h" #include "cmds/status_cmd.h" #include "cmds/top_cmd.h" #include "cmds/trace_cmd.h" @@ -429,6 +430,7 @@ main(int argc, char** argv) VfsCommand VfsCmd; WorkspaceCommand WorkspaceCmd; WorkspaceShareCommand WorkspaceShareCmd; + ServiceCommand ServiceCmd; const struct CommandInfo { @@ -484,6 +486,7 @@ main(int argc, char** argv) {"flush", &FlushCmd, "Flush storage"}, {WorkspaceCommand::Name, &WorkspaceCmd, WorkspaceCommand::Description}, {WorkspaceShareCommand::Name, &WorkspaceShareCmd, WorkspaceShareCommand::Description}, + {ServiceCommand::Name, &ServiceCmd, ServiceCommand::Description}, // clang-format on }; diff --git a/src/zenutil/include/zenutil/service.h b/src/zenutil/include/zenutil/service.h new file mode 100644 index 000000000..79be16052 --- /dev/null +++ b/src/zenutil/include/zenutil/service.h @@ -0,0 +1,38 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +#pragma once + +#include + +namespace zen { +enum class ServiceStatus +{ + NotInstalled, + Starting, + Running, + Stopping, + Stopped, + Pausing, + Paused, + Resuming +}; + +std::string_view ToString(ServiceStatus Status); + +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::error_code UninstallService(std::string_view ServiceName); +struct ServiceInfo +{ + ServiceStatus Status; + std::filesystem::path ExecutablePath; + std::string DisplayName; + std::string Description; +}; +std::error_code QueryInstalledService(std::string_view ServiceName, ServiceInfo& OutStatus); +std::error_code StartService(std::string_view ServiceName); +std::error_code StopService(std::string_view ServiceName); +} // namespace zen diff --git a/src/zenutil/service.cpp b/src/zenutil/service.cpp new file mode 100644 index 000000000..840fe3104 --- /dev/null +++ b/src/zenutil/service.cpp @@ -0,0 +1,393 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +#include + +#include +#include +#include + +#if ZEN_PLATFORM_WINDOWS +# include +#endif + +namespace zen { +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; + } +} + +#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) +{ + // 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(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 {}; +} + +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 Buffer(8192); + QUERY_SERVICE_CONFIG* ServiceConfig = reinterpret_cast(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 {}; +} + +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 {}; +} +#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) +{ + 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 {}; +} + +std::error_code +QueryInstalledService(std::string_view ServiceName, ServiceInfo& OutStatus) +{ + 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 {}; +} + +std::error_code +StopService(std::string_view ServiceName) +{ + ZEN_UNUSED(ServiceName); + ZEN_NOT_IMPLEMENTED("StopService"); + return {}; +} + +#endif +} // namespace zen -- cgit v1.2.3 From f5e723c33b7fb022a6191e05e4df98b883b0cbb4 Mon Sep 17 00:00:00 2001 From: Dan Engelbrecht Date: Wed, 28 Aug 2024 10:47:49 +0200 Subject: wip --- src/zenutil/service.cpp | 753 ++++++++++++++++++++++++++++-------------------- 1 file 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(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(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 Buffer(8192); - QUERY_SERVICE_CONFIG* ServiceConfig = reinterpret_cast(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 Buffer(8192); + QUERY_SERVICE_CONFIG* ServiceConfig = reinterpret_cast(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 = + "" + "" + "" + "" + "Label" + "{}" // ServiceName + "ProgramArguments" + "" + "{}" // Program name + "{}" // "arg\n" * number of arguments + "" + "KeepAlive" + "" +// "Sockets" +// "" +// "Listeners" +// "" +// "SockServiceName" +// "{}" // Listen socket +// "SockType" +// "tcp" +// "SockFamily" +// "IPv4" +// "" +// "" + "StandardOutPath" + "/var/log/myjob.log" + "StandardErrorPath" + "/var/log/myjob.log" + "Debug" + "<{}/>" + + "" + ""; + + 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(""); + CommandLineOptions.Append(CommandLineOptions.substr(Start, Offset - Start)); + CommandLineOptions.Append("\n"); + Start = Offset + 1; + } + break; + case '"': + if (IsQuite) + { + IsQuote = false; + if (Offset - Start > 1) + { + CommandLineOptions.Append(""); + CommandLineOptions.Append(CommandLineOptions.substr(Start + 1, (Offset - 1) - (Start + 1)); + CommandLineOptions.Append("\n"); + } + Start = Offset + 1; + } + else{ + IsQuite = true; + } + break; + default: + break; + } + } + ZEN_ASSERT(!IsQuote); + if (Offset > Start) + { + CommandLineOptions.Append(""); + CommandLineOptions.Append(CommandLineOptions.substr(Start, Offset - Start)); + CommandLineOptions.Append("\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 + -- cgit v1.2.3 From d108042c4e654cb79cbb843630c8059b823d63b7 Mon Sep 17 00:00:00 2001 From: Dan Engelbrecht Date: Wed, 28 Aug 2024 11:42:35 +0200 Subject: more WIP --- src/zenutil/service.cpp | 931 ++++++++++++++++++++++++++---------------------- 1 file 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 #endif +#if ZEN_PLATFORM_MAC +# include +#endif namespace zen { using namespace std::literals; +#if ZEN_PLATFORM_MAC + +namespace { + std::vector SplitArguments(std::string_view Arguments) + { + bool IsQuote = false; + size_t Start = 0; + size_t Offset = 0; + std::vector 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 Arguments = SplitArguments(CommandLineOptions); + ExtendableStringBuilder<256> ProgramArguments; + for (const std::string_view Argument : Arguments) + { + ProgramArguments.Append("\n"); + AppendEscaped(Argument, ProgramArguments); + ProgramArguments.Append("\n\n"); + } + + return fmt::format( + "\n" + "\n" + "\n" + " \n" + " Label\n" + " {}\n" // ServiceName + " ProgramArguments\n" + " \n" + " {}\n" // Program name + " {}" // "arg\n" * number of arguments + " \n" + " KeepAlive\n" + " \n" + " StandardOutPath\n" + " /var/log/myjob.log\n" + " StandardErrorPath\n" + " /var/log/myjob.log\n" + " Debug\n" + " <{}/>\n" + " \n" + "\n", + ServiceName, + ExecutablePath.filename().string(), + ProgramArguments.ToView(), + Debug ? "true"sv : "false"sv); + + // "Sockets" + // "" + // "Listeners" + // "" + // "SockServiceName" + // "{}" // Listen socket + // "SockType" + // "tcp" + // "SockFamily" + // "IPv4" + // "" + // "" + } +} // 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(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(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 Buffer(8192); - QUERY_SERVICE_CONFIG* ServiceConfig = reinterpret_cast(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 Buffer(8192); + QUERY_SERVICE_CONFIG* ServiceConfig = reinterpret_cast(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 = - "" - "" - "" - "" - "Label" - "{}" // ServiceName - "ProgramArguments" - "" - "{}" // Program name - "{}" // "arg\n" * number of arguments - "" - "KeepAlive" - "" -// "Sockets" -// "" -// "Listeners" -// "" -// "SockServiceName" -// "{}" // Listen socket -// "SockType" -// "tcp" -// "SockFamily" -// "IPv4" -// "" -// "" - "StandardOutPath" - "/var/log/myjob.log" - "StandardErrorPath" - "/var/log/myjob.log" - "Debug" - "<{}/>" - - "" - ""; - - 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(""); - CommandLineOptions.Append(CommandLineOptions.substr(Start, Offset - Start)); - CommandLineOptions.Append("\n"); - Start = Offset + 1; - } - break; - case '"': - if (IsQuite) - { - IsQuote = false; - if (Offset - Start > 1) - { - CommandLineOptions.Append(""); - CommandLineOptions.Append(CommandLineOptions.substr(Start + 1, (Offset - 1) - (Start + 1)); - CommandLineOptions.Append("\n"); - } - Start = Offset + 1; - } - else{ - IsQuite = true; - } - break; - default: - break; - } - } - ZEN_ASSERT(!IsQuote); - if (Offset > Start) - { - CommandLineOptions.Append(""); - CommandLineOptions.Append(CommandLineOptions.substr(Start, Offset - Start)); - CommandLineOptions.Append("\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 - -- cgit v1.2.3 From 69ddcf16ae53ded7856f2c46505d55fb44b61f11 Mon Sep 17 00:00:00 2001 From: Dan Engelbrecht Date: Wed, 28 Aug 2024 12:55:33 +0200 Subject: launchd plist install --- .DS_Store | Bin 0 -> 6148 bytes src/zen/cmds/service_cmd.h | 2 +- src/zenstore/cache/cachedisklayer.cpp | 4 ++-- src/zenutil/service.cpp | 19 +++++++++++++++---- 4 files changed, 18 insertions(+), 7 deletions(-) create mode 100644 .DS_Store diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 000000000..038340bdc Binary files /dev/null and b/.DS_Store differ diff --git a/src/zen/cmds/service_cmd.h b/src/zen/cmds/service_cmd.h index d61748421..15273e952 100644 --- a/src/zen/cmds/service_cmd.h +++ b/src/zen/cmds/service_cmd.h @@ -25,7 +25,7 @@ private: std::string m_Verb; // create, info, remove - std::string m_ServiceName = "Unreal Zen Storage"; + std::string m_ServiceName = "ZenServer"; cxxopts::Options m_StatusOptions{"status", "Show information about an installed zenserver service"}; diff --git a/src/zenstore/cache/cachedisklayer.cpp b/src/zenstore/cache/cachedisklayer.cpp index cbc1d6e83..059ab5409 100644 --- a/src/zenstore/cache/cachedisklayer.cpp +++ b/src/zenstore/cache/cachedisklayer.cpp @@ -546,8 +546,8 @@ BucketManifestSerializer::ReadSidecarFile(RwLock::ExclusiveLockScope& B auto InvalidGuard = MakeGuard([&] { ZEN_WARN("skipping invalid sidecar file '{}'", SidecarPath); }); if (FileSize < sizeof(BucketMetaHeader)) - { - ZEN_WARN("Failed to read sidecar file '{}'. Minimum size {} expected, actual size: ", + { + ZEN_WARN("Failed to read sidecar file '{}'. Minimum size {} expected, actual size: ", SidecarPath, sizeof(BucketMetaHeader), FileSize); diff --git a/src/zenutil/service.cpp b/src/zenutil/service.cpp index cce154988..8ac35d969 100644 --- a/src/zenutil/service.cpp +++ b/src/zenutil/service.cpp @@ -11,6 +11,9 @@ #endif #if ZEN_PLATFORM_MAC # include + +# include +# include #endif namespace zen { @@ -137,9 +140,9 @@ namespace { ExtendableStringBuilder<256> ProgramArguments; for (const std::string_view Argument : Arguments) { - ProgramArguments.Append("\n"); + ProgramArguments.Append(" "); AppendEscaped(Argument, ProgramArguments); - ProgramArguments.Append("\n\n"); + ProgramArguments.Append("\n"); } return fmt::format( @@ -152,7 +155,7 @@ namespace { " ProgramArguments\n" " \n" " {}\n" // Program name - " {}" // "arg\n" * number of arguments + "{}" // "arg\n" * number of arguments " \n" " KeepAlive\n" " \n" @@ -526,13 +529,21 @@ InstallService(const std::filesystem::path& ExecutablePath, std::string_view ServiceDescription) { std::filesystem::path SymLink = std::filesystem::path("/usr/local/libexec") / ExecutablePath.filename(); + ZEN_INFO("Attempting synlink from {} to {}", ExecutablePath.string(), SymLink.string()); 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())); + std::filesystem::path PListPath = std::filesystem::path("/Library/LaunchDaemons") / (DaemonName + ".plist"); + ZEN_INFO("Writing launchd plist to {}", PListPath.string()); + zen::WriteFile(PListPath, IoBuffer(IoBuffer::Wrap, PList.data(), PList.size())); + ZEN_INFO("Changing permissions to 0555 for {}", PListPath.string()); + if (chmod(PListPath.c_str(), 0555) == -1) + { + return MakeErrorCodeFromLastError(); + } return {}; } -- cgit v1.2.3 From 614ecb211ba5c630ad0a23c4127e57e1fbb93d91 Mon Sep 17 00:00:00 2001 From: Dan Engelbrecht Date: Wed, 28 Aug 2024 12:56:18 +0200 Subject: clang format --- src/zenstore/cache/cachedisklayer.cpp | 4 ++-- src/zenutil/service.cpp | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/zenstore/cache/cachedisklayer.cpp b/src/zenstore/cache/cachedisklayer.cpp index 059ab5409..cbc1d6e83 100644 --- a/src/zenstore/cache/cachedisklayer.cpp +++ b/src/zenstore/cache/cachedisklayer.cpp @@ -546,8 +546,8 @@ BucketManifestSerializer::ReadSidecarFile(RwLock::ExclusiveLockScope& B auto InvalidGuard = MakeGuard([&] { ZEN_WARN("skipping invalid sidecar file '{}'", SidecarPath); }); if (FileSize < sizeof(BucketMetaHeader)) - { - ZEN_WARN("Failed to read sidecar file '{}'. Minimum size {} expected, actual size: ", + { + ZEN_WARN("Failed to read sidecar file '{}'. Minimum size {} expected, actual size: ", SidecarPath, sizeof(BucketMetaHeader), FileSize); diff --git a/src/zenutil/service.cpp b/src/zenutil/service.cpp index 8ac35d969..637974c13 100644 --- a/src/zenutil/service.cpp +++ b/src/zenutil/service.cpp @@ -155,7 +155,7 @@ namespace { " ProgramArguments\n" " \n" " {}\n" // Program name - "{}" // "arg\n" * number of arguments + "{}" // "arg\n" * number of arguments " \n" " KeepAlive\n" " \n" @@ -534,8 +534,8 @@ InstallService(const std::filesystem::path& ExecutablePath, { return MakeErrorCodeFromLastError(); } - std::string DaemonName = fmt::format("com.epicgames.unreal.{}", ServiceName); - std::string PList = BuildPlist(ExecutablePath, CommandLineOptions, DaemonName, ServiceDisplayName, ServiceDescription, true); + std::string DaemonName = fmt::format("com.epicgames.unreal.{}", ServiceName); + std::string PList = BuildPlist(ExecutablePath, CommandLineOptions, DaemonName, ServiceDisplayName, ServiceDescription, true); std::filesystem::path PListPath = std::filesystem::path("/Library/LaunchDaemons") / (DaemonName + ".plist"); ZEN_INFO("Writing launchd plist to {}", PListPath.string()); zen::WriteFile(PListPath, IoBuffer(IoBuffer::Wrap, PList.data(), PList.size())); -- cgit v1.2.3 From 54ffc9b9ad7d3d8d32e4de00980d66d4a31f3c09 Mon Sep 17 00:00:00 2001 From: Dan Engelbrecht Date: Wed, 28 Aug 2024 14:40:03 +0200 Subject: Add ServiceSpec struct --- src/zen/cmds/service_cmd.cpp | 26 ++++++------ src/zenutil/include/zenutil/service.h | 30 ++++++++------ src/zenutil/service.cpp | 74 +++++++++++++++++++++++------------ 3 files changed, 79 insertions(+), 51 deletions(-) diff --git a/src/zen/cmds/service_cmd.cpp b/src/zen/cmds/service_cmd.cpp index a93bac37c..51b41f2a0 100644 --- a/src/zen/cmds/service_cmd.cpp +++ b/src/zen/cmds/service_cmd.cpp @@ -211,15 +211,17 @@ ServiceCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) } ZEN_CONSOLE( "Service '{}':\n" - " Status: {}\n" - " Executable: '{}'\n" - " Display Name: '{}'\n" - " Description: '{}'", + " Status: {}\n" + " Executable: {}\n" + " CommandLineOptions: {}\n" + " Display Name: {}\n" + " Description: {}", m_ServiceName, ToString(Info.Status), - Info.ExecutablePath, - Info.DisplayName, - Info.Description); + Info.Spec.ExecutablePath, + Info.Spec.CommandLineOptions, + Info.Spec.DisplayName, + Info.Spec.Description); } if (SubOption == &m_InstallOptions) @@ -238,11 +240,11 @@ ServiceCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) } m_ServerExecutable = std::filesystem::absolute(m_ServerExecutable); - std::error_code Ec = InstallService(m_ServerExecutable, - GlobalOptions.PassthroughCommandLine, - m_ServiceName, - m_ServiceDisplayName, - m_ServiceDescription); + std::error_code Ec = InstallService(m_ServiceName, + ServiceSpec{.ExecutablePath = m_ServerExecutable, + .CommandLineOptions = GlobalOptions.PassthroughCommandLine, + .DisplayName = m_ServiceDisplayName, + .Description = m_ServiceDescription}); if (Ec) { ZEN_CONSOLE("Failed to install service '{}' using '{}' . Reason: '{}'", m_ServiceName, m_ServerExecutable, Ec.message()); diff --git a/src/zenutil/include/zenutil/service.h b/src/zenutil/include/zenutil/service.h index 79be16052..cd769b67b 100644 --- a/src/zenutil/include/zenutil/service.h +++ b/src/zenutil/include/zenutil/service.h @@ -5,6 +5,15 @@ #include namespace zen { + +struct ServiceSpec +{ + std::filesystem::path ExecutablePath; + std::string CommandLineOptions; + std::string DisplayName; + std::string Description; +}; + enum class ServiceStatus { NotInstalled, @@ -17,22 +26,17 @@ enum class ServiceStatus Resuming }; -std::string_view ToString(ServiceStatus Status); - -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::error_code UninstallService(std::string_view ServiceName); struct ServiceInfo { - ServiceStatus Status; - std::filesystem::path ExecutablePath; - std::string DisplayName; - std::string Description; + ServiceStatus Status; + ServiceSpec Spec; }; -std::error_code QueryInstalledService(std::string_view ServiceName, ServiceInfo& OutStatus); + +std::string_view ToString(ServiceStatus Status); + +std::error_code InstallService(std::string_view ServiceName, const ServiceSpec& Spec); +std::error_code UninstallService(std::string_view ServiceName); +std::error_code QueryInstalledService(std::string_view ServiceName, ServiceInfo& OutInfo); std::error_code StartService(std::string_view ServiceName); std::error_code StopService(std::string_view ServiceName); } // namespace zen diff --git a/src/zenutil/service.cpp b/src/zenutil/service.cpp index 637974c13..870a47df5 100644 --- a/src/zenutil/service.cpp +++ b/src/zenutil/service.cpp @@ -219,11 +219,7 @@ ToString(ServiceStatus Status) #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) +InstallService(std::string_view ServiceName, const ServiceSpec& Spec) { // Get a handle to the SCM database. @@ -244,14 +240,14 @@ InstallService(const std::filesystem::path& ExecutablePath, Utf8ToWide(ServiceName, Name); ExtendableWideStringBuilder<128> DisplayName; - Utf8ToWide(ServiceDisplayName, DisplayName); + Utf8ToWide(Spec.DisplayName, DisplayName); ExtendableWideStringBuilder<128> Path; - Path.Append(ExecutablePath.c_str()); - if (!CommandLineOptions.empty()) + Path.Append(Spec.ExecutablePath.c_str()); + if (!Spec.CommandLineOptions.empty()) { Path.AppendAscii(" "); - Utf8ToWide(CommandLineOptions, Path); + Utf8ToWide(Spec.CommandLineOptions, Path); } SC_HANDLE schService = CreateService(schSCManager, // SCM database @@ -273,10 +269,10 @@ InstallService(const std::filesystem::path& ExecutablePath, return MakeErrorCodeFromLastError(); } - if (!ServiceDescription.empty()) + if (!Spec.Description.empty()) { ExtendableWideStringBuilder<128> DescriptionBuilder; - Utf8ToWide(ServiceDescription, DescriptionBuilder); + Utf8ToWide(Spec.Description, DescriptionBuilder); SERVICE_DESCRIPTION Description; Description.lpDescription = const_cast(DescriptionBuilder.c_str()); @@ -380,8 +376,38 @@ QueryInstalledService(std::string_view ServiceName, ServiceInfo& OutInfo) return MakeErrorCodeFromLastError(); } - OutInfo.ExecutablePath = std::filesystem::path(ServiceConfig->lpBinaryPathName); - OutInfo.DisplayName = WideToUtf8(ServiceConfig->lpDisplayName); + std::wstring BinaryWithArguments(ServiceConfig->lpBinaryPathName); + if (BinaryWithArguments.size()) + { + if (BinaryWithArguments[0] == '"') + { + std::wstring::size_type ExecutableEnd = BinaryWithArguments.find('"', 1); + if (ExecutableEnd == std::wstring::npos) + { + OutInfo.Spec.ExecutablePath = BinaryWithArguments; + } + else + { + OutInfo.Spec.ExecutablePath = BinaryWithArguments.substr(0, ExecutableEnd + 1); + OutInfo.Spec.CommandLineOptions = + WideToUtf8(BinaryWithArguments.substr(ExecutableEnd + 1 + BinaryWithArguments[ExecutableEnd + 1] == ' ' ? 1 : 0)); + } + } + else + { + std::wstring::size_type ExecutableEnd = BinaryWithArguments.find(' ', 1); + if (ExecutableEnd == std::wstring::npos) + { + OutInfo.Spec.ExecutablePath = BinaryWithArguments; + } + else + { + OutInfo.Spec.ExecutablePath = BinaryWithArguments.substr(0, ExecutableEnd); + OutInfo.Spec.CommandLineOptions = WideToUtf8(BinaryWithArguments.substr(ExecutableEnd + 1)); + } + } + } + OutInfo.Spec.DisplayName = WideToUtf8(ServiceConfig->lpDisplayName); SERVICE_STATUS ServiceStatus; if (!::QueryServiceStatus(schService, &ServiceStatus)) @@ -435,7 +461,7 @@ QueryInstalledService(std::string_view ServiceName, ServiceInfo& OutInfo) SERVICE_DESCRIPTION* Description = (SERVICE_DESCRIPTION*)Buffer.data(); if (Description->lpDescription != NULL) { - OutInfo.Description = WideToUtf8(std::wstring(Description->lpDescription)); + OutInfo.Spec.Description = WideToUtf8(std::wstring(Description->lpDescription)); } return {}; @@ -522,20 +548,16 @@ StopService(std::string_view ServiceName) } #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) +InstallService(std::string_view ServiceName, const ServiceSpec& Spec) { - std::filesystem::path SymLink = std::filesystem::path("/usr/local/libexec") / ExecutablePath.filename(); - ZEN_INFO("Attempting synlink from {} to {}", ExecutablePath.string(), SymLink.string()); - if (symlink(ExecutablePath.c_str(), SymLink.c_str()) == -1) + std::filesystem::path SymLink = std::filesystem::path("/usr/local/libexec") / Spec.ExecutablePath.filename(); + ZEN_INFO("Attempting symlink from {} to {}", Spec.ExecutablePath.string(), SymLink.string()); + if (symlink(Spec.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); + std::string DaemonName = fmt::format("com.epicgames.unreal.{}", ServiceName); + std::string PList = BuildPlist(Spec.ExecutablePath, Spec.CommandLineOptions, DaemonName, Spec.DisplayName, Spec.Description, true); std::filesystem::path PListPath = std::filesystem::path("/Library/LaunchDaemons") / (DaemonName + ".plist"); ZEN_INFO("Writing launchd plist to {}", PListPath.string()); zen::WriteFile(PListPath, IoBuffer(IoBuffer::Wrap, PList.data(), PList.size())); @@ -556,9 +578,9 @@ UninstallService(std::string_view ServiceName) } std::error_code -QueryInstalledService(std::string_view ServiceName, ServiceInfo& OutStatus) +QueryInstalledService(std::string_view ServiceName, ServiceInfo& OutInfo) { - ZEN_UNUSED(ServiceName, OutStatus); + ZEN_UNUSED(ServiceName, OutInfo); ZEN_NOT_IMPLEMENTED("QueryInstalledService"); return {}; } -- cgit v1.2.3 From a8a7155e9620ab1c02136662905f09e2fa7a6f18 Mon Sep 17 00:00:00 2001 From: Dan Engelbrecht Date: Wed, 8 Jan 2025 10:32:27 +0100 Subject: check if service is already installed before attempting install --- src/zen/cmds/service_cmd.cpp | 34 +++++++++++++++++++++++++++------- 1 file changed, 27 insertions(+), 7 deletions(-) diff --git a/src/zen/cmds/service_cmd.cpp b/src/zen/cmds/service_cmd.cpp index 51b41f2a0..aede68573 100644 --- a/src/zen/cmds/service_cmd.cpp +++ b/src/zen/cmds/service_cmd.cpp @@ -66,7 +66,7 @@ WinRelaunchElevated() shExInfo.lpFile = ExecutablePath.c_str(); // Application to start shExInfo.lpParameters = CommandArguments.c_str(); // Additional parameters shExInfo.lpDirectory = CurrentDir; - shExInfo.nShow = SW_HIDE; + shExInfo.nShow = SW_SHOW; shExInfo.hInstApp = 0; DWORD ReturnCode = 1; @@ -86,7 +86,7 @@ WinRelaunchElevated() } else { - ZEN_CONSOLE("Failed to elevated, operation did not complete."); + ZEN_CONSOLE("Failed to run elevated, operation did not complete."); } return (int)ReturnCode; } @@ -226,6 +226,26 @@ ServiceCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) if (SubOption == &m_InstallOptions) { + ServiceInfo Info; + std::error_code Ec = QueryInstalledService(m_ServiceName, Info); + if (!Ec && Info.Status != ServiceStatus::NotInstalled) + { + ZEN_CONSOLE( + "Service '{}' already installed:\n" + " Status: {}\n" + " Executable: {}\n" + " CommandLineOptions: {}\n" + " Display Name: {}\n" + " Description: {}", + m_ServiceName, + ToString(Info.Status), + Info.Spec.ExecutablePath, + Info.Spec.CommandLineOptions, + Info.Spec.DisplayName, + Info.Spec.Description); + return 1; + } + #if ZEN_PLATFORM_WINDOWS if (!WinIsElevated()) { @@ -240,11 +260,11 @@ ServiceCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) } m_ServerExecutable = std::filesystem::absolute(m_ServerExecutable); - std::error_code Ec = InstallService(m_ServiceName, - ServiceSpec{.ExecutablePath = m_ServerExecutable, - .CommandLineOptions = GlobalOptions.PassthroughCommandLine, - .DisplayName = m_ServiceDisplayName, - .Description = m_ServiceDescription}); + Ec = InstallService(m_ServiceName, + ServiceSpec{.ExecutablePath = m_ServerExecutable, + .CommandLineOptions = GlobalOptions.PassthroughCommandLine, + .DisplayName = m_ServiceDisplayName, + .Description = m_ServiceDescription}); if (Ec) { ZEN_CONSOLE("Failed to install service '{}' using '{}' . Reason: '{}'", m_ServiceName, m_ServerExecutable, Ec.message()); -- cgit v1.2.3 From 995aec217bbb26c9c2a701cc77edb067ffbf8d36 Mon Sep 17 00:00:00 2001 From: Dan Engelbrecht Date: Wed, 8 Jan 2025 13:49:56 +0100 Subject: add ServiceLevel for service processes: User, AllUsers and Service --- src/zen/cmds/service_cmd.cpp | 129 ++++++++--- src/zen/cmds/service_cmd.h | 1 + src/zencore/include/zencore/process.h | 1 + src/zencore/process.cpp | 8 + src/zenutil/include/zenutil/service.h | 17 +- src/zenutil/service.cpp | 391 +++++++++++++++++++++++++++++++--- 6 files changed, 483 insertions(+), 64 deletions(-) diff --git a/src/zen/cmds/service_cmd.cpp b/src/zen/cmds/service_cmd.cpp index aede68573..7727ed8a2 100644 --- a/src/zen/cmds/service_cmd.cpp +++ b/src/zen/cmds/service_cmd.cpp @@ -120,6 +120,12 @@ ServiceCommand::ServiceCommand() m_StatusOptions.positional_help("name"); m_InstallOptions.add_options()("h,help", "Print help"); + m_InstallOptions.add_option("", + "l", + "service-level", + "Service level: CurrentUser, AllUsers, System. Default is CurrentUser", + cxxopts::value(m_ServiceLevel), + ""); m_InstallOptions.add_option("", "s", "executable", "Path to server executable", cxxopts::value(m_ServerExecutable), ""); m_InstallOptions.add_option("", "n", @@ -209,19 +215,29 @@ ServiceCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) ZEN_CONSOLE("Can't get information about service '{}'. Reason: '{}'", m_ServiceName, Ec.message()); return 1; } - ZEN_CONSOLE( - "Service '{}':\n" - " Status: {}\n" - " Executable: {}\n" - " CommandLineOptions: {}\n" - " Display Name: {}\n" - " Description: {}", - m_ServiceName, - ToString(Info.Status), - Info.Spec.ExecutablePath, - Info.Spec.CommandLineOptions, - Info.Spec.DisplayName, - Info.Spec.Description); + if (Info.Status == ServiceStatus::NotInstalled) + { + ZEN_CONSOLE("Service '{}' is not installed", m_ServiceName); + return 0; + } + else + { + ZEN_CONSOLE( + "Service '{}':\n" + " Status: {}\n" + " Level: {}\n" + " Executable: {}\n" + " CommandLineOptions: {}\n" + " Display Name: {}\n" + " Description: {}", + m_ServiceName, + ToString(Info.Status), + ToString(Info.Spec.ServiceLevel), + Info.Spec.ExecutablePath, + Info.Spec.CommandLineOptions, + Info.Spec.DisplayName, + Info.Spec.Description); + } } if (SubOption == &m_InstallOptions) @@ -233,12 +249,14 @@ ServiceCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) ZEN_CONSOLE( "Service '{}' already installed:\n" " Status: {}\n" + " Level: {}\n" " Executable: {}\n" " CommandLineOptions: {}\n" " Display Name: {}\n" " Description: {}", m_ServiceName, ToString(Info.Status), + ToString(Info.Spec.ServiceLevel), Info.Spec.ExecutablePath, Info.Spec.CommandLineOptions, Info.Spec.DisplayName, @@ -246,8 +264,18 @@ ServiceCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) return 1; } + std::optional Level = ServiceLevel::CurrentUser; + if (!m_ServiceLevel.empty()) + { + Level = FromString(m_ServiceLevel); + } + if (!Level.has_value()) + { + throw zen::OptionParseException("invalid service level"); + } + #if ZEN_PLATFORM_WINDOWS - if (!WinIsElevated()) + if (!WinIsElevated() && (Level.value() != ServiceLevel::CurrentUser)) { return WinRelaunchElevated(); } @@ -259,12 +287,12 @@ ServiceCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) m_ServerExecutable = ExePath; } m_ServerExecutable = std::filesystem::absolute(m_ServerExecutable); - - Ec = InstallService(m_ServiceName, - ServiceSpec{.ExecutablePath = m_ServerExecutable, - .CommandLineOptions = GlobalOptions.PassthroughCommandLine, - .DisplayName = m_ServiceDisplayName, - .Description = m_ServiceDescription}); + Ec = InstallService(m_ServiceName, + ServiceSpec{.ServiceLevel = Level.value(), + .ExecutablePath = m_ServerExecutable, + .CommandLineOptions = GlobalOptions.PassthroughCommandLine, + .DisplayName = m_ServiceDisplayName, + .Description = m_ServiceDescription}); if (Ec) { ZEN_CONSOLE("Failed to install service '{}' using '{}' . Reason: '{}'", m_ServiceName, m_ServerExecutable, Ec.message()); @@ -275,13 +303,26 @@ ServiceCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) if (SubOption == &m_UninstallOptions) { + ServiceInfo Info; + std::error_code Ec = QueryInstalledService(m_ServiceName, Info); + if (Ec) + { + ZEN_CONSOLE("Failed to inspect installed service '{}'. Reason: '{}'", m_ServiceName, Ec.message()); + return 1; + } + if (Info.Status == ServiceStatus::NotInstalled) + { + ZEN_CONSOLE("Service '{}' is not installed", m_ServiceName); + return 0; + } + #if ZEN_PLATFORM_WINDOWS - if (!WinIsElevated()) + if (!WinIsElevated() && (Info.Spec.ServiceLevel != ServiceLevel::CurrentUser)) { return WinRelaunchElevated(); } #endif // ZEN_PLATFORM_WINDOWS - std::error_code Ec = UninstallService(m_ServiceName); + Ec = UninstallService(m_ServiceName, Info.Spec.ServiceLevel); if (Ec) { ZEN_CONSOLE("Failed to uninstall service '{}'. Reason: '{}'", m_ServiceName, Ec.message()); @@ -292,13 +333,31 @@ ServiceCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) if (SubOption == &m_StartOptions) { + ServiceInfo Info; + std::error_code Ec = QueryInstalledService(m_ServiceName, Info); + if (Ec) + { + ZEN_CONSOLE("Failed to inspect installed service '{}'. Reason: '{}'", m_ServiceName, Ec.message()); + return 1; + } + if (Info.Status == ServiceStatus::NotInstalled) + { + ZEN_CONSOLE("Service '{}' is not installed", m_ServiceName); + return 1; + } + if (Info.Status != ServiceStatus::Stopped) + { + ZEN_CONSOLE("Service '{}' is already running", m_ServiceName); + return 1; + } + #if ZEN_PLATFORM_WINDOWS - if (!WinIsElevated()) + if (!WinIsElevated() && (Info.Spec.ServiceLevel != ServiceLevel::CurrentUser)) { return WinRelaunchElevated(); } #endif // ZEN_PLATFORM_WINDOWS - std::error_code Ec = StartService(m_ServiceName); + Ec = StartService(m_ServiceName, Info.Spec.ServiceLevel); if (Ec) { ZEN_CONSOLE("Failed to start service '{}'. Reason: '{}'", m_ServiceName, Ec.message()); @@ -309,13 +368,31 @@ ServiceCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) if (SubOption == &m_StopOptions) { + ServiceInfo Info; + std::error_code Ec = QueryInstalledService(m_ServiceName, Info); + if (Ec) + { + ZEN_CONSOLE("Failed to inspect installed service '{}'. Reason: '{}'", m_ServiceName, Ec.message()); + return 1; + } + if (Info.Status == ServiceStatus::NotInstalled) + { + ZEN_CONSOLE("Service '{}' is not installed", m_ServiceName); + return 1; + } + if (Info.Status != ServiceStatus::Running) + { + ZEN_CONSOLE("Service '{}' is not running", m_ServiceName); + return 1; + } + #if ZEN_PLATFORM_WINDOWS - if (!WinIsElevated()) + if (!WinIsElevated() && (Info.Spec.ServiceLevel != ServiceLevel::CurrentUser)) { return WinRelaunchElevated(); } #endif // ZEN_PLATFORM_WINDOWS - std::error_code Ec = StopService(m_ServiceName); + Ec = StopService(m_ServiceName, Info.Spec.ServiceLevel); if (Ec) { ZEN_CONSOLE("Failed to stop service '{}'. Reason: '{}'", m_ServiceName, Ec.message()); diff --git a/src/zen/cmds/service_cmd.h b/src/zen/cmds/service_cmd.h index 15273e952..e058915e5 100644 --- a/src/zen/cmds/service_cmd.h +++ b/src/zen/cmds/service_cmd.h @@ -32,6 +32,7 @@ private: cxxopts::Options m_InstallOptions{ "install", "Install zenserver as a service. Arguments following \" -- \" will be added as parameters to the installed service."}; + std::string m_ServiceLevel; std::filesystem::path m_ServerExecutable; std::string m_ServiceDisplayName = "Unreal Zen Storage Service"; std::string m_ServiceDescription; diff --git a/src/zencore/include/zencore/process.h b/src/zencore/include/zencore/process.h index d1394cd9a..42b997c39 100644 --- a/src/zencore/include/zencore/process.h +++ b/src/zencore/include/zencore/process.h @@ -51,6 +51,7 @@ struct CreateProcOptions Flag_NewConsole = 1 << 0, Flag_Elevated = 1 << 1, Flag_Unelevated = 1 << 2, + Flag_NoConsole = 1 << 3, }; const std::filesystem::path* WorkingDirectory = nullptr; diff --git a/src/zencore/process.cpp b/src/zencore/process.cpp index b1da034d2..11273a2b9 100644 --- a/src/zencore/process.cpp +++ b/src/zencore/process.cpp @@ -443,6 +443,10 @@ CreateProcNormal(const std::filesystem::path& Executable, std::string_view Comma { CreationFlags |= CREATE_NEW_CONSOLE; } + if (Options.Flags & CreateProcOptions::Flag_NoConsole) + { + CreationFlags |= CREATE_NO_WINDOW; + } const wchar_t* WorkingDir = nullptr; if (Options.WorkingDirectory != nullptr) @@ -588,6 +592,10 @@ CreateProcUnelevated(const std::filesystem::path& Executable, std::string_view C { CreateProcFlags |= CREATE_NEW_CONSOLE; } + if (Options.Flags & CreateProcOptions::Flag_NoConsole) + { + CreateProcFlags |= CREATE_NO_WINDOW; + } ExtendableWideStringBuilder<256> CommandLineZ; CommandLineZ << CommandLine; diff --git a/src/zenutil/include/zenutil/service.h b/src/zenutil/include/zenutil/service.h index cd769b67b..1a0ebc235 100644 --- a/src/zenutil/include/zenutil/service.h +++ b/src/zenutil/include/zenutil/service.h @@ -6,8 +6,16 @@ namespace zen { +enum class ServiceLevel +{ + CurrentUser, + AllUsers, + SystemService +}; + struct ServiceSpec { + ServiceLevel ServiceLevel = ServiceLevel::CurrentUser; std::filesystem::path ExecutablePath; std::string CommandLineOptions; std::string DisplayName; @@ -26,6 +34,9 @@ enum class ServiceStatus Resuming }; +std::string_view ToString(ServiceLevel Level); +std::optional FromString(const std::string& Level); + struct ServiceInfo { ServiceStatus Status; @@ -35,8 +46,8 @@ struct ServiceInfo std::string_view ToString(ServiceStatus Status); std::error_code InstallService(std::string_view ServiceName, const ServiceSpec& Spec); -std::error_code UninstallService(std::string_view ServiceName); +std::error_code UninstallService(std::string_view ServiceName, ServiceLevel Level); std::error_code QueryInstalledService(std::string_view ServiceName, ServiceInfo& OutInfo); -std::error_code StartService(std::string_view ServiceName); -std::error_code StopService(std::string_view ServiceName); +std::error_code StartService(std::string_view ServiceName, ServiceLevel Level); +std::error_code StopService(std::string_view ServiceName, ServiceLevel Level); } // namespace zen diff --git a/src/zenutil/service.cpp b/src/zenutil/service.cpp index fd96af0c8..44aa50494 100644 --- a/src/zenutil/service.cpp +++ b/src/zenutil/service.cpp @@ -3,8 +3,14 @@ #include #include +#include #include #include +#include + +ZEN_THIRD_PARTY_INCLUDES_START +#include +ZEN_THIRD_PARTY_INCLUDES_END #if ZEN_PLATFORM_WINDOWS # include @@ -19,6 +25,46 @@ namespace zen { using namespace std::literals; +namespace { + bool SplitExecutableAndArgs(const std::wstring& ExeAndArgs, std::filesystem::path& OutExecutablePath, std::string& OutArguments) + { + if (ExeAndArgs.size()) + { + if (ExeAndArgs[0] == '"') + { + std::wstring::size_type ExecutableEnd = ExeAndArgs.find('"', 1); + if (ExecutableEnd == std::wstring::npos) + { + OutExecutablePath = ExeAndArgs; + return true; + } + else + { + OutExecutablePath = ExeAndArgs.substr(0, ExecutableEnd + 1); + OutArguments = WideToUtf8(ExeAndArgs.substr(ExecutableEnd + 1 + ExeAndArgs[ExecutableEnd + 1] == ' ' ? 1 : 0)); + return true; + } + } + else + { + std::wstring::size_type ExecutableEnd = ExeAndArgs.find(' ', 1); + if (ExecutableEnd == std::wstring::npos) + { + OutExecutablePath = ExeAndArgs; + return true; + } + else + { + OutExecutablePath = ExeAndArgs.substr(0, ExecutableEnd); + OutArguments = WideToUtf8(ExeAndArgs.substr(ExecutableEnd + 1)); + return true; + } + } + } + return false; + } +} // namespace + #if ZEN_PLATFORM_MAC namespace { @@ -201,6 +247,44 @@ namespace { #endif // ZEN_PLATFORM_MAC +std::string_view +ToString(ServiceLevel Level) +{ + switch (Level) + { + case ServiceLevel::CurrentUser: + return "Current User"sv; + case ServiceLevel::AllUsers: + return "All Users"sv; + case ServiceLevel::SystemService: + return "System Service"sv; + default: + ZEN_ASSERT(false); + return ""sv; + } +} + +std::optional +FromString(const std::string& Level) +{ + if (StrCaseCompare(Level.c_str(), std::string(ToString(ServiceLevel::CurrentUser)).c_str()) == 0 || + StrCaseCompare(Level.c_str(), "CurrentUser") == 0) + { + return ServiceLevel::CurrentUser; + } + if (StrCaseCompare(Level.c_str(), std::string(ToString(ServiceLevel::AllUsers)).c_str()) == 0 || + StrCaseCompare(Level.c_str(), "AllUsers") == 0) + { + return ServiceLevel::AllUsers; + } + if (StrCaseCompare(Level.c_str(), std::string(ToString(ServiceLevel::SystemService)).c_str()) == 0 || + StrCaseCompare(Level.c_str(), "SystemService") == 0) + { + return ServiceLevel::SystemService; + } + return {}; +} + std::string_view ToString(ServiceStatus Status) { @@ -231,7 +315,36 @@ ToString(ServiceStatus Status) #if ZEN_PLATFORM_WINDOWS std::error_code -InstallService(std::string_view ServiceName, const ServiceSpec& Spec) +InstallRunService(std::string_view ServiceName, const ServiceSpec& Spec) +{ + HKEY RegKey = NULL; + if (LSTATUS Status = RegOpenKey(Spec.ServiceLevel == ServiceLevel::AllUsers ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER, + L"Software\\Microsoft\\Windows\\CurrentVersion\\Run", + &RegKey); + Status == ERROR_SUCCESS) + { + auto _ = MakeGuard([&]() { RegCloseKey(RegKey); }); + std::wstring PathIncludingArgs = Spec.ExecutablePath.wstring(); + if (!Spec.CommandLineOptions.empty()) + { + PathIncludingArgs += (L" " + Utf8ToWide(Spec.CommandLineOptions)); + } + if (Status = RegSetValueEx(RegKey, + Utf8ToWide(ServiceName).c_str(), + 0, + REG_SZ, + (BYTE*)PathIncludingArgs.c_str(), + DWORD((PathIncludingArgs.length() * 2) + 1)); + Status == ERROR_SUCCESS) + { + return {}; + } + } + return MakeErrorCodeFromLastError(); +} + +std::error_code +InstallSystemService(std::string_view ServiceName, const ServiceSpec& Spec) { // Get a handle to the SCM database. @@ -300,7 +413,40 @@ InstallService(std::string_view ServiceName, const ServiceSpec& Spec) } std::error_code -UninstallService(std::string_view ServiceName) +InstallService(std::string_view ServiceName, const ServiceSpec& Spec) +{ + if (Spec.ServiceLevel == ServiceLevel::SystemService) + { + return InstallSystemService(ServiceName, Spec); + } + return InstallRunService(ServiceName, Spec); +} + +std::error_code +UninstallRunService(bool AllUsers, std::string_view ServiceName) +{ + HKEY RegKey = NULL; + if (LSTATUS Status = + RegOpenKey(AllUsers ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER, L"Software\\Microsoft\\Windows\\CurrentVersion\\Run", &RegKey); + Status == ERROR_SUCCESS) + { + auto _ = MakeGuard([&]() { RegCloseKey(RegKey); }); + TCHAR Value[4096]; + DWORD Type; + DWORD Size = sizeof(Value); + if (ERROR_SUCCESS == RegQueryValueEx(RegKey, Utf8ToWide(ServiceName).c_str(), 0, &Type, (BYTE*)Value, &Size)) + { + if (Status = RegDeleteValue(RegKey, Utf8ToWide(ServiceName).c_str()); Status == ERROR_SUCCESS) + { + return std::error_code{}; + } + } + } + return MakeErrorCodeFromLastError(); +} + +std::error_code +UninstallSystemService(std::string_view ServiceName) { // Get a handle to the SCM database. SC_HANDLE schSCManager = OpenSCManager(NULL, // local computer @@ -345,7 +491,57 @@ UninstallService(std::string_view ServiceName) } std::error_code -QueryInstalledService(std::string_view ServiceName, ServiceInfo& OutInfo) +UninstallService(std::string_view ServiceName, ServiceLevel Level) +{ + switch (Level) + { + case ServiceLevel::CurrentUser: + return UninstallRunService(/*AllUsers*/ false, ServiceName); + case ServiceLevel::AllUsers: + return UninstallRunService(/*AllUsers*/ true, ServiceName); + case ServiceLevel::SystemService: + return UninstallSystemService(ServiceName); + default: + ZEN_ASSERT(false); + return {}; + } +} + +bool +QueryRunServiceStatus(bool AllUsers, std::string_view ServiceName, ServiceInfo& OutInfo) +{ + HKEY RegKey = NULL; + if (ERROR_SUCCESS == + RegOpenKey(AllUsers ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER, L"Software\\Microsoft\\Windows\\CurrentVersion\\Run", &RegKey)) + { + auto _ = MakeGuard([&]() { RegCloseKey(RegKey); }); + TCHAR Value[4096]; + DWORD Type; + DWORD Size = sizeof(Value); + if (ERROR_SUCCESS == RegQueryValueEx(RegKey, Utf8ToWide(ServiceName).c_str(), 0, &Type, (BYTE*)Value, &Size)) + { + OutInfo.Spec.ServiceLevel = AllUsers ? ServiceLevel::AllUsers : ServiceLevel::CurrentUser; + std::wstring PathIncludingArgs(Value); + + (void)SplitExecutableAndArgs(PathIncludingArgs, OutInfo.Spec.ExecutablePath, OutInfo.Spec.CommandLineOptions); + + OutInfo.Spec.DisplayName = ServiceName; + OutInfo.Spec.Description = ""; + OutInfo.Status = ServiceStatus::Stopped; + ProcessHandle Process; + std::error_code Ec = FindProcess(OutInfo.Spec.ExecutablePath, Process); + if (!Ec) + { + OutInfo.Status = ServiceStatus::Running; + } + return true; + } + } + return false; +} + +std::error_code +QuerySystemServiceStatus(std::string_view ServiceName, ServiceInfo& OutInfo) { // Get a handle to the SCM database. SC_HANDLE schSCManager = OpenSCManager(NULL, // local computer @@ -389,36 +585,7 @@ QueryInstalledService(std::string_view ServiceName, ServiceInfo& OutInfo) } std::wstring BinaryWithArguments(ServiceConfig->lpBinaryPathName); - if (BinaryWithArguments.size()) - { - if (BinaryWithArguments[0] == '"') - { - std::wstring::size_type ExecutableEnd = BinaryWithArguments.find('"', 1); - if (ExecutableEnd == std::wstring::npos) - { - OutInfo.Spec.ExecutablePath = BinaryWithArguments; - } - else - { - OutInfo.Spec.ExecutablePath = BinaryWithArguments.substr(0, ExecutableEnd + 1); - OutInfo.Spec.CommandLineOptions = - WideToUtf8(BinaryWithArguments.substr(ExecutableEnd + 1 + BinaryWithArguments[ExecutableEnd + 1] == ' ' ? 1 : 0)); - } - } - else - { - std::wstring::size_type ExecutableEnd = BinaryWithArguments.find(' ', 1); - if (ExecutableEnd == std::wstring::npos) - { - OutInfo.Spec.ExecutablePath = BinaryWithArguments; - } - else - { - OutInfo.Spec.ExecutablePath = BinaryWithArguments.substr(0, ExecutableEnd); - OutInfo.Spec.CommandLineOptions = WideToUtf8(BinaryWithArguments.substr(ExecutableEnd + 1)); - } - } - } + (void)SplitExecutableAndArgs(BinaryWithArguments, OutInfo.Spec.ExecutablePath, OutInfo.Spec.CommandLineOptions); OutInfo.Spec.DisplayName = WideToUtf8(ServiceConfig->lpDisplayName); SERVICE_STATUS ServiceStatus; @@ -480,7 +647,54 @@ QueryInstalledService(std::string_view ServiceName, ServiceInfo& OutInfo) } std::error_code -StartService(std::string_view ServiceName) +QueryInstalledService(std::string_view ServiceName, ServiceInfo& OutInfo) +{ + if (QueryRunServiceStatus(/*AllUsers*/ false, ServiceName, OutInfo)) + { + return {}; + } + if (QueryRunServiceStatus(/*AllUsers*/ true, ServiceName, OutInfo)) + { + return {}; + } + return QuerySystemServiceStatus(ServiceName, OutInfo); +} + +std::error_code +StartRunService(bool AllUsers, std::string_view ServiceName) +{ + HKEY RegKey = NULL; + if (ERROR_SUCCESS == + RegOpenKey(AllUsers ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER, L"Software\\Microsoft\\Windows\\CurrentVersion\\Run", &RegKey)) + { + auto _ = MakeGuard([&]() { RegCloseKey(RegKey); }); + TCHAR Value[4096]; + DWORD Type; + DWORD Size = sizeof(Value); + if (ERROR_SUCCESS == RegQueryValueEx(RegKey, Utf8ToWide(ServiceName).c_str(), 0, &Type, (BYTE*)Value, &Size)) + { + std::wstring PathIncludingArgs(Value); + + std::filesystem::path ExecutablePath; + std::string CommandLineOptions; + if (SplitExecutableAndArgs(PathIncludingArgs, ExecutablePath, CommandLineOptions)) + { + ProcessHandle Proc; + Proc.Initialize(CreateProc(ExecutablePath, WideToUtf8(PathIncludingArgs), {.Flags = CreateProcOptions::Flag_NoConsole})); + if (Proc.IsValid()) + { + return {}; + } + MakeErrorCode(ERROR_PATH_NOT_FOUND); + } + MakeErrorCode(ERROR_INVALID_PARAMETER); + } + } + return MakeErrorCodeFromLastError(); +} + +std::error_code +StartSystemService(std::string_view ServiceName) { // Get a handle to the SCM database. SC_HANDLE schSCManager = OpenSCManager(NULL, // local computer @@ -520,7 +734,96 @@ StartService(std::string_view ServiceName) } std::error_code -StopService(std::string_view ServiceName) +StartService(std::string_view ServiceName, ServiceLevel Level) +{ + switch (Level) + { + case ServiceLevel::CurrentUser: + return StartRunService(/*AllUsers*/ false, ServiceName); + case ServiceLevel::AllUsers: + return StartRunService(/*AllUsers*/ true, ServiceName); + case ServiceLevel::SystemService: + return StartSystemService(ServiceName); + default: + ZEN_ASSERT(false); + return {}; + } +} + +std::error_code +StopRunService(bool AllUsers, std::string_view ServiceName) +{ + HKEY RegKey = NULL; + if (ERROR_SUCCESS == + RegOpenKey(AllUsers ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER, L"Software\\Microsoft\\Windows\\CurrentVersion\\Run", &RegKey)) + { + auto _ = MakeGuard([&]() { RegCloseKey(RegKey); }); + TCHAR Value[4096]; + DWORD Type; + DWORD Size = sizeof(Value); + if (ERROR_SUCCESS == RegQueryValueEx(RegKey, Utf8ToWide(ServiceName).c_str(), 0, &Type, (BYTE*)Value, &Size)) + { + std::wstring PathIncludingArgs(Value); + + std::filesystem::path ExecutablePath; + std::string CommandLineOptions; + if (SplitExecutableAndArgs(PathIncludingArgs, ExecutablePath, CommandLineOptions)) + { + ProcessHandle Proc; + std::error_code Ec = FindProcess(ExecutablePath, Proc); + if (Ec) + { + return Ec; + } + else + { + // This is hacky and checks if the running service is a zenserver instance and tries to shut down using the shutdown + // event + ExtendableStringBuilder<32> ChildShutdownEventName; + ZenServerState State; + if (State.InitializeReadOnly()) + { + State.Snapshot([&](const ZenServerState::ZenServerEntry& Entry) { + if (Entry.Pid == gsl::narrow(Proc.Pid())) + { + ChildShutdownEventName << "Zen_" << Entry.EffectiveListenPort; + ChildShutdownEventName << "_Shutdown"; + } + }); + if (ChildShutdownEventName.Size() > 0) + { + NamedEvent Event(ChildShutdownEventName); + Ec = Event.Set(); + if (Ec) + { + return Ec; + } + return {}; + } + } + // This only works for a running process that does not already have a console attached - zenserver does have one + // hence the attempt to shut down using event above + if (AttachConsole(Proc.Pid())) + { + if (SetConsoleCtrlHandler(NULL, TRUE)) + { + if (GenerateConsoleCtrlEvent(CTRL_C_EVENT, 0)) + { + return {}; + } + } + } + return MakeErrorCodeFromLastError(); + } + } + return MakeErrorCode(ERROR_INVALID_PARAMETER); + } + } + return MakeErrorCodeFromLastError(); +} + +std::error_code +StopSystemService(std::string_view ServiceName) { // Get a handle to the SCM database. SC_HANDLE schSCManager = OpenSCManager(NULL, // local computer @@ -558,6 +861,24 @@ StopService(std::string_view ServiceName) return {}; } + +std::error_code +StopService(std::string_view ServiceName, ServiceLevel Level) +{ + switch (Level) + { + case ServiceLevel::CurrentUser: + return StopRunService(/*AllUsers*/ false, ServiceName); + case ServiceLevel::AllUsers: + return StopRunService(/*AllUsers*/ true, ServiceName); + case ServiceLevel::SystemService: + return StopSystemService(ServiceName); + default: + ZEN_ASSERT(false); + return {}; + } +} + #else # if 0 -- cgit v1.2.3 From 2696ea2322dfdd7b7a5b08843b6d8aca9aedd741 Mon Sep 17 00:00:00 2001 From: Dan Engelbrecht Date: Wed, 8 Jan 2025 14:21:11 +0100 Subject: use attachconsole to send ctrl+c to running process --- src/zen/cmds/service_cmd.cpp | 9 +++-- src/zenutil/service.cpp | 83 ++++++++++++++++++++++---------------------- 2 files changed, 48 insertions(+), 44 deletions(-) diff --git a/src/zen/cmds/service_cmd.cpp b/src/zen/cmds/service_cmd.cpp index 7727ed8a2..fc56293af 100644 --- a/src/zen/cmds/service_cmd.cpp +++ b/src/zen/cmds/service_cmd.cpp @@ -315,6 +315,11 @@ ServiceCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) ZEN_CONSOLE("Service '{}' is not installed", m_ServiceName); return 0; } + if (Info.Status != ServiceStatus::Stopped) + { + ZEN_CONSOLE("Service '{}' is running, stop before uninstalling", m_ServiceName); + return 0; + } #if ZEN_PLATFORM_WINDOWS if (!WinIsElevated() && (Info.Spec.ServiceLevel != ServiceLevel::CurrentUser)) @@ -352,7 +357,7 @@ ServiceCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) } #if ZEN_PLATFORM_WINDOWS - if (!WinIsElevated() && (Info.Spec.ServiceLevel != ServiceLevel::CurrentUser)) + if (!WinIsElevated() && (Info.Spec.ServiceLevel == ServiceLevel::SystemService)) { return WinRelaunchElevated(); } @@ -387,7 +392,7 @@ ServiceCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) } #if ZEN_PLATFORM_WINDOWS - if (!WinIsElevated() && (Info.Spec.ServiceLevel != ServiceLevel::CurrentUser)) + if (!WinIsElevated() && (Info.Spec.ServiceLevel == ServiceLevel::SystemService)) { return WinRelaunchElevated(); } diff --git a/src/zenutil/service.cpp b/src/zenutil/service.cpp index 44aa50494..3d7808bba 100644 --- a/src/zenutil/service.cpp +++ b/src/zenutil/service.cpp @@ -6,11 +6,6 @@ #include #include #include -#include - -ZEN_THIRD_PARTY_INCLUDES_START -#include -ZEN_THIRD_PARTY_INCLUDES_END #if ZEN_PLATFORM_WINDOWS # include @@ -750,6 +745,46 @@ StartService(std::string_view ServiceName, ServiceLevel Level) } } +std::error_code +SendSignalToProcess(DWORD dwProcessId, DWORD dwCtrlEvent) +{ + std::error_code Ec = {}; + DWORD ConsoleId = GetCurrentProcessId(); + // Leave current console if it exists + // (otherwise AttachConsole will return ERROR_ACCESS_DENIED) + bool ConsoleDetached = FreeConsole() != FALSE; + + if (AttachConsole(dwProcessId) != FALSE) + { + // Add a fake Ctrl-C handler for avoid instant kill is this console + // WARNING: do not revert it or current program will be also killed + SetConsoleCtrlHandler(nullptr, true); + if (GenerateConsoleCtrlEvent(dwCtrlEvent, 0) == FALSE) + { + Ec = MakeErrorCodeFromLastError(); + } + FreeConsole(); + } + else + { + Ec = MakeErrorCodeFromLastError(); + } + + if (ConsoleDetached) + { + // Create a new console if previous was deleted by OS + if (AttachConsole(ConsoleId) == FALSE) + { + int errorCode = GetLastError(); + if (errorCode == 31) // 31=ERROR_GEN_FAILURE + { + AllocConsole(); + } + } + } + return Ec; +} + std::error_code StopRunService(bool AllUsers, std::string_view ServiceName) { @@ -777,43 +812,7 @@ StopRunService(bool AllUsers, std::string_view ServiceName) } else { - // This is hacky and checks if the running service is a zenserver instance and tries to shut down using the shutdown - // event - ExtendableStringBuilder<32> ChildShutdownEventName; - ZenServerState State; - if (State.InitializeReadOnly()) - { - State.Snapshot([&](const ZenServerState::ZenServerEntry& Entry) { - if (Entry.Pid == gsl::narrow(Proc.Pid())) - { - ChildShutdownEventName << "Zen_" << Entry.EffectiveListenPort; - ChildShutdownEventName << "_Shutdown"; - } - }); - if (ChildShutdownEventName.Size() > 0) - { - NamedEvent Event(ChildShutdownEventName); - Ec = Event.Set(); - if (Ec) - { - return Ec; - } - return {}; - } - } - // This only works for a running process that does not already have a console attached - zenserver does have one - // hence the attempt to shut down using event above - if (AttachConsole(Proc.Pid())) - { - if (SetConsoleCtrlHandler(NULL, TRUE)) - { - if (GenerateConsoleCtrlEvent(CTRL_C_EVENT, 0)) - { - return {}; - } - } - } - return MakeErrorCodeFromLastError(); + return SendSignalToProcess(Proc.Pid(), CTRL_C_EVENT); } } return MakeErrorCode(ERROR_INVALID_PARAMETER); -- cgit v1.2.3 From 40c2cbe70ee6868606761d205d1d105c846b170f Mon Sep 17 00:00:00 2001 From: Dan Engelbrecht Date: Wed, 8 Jan 2025 14:24:42 +0100 Subject: mac function signature fix --- src/zenutil/service.cpp | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/zenutil/service.cpp b/src/zenutil/service.cpp index 3d7808bba..0bacc03f5 100644 --- a/src/zenutil/service.cpp +++ b/src/zenutil/service.cpp @@ -951,16 +951,15 @@ InstallService(std::string_view ServiceName, const ServiceSpec& Spec) } std::error_code -UninstallService(std::string_view ServiceName) +UninstallService(std::string_view ServiceName, ServiceLevel Level) { - ZEN_UNUSED(ServiceName); + ZEN_UNUSED(Level); std::filesystem::path ServicePath = std::filesystem::path("/usr/local/libexec") / ServiceName; ZEN_INFO("Attempting to remove service from {}", ServicePath.string()); if (unlink(ServicePath.string().c_str()) == -1) { return MakeErrorCodeFromLastError(); - ; } std::string DaemonName = fmt::format("com.epicgames.unreal.{}", ServiceName); std::filesystem::path PListPath = std::filesystem::path("/Library/LaunchDaemons") / (DaemonName + ".plist"); @@ -968,7 +967,6 @@ UninstallService(std::string_view ServiceName) if (unlink(ServicePath.string().c_str()) == -1) { return MakeErrorCodeFromLastError(); - ; } return {}; @@ -983,17 +981,17 @@ QueryInstalledService(std::string_view ServiceName, ServiceInfo& OutInfo) } std::error_code -StartService(std::string_view ServiceName) +StartService(std::string_view ServiceName, ServiceLevel Level) { - ZEN_UNUSED(ServiceName); + ZEN_UNUSED(ServiceName, Level); ZEN_NOT_IMPLEMENTED("StartService"); return {}; } std::error_code -StopService(std::string_view ServiceName) +StopService(std::string_view ServiceName, ServiceLevel Level) { - ZEN_UNUSED(ServiceName); + ZEN_UNUSED(ServiceName, Level); ZEN_NOT_IMPLEMENTED("StopService"); return {}; } -- cgit v1.2.3 From cf4f59b6f7dc60703dea2470a38fa741cba80501 Mon Sep 17 00:00:00 2001 From: Dan Engelbrecht Date: Thu, 9 Jan 2025 15:13:38 +0100 Subject: partial macos implementation --- src/zenutil/include/zenutil/service.h | 1 + src/zenutil/service.cpp | 71 +++++++++++++++++++++++++++++------ 2 files changed, 60 insertions(+), 12 deletions(-) diff --git a/src/zenutil/include/zenutil/service.h b/src/zenutil/include/zenutil/service.h index 1a0ebc235..d914c31a0 100644 --- a/src/zenutil/include/zenutil/service.h +++ b/src/zenutil/include/zenutil/service.h @@ -3,6 +3,7 @@ #pragma once #include +#include namespace zen { diff --git a/src/zenutil/service.cpp b/src/zenutil/service.cpp index 0bacc03f5..60db75f00 100644 --- a/src/zenutil/service.cpp +++ b/src/zenutil/service.cpp @@ -15,12 +15,16 @@ # include # include + +#include #endif namespace zen { using namespace std::literals; namespace { +#if ZEN_PLATFORM_WINDOWS + bool SplitExecutableAndArgs(const std::wstring& ExeAndArgs, std::filesystem::path& OutExecutablePath, std::string& OutArguments) { if (ExeAndArgs.size()) @@ -58,11 +62,10 @@ namespace { } return false; } -} // namespace -#if ZEN_PLATFORM_MAC +#endif // ZEN_PLATFORM_WINDOWS -namespace { +#if ZEN_PLATFORM_MAC std::vector SplitArguments(std::string_view Arguments) { bool IsQuote = false; @@ -238,9 +241,10 @@ namespace { // "" // "" } -} // namespace #endif // ZEN_PLATFORM_MAC +} // namespace + std::string_view ToString(ServiceLevel Level) @@ -926,6 +930,22 @@ static int CopyFile(std::filesystem::path source, std::filesystem::path dest) } # endif +std::filesystem::path GetDaemonBasePath(ServiceLevel Level) +{ + switch(Level) + { + case ServiceLevel::CurrentUser: + return GetUserHomeFolder() / "Library/LaunchAgents"; + case ServiceLevel::AllUsers: + return "/Library/LaunchAgents"; + case ServiceLevel::SystemService: + return "/Library/LaunchDaemon"; + default: + ZEN_ASSERT(false); + return {}; + } +} + std::error_code InstallService(std::string_view ServiceName, const ServiceSpec& Spec) { @@ -936,17 +956,37 @@ InstallService(std::string_view ServiceName, const ServiceSpec& Spec) // { // return MakeErrorCodeFromLastError(); // } + + // System: /Library/LaunchDaemons + // All users: /Library/LaunchAgents + // Current user: ~/Library/LaunchAgents + + std::string DaemonName = fmt::format("com.epicgames.unreal.{}", ServiceName); std::string PList = BuildPlist(ServiceName, Spec.ExecutablePath, Spec.CommandLineOptions, DaemonName, Spec.DisplayName, Spec.Description, true); - std::filesystem::path PListPath = std::filesystem::path("/Users/dan.engelbrecht/Library/LaunchAgents") / (DaemonName + ".plist"); + + std::filesystem::path PListFolder = GetDaemonBasePath(Spec.ServiceLevel); + + std::filesystem::path PListPath = PListFolder / (DaemonName + ".plist"); ZEN_INFO("Writing launchd plist to {}", PListPath.string()); - zen::WriteFile(PListPath, IoBuffer(IoBuffer::Wrap, PList.data(), PList.size())); - ZEN_INFO("Changing permissions to 0555 for {}", PListPath.string()); - // if (chmod(PListPath.string().c_str(), 0555) == -1) - // { - // return MakeErrorCodeFromLastError(); - // } + try + { + zen::WriteFile(PListPath, IoBuffer(IoBuffer::Wrap, PList.data(), PList.size())); + } + catch(const std::system_error& Ex) + { + return MakeErrorCode(Ex.code().value()); + } + + if (Spec.ServiceLevel != ServiceLevel::CurrentUser) // ???? Correct? + { + ZEN_INFO("Changing permissions to 600 for {}", PListPath.string()); + if (chmod(PListPath.string().c_str(), 0600) == -1) + { + return MakeErrorCodeFromLastError(); + } + } return {}; } @@ -976,7 +1016,12 @@ std::error_code QueryInstalledService(std::string_view ServiceName, ServiceInfo& OutInfo) { ZEN_UNUSED(ServiceName, OutInfo); - ZEN_NOT_IMPLEMENTED("QueryInstalledService"); +// ZEN_NOT_IMPLEMENTED("QueryInstalledService"); + //std::filesystem::path PListFolder = GetDaemonBasePath(Spec.ServiceLevel); + //sudo launchctl list + + OutInfo.Status = ServiceStatus::NotInstalled; + return {}; } @@ -985,6 +1030,7 @@ StartService(std::string_view ServiceName, ServiceLevel Level) { ZEN_UNUSED(ServiceName, Level); ZEN_NOT_IMPLEMENTED("StartService"); + // sudo launchctl bootstrap system /Library/LaunchDaemon/com.epicgames.unreal.ZenServer.plist return {}; } @@ -993,6 +1039,7 @@ StopService(std::string_view ServiceName, ServiceLevel Level) { ZEN_UNUSED(ServiceName, Level); ZEN_NOT_IMPLEMENTED("StopService"); + // sudo launchctl bootout system /Library/LaunchDaemon/com.epicgames.unreal.ZenServer.plist return {}; } -- cgit v1.2.3 From be24af604c26cca81d4b6d4f1789238884c7a12d Mon Sep 17 00:00:00 2001 From: Dan Engelbrecht Date: Fri, 10 Jan 2025 09:56:33 +0100 Subject: get home folder --- src/zencore/filesystem.cpp | 8 ++++++++ src/zencore/include/zencore/filesystem.h | 1 + 2 files changed, 9 insertions(+) diff --git a/src/zencore/filesystem.cpp b/src/zencore/filesystem.cpp index 52f2c4adc..3aa2dd0e1 100644 --- a/src/zencore/filesystem.cpp +++ b/src/zencore/filesystem.cpp @@ -1755,6 +1755,14 @@ PickDefaultSystemRootDirectory() #endif // ZEN_PLATFORM_WINDOWS } +std::filesystem::path +GetUserHomeFolder() +{ + int UserId = getuid(); + const passwd* Passwd = getpwuid(UserId); + return std::filesystem::path(Passwd->pw_dir); +} + ////////////////////////////////////////////////////////////////////////// // // Testing related code follows... diff --git a/src/zencore/include/zencore/filesystem.h b/src/zencore/include/zencore/filesystem.h index dba4981f0..13b7521ca 100644 --- a/src/zencore/include/zencore/filesystem.h +++ b/src/zencore/include/zencore/filesystem.h @@ -226,6 +226,7 @@ std::error_code RotateFiles(const std::filesystem::path& Filename, std::size_t M std::error_code RotateDirectories(const std::filesystem::path& DirectoryName, std::size_t MaxDirectories); std::filesystem::path PickDefaultSystemRootDirectory(); +std::filesystem::path GetUserHomeFolder(); ////////////////////////////////////////////////////////////////////////// -- cgit v1.2.3 From 9c101d0bdd3c3ada0d263dd57116931af9bcad88 Mon Sep 17 00:00:00 2001 From: Dan Engelbrecht Date: Fri, 10 Jan 2025 10:11:04 +0100 Subject: remove ServiceLevel --- src/zen/cmds/service_cmd.cpp | 37 +--- src/zen/cmds/service_cmd.h | 1 - src/zencore/filesystem.cpp | 8 - src/zencore/include/zencore/filesystem.h | 1 - src/zenutil/include/zenutil/service.h | 11 +- src/zenutil/service.cpp | 370 ++----------------------------- 6 files changed, 33 insertions(+), 395 deletions(-) diff --git a/src/zen/cmds/service_cmd.cpp b/src/zen/cmds/service_cmd.cpp index fc56293af..294ed6bf8 100644 --- a/src/zen/cmds/service_cmd.cpp +++ b/src/zen/cmds/service_cmd.cpp @@ -120,12 +120,6 @@ ServiceCommand::ServiceCommand() m_StatusOptions.positional_help("name"); m_InstallOptions.add_options()("h,help", "Print help"); - m_InstallOptions.add_option("", - "l", - "service-level", - "Service level: CurrentUser, AllUsers, System. Default is CurrentUser", - cxxopts::value(m_ServiceLevel), - ""); m_InstallOptions.add_option("", "s", "executable", "Path to server executable", cxxopts::value(m_ServerExecutable), ""); m_InstallOptions.add_option("", "n", @@ -225,14 +219,12 @@ ServiceCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) ZEN_CONSOLE( "Service '{}':\n" " Status: {}\n" - " Level: {}\n" " Executable: {}\n" " CommandLineOptions: {}\n" " Display Name: {}\n" " Description: {}", m_ServiceName, ToString(Info.Status), - ToString(Info.Spec.ServiceLevel), Info.Spec.ExecutablePath, Info.Spec.CommandLineOptions, Info.Spec.DisplayName, @@ -249,14 +241,12 @@ ServiceCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) ZEN_CONSOLE( "Service '{}' already installed:\n" " Status: {}\n" - " Level: {}\n" " Executable: {}\n" " CommandLineOptions: {}\n" " Display Name: {}\n" " Description: {}", m_ServiceName, ToString(Info.Status), - ToString(Info.Spec.ServiceLevel), Info.Spec.ExecutablePath, Info.Spec.CommandLineOptions, Info.Spec.DisplayName, @@ -264,18 +254,8 @@ ServiceCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) return 1; } - std::optional Level = ServiceLevel::CurrentUser; - if (!m_ServiceLevel.empty()) - { - Level = FromString(m_ServiceLevel); - } - if (!Level.has_value()) - { - throw zen::OptionParseException("invalid service level"); - } - #if ZEN_PLATFORM_WINDOWS - if (!WinIsElevated() && (Level.value() != ServiceLevel::CurrentUser)) + if (!WinIsElevated()) { return WinRelaunchElevated(); } @@ -288,8 +268,7 @@ ServiceCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) } m_ServerExecutable = std::filesystem::absolute(m_ServerExecutable); Ec = InstallService(m_ServiceName, - ServiceSpec{.ServiceLevel = Level.value(), - .ExecutablePath = m_ServerExecutable, + ServiceSpec{.ExecutablePath = m_ServerExecutable, .CommandLineOptions = GlobalOptions.PassthroughCommandLine, .DisplayName = m_ServiceDisplayName, .Description = m_ServiceDescription}); @@ -322,12 +301,12 @@ ServiceCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) } #if ZEN_PLATFORM_WINDOWS - if (!WinIsElevated() && (Info.Spec.ServiceLevel != ServiceLevel::CurrentUser)) + if (!WinIsElevated()) { return WinRelaunchElevated(); } #endif // ZEN_PLATFORM_WINDOWS - Ec = UninstallService(m_ServiceName, Info.Spec.ServiceLevel); + Ec = UninstallService(m_ServiceName); if (Ec) { ZEN_CONSOLE("Failed to uninstall service '{}'. Reason: '{}'", m_ServiceName, Ec.message()); @@ -357,12 +336,12 @@ ServiceCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) } #if ZEN_PLATFORM_WINDOWS - if (!WinIsElevated() && (Info.Spec.ServiceLevel == ServiceLevel::SystemService)) + if (!WinIsElevated()) { return WinRelaunchElevated(); } #endif // ZEN_PLATFORM_WINDOWS - Ec = StartService(m_ServiceName, Info.Spec.ServiceLevel); + Ec = StartService(m_ServiceName); if (Ec) { ZEN_CONSOLE("Failed to start service '{}'. Reason: '{}'", m_ServiceName, Ec.message()); @@ -392,12 +371,12 @@ ServiceCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) } #if ZEN_PLATFORM_WINDOWS - if (!WinIsElevated() && (Info.Spec.ServiceLevel == ServiceLevel::SystemService)) + if (!WinIsElevated()) { return WinRelaunchElevated(); } #endif // ZEN_PLATFORM_WINDOWS - Ec = StopService(m_ServiceName, Info.Spec.ServiceLevel); + Ec = StopService(m_ServiceName); if (Ec) { ZEN_CONSOLE("Failed to stop service '{}'. Reason: '{}'", m_ServiceName, Ec.message()); diff --git a/src/zen/cmds/service_cmd.h b/src/zen/cmds/service_cmd.h index e058915e5..15273e952 100644 --- a/src/zen/cmds/service_cmd.h +++ b/src/zen/cmds/service_cmd.h @@ -32,7 +32,6 @@ private: cxxopts::Options m_InstallOptions{ "install", "Install zenserver as a service. Arguments following \" -- \" will be added as parameters to the installed service."}; - std::string m_ServiceLevel; std::filesystem::path m_ServerExecutable; std::string m_ServiceDisplayName = "Unreal Zen Storage Service"; std::string m_ServiceDescription; diff --git a/src/zencore/filesystem.cpp b/src/zencore/filesystem.cpp index 3aa2dd0e1..52f2c4adc 100644 --- a/src/zencore/filesystem.cpp +++ b/src/zencore/filesystem.cpp @@ -1755,14 +1755,6 @@ PickDefaultSystemRootDirectory() #endif // ZEN_PLATFORM_WINDOWS } -std::filesystem::path -GetUserHomeFolder() -{ - int UserId = getuid(); - const passwd* Passwd = getpwuid(UserId); - return std::filesystem::path(Passwd->pw_dir); -} - ////////////////////////////////////////////////////////////////////////// // // Testing related code follows... diff --git a/src/zencore/include/zencore/filesystem.h b/src/zencore/include/zencore/filesystem.h index 13b7521ca..dba4981f0 100644 --- a/src/zencore/include/zencore/filesystem.h +++ b/src/zencore/include/zencore/filesystem.h @@ -226,7 +226,6 @@ std::error_code RotateFiles(const std::filesystem::path& Filename, std::size_t M std::error_code RotateDirectories(const std::filesystem::path& DirectoryName, std::size_t MaxDirectories); std::filesystem::path PickDefaultSystemRootDirectory(); -std::filesystem::path GetUserHomeFolder(); ////////////////////////////////////////////////////////////////////////// diff --git a/src/zenutil/include/zenutil/service.h b/src/zenutil/include/zenutil/service.h index d914c31a0..53c3483aa 100644 --- a/src/zenutil/include/zenutil/service.h +++ b/src/zenutil/include/zenutil/service.h @@ -16,7 +16,6 @@ enum class ServiceLevel struct ServiceSpec { - ServiceLevel ServiceLevel = ServiceLevel::CurrentUser; std::filesystem::path ExecutablePath; std::string CommandLineOptions; std::string DisplayName; @@ -35,9 +34,6 @@ enum class ServiceStatus Resuming }; -std::string_view ToString(ServiceLevel Level); -std::optional FromString(const std::string& Level); - struct ServiceInfo { ServiceStatus Status; @@ -47,8 +43,9 @@ struct ServiceInfo std::string_view ToString(ServiceStatus Status); std::error_code InstallService(std::string_view ServiceName, const ServiceSpec& Spec); -std::error_code UninstallService(std::string_view ServiceName, ServiceLevel Level); +std::error_code UninstallService(std::string_view ServiceName); std::error_code QueryInstalledService(std::string_view ServiceName, ServiceInfo& OutInfo); -std::error_code StartService(std::string_view ServiceName, ServiceLevel Level); -std::error_code StopService(std::string_view ServiceName, ServiceLevel Level); +std::error_code StartService(std::string_view ServiceName); +std::error_code StopService(std::string_view ServiceName); + } // namespace zen diff --git a/src/zenutil/service.cpp b/src/zenutil/service.cpp index 60db75f00..34d7c2eb6 100644 --- a/src/zenutil/service.cpp +++ b/src/zenutil/service.cpp @@ -16,7 +16,7 @@ # include # include -#include +# include #endif namespace zen { @@ -63,7 +63,7 @@ namespace { return false; } -#endif // ZEN_PLATFORM_WINDOWS +#endif // ZEN_PLATFORM_WINDOWS #if ZEN_PLATFORM_MAC std::vector SplitArguments(std::string_view Arguments) @@ -245,45 +245,6 @@ namespace { #endif // ZEN_PLATFORM_MAC } // namespace - -std::string_view -ToString(ServiceLevel Level) -{ - switch (Level) - { - case ServiceLevel::CurrentUser: - return "Current User"sv; - case ServiceLevel::AllUsers: - return "All Users"sv; - case ServiceLevel::SystemService: - return "System Service"sv; - default: - ZEN_ASSERT(false); - return ""sv; - } -} - -std::optional -FromString(const std::string& Level) -{ - if (StrCaseCompare(Level.c_str(), std::string(ToString(ServiceLevel::CurrentUser)).c_str()) == 0 || - StrCaseCompare(Level.c_str(), "CurrentUser") == 0) - { - return ServiceLevel::CurrentUser; - } - if (StrCaseCompare(Level.c_str(), std::string(ToString(ServiceLevel::AllUsers)).c_str()) == 0 || - StrCaseCompare(Level.c_str(), "AllUsers") == 0) - { - return ServiceLevel::AllUsers; - } - if (StrCaseCompare(Level.c_str(), std::string(ToString(ServiceLevel::SystemService)).c_str()) == 0 || - StrCaseCompare(Level.c_str(), "SystemService") == 0) - { - return ServiceLevel::SystemService; - } - return {}; -} - std::string_view ToString(ServiceStatus Status) { @@ -314,36 +275,7 @@ ToString(ServiceStatus Status) #if ZEN_PLATFORM_WINDOWS std::error_code -InstallRunService(std::string_view ServiceName, const ServiceSpec& Spec) -{ - HKEY RegKey = NULL; - if (LSTATUS Status = RegOpenKey(Spec.ServiceLevel == ServiceLevel::AllUsers ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER, - L"Software\\Microsoft\\Windows\\CurrentVersion\\Run", - &RegKey); - Status == ERROR_SUCCESS) - { - auto _ = MakeGuard([&]() { RegCloseKey(RegKey); }); - std::wstring PathIncludingArgs = Spec.ExecutablePath.wstring(); - if (!Spec.CommandLineOptions.empty()) - { - PathIncludingArgs += (L" " + Utf8ToWide(Spec.CommandLineOptions)); - } - if (Status = RegSetValueEx(RegKey, - Utf8ToWide(ServiceName).c_str(), - 0, - REG_SZ, - (BYTE*)PathIncludingArgs.c_str(), - DWORD((PathIncludingArgs.length() * 2) + 1)); - Status == ERROR_SUCCESS) - { - return {}; - } - } - return MakeErrorCodeFromLastError(); -} - -std::error_code -InstallSystemService(std::string_view ServiceName, const ServiceSpec& Spec) +InstallService(std::string_view ServiceName, const ServiceSpec& Spec) { // Get a handle to the SCM database. @@ -412,40 +344,7 @@ InstallSystemService(std::string_view ServiceName, const ServiceSpec& Spec) } std::error_code -InstallService(std::string_view ServiceName, const ServiceSpec& Spec) -{ - if (Spec.ServiceLevel == ServiceLevel::SystemService) - { - return InstallSystemService(ServiceName, Spec); - } - return InstallRunService(ServiceName, Spec); -} - -std::error_code -UninstallRunService(bool AllUsers, std::string_view ServiceName) -{ - HKEY RegKey = NULL; - if (LSTATUS Status = - RegOpenKey(AllUsers ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER, L"Software\\Microsoft\\Windows\\CurrentVersion\\Run", &RegKey); - Status == ERROR_SUCCESS) - { - auto _ = MakeGuard([&]() { RegCloseKey(RegKey); }); - TCHAR Value[4096]; - DWORD Type; - DWORD Size = sizeof(Value); - if (ERROR_SUCCESS == RegQueryValueEx(RegKey, Utf8ToWide(ServiceName).c_str(), 0, &Type, (BYTE*)Value, &Size)) - { - if (Status = RegDeleteValue(RegKey, Utf8ToWide(ServiceName).c_str()); Status == ERROR_SUCCESS) - { - return std::error_code{}; - } - } - } - return MakeErrorCodeFromLastError(); -} - -std::error_code -UninstallSystemService(std::string_view ServiceName) +UninstallService(std::string_view ServiceName) { // Get a handle to the SCM database. SC_HANDLE schSCManager = OpenSCManager(NULL, // local computer @@ -490,57 +389,7 @@ UninstallSystemService(std::string_view ServiceName) } std::error_code -UninstallService(std::string_view ServiceName, ServiceLevel Level) -{ - switch (Level) - { - case ServiceLevel::CurrentUser: - return UninstallRunService(/*AllUsers*/ false, ServiceName); - case ServiceLevel::AllUsers: - return UninstallRunService(/*AllUsers*/ true, ServiceName); - case ServiceLevel::SystemService: - return UninstallSystemService(ServiceName); - default: - ZEN_ASSERT(false); - return {}; - } -} - -bool -QueryRunServiceStatus(bool AllUsers, std::string_view ServiceName, ServiceInfo& OutInfo) -{ - HKEY RegKey = NULL; - if (ERROR_SUCCESS == - RegOpenKey(AllUsers ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER, L"Software\\Microsoft\\Windows\\CurrentVersion\\Run", &RegKey)) - { - auto _ = MakeGuard([&]() { RegCloseKey(RegKey); }); - TCHAR Value[4096]; - DWORD Type; - DWORD Size = sizeof(Value); - if (ERROR_SUCCESS == RegQueryValueEx(RegKey, Utf8ToWide(ServiceName).c_str(), 0, &Type, (BYTE*)Value, &Size)) - { - OutInfo.Spec.ServiceLevel = AllUsers ? ServiceLevel::AllUsers : ServiceLevel::CurrentUser; - std::wstring PathIncludingArgs(Value); - - (void)SplitExecutableAndArgs(PathIncludingArgs, OutInfo.Spec.ExecutablePath, OutInfo.Spec.CommandLineOptions); - - OutInfo.Spec.DisplayName = ServiceName; - OutInfo.Spec.Description = ""; - OutInfo.Status = ServiceStatus::Stopped; - ProcessHandle Process; - std::error_code Ec = FindProcess(OutInfo.Spec.ExecutablePath, Process); - if (!Ec) - { - OutInfo.Status = ServiceStatus::Running; - } - return true; - } - } - return false; -} - -std::error_code -QuerySystemServiceStatus(std::string_view ServiceName, ServiceInfo& OutInfo) +QueryInstalledService(std::string_view ServiceName, ServiceInfo& OutInfo) { // Get a handle to the SCM database. SC_HANDLE schSCManager = OpenSCManager(NULL, // local computer @@ -646,54 +495,7 @@ QuerySystemServiceStatus(std::string_view ServiceName, ServiceInfo& OutInfo) } std::error_code -QueryInstalledService(std::string_view ServiceName, ServiceInfo& OutInfo) -{ - if (QueryRunServiceStatus(/*AllUsers*/ false, ServiceName, OutInfo)) - { - return {}; - } - if (QueryRunServiceStatus(/*AllUsers*/ true, ServiceName, OutInfo)) - { - return {}; - } - return QuerySystemServiceStatus(ServiceName, OutInfo); -} - -std::error_code -StartRunService(bool AllUsers, std::string_view ServiceName) -{ - HKEY RegKey = NULL; - if (ERROR_SUCCESS == - RegOpenKey(AllUsers ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER, L"Software\\Microsoft\\Windows\\CurrentVersion\\Run", &RegKey)) - { - auto _ = MakeGuard([&]() { RegCloseKey(RegKey); }); - TCHAR Value[4096]; - DWORD Type; - DWORD Size = sizeof(Value); - if (ERROR_SUCCESS == RegQueryValueEx(RegKey, Utf8ToWide(ServiceName).c_str(), 0, &Type, (BYTE*)Value, &Size)) - { - std::wstring PathIncludingArgs(Value); - - std::filesystem::path ExecutablePath; - std::string CommandLineOptions; - if (SplitExecutableAndArgs(PathIncludingArgs, ExecutablePath, CommandLineOptions)) - { - ProcessHandle Proc; - Proc.Initialize(CreateProc(ExecutablePath, WideToUtf8(PathIncludingArgs), {.Flags = CreateProcOptions::Flag_NoConsole})); - if (Proc.IsValid()) - { - return {}; - } - MakeErrorCode(ERROR_PATH_NOT_FOUND); - } - MakeErrorCode(ERROR_INVALID_PARAMETER); - } - } - return MakeErrorCodeFromLastError(); -} - -std::error_code -StartSystemService(std::string_view ServiceName) +StartService(std::string_view ServiceName) { // Get a handle to the SCM database. SC_HANDLE schSCManager = OpenSCManager(NULL, // local computer @@ -733,100 +535,7 @@ StartSystemService(std::string_view ServiceName) } std::error_code -StartService(std::string_view ServiceName, ServiceLevel Level) -{ - switch (Level) - { - case ServiceLevel::CurrentUser: - return StartRunService(/*AllUsers*/ false, ServiceName); - case ServiceLevel::AllUsers: - return StartRunService(/*AllUsers*/ true, ServiceName); - case ServiceLevel::SystemService: - return StartSystemService(ServiceName); - default: - ZEN_ASSERT(false); - return {}; - } -} - -std::error_code -SendSignalToProcess(DWORD dwProcessId, DWORD dwCtrlEvent) -{ - std::error_code Ec = {}; - DWORD ConsoleId = GetCurrentProcessId(); - // Leave current console if it exists - // (otherwise AttachConsole will return ERROR_ACCESS_DENIED) - bool ConsoleDetached = FreeConsole() != FALSE; - - if (AttachConsole(dwProcessId) != FALSE) - { - // Add a fake Ctrl-C handler for avoid instant kill is this console - // WARNING: do not revert it or current program will be also killed - SetConsoleCtrlHandler(nullptr, true); - if (GenerateConsoleCtrlEvent(dwCtrlEvent, 0) == FALSE) - { - Ec = MakeErrorCodeFromLastError(); - } - FreeConsole(); - } - else - { - Ec = MakeErrorCodeFromLastError(); - } - - if (ConsoleDetached) - { - // Create a new console if previous was deleted by OS - if (AttachConsole(ConsoleId) == FALSE) - { - int errorCode = GetLastError(); - if (errorCode == 31) // 31=ERROR_GEN_FAILURE - { - AllocConsole(); - } - } - } - return Ec; -} - -std::error_code -StopRunService(bool AllUsers, std::string_view ServiceName) -{ - HKEY RegKey = NULL; - if (ERROR_SUCCESS == - RegOpenKey(AllUsers ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER, L"Software\\Microsoft\\Windows\\CurrentVersion\\Run", &RegKey)) - { - auto _ = MakeGuard([&]() { RegCloseKey(RegKey); }); - TCHAR Value[4096]; - DWORD Type; - DWORD Size = sizeof(Value); - if (ERROR_SUCCESS == RegQueryValueEx(RegKey, Utf8ToWide(ServiceName).c_str(), 0, &Type, (BYTE*)Value, &Size)) - { - std::wstring PathIncludingArgs(Value); - - std::filesystem::path ExecutablePath; - std::string CommandLineOptions; - if (SplitExecutableAndArgs(PathIncludingArgs, ExecutablePath, CommandLineOptions)) - { - ProcessHandle Proc; - std::error_code Ec = FindProcess(ExecutablePath, Proc); - if (Ec) - { - return Ec; - } - else - { - return SendSignalToProcess(Proc.Pid(), CTRL_C_EVENT); - } - } - return MakeErrorCode(ERROR_INVALID_PARAMETER); - } - } - return MakeErrorCodeFromLastError(); -} - -std::error_code -StopSystemService(std::string_view ServiceName) +StopService(std::string_view ServiceName) { // Get a handle to the SCM database. SC_HANDLE schSCManager = OpenSCManager(NULL, // local computer @@ -865,23 +574,6 @@ StopSystemService(std::string_view ServiceName) return {}; } -std::error_code -StopService(std::string_view ServiceName, ServiceLevel Level) -{ - switch (Level) - { - case ServiceLevel::CurrentUser: - return StopRunService(/*AllUsers*/ false, ServiceName); - case ServiceLevel::AllUsers: - return StopRunService(/*AllUsers*/ true, ServiceName); - case ServiceLevel::SystemService: - return StopSystemService(ServiceName); - default: - ZEN_ASSERT(false); - return {}; - } -} - #else # if 0 @@ -930,22 +622,6 @@ static int CopyFile(std::filesystem::path source, std::filesystem::path dest) } # endif -std::filesystem::path GetDaemonBasePath(ServiceLevel Level) -{ - switch(Level) - { - case ServiceLevel::CurrentUser: - return GetUserHomeFolder() / "Library/LaunchAgents"; - case ServiceLevel::AllUsers: - return "/Library/LaunchAgents"; - case ServiceLevel::SystemService: - return "/Library/LaunchDaemon"; - default: - ZEN_ASSERT(false); - return {}; - } -} - std::error_code InstallService(std::string_view ServiceName, const ServiceSpec& Spec) { @@ -956,17 +632,16 @@ InstallService(std::string_view ServiceName, const ServiceSpec& Spec) // { // return MakeErrorCodeFromLastError(); // } - + // System: /Library/LaunchDaemons // All users: /Library/LaunchAgents // Current user: ~/Library/LaunchAgents - std::string DaemonName = fmt::format("com.epicgames.unreal.{}", ServiceName); std::string PList = BuildPlist(ServiceName, Spec.ExecutablePath, Spec.CommandLineOptions, DaemonName, Spec.DisplayName, Spec.Description, true); - std::filesystem::path PListFolder = GetDaemonBasePath(Spec.ServiceLevel); + std::filesystem::path PListFolder = "/Library/LaunchDaemon"; std::filesystem::path PListPath = PListFolder / (DaemonName + ".plist"); ZEN_INFO("Writing launchd plist to {}", PListPath.string()); @@ -974,24 +649,21 @@ InstallService(std::string_view ServiceName, const ServiceSpec& Spec) { zen::WriteFile(PListPath, IoBuffer(IoBuffer::Wrap, PList.data(), PList.size())); } - catch(const std::system_error& Ex) + catch (const std::system_error& Ex) { return MakeErrorCode(Ex.code().value()); } - - if (Spec.ServiceLevel != ServiceLevel::CurrentUser) // ???? Correct? + + ZEN_INFO("Changing permissions to 600 for {}", PListPath.string()); + if (chmod(PListPath.string().c_str(), 0600) == -1) { - ZEN_INFO("Changing permissions to 600 for {}", PListPath.string()); - if (chmod(PListPath.string().c_str(), 0600) == -1) - { - return MakeErrorCodeFromLastError(); - } + return MakeErrorCodeFromLastError(); } return {}; } std::error_code -UninstallService(std::string_view ServiceName, ServiceLevel Level) +UninstallService(std::string_view ServiceName) { ZEN_UNUSED(Level); @@ -1016,9 +688,9 @@ std::error_code QueryInstalledService(std::string_view ServiceName, ServiceInfo& OutInfo) { ZEN_UNUSED(ServiceName, OutInfo); -// ZEN_NOT_IMPLEMENTED("QueryInstalledService"); - //std::filesystem::path PListFolder = GetDaemonBasePath(Spec.ServiceLevel); - //sudo launchctl list + // ZEN_NOT_IMPLEMENTED("QueryInstalledService"); + // std::filesystem::path PListFolder = "/Library/LaunchDaemon"; + // sudo launchctl list OutInfo.Status = ServiceStatus::NotInstalled; @@ -1026,7 +698,7 @@ QueryInstalledService(std::string_view ServiceName, ServiceInfo& OutInfo) } std::error_code -StartService(std::string_view ServiceName, ServiceLevel Level) +StartService(std::string_view ServiceName) { ZEN_UNUSED(ServiceName, Level); ZEN_NOT_IMPLEMENTED("StartService"); @@ -1035,11 +707,11 @@ StartService(std::string_view ServiceName, ServiceLevel Level) } std::error_code -StopService(std::string_view ServiceName, ServiceLevel Level) +StopService(std::string_view ServiceName) { ZEN_UNUSED(ServiceName, Level); ZEN_NOT_IMPLEMENTED("StopService"); - // sudo launchctl bootout system /Library/LaunchDaemon/com.epicgames.unreal.ZenServer.plist + // sudo launchctl bootout system /Library/LaunchDaemon/com.epicgames.unreal.ZenServer.plist return {}; } -- cgit v1.2.3 From d2669359113583e6329764e5bfebaa0b22731a8e Mon Sep 17 00:00:00 2001 From: Dan Engelbrecht Date: Fri, 10 Jan 2025 10:58:26 +0100 Subject: if we can't open a process handle, assume it is running under other user id --- src/zencore/process.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/zencore/process.cpp b/src/zencore/process.cpp index 11273a2b9..3d6a67ada 100644 --- a/src/zencore/process.cpp +++ b/src/zencore/process.cpp @@ -806,6 +806,11 @@ IsProcessRunning(int pid, std::error_code& OutEc) { return false; } + if (Error == ERROR_ACCESS_DENIED) + { + // Process is running under other user probably, assume it is running + return true; + } OutEc = MakeErrorCode(Error); return false; } -- cgit v1.2.3 From c2cb52446fddbcca417387e2a7ef2ee8ec0a9549 Mon Sep 17 00:00:00 2001 From: Dan Engelbrecht Date: Fri, 10 Jan 2025 11:01:41 +0100 Subject: use local service account for zen service --- src/zenutil/service.cpp | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/zenutil/service.cpp b/src/zenutil/service.cpp index 34d7c2eb6..72367a070 100644 --- a/src/zenutil/service.cpp +++ b/src/zenutil/service.cpp @@ -306,19 +306,19 @@ InstallService(std::string_view ServiceName, const ServiceSpec& Spec) Utf8ToWide(Spec.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 + 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_AUTO_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 + TEXT("NT AUTHORITY\\LocalService"), // LocalService account + NULL); // no password if (schService == NULL) { -- cgit v1.2.3 From 997c23cd9b6a7c21a4cfc1744ec338e9ee55b36e Mon Sep 17 00:00:00 2001 From: Dan Engelbrecht Date: Fri, 10 Jan 2025 12:10:39 +0100 Subject: make windows elevation optional --- src/zen/cmds/service_cmd.cpp | 200 ++++++++++++++++++++++++++++--------------- src/zen/cmds/service_cmd.h | 2 + 2 files changed, 132 insertions(+), 70 deletions(-) diff --git a/src/zen/cmds/service_cmd.cpp b/src/zen/cmds/service_cmd.cpp index 294ed6bf8..0d538ffda 100644 --- a/src/zen/cmds/service_cmd.cpp +++ b/src/zen/cmds/service_cmd.cpp @@ -13,6 +13,10 @@ # include #endif +#if ZEN_PLATFORM_LINUX || ZEN_PLATFORM_MAC +# include +#endif + ZEN_THIRD_PARTY_INCLUDES_START #include ZEN_THIRD_PARTY_INCLUDES_END @@ -22,76 +26,104 @@ ZEN_THIRD_PARTY_INCLUDES_END namespace zen { ////////////////////////////////////////////////////////////////////////// + +namespace { + #if ZEN_PLATFORM_WINDOWS -BOOL -WinIsElevated() -{ - BOOL fRet = FALSE; - HANDLE hToken = NULL; - if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken)) + BOOL IsElevated() { - TOKEN_ELEVATION Elevation; - DWORD cbSize = sizeof(TOKEN_ELEVATION); - if (GetTokenInformation(hToken, TokenElevation, &Elevation, sizeof(Elevation), &cbSize)) + BOOL fRet = FALSE; + HANDLE hToken = NULL; + if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken)) { - fRet = Elevation.TokenIsElevated; + TOKEN_ELEVATION Elevation; + DWORD cbSize = sizeof(TOKEN_ELEVATION); + if (GetTokenInformation(hToken, TokenElevation, &Elevation, sizeof(Elevation), &cbSize)) + { + fRet = Elevation.TokenIsElevated; + } } + if (hToken) + { + CloseHandle(hToken); + } + return fRet; } - if (hToken) - { - CloseHandle(hToken); - } - return fRet; -} -int -WinRelaunchElevated() -{ - TCHAR CurrentDir[4096]; - GetCurrentDirectory(4096, CurrentDir); - - ExtendableWideStringBuilder<256> Parameters; - std::filesystem::path ExecutablePath = GetRunningExecutablePath(); - std::wstring CommandLine(GetCommandLine()); - std::string::size_type ExtraLength = CommandLine[0] == '\"' ? 2 : 0; - std::wstring CommandArguments = CommandLine.substr(ExecutablePath.string().length() + ExtraLength + 1); - - ZEN_CONSOLE("Attempting to run '{} {}' elevated...", ExecutablePath, WideToUtf8(CommandArguments)); - - SHELLEXECUTEINFO shExInfo = {0}; - shExInfo.cbSize = sizeof(shExInfo); - shExInfo.fMask = SEE_MASK_NOCLOSEPROCESS | SEE_MASK_NO_CONSOLE; - shExInfo.hwnd = 0; - shExInfo.lpVerb = TEXT("runas"); // Operation to perform - shExInfo.lpFile = ExecutablePath.c_str(); // Application to start - shExInfo.lpParameters = CommandArguments.c_str(); // Additional parameters - shExInfo.lpDirectory = CurrentDir; - shExInfo.nShow = SW_SHOW; - shExInfo.hInstApp = 0; - - DWORD ReturnCode = 1; - if (ShellExecuteEx(&shExInfo)) + int WinRelaunchElevated() { - WaitForSingleObject(shExInfo.hProcess, INFINITE); - GetExitCodeProcess(shExInfo.hProcess, &ReturnCode); - CloseHandle(shExInfo.hProcess); - if (ReturnCode == 0) + TCHAR CurrentDir[4096]; + GetCurrentDirectory(4096, CurrentDir); + + ExtendableWideStringBuilder<256> Parameters; + std::filesystem::path ExecutablePath = GetRunningExecutablePath(); + std::wstring CommandLine(GetCommandLine()); + std::string::size_type ExtraLength = CommandLine[0] == '\"' ? 2 : 0; + std::wstring CommandArguments = CommandLine.substr(ExecutablePath.string().length() + ExtraLength + 1); + + ZEN_CONSOLE("Attempting to run '{} {}' elevated...", ExecutablePath, WideToUtf8(CommandArguments)); + + SHELLEXECUTEINFO shExInfo = {0}; + shExInfo.cbSize = sizeof(shExInfo); + shExInfo.fMask = SEE_MASK_NOCLOSEPROCESS | SEE_MASK_NO_CONSOLE; + shExInfo.hwnd = 0; + shExInfo.lpVerb = TEXT("runas"); // Operation to perform + shExInfo.lpFile = ExecutablePath.c_str(); // Application to start + shExInfo.lpParameters = CommandArguments.c_str(); // Additional parameters + shExInfo.lpDirectory = CurrentDir; + shExInfo.nShow = SW_SHOW; + shExInfo.hInstApp = 0; + + DWORD ReturnCode = 1; + if (ShellExecuteEx(&shExInfo)) { - ZEN_CONSOLE("Elevated execution completed successfully."); + WaitForSingleObject(shExInfo.hProcess, INFINITE); + GetExitCodeProcess(shExInfo.hProcess, &ReturnCode); + CloseHandle(shExInfo.hProcess); + if (ReturnCode == 0) + { + ZEN_CONSOLE("Elevated execution completed successfully."); + } + else + { + ZEN_CONSOLE("Elevated execution completed unsuccessfully, return code: '{}'.", ReturnCode); + } } else { - ZEN_CONSOLE("Elevated execution completed unsuccessfully, return code: '{}'.", ReturnCode); + ZEN_CONSOLE("Failed to run elevated, operation did not complete."); } + return (int)ReturnCode; } - else + +#else // ZEN_PLATFORM_WINDOWS + + bool IsElevated() { return geteuid() == 0; } + +#endif // ZEN_PLATFORM_WINDOWS + + int RunElevated(bool AllowElevation) { - ZEN_CONSOLE("Failed to run elevated, operation did not complete."); +#if ZEN_PLATFORM_WINDOWS + if (AllowElevation) + { + return WinRelaunchElevated(); + } + else + { + ZEN_CONSOLE( + "This command requires elevated priviliges. Run command with elevated priviliges or add '--allow-elevation' command line " + "option."); + return 1; + } +#endif // ZEN_PLATFORM_WINDOWS +#if ZEN_PLATFORM_LINUX || ZEN_PLATFORM_MAC + ZEN_CONSOLE("This command requires elevated priviliges. Run the command with `sudo`"); + return 1; +#endif // ZEN_PLATFORM_LINUX || ZEN_PLATFORM_MAC } - return (int)ReturnCode; -} -#endif +} // namespace ServiceCommand::ServiceCommand() { @@ -141,6 +173,14 @@ ServiceCommand::ServiceCommand() ""); m_InstallOptions.parse_positional({"executable", "name", "display-name"}); m_InstallOptions.positional_help("executable name display-name"); +#if ZEN_PLATFORM_WINDOWS + m_InstallOptions.add_option("", + "", + "allow-elevation", + fmt::format("Allow attempt to relauch command using an elevated"), + cxxopts::value(m_AllowElevation), + ""); +#endif // ZEN_PLATFORM_WINDOWS m_UninstallOptions.add_options()("h,help", "Print help"); m_UninstallOptions.add_option("", @@ -151,6 +191,14 @@ ServiceCommand::ServiceCommand() ""); m_UninstallOptions.parse_positional({"name"}); m_UninstallOptions.positional_help("name"); +#if ZEN_PLATFORM_WINDOWS + m_UninstallOptions.add_option("", + "", + "allow-elevation", + fmt::format("Allow attempt to relauch command using an elevated"), + cxxopts::value(m_AllowElevation), + ""); +#endif // ZEN_PLATFORM_WINDOWS m_StartOptions.add_options()("h,help", "Print help"); m_StartOptions.add_option("", @@ -161,6 +209,14 @@ ServiceCommand::ServiceCommand() ""); m_StartOptions.parse_positional({"name"}); m_StartOptions.positional_help("name"); +#if ZEN_PLATFORM_WINDOWS + m_StartOptions.add_option("", + "", + "allow-elevation", + fmt::format("Allow attempt to relauch command using an elevated"), + cxxopts::value(m_AllowElevation), + ""); +#endif // ZEN_PLATFORM_WINDOWS m_StopOptions.add_options()("h,help", "Print help"); m_StopOptions.add_option("", @@ -171,6 +227,14 @@ ServiceCommand::ServiceCommand() ""); m_StopOptions.parse_positional({"name"}); m_StopOptions.positional_help("name"); +#if ZEN_PLATFORM_WINDOWS + m_StopOptions.add_option("", + mak "", + "allow-elevation", + fmt::format("Allow attempt to relauch command using an elevated"), + cxxopts::value(m_AllowElevation), + ""); +#endif // ZEN_PLATFORM_WINDOWS } ServiceCommand::~ServiceCommand() = default; @@ -254,12 +318,11 @@ ServiceCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) return 1; } -#if ZEN_PLATFORM_WINDOWS - if (!WinIsElevated()) + if (!IsElevated()) { - return WinRelaunchElevated(); + return RunElevated(m_AllowElevation); } -#endif // ZEN_PLATFORM_WINDOWS + if (m_ServerExecutable.empty()) { std::filesystem::path ExePath = zen::GetRunningExecutablePath(); @@ -300,12 +363,11 @@ ServiceCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) return 0; } -#if ZEN_PLATFORM_WINDOWS - if (!WinIsElevated()) + if (!IsElevated()) { - return WinRelaunchElevated(); + return RunElevated(m_AllowElevation); } -#endif // ZEN_PLATFORM_WINDOWS + Ec = UninstallService(m_ServiceName); if (Ec) { @@ -335,12 +397,11 @@ ServiceCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) return 1; } -#if ZEN_PLATFORM_WINDOWS - if (!WinIsElevated()) + if (!IsElevated()) { - return WinRelaunchElevated(); + return RunElevated(m_AllowElevation); } -#endif // ZEN_PLATFORM_WINDOWS + Ec = StartService(m_ServiceName); if (Ec) { @@ -370,12 +431,11 @@ ServiceCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) return 1; } -#if ZEN_PLATFORM_WINDOWS - if (!WinIsElevated()) + if (!IsElevated()) { - return WinRelaunchElevated(); + return RunElevated(m_AllowElevation); } -#endif // ZEN_PLATFORM_WINDOWS + Ec = StopService(m_ServiceName); if (Ec) { diff --git a/src/zen/cmds/service_cmd.h b/src/zen/cmds/service_cmd.h index 15273e952..b247a63ce 100644 --- a/src/zen/cmds/service_cmd.h +++ b/src/zen/cmds/service_cmd.h @@ -27,6 +27,8 @@ private: std::string m_ServiceName = "ZenServer"; + bool m_AllowElevation = false; + cxxopts::Options m_StatusOptions{"status", "Show information about an installed zenserver service"}; cxxopts::Options m_InstallOptions{ -- cgit v1.2.3 From 5e02ddf6cc3d311cafbd4fcff326ece0141a992e Mon Sep 17 00:00:00 2001 From: Dan Engelbrecht Date: Fri, 10 Jan 2025 14:34:49 +0100 Subject: partially working service commands for macos --- src/zen/cmds/service_cmd.cpp | 3 +- src/zenutil/service.cpp | 275 +++++++++++++++++++++++++++++++++++++++---- 2 files changed, 252 insertions(+), 26 deletions(-) diff --git a/src/zen/cmds/service_cmd.cpp b/src/zen/cmds/service_cmd.cpp index 0d538ffda..de3466eff 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 geteuid() == 0; } + bool IsElevated() { return true;/*geteuid() == 0;*/ } #endif // ZEN_PLATFORM_WINDOWS @@ -118,6 +118,7 @@ namespace { } #endif // ZEN_PLATFORM_WINDOWS #if ZEN_PLATFORM_LINUX || ZEN_PLATFORM_MAC + ZEN_UNUSED(AllowElevation); ZEN_CONSOLE("This command requires elevated priviliges. Run the command with `sudo`"); return 1; #endif // ZEN_PLATFORM_LINUX || ZEN_PLATFORM_MAC diff --git a/src/zenutil/service.cpp b/src/zenutil/service.cpp index 72367a070..feebeb7f4 100644 --- a/src/zenutil/service.cpp +++ b/src/zenutil/service.cpp @@ -243,6 +243,110 @@ namespace { } #endif // ZEN_PLATFORM_MAC + + +#if ZEN_PLATFORM_MAC || ZEN_PLATFORM_LINUX + +std::pair ExecuteProgram(std::string_view Cmd) +{ + std::string data; + const int max_buffer = 256; + char buffer[max_buffer]; + std::string Command(Cmd); + Command.append(" 2>&1"); + + FILE * stream = popen(Command.c_str(), "r"); + if (stream) + { + while (!feof(stream)) + { + if (fgets(buffer, max_buffer, stream) != NULL) + { + data.append(buffer); + } + } + + int Res = -1; + int st = pclose(stream); + if (WIFEXITED(st)) Res = WEXITSTATUS(st); + return {Res, 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 || ZEN_PLATFORM_LINUX + + } // namespace std::string_view @@ -633,7 +737,7 @@ InstallService(std::string_view ServiceName, const ServiceSpec& Spec) // return MakeErrorCodeFromLastError(); // } - // System: /Library/LaunchDaemons + // System: /Library/LaunchDaemon // All users: /Library/LaunchAgents // Current user: ~/Library/LaunchAgents @@ -665,34 +769,137 @@ InstallService(std::string_view ServiceName, const ServiceSpec& Spec) std::error_code UninstallService(std::string_view ServiceName) { - ZEN_UNUSED(Level); - - std::filesystem::path ServicePath = std::filesystem::path("/usr/local/libexec") / ServiceName; - ZEN_INFO("Attempting to remove service from {}", ServicePath.string()); - if (unlink(ServicePath.string().c_str()) == -1) - { - return MakeErrorCodeFromLastError(); - } +// std::filesystem::path ServicePath = std::filesystem::path("/usr/local/libexec") / ServiceName; +// ZEN_INFO("Attempting to remove service from {}", ServicePath.string()); +// if (unlink(ServicePath.string().c_str()) == -1) +// { +// return MakeErrorCodeFromLastError(); +// } std::string DaemonName = fmt::format("com.epicgames.unreal.{}", ServiceName); - std::filesystem::path PListPath = std::filesystem::path("/Library/LaunchDaemons") / (DaemonName + ".plist"); + std::filesystem::path PListPath = std::filesystem::path("/Library/LaunchDaemon") / (DaemonName + ".plist"); ZEN_INFO("Attempting to remove launchd plist from {}", PListPath.string()); - if (unlink(ServicePath.string().c_str()) == -1) - { - return MakeErrorCodeFromLastError(); - } - - return {}; + std::error_code Ec; + std::filesystem::remove(PListPath, Ec); + return Ec; + +// if (unlink(PListPath.string().c_str()) == -1) +// { +// return MakeErrorCodeFromLastError(); +// } + +// return {}; } std::error_code QueryInstalledService(std::string_view ServiceName, ServiceInfo& OutInfo) { ZEN_UNUSED(ServiceName, OutInfo); - // ZEN_NOT_IMPLEMENTED("QueryInstalledService"); - // std::filesystem::path PListFolder = "/Library/LaunchDaemon"; - // sudo launchctl list OutInfo.Status = ServiceStatus::NotInstalled; + std::string DaemonName = fmt::format("com.epicgames.unreal.{}", ServiceName); + + std::filesystem::path PListFolder = "/Library/LaunchDaemon"; + + std::filesystem::path PListPath = PListFolder / (DaemonName + ".plist"); + if (std::filesystem::is_regular_file(PListPath)) + { + OutInfo.Status = ServiceStatus::Stopped; + + { + // Parse plist :( + IoBuffer Buffer = ReadFile(PListPath).Flatten(); + MemoryView Data = Buffer.GetView(); + std::string PList((const char*)Data.GetData(), Data.GetSize()); + ZEN_INFO("{}", PList); + } + { + std::pair Res = ExecuteProgram(std::string("launchctl list ") + DaemonName); + if (Res.first == 0 && !Res.second.empty()) + { +// bool IsParsingArguments = false; + ForEachStrTok(Res.second, '\n', [&](std::string_view Line){ +// if (IsParsingArguments) +// { +// if (Line.find(");") == Line.length() - 2) +// { +// IsParsingArguments = false; +// return true; +// } +// std::string_view::size_type ProgramArgumentsEnd = Line.find_last_of('\"'); +// std::string_view::size_type ProgramArgumentsStart = Line.find('\"') + 1; +// std::string_view ProgramArgumentsString = Line.substr(ProgramArgumentsStart, ProgramArgumentsEnd - ProgramArgumentsStart); +// if (ProgramArgumentsString != OutInfo.Spec.ExecutablePath) +// { +// if (!OutInfo.Spec.CommandLineOptions.empty()) +// { +// OutInfo.Spec.CommandLineOptions += " "; +// } +// OutInfo.Spec.CommandLineOptions += ProgramArgumentsString; +// } +// return true; +// } + if (Line.find("\"PID\"") != std::string_view::npos) + { + std::string_view::size_type PidStart = Line.find('='); + std::string_view::size_type PidEnd = Line.find(';'); + std::string_view PidString = Line.substr(PidStart+2, PidEnd - (PidStart + 2)); + if (ParseInt(PidString).has_value()) + { + OutInfo.Status = ServiceStatus::Running; + } + return false; + } +// if (Line.find("\"Program\"") != std::string_view::npos) +// { +// std::string_view::size_type ProgramEnd = Line.find_last_of('\"'); +// std::string_view::size_type ProgramStart = Line.find_last_of('\"', ProgramEnd - 1) + 1; +// std::string_view ProgramString = Line.substr(ProgramStart, ProgramEnd - ProgramStart); +// OutInfo.Spec.ExecutablePath = ProgramString; +// return true; +// } +// if (Line.find("\"ProgramArguments\"") != std::string_view::npos) +// { +// IsParsingArguments = true; +// return true; +// } + return true; + }); + // Parse installed info + } + } +#if 0 + { + std::pair Res = ExecuteProgram("launchctl list"); + if (Res.first != 0) + { + return MakeErrorCode(Res.first); + } + std::string ZenServerLine; + ForEachStrTok(Res.second, '\n', [&](std::string_view Line){ + if (Line.find(DaemonName) != std::string_view::npos) + { + ZenServerLine = Line; + } + return true; + }); + if (!ZenServerLine.empty()) + { + std::vector Parts; + ForEachStrTok(ZenServerLine, '\t', [&](std::string_view Line){ + Parts.push_back(Line); + return true; + }); + if (Parts.size() == 3) + { + if (Parts[0] != "-" && ParseInt(Parts[0]).has_value()) + { + OutInfo.Status = ServiceStatus::Running; + } + } + } + } +#endif // 0 + } return {}; } @@ -700,18 +907,36 @@ QueryInstalledService(std::string_view ServiceName, ServiceInfo& OutInfo) std::error_code StartService(std::string_view ServiceName) { - ZEN_UNUSED(ServiceName, Level); - ZEN_NOT_IMPLEMENTED("StartService"); - // sudo launchctl bootstrap system /Library/LaunchDaemon/com.epicgames.unreal.ZenServer.plist + ZEN_UNUSED(ServiceName); + + std::string DaemonName = fmt::format("com.epicgames.unreal.{}", ServiceName); + std::filesystem::path PListFolder = "/Library/LaunchDaemon"; + std::filesystem::path PListPath = PListFolder / (DaemonName + ".plist"); + + std::pair Res = ExecuteProgram(std::string("launchctl bootstrap system ") + PListPath.string()); + if (Res.first != 0) + { + return MakeErrorCode(Res.first); + } + return {}; } std::error_code StopService(std::string_view ServiceName) { - ZEN_UNUSED(ServiceName, Level); - ZEN_NOT_IMPLEMENTED("StopService"); - // sudo launchctl bootout system /Library/LaunchDaemon/com.epicgames.unreal.ZenServer.plist + ZEN_UNUSED(ServiceName); + + std::string DaemonName = fmt::format("com.epicgames.unreal.{}", ServiceName); + std::filesystem::path PListFolder = "/Library/LaunchDaemon"; + std::filesystem::path PListPath = PListFolder / (DaemonName + ".plist"); + + std::pair Res = ExecuteProgram(std::string("launchctl bootout system ") + PListPath.string()); + if (Res.first != 0) + { + return MakeErrorCode(Res.first); + } + return {}; } -- cgit v1.2.3 From 782745c3eaf0514172deccd37e443e109d779d5d Mon Sep 17 00:00:00 2001 From: Dan Engelbrecht Date: Fri, 10 Jan 2025 15:40:04 +0100 Subject: cleanups --- src/zenutil/service.cpp | 264 +++++++++++++++++++----------------------------- 1 file changed, 105 insertions(+), 159 deletions(-) diff --git a/src/zenutil/service.cpp b/src/zenutil/service.cpp index feebeb7f4..08ccac512 100644 --- a/src/zenutil/service.cpp +++ b/src/zenutil/service.cpp @@ -173,14 +173,26 @@ namespace { } } + std::string GetDaemonName(std::string_view ServiceName) + { + return fmt::format("com.epicgames.unreal.{}", ServiceName); + } + + std::filesystem::path GetPListPath(const std::string& DaemonName) + { + const std::filesystem::path PListFolder = "/Library/LaunchDaemons"; + return PListFolder / (DaemonName + ".plist"); + } + std::string BuildPlist(std::string_view ServiceName, const std::filesystem::path& ExecutablePath, std::string_view CommandLineOptions, std::string_view DaemonName, - std::string_view /*ServiceDisplayName*/, - std::string_view /*ServiceDescription*/, + std::string_view ServiceDisplayName, + std::string_view ServiceDescription, bool Debug) { + ZEN_UNUSED(ServiceDisplayName, ServiceDescription); std::vector Arguments = SplitArguments(CommandLineOptions); ExtendableStringBuilder<256> ProgramArguments; for (const std::string_view Argument : Arguments) @@ -678,76 +690,18 @@ StopService(std::string_view ServiceName) return {}; } -#else - -# if 0 -static int CopyFile(std::filesystem::path source, std::filesystem::path dest) -{ +#endif - int childExitStatus; - pid_t pid; - int status; - - pid = fork(); - - if (pid == 0) { /* child */ - execl("/bin/cp", "/bin/cp", source.string().c_str(), dest.string().c_str(), (char *)0); - return 0; - } - else if (pid < 0) { - return -1; - } - else { - /* parent - wait for child - this has all error handling, you - * could just call wait() as long as you are only expecting to - * have one child process at a time. - */ - pid_t ws = waitpid( pid, &childExitStatus, WNOHANG); - if (ws == -1) - { - return -1; - } - - if( WIFEXITED(childExitStatus)) /* exit code in childExitStatus */ - { - status = WEXITSTATUS(childExitStatus); /* zero is normal exit */ - return status; - } - else if (WIFSIGNALED(childExitStatus)) /* killed */ - { - return -1; - } - else if (WIFSTOPPED(childExitStatus)) /* stopped */ - { - return -1; - } - return -1; - } -} -# endif +#if ZEN_PLATFORM_MAC std::error_code InstallService(std::string_view ServiceName, const ServiceSpec& Spec) { - // std::filesystem::path ServicePath = std::filesystem::path("/usr/local/libexec") / ServiceName; - // ZEN_INFO("Attempting to copy service from {} to {}", Spec.ExecutablePath.string(), ServicePath.string()); - // if (false && CopyFile(Spec.ExecutablePath, ServicePath) == -1) - //// if (symlink(Spec.ExecutablePath.c_str(), ServicePath.c_str()) == -1) - // { - // return MakeErrorCodeFromLastError(); - // } - - // System: /Library/LaunchDaemon - // All users: /Library/LaunchAgents - // Current user: ~/Library/LaunchAgents - - std::string DaemonName = fmt::format("com.epicgames.unreal.{}", ServiceName); + const std::string DaemonName = GetDaemonName(ServiceName); std::string PList = BuildPlist(ServiceName, Spec.ExecutablePath, Spec.CommandLineOptions, DaemonName, Spec.DisplayName, Spec.Description, true); - std::filesystem::path PListFolder = "/Library/LaunchDaemon"; - - std::filesystem::path PListPath = PListFolder / (DaemonName + ".plist"); + const std::filesystem::path PListPath = GetPListPath(DaemonName); ZEN_INFO("Writing launchd plist to {}", PListPath.string()); try { @@ -758,8 +712,8 @@ InstallService(std::string_view ServiceName, const ServiceSpec& Spec) return MakeErrorCode(Ex.code().value()); } - ZEN_INFO("Changing permissions to 600 for {}", PListPath.string()); - if (chmod(PListPath.string().c_str(), 0600) == -1) + ZEN_INFO("Changing permissions to 644 for {}", PListPath.string()); + if (chmod(PListPath.string().c_str(), 0644) == -1) { return MakeErrorCodeFromLastError(); } @@ -769,25 +723,12 @@ InstallService(std::string_view ServiceName, const ServiceSpec& Spec) std::error_code UninstallService(std::string_view ServiceName) { -// std::filesystem::path ServicePath = std::filesystem::path("/usr/local/libexec") / ServiceName; -// ZEN_INFO("Attempting to remove service from {}", ServicePath.string()); -// if (unlink(ServicePath.string().c_str()) == -1) -// { -// return MakeErrorCodeFromLastError(); -// } - std::string DaemonName = fmt::format("com.epicgames.unreal.{}", ServiceName); - std::filesystem::path PListPath = std::filesystem::path("/Library/LaunchDaemon") / (DaemonName + ".plist"); + const std::string DaemonName = GetDaemonName(ServiceName); + const std::filesystem::path PListPath = GetPListPath(DaemonName); ZEN_INFO("Attempting to remove launchd plist from {}", PListPath.string()); std::error_code Ec; std::filesystem::remove(PListPath, Ec); return Ec; - -// if (unlink(PListPath.string().c_str()) == -1) -// { -// return MakeErrorCodeFromLastError(); -// } - -// return {}; } std::error_code @@ -796,11 +737,9 @@ QueryInstalledService(std::string_view ServiceName, ServiceInfo& OutInfo) ZEN_UNUSED(ServiceName, OutInfo); OutInfo.Status = ServiceStatus::NotInstalled; - std::string DaemonName = fmt::format("com.epicgames.unreal.{}", ServiceName); - - std::filesystem::path PListFolder = "/Library/LaunchDaemon"; + const std::string DaemonName = GetDaemonName(ServiceName); - std::filesystem::path PListPath = PListFolder / (DaemonName + ".plist"); + const std::filesystem::path PListPath = GetPListPath(DaemonName); if (std::filesystem::is_regular_file(PListPath)) { OutInfo.Status = ServiceStatus::Stopped; @@ -810,34 +749,88 @@ QueryInstalledService(std::string_view ServiceName, ServiceInfo& OutInfo) IoBuffer Buffer = ReadFile(PListPath).Flatten(); MemoryView Data = Buffer.GetView(); std::string PList((const char*)Data.GetData(), Data.GetSize()); - ZEN_INFO("{}", PList); + + enum class ParseMode + { + None, + ExpectingProgramArgumentsArray, + ExpectingProgramExecutablePath, + ExpectingCommandLineOption + }; + + ParseMode Mode = ParseMode::None; + + ForEachStrTok(PList, '\n', [&](std::string_view Line){ + switch (Mode) + { + case ParseMode::None: + { + if (Line.find("ProgramArguments") != std::string_view::npos) + { + Mode = ParseMode::ExpectingProgramArgumentsArray; + return true; + } + } + break; + case ParseMode::ExpectingProgramArgumentsArray: + { + if (Line.find("") != std::string_view::npos) + { + Mode = ParseMode::ExpectingProgramExecutablePath; + return true; + } + Mode = ParseMode::None; + } + break; + case ParseMode::ExpectingProgramExecutablePath: + { + if (std::string_view::size_type ArgStart = Line.find(""); ArgStart != std::string_view::npos) + { + ArgStart += 8; + if (std::string_view::size_type ArgEnd = Line.find("", ArgStart); ArgEnd != std::string_view::npos) + { + std::string_view ProgramString = Line.substr(ArgStart, ArgEnd - ArgStart); + OutInfo.Spec.ExecutablePath = ProgramString; + Mode = ParseMode::ExpectingCommandLineOption; + return true; + } + } + Mode = ParseMode::None; + } + break; + case ParseMode::ExpectingCommandLineOption: + { + if (std::string_view::size_type ArgStart = Line.find(""); ArgStart != std::string_view::npos) + { + Mode = ParseMode::None; + return true; + } + else if (std::string_view::size_type ArgStart = Line.find(""); ArgStart != std::string_view::npos) + { + ArgStart += 8; + if (std::string_view::size_type ArgEnd = Line.find("", ArgStart); ArgEnd != std::string_view::npos) + { + std::string_view ArgumentString = Line.substr(ArgStart, ArgEnd - ArgStart); + if (!OutInfo.Spec.CommandLineOptions.empty()) + { + OutInfo.Spec.CommandLineOptions += " "; + } + OutInfo.Spec.CommandLineOptions += ArgumentString; + return true; + } + } + Mode = ParseMode::None; + } + break; + } + return true; + }); } { std::pair Res = ExecuteProgram(std::string("launchctl list ") + DaemonName); if (Res.first == 0 && !Res.second.empty()) { -// bool IsParsingArguments = false; ForEachStrTok(Res.second, '\n', [&](std::string_view Line){ -// if (IsParsingArguments) -// { -// if (Line.find(");") == Line.length() - 2) -// { -// IsParsingArguments = false; -// return true; -// } -// std::string_view::size_type ProgramArgumentsEnd = Line.find_last_of('\"'); -// std::string_view::size_type ProgramArgumentsStart = Line.find('\"') + 1; -// std::string_view ProgramArgumentsString = Line.substr(ProgramArgumentsStart, ProgramArgumentsEnd - ProgramArgumentsStart); -// if (ProgramArgumentsString != OutInfo.Spec.ExecutablePath) -// { -// if (!OutInfo.Spec.CommandLineOptions.empty()) -// { -// OutInfo.Spec.CommandLineOptions += " "; -// } -// OutInfo.Spec.CommandLineOptions += ProgramArgumentsString; -// } -// return true; -// } if (Line.find("\"PID\"") != std::string_view::npos) { std::string_view::size_type PidStart = Line.find('='); @@ -849,56 +842,11 @@ QueryInstalledService(std::string_view ServiceName, ServiceInfo& OutInfo) } return false; } -// if (Line.find("\"Program\"") != std::string_view::npos) -// { -// std::string_view::size_type ProgramEnd = Line.find_last_of('\"'); -// std::string_view::size_type ProgramStart = Line.find_last_of('\"', ProgramEnd - 1) + 1; -// std::string_view ProgramString = Line.substr(ProgramStart, ProgramEnd - ProgramStart); -// OutInfo.Spec.ExecutablePath = ProgramString; -// return true; -// } -// if (Line.find("\"ProgramArguments\"") != std::string_view::npos) -// { -// IsParsingArguments = true; -// return true; -// } return true; }); // Parse installed info } } -#if 0 - { - std::pair Res = ExecuteProgram("launchctl list"); - if (Res.first != 0) - { - return MakeErrorCode(Res.first); - } - std::string ZenServerLine; - ForEachStrTok(Res.second, '\n', [&](std::string_view Line){ - if (Line.find(DaemonName) != std::string_view::npos) - { - ZenServerLine = Line; - } - return true; - }); - if (!ZenServerLine.empty()) - { - std::vector Parts; - ForEachStrTok(ZenServerLine, '\t', [&](std::string_view Line){ - Parts.push_back(Line); - return true; - }); - if (Parts.size() == 3) - { - if (Parts[0] != "-" && ParseInt(Parts[0]).has_value()) - { - OutInfo.Status = ServiceStatus::Running; - } - } - } - } -#endif // 0 } return {}; @@ -909,9 +857,8 @@ StartService(std::string_view ServiceName) { ZEN_UNUSED(ServiceName); - std::string DaemonName = fmt::format("com.epicgames.unreal.{}", ServiceName); - std::filesystem::path PListFolder = "/Library/LaunchDaemon"; - std::filesystem::path PListPath = PListFolder / (DaemonName + ".plist"); + const std::string DaemonName = GetDaemonName(ServiceName); + const std::filesystem::path PListPath = GetPListPath(DaemonName); std::pair Res = ExecuteProgram(std::string("launchctl bootstrap system ") + PListPath.string()); if (Res.first != 0) @@ -927,9 +874,8 @@ StopService(std::string_view ServiceName) { ZEN_UNUSED(ServiceName); - std::string DaemonName = fmt::format("com.epicgames.unreal.{}", ServiceName); - std::filesystem::path PListFolder = "/Library/LaunchDaemon"; - std::filesystem::path PListPath = PListFolder / (DaemonName + ".plist"); + const std::string DaemonName = GetDaemonName(ServiceName); + const std::filesystem::path PListPath = GetPListPath(DaemonName); std::pair Res = ExecuteProgram(std::string("launchctl bootout system ") + PListPath.string()); if (Res.first != 0) @@ -940,6 +886,6 @@ StopService(std::string_view ServiceName) return {}; } -#endif // ZEN_PLATFORM_WINDOWS +#endif // ZEN_PLATFORM_MAC } // namespace zen -- cgit v1.2.3 From 7a73a49de6ddbc6712554c5c9b2863dd8e7de0f2 Mon Sep 17 00:00:00 2001 From: Dan Engelbrecht Date: Fri, 10 Jan 2025 15:52:34 +0100 Subject: displayname and description are windows-only properties --- src/zen/cmds/service_cmd.cpp | 62 +++++++++++++++++++---------------- src/zen/cmds/service_cmd.h | 2 ++ src/zenutil/include/zenutil/service.h | 4 +++ src/zenutil/service.cpp | 5 +-- 4 files changed, 41 insertions(+), 32 deletions(-) diff --git a/src/zen/cmds/service_cmd.cpp b/src/zen/cmds/service_cmd.cpp index de3466eff..db60d9b54 100644 --- a/src/zen/cmds/service_cmd.cpp +++ b/src/zen/cmds/service_cmd.cpp @@ -160,6 +160,7 @@ ServiceCommand::ServiceCommand() fmt::format("Service name, defaults to \"{}\"", m_ServiceName), cxxopts::value(m_ServiceName), ""); +#if ZEN_PLATFORM_WINDOWS m_InstallOptions.add_option("", "d", "display-name", @@ -172,9 +173,6 @@ ServiceCommand::ServiceCommand() fmt::format("Service description", m_ServiceDescription), cxxopts::value(m_ServiceDescription), ""); - m_InstallOptions.parse_positional({"executable", "name", "display-name"}); - m_InstallOptions.positional_help("executable name display-name"); -#if ZEN_PLATFORM_WINDOWS m_InstallOptions.add_option("", "", "allow-elevation", @@ -182,6 +180,8 @@ ServiceCommand::ServiceCommand() cxxopts::value(m_AllowElevation), ""); #endif // ZEN_PLATFORM_WINDOWS + m_InstallOptions.parse_positional({"executable", "name", "display-name"}); + m_InstallOptions.positional_help("executable name display-name"); m_UninstallOptions.add_options()("h,help", "Print help"); m_UninstallOptions.add_option("", @@ -240,6 +240,27 @@ ServiceCommand::ServiceCommand() ServiceCommand::~ServiceCommand() = default; +std::string FmtServiceInfo(const ServiceInfo& Info, std::string_view Prefix) +{ + std::string Result = fmt::format( + "{}Status: {}\n" + "{}Executable: {}\n" + "{}CommandLineOptions: {}", + Prefix, ToString(Info.Status), + Prefix, Info.Spec.ExecutablePath, + Prefix, Info.Spec.CommandLineOptions); + +#if ZEN_PLATFORM_WINDOWS + Result += fmt::format("\n" + "{}Display Name: {}\n" + "{}Description: {}", + Prefix, Info.Spec.DisplayName, + Prefix, Info.Spec.Description); +#endif // ZEN_PLATFORM_WINDOWS + + return Result; +} + int ServiceCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) { @@ -282,18 +303,9 @@ ServiceCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) else { ZEN_CONSOLE( - "Service '{}':\n" - " Status: {}\n" - " Executable: {}\n" - " CommandLineOptions: {}\n" - " Display Name: {}\n" - " Description: {}", + "Service '{}':\n{}", m_ServiceName, - ToString(Info.Status), - Info.Spec.ExecutablePath, - Info.Spec.CommandLineOptions, - Info.Spec.DisplayName, - Info.Spec.Description); + FmtServiceInfo(Info, " ")); } } @@ -304,18 +316,9 @@ ServiceCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) if (!Ec && Info.Status != ServiceStatus::NotInstalled) { ZEN_CONSOLE( - "Service '{}' already installed:\n" - " Status: {}\n" - " Executable: {}\n" - " CommandLineOptions: {}\n" - " Display Name: {}\n" - " Description: {}", + "Service '{}' already installed:\n{}", m_ServiceName, - ToString(Info.Status), - Info.Spec.ExecutablePath, - Info.Spec.CommandLineOptions, - Info.Spec.DisplayName, - Info.Spec.Description); + FmtServiceInfo(Info, " ")); return 1; } @@ -333,9 +336,12 @@ ServiceCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) m_ServerExecutable = std::filesystem::absolute(m_ServerExecutable); Ec = InstallService(m_ServiceName, ServiceSpec{.ExecutablePath = m_ServerExecutable, - .CommandLineOptions = GlobalOptions.PassthroughCommandLine, - .DisplayName = m_ServiceDisplayName, - .Description = m_ServiceDescription}); + .CommandLineOptions = GlobalOptions.PassthroughCommandLine + #if ZEN_PLATFORM_WINDOWS + ,.DisplayName = m_ServiceDisplayName + ,.Description = m_ServiceDescription + #endif // ZEN_PLATFORM_WINDOWS + }); if (Ec) { ZEN_CONSOLE("Failed to install service '{}' using '{}' . Reason: '{}'", m_ServiceName, m_ServerExecutable, Ec.message()); diff --git a/src/zen/cmds/service_cmd.h b/src/zen/cmds/service_cmd.h index b247a63ce..00a442048 100644 --- a/src/zen/cmds/service_cmd.h +++ b/src/zen/cmds/service_cmd.h @@ -35,8 +35,10 @@ private: "install", "Install zenserver as a service. Arguments following \" -- \" will be added as parameters to the installed service."}; std::filesystem::path m_ServerExecutable; +#if ZEN_PLATFORM_WINDOWS std::string m_ServiceDisplayName = "Unreal Zen Storage Service"; std::string m_ServiceDescription; +#endif // ZEN_PLATFORM_WINDOWS cxxopts::Options m_UninstallOptions{"uninstall", "Uninstall zenserver as a service"}; diff --git a/src/zenutil/include/zenutil/service.h b/src/zenutil/include/zenutil/service.h index 53c3483aa..1e96031f7 100644 --- a/src/zenutil/include/zenutil/service.h +++ b/src/zenutil/include/zenutil/service.h @@ -2,6 +2,8 @@ #pragma once +#include + #include #include @@ -18,8 +20,10 @@ struct ServiceSpec { std::filesystem::path ExecutablePath; std::string CommandLineOptions; +#if ZEN_PLATFORM_WINDOWS std::string DisplayName; std::string Description; +#endif // ZEN_PLATFORM_WINDOWS }; enum class ServiceStatus diff --git a/src/zenutil/service.cpp b/src/zenutil/service.cpp index 08ccac512..f85a8e7e3 100644 --- a/src/zenutil/service.cpp +++ b/src/zenutil/service.cpp @@ -188,11 +188,8 @@ namespace { const std::filesystem::path& ExecutablePath, std::string_view CommandLineOptions, std::string_view DaemonName, - std::string_view ServiceDisplayName, - std::string_view ServiceDescription, bool Debug) { - ZEN_UNUSED(ServiceDisplayName, ServiceDescription); std::vector Arguments = SplitArguments(CommandLineOptions); ExtendableStringBuilder<256> ProgramArguments; for (const std::string_view Argument : Arguments) @@ -699,7 +696,7 @@ InstallService(std::string_view ServiceName, const ServiceSpec& Spec) { const std::string DaemonName = GetDaemonName(ServiceName); std::string PList = - BuildPlist(ServiceName, Spec.ExecutablePath, Spec.CommandLineOptions, DaemonName, Spec.DisplayName, Spec.Description, true); + BuildPlist(ServiceName, Spec.ExecutablePath, Spec.CommandLineOptions, DaemonName, true); const std::filesystem::path PListPath = GetPListPath(DaemonName); ZEN_INFO("Writing launchd plist to {}", PListPath.string()); -- cgit v1.2.3 From 6aeec009d348a6bef2923f6eebb9997dfd363cdf Mon Sep 17 00:00:00 2001 From: Dan Engelbrecht Date: Fri, 10 Jan 2025 15:53:34 +0100 Subject: clang format --- src/zen/cmds/service_cmd.cpp | 54 +++++++-------- src/zen/cmds/service_cmd.h | 6 +- src/zenutil/include/zenutil/service.h | 6 +- src/zenutil/service.cpp | 119 ++++++++++++++++------------------ 4 files changed, 91 insertions(+), 94 deletions(-) diff --git a/src/zen/cmds/service_cmd.cpp b/src/zen/cmds/service_cmd.cpp index db60d9b54..5011ac12f 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 true; /*geteuid() == 0;*/ } #endif // ZEN_PLATFORM_WINDOWS @@ -240,25 +240,32 @@ ServiceCommand::ServiceCommand() ServiceCommand::~ServiceCommand() = default; -std::string FmtServiceInfo(const ServiceInfo& Info, std::string_view Prefix) +std::string +FmtServiceInfo(const ServiceInfo& Info, std::string_view Prefix) { std::string Result = fmt::format( "{}Status: {}\n" "{}Executable: {}\n" "{}CommandLineOptions: {}", - Prefix, ToString(Info.Status), - Prefix, Info.Spec.ExecutablePath, - Prefix, Info.Spec.CommandLineOptions); + Prefix, + ToString(Info.Status), + Prefix, + Info.Spec.ExecutablePath, + Prefix, + Info.Spec.CommandLineOptions); #if ZEN_PLATFORM_WINDOWS - Result += fmt::format("\n" + Result += fmt::format( + "\n" "{}Display Name: {}\n" "{}Description: {}", - Prefix, Info.Spec.DisplayName, - Prefix, Info.Spec.Description); -#endif // ZEN_PLATFORM_WINDOWS + Prefix, + Info.Spec.DisplayName, + Prefix, + Info.Spec.Description); +#endif // ZEN_PLATFORM_WINDOWS - return Result; + return Result; } int @@ -302,10 +309,7 @@ ServiceCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) } else { - ZEN_CONSOLE( - "Service '{}':\n{}", - m_ServiceName, - FmtServiceInfo(Info, " ")); + ZEN_CONSOLE("Service '{}':\n{}", m_ServiceName, FmtServiceInfo(Info, " ")); } } @@ -315,10 +319,7 @@ ServiceCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) std::error_code Ec = QueryInstalledService(m_ServiceName, Info); if (!Ec && Info.Status != ServiceStatus::NotInstalled) { - ZEN_CONSOLE( - "Service '{}' already installed:\n{}", - m_ServiceName, - FmtServiceInfo(Info, " ")); + ZEN_CONSOLE("Service '{}' already installed:\n{}", m_ServiceName, FmtServiceInfo(Info, " ")); return 1; } @@ -334,14 +335,15 @@ ServiceCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) m_ServerExecutable = ExePath; } m_ServerExecutable = std::filesystem::absolute(m_ServerExecutable); - Ec = InstallService(m_ServiceName, - ServiceSpec{.ExecutablePath = m_ServerExecutable, - .CommandLineOptions = GlobalOptions.PassthroughCommandLine - #if ZEN_PLATFORM_WINDOWS - ,.DisplayName = m_ServiceDisplayName - ,.Description = m_ServiceDescription - #endif // ZEN_PLATFORM_WINDOWS - }); + Ec = InstallService( + m_ServiceName, + ServiceSpec { + .ExecutablePath = m_ServerExecutable, .CommandLineOptions = GlobalOptions.PassthroughCommandLine +#if ZEN_PLATFORM_WINDOWS + , + .DisplayName = m_ServiceDisplayName, .Description = m_ServiceDescription +#endif // ZEN_PLATFORM_WINDOWS + }); if (Ec) { ZEN_CONSOLE("Failed to install service '{}' using '{}' . Reason: '{}'", m_ServiceName, m_ServerExecutable, Ec.message()); diff --git a/src/zen/cmds/service_cmd.h b/src/zen/cmds/service_cmd.h index 00a442048..4d370b29c 100644 --- a/src/zen/cmds/service_cmd.h +++ b/src/zen/cmds/service_cmd.h @@ -36,9 +36,9 @@ private: "Install zenserver as a service. Arguments following \" -- \" will be added as parameters to the installed service."}; std::filesystem::path m_ServerExecutable; #if ZEN_PLATFORM_WINDOWS - std::string m_ServiceDisplayName = "Unreal Zen Storage Service"; - std::string m_ServiceDescription; -#endif // ZEN_PLATFORM_WINDOWS + std::string m_ServiceDisplayName = "Unreal Zen Storage Service"; + std::string m_ServiceDescription; +#endif // ZEN_PLATFORM_WINDOWS cxxopts::Options m_UninstallOptions{"uninstall", "Uninstall zenserver as a service"}; diff --git a/src/zenutil/include/zenutil/service.h b/src/zenutil/include/zenutil/service.h index 1e96031f7..492e5c80a 100644 --- a/src/zenutil/include/zenutil/service.h +++ b/src/zenutil/include/zenutil/service.h @@ -21,9 +21,9 @@ struct ServiceSpec std::filesystem::path ExecutablePath; std::string CommandLineOptions; #if ZEN_PLATFORM_WINDOWS - std::string DisplayName; - std::string Description; -#endif // ZEN_PLATFORM_WINDOWS + std::string DisplayName; + std::string Description; +#endif // ZEN_PLATFORM_WINDOWS }; enum class ServiceStatus diff --git a/src/zenutil/service.cpp b/src/zenutil/service.cpp index f85a8e7e3..cfb19e9a9 100644 --- a/src/zenutil/service.cpp +++ b/src/zenutil/service.cpp @@ -173,10 +173,7 @@ namespace { } } - std::string GetDaemonName(std::string_view ServiceName) - { - return fmt::format("com.epicgames.unreal.{}", ServiceName); - } + std::string GetDaemonName(std::string_view ServiceName) { return fmt::format("com.epicgames.unreal.{}", ServiceName); } std::filesystem::path GetPListPath(const std::string& DaemonName) { @@ -188,7 +185,7 @@ namespace { const std::filesystem::path& ExecutablePath, std::string_view CommandLineOptions, std::string_view DaemonName, - bool Debug) + bool Debug) { std::vector Arguments = SplitArguments(CommandLineOptions); ExtendableStringBuilder<256> ProgramArguments; @@ -253,36 +250,36 @@ namespace { #endif // ZEN_PLATFORM_MAC - #if ZEN_PLATFORM_MAC || ZEN_PLATFORM_LINUX -std::pair ExecuteProgram(std::string_view Cmd) -{ - std::string data; - const int max_buffer = 256; - char buffer[max_buffer]; - std::string Command(Cmd); - Command.append(" 2>&1"); - - FILE * stream = popen(Command.c_str(), "r"); - if (stream) + std::pair ExecuteProgram(std::string_view Cmd) { - while (!feof(stream)) + std::string data; + const int max_buffer = 256; + char buffer[max_buffer]; + std::string Command(Cmd); + Command.append(" 2>&1"); + + FILE* stream = popen(Command.c_str(), "r"); + if (stream) { - if (fgets(buffer, max_buffer, stream) != NULL) + while (!feof(stream)) { - data.append(buffer); + if (fgets(buffer, max_buffer, stream) != NULL) + { + data.append(buffer); + } } - } - int Res = -1; - int st = pclose(stream); - if (WIFEXITED(st)) Res = WEXITSTATUS(st); - return {Res, data}; - } - return {errno, ""}; + int Res = -1; + int st = pclose(stream); + if (WIFEXITED(st)) + Res = WEXITSTATUS(st); + return {Res, data}; + } + return {errno, ""}; -#if 0 +# if 0 int in[2], out[2], n, pid; char buf[255]; @@ -350,11 +347,10 @@ std::pair ExecuteProgram(std::string_view Cmd) std::string Output(buf); return {0, Output}; -#endif // 0 -} - -#endif // ZEN_PLATFORM_MAC || ZEN_PLATFORM_LINUX +# endif // 0 + } +#endif // ZEN_PLATFORM_MAC || ZEN_PLATFORM_LINUX } // namespace @@ -695,8 +691,7 @@ std::error_code InstallService(std::string_view ServiceName, const ServiceSpec& Spec) { const std::string DaemonName = GetDaemonName(ServiceName); - std::string PList = - BuildPlist(ServiceName, Spec.ExecutablePath, Spec.CommandLineOptions, DaemonName, true); + std::string PList = BuildPlist(ServiceName, Spec.ExecutablePath, Spec.CommandLineOptions, DaemonName, true); const std::filesystem::path PListPath = GetPListPath(DaemonName); ZEN_INFO("Writing launchd plist to {}", PListPath.string()); @@ -720,8 +715,8 @@ InstallService(std::string_view ServiceName, const ServiceSpec& Spec) std::error_code UninstallService(std::string_view ServiceName) { - const std::string DaemonName = GetDaemonName(ServiceName); - const std::filesystem::path PListPath = GetPListPath(DaemonName); + const std::string DaemonName = GetDaemonName(ServiceName); + const std::filesystem::path PListPath = GetPListPath(DaemonName); ZEN_INFO("Attempting to remove launchd plist from {}", PListPath.string()); std::error_code Ec; std::filesystem::remove(PListPath, Ec); @@ -733,7 +728,7 @@ QueryInstalledService(std::string_view ServiceName, ServiceInfo& OutInfo) { ZEN_UNUSED(ServiceName, OutInfo); - OutInfo.Status = ServiceStatus::NotInstalled; + OutInfo.Status = ServiceStatus::NotInstalled; const std::string DaemonName = GetDaemonName(ServiceName); const std::filesystem::path PListPath = GetPListPath(DaemonName); @@ -743,8 +738,8 @@ QueryInstalledService(std::string_view ServiceName, ServiceInfo& OutInfo) { // Parse plist :( - IoBuffer Buffer = ReadFile(PListPath).Flatten(); - MemoryView Data = Buffer.GetView(); + IoBuffer Buffer = ReadFile(PListPath).Flatten(); + MemoryView Data = Buffer.GetView(); std::string PList((const char*)Data.GetData(), Data.GetSize()); enum class ParseMode @@ -757,10 +752,10 @@ QueryInstalledService(std::string_view ServiceName, ServiceInfo& OutInfo) ParseMode Mode = ParseMode::None; - ForEachStrTok(PList, '\n', [&](std::string_view Line){ - switch (Mode) - { - case ParseMode::None: + ForEachStrTok(PList, '\n', [&](std::string_view Line) { + switch (Mode) + { + case ParseMode::None: { if (Line.find("ProgramArguments") != std::string_view::npos) { @@ -769,7 +764,7 @@ QueryInstalledService(std::string_view ServiceName, ServiceInfo& OutInfo) } } break; - case ParseMode::ExpectingProgramArgumentsArray: + case ParseMode::ExpectingProgramArgumentsArray: { if (Line.find("") != std::string_view::npos) { @@ -779,7 +774,7 @@ QueryInstalledService(std::string_view ServiceName, ServiceInfo& OutInfo) Mode = ParseMode::None; } break; - case ParseMode::ExpectingProgramExecutablePath: + case ParseMode::ExpectingProgramExecutablePath: { if (std::string_view::size_type ArgStart = Line.find(""); ArgStart != std::string_view::npos) { @@ -787,15 +782,15 @@ QueryInstalledService(std::string_view ServiceName, ServiceInfo& OutInfo) if (std::string_view::size_type ArgEnd = Line.find("", ArgStart); ArgEnd != std::string_view::npos) { std::string_view ProgramString = Line.substr(ArgStart, ArgEnd - ArgStart); - OutInfo.Spec.ExecutablePath = ProgramString; - Mode = ParseMode::ExpectingCommandLineOption; + OutInfo.Spec.ExecutablePath = ProgramString; + Mode = ParseMode::ExpectingCommandLineOption; return true; } } Mode = ParseMode::None; } break; - case ParseMode::ExpectingCommandLineOption: + case ParseMode::ExpectingCommandLineOption: { if (std::string_view::size_type ArgStart = Line.find(""); ArgStart != std::string_view::npos) { @@ -819,20 +814,20 @@ QueryInstalledService(std::string_view ServiceName, ServiceInfo& OutInfo) Mode = ParseMode::None; } break; - } - return true; + } + return true; }); } { - std::pair Res = ExecuteProgram(std::string("launchctl list ") + DaemonName); + std::pair Res = ExecuteProgram(std::string("launchctl list ") + DaemonName); if (Res.first == 0 && !Res.second.empty()) { - ForEachStrTok(Res.second, '\n', [&](std::string_view Line){ + ForEachStrTok(Res.second, '\n', [&](std::string_view Line) { if (Line.find("\"PID\"") != std::string_view::npos) { - std::string_view::size_type PidStart = Line.find('='); - std::string_view::size_type PidEnd = Line.find(';'); - std::string_view PidString = Line.substr(PidStart+2, PidEnd - (PidStart + 2)); + std::string_view::size_type PidStart = Line.find('='); + std::string_view::size_type PidEnd = Line.find(';'); + std::string_view PidString = Line.substr(PidStart + 2, PidEnd - (PidStart + 2)); if (ParseInt(PidString).has_value()) { OutInfo.Status = ServiceStatus::Running; @@ -854,15 +849,15 @@ StartService(std::string_view ServiceName) { ZEN_UNUSED(ServiceName); - const std::string DaemonName = GetDaemonName(ServiceName); - const std::filesystem::path PListPath = GetPListPath(DaemonName); + const std::string DaemonName = GetDaemonName(ServiceName); + const std::filesystem::path PListPath = GetPListPath(DaemonName); - std::pair Res = ExecuteProgram(std::string("launchctl bootstrap system ") + PListPath.string()); + std::pair Res = ExecuteProgram(std::string("launchctl bootstrap system ") + PListPath.string()); if (Res.first != 0) { return MakeErrorCode(Res.first); } - + return {}; } @@ -871,15 +866,15 @@ StopService(std::string_view ServiceName) { ZEN_UNUSED(ServiceName); - const std::string DaemonName = GetDaemonName(ServiceName); - const std::filesystem::path PListPath = GetPListPath(DaemonName); + const std::string DaemonName = GetDaemonName(ServiceName); + const std::filesystem::path PListPath = GetPListPath(DaemonName); - std::pair Res = ExecuteProgram(std::string("launchctl bootout system ") + PListPath.string()); + std::pair Res = ExecuteProgram(std::string("launchctl bootout system ") + PListPath.string()); if (Res.first != 0) { return MakeErrorCode(Res.first); } - + return {}; } -- cgit v1.2.3 From 95b31d1898159b2c03475fe52058ae8a00047b3d Mon Sep 17 00:00:00 2001 From: Dan Engelbrecht Date: Fri, 10 Jan 2025 15:54:35 +0100 Subject: typo fix --- src/zen/cmds/service_cmd.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/zen/cmds/service_cmd.cpp b/src/zen/cmds/service_cmd.cpp index 5011ac12f..16a2940a8 100644 --- a/src/zen/cmds/service_cmd.cpp +++ b/src/zen/cmds/service_cmd.cpp @@ -230,7 +230,7 @@ ServiceCommand::ServiceCommand() m_StopOptions.positional_help("name"); #if ZEN_PLATFORM_WINDOWS m_StopOptions.add_option("", - mak "", + "", "allow-elevation", fmt::format("Allow attempt to relauch command using an elevated"), cxxopts::value(m_AllowElevation), -- cgit v1.2.3 From 5ba659f322823445ff22573b580206ec6464898e Mon Sep 17 00:00:00 2001 From: Dan Engelbrecht Date: Mon, 13 Jan 2025 11:04:53 +0100 Subject: linux service --- src/zenutil/service.cpp | 199 +++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 198 insertions(+), 1 deletion(-) diff --git a/src/zenutil/service.cpp b/src/zenutil/service.cpp index cfb19e9a9..8926accb3 100644 --- a/src/zenutil/service.cpp +++ b/src/zenutil/service.cpp @@ -351,7 +351,9 @@ namespace { } #endif // ZEN_PLATFORM_MAC || ZEN_PLATFORM_LINUX - +#if ZEN_PLATFORM_LINUX + const char* SystemInstallFolder = "/etc/systemd/system/"; +#endif // ZEN_PLATFORM_LINUX } // namespace std::string_view @@ -880,4 +882,199 @@ StopService(std::string_view ServiceName) #endif // ZEN_PLATFORM_MAC +#if ZEN_PLATFORM_LINUX + +std::error_code +InstallService(std::string_view ServiceName, const ServiceSpec& Spec) +{ + const std::string DaemonName = GetDaemonName(ServiceName); + std::string PList = BuildPlist(ServiceName, Spec.ExecutablePath, Spec.CommandLineOptions, DaemonName, true); + + const std::filesystem::path PListPath = GetPListPath(DaemonName); + ZEN_INFO("Writing launchd plist to {}", PListPath.string()); + try + { + zen::WriteFile(PListPath, IoBuffer(IoBuffer::Wrap, PList.data(), PList.size())); + } + catch (const std::system_error& Ex) + { + return MakeErrorCode(Ex.code().value()); + } + + ZEN_INFO("Changing permissions to 644 for {}", PListPath.string()); + if (chmod(PListPath.string().c_str(), 0644) == -1) + { + return MakeErrorCodeFromLastError(); + } + return {}; +} + +std::error_code +UninstallService(std::string_view ServiceName) +{ + const std::string DaemonName = GetDaemonName(ServiceName); + const std::filesystem::path PListPath = GetPListPath(DaemonName); + ZEN_INFO("Attempting to remove launchd plist from {}", PListPath.string()); + std::error_code Ec; + std::filesystem::remove(PListPath, Ec); + return Ec; +} + +std::error_code +QueryInstalledService(std::string_view ServiceName, ServiceInfo& OutInfo) +{ + ZEN_UNUSED(ServiceName, OutInfo); + + OutInfo.Status = ServiceStatus::NotInstalled; + const std::string DaemonName = GetDaemonName(ServiceName); + + const std::filesystem::path PListPath = GetPListPath(DaemonName); + if (std::filesystem::is_regular_file(PListPath)) + { + OutInfo.Status = ServiceStatus::Stopped; + + { + // Parse plist :( + IoBuffer Buffer = ReadFile(PListPath).Flatten(); + MemoryView Data = Buffer.GetView(); + std::string PList((const char*)Data.GetData(), Data.GetSize()); + + enum class ParseMode + { + None, + ExpectingProgramArgumentsArray, + ExpectingProgramExecutablePath, + ExpectingCommandLineOption + }; + + ParseMode Mode = ParseMode::None; + + ForEachStrTok(PList, '\n', [&](std::string_view Line) { + switch (Mode) + { + case ParseMode::None: + { + if (Line.find("ProgramArguments") != std::string_view::npos) + { + Mode = ParseMode::ExpectingProgramArgumentsArray; + return true; + } + } + break; + case ParseMode::ExpectingProgramArgumentsArray: + { + if (Line.find("") != std::string_view::npos) + { + Mode = ParseMode::ExpectingProgramExecutablePath; + return true; + } + Mode = ParseMode::None; + } + break; + case ParseMode::ExpectingProgramExecutablePath: + { + if (std::string_view::size_type ArgStart = Line.find(""); ArgStart != std::string_view::npos) + { + ArgStart += 8; + if (std::string_view::size_type ArgEnd = Line.find("", ArgStart); ArgEnd != std::string_view::npos) + { + std::string_view ProgramString = Line.substr(ArgStart, ArgEnd - ArgStart); + OutInfo.Spec.ExecutablePath = ProgramString; + Mode = ParseMode::ExpectingCommandLineOption; + return true; + } + } + Mode = ParseMode::None; + } + break; + case ParseMode::ExpectingCommandLineOption: + { + if (std::string_view::size_type ArgStart = Line.find(""); ArgStart != std::string_view::npos) + { + Mode = ParseMode::None; + return true; + } + else if (std::string_view::size_type ArgStart = Line.find(""); ArgStart != std::string_view::npos) + { + ArgStart += 8; + if (std::string_view::size_type ArgEnd = Line.find("", ArgStart); ArgEnd != std::string_view::npos) + { + std::string_view ArgumentString = Line.substr(ArgStart, ArgEnd - ArgStart); + if (!OutInfo.Spec.CommandLineOptions.empty()) + { + OutInfo.Spec.CommandLineOptions += " "; + } + OutInfo.Spec.CommandLineOptions += ArgumentString; + return true; + } + } + Mode = ParseMode::None; + } + break; + } + return true; + }); + } + { + std::pair Res = ExecuteProgram(std::string("launchctl list ") + DaemonName); + if (Res.first == 0 && !Res.second.empty()) + { + ForEachStrTok(Res.second, '\n', [&](std::string_view Line) { + if (Line.find("\"PID\"") != std::string_view::npos) + { + std::string_view::size_type PidStart = Line.find('='); + std::string_view::size_type PidEnd = Line.find(';'); + std::string_view PidString = Line.substr(PidStart + 2, PidEnd - (PidStart + 2)); + if (ParseInt(PidString).has_value()) + { + OutInfo.Status = ServiceStatus::Running; + } + return false; + } + return true; + }); + // Parse installed info + } + } + } + + return {}; +} + +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 Res = ExecuteProgram(std::string("launchctl bootstrap system ") + PListPath.string()); + if (Res.first != 0) + { + return MakeErrorCode(Res.first); + } + + return {}; +} + +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 Res = ExecuteProgram(std::string("launchctl bootout system ") + PListPath.string()); + if (Res.first != 0) + { + return MakeErrorCode(Res.first); + } + + return {}; +} + +#endif // ZEN_PLATFORM_LINUX + } // namespace zen -- cgit v1.2.3 From 343d596ba0def12363907bca379e084e96793f96 Mon Sep 17 00:00:00 2001 From: Dan Engelbrecht Date: Mon, 13 Jan 2025 11:09:58 +0100 Subject: linux service stop --- src/zenutil/service.cpp | 172 ++++-------------------------------------------- 1 file changed, 13 insertions(+), 159 deletions(-) diff --git a/src/zenutil/service.cpp b/src/zenutil/service.cpp index 8926accb3..01ef66431 100644 --- a/src/zenutil/service.cpp +++ b/src/zenutil/service.cpp @@ -250,7 +250,7 @@ namespace { #endif // ZEN_PLATFORM_MAC -#if ZEN_PLATFORM_MAC || ZEN_PLATFORM_LINUX +#if ZEN_PLATFORM_MAC std::pair ExecuteProgram(std::string_view Cmd) { @@ -350,9 +350,10 @@ namespace { # endif // 0 } -#endif // ZEN_PLATFORM_MAC || ZEN_PLATFORM_LINUX +#endif // ZEN_PLATFORM_MAC + #if ZEN_PLATFORM_LINUX - const char* SystemInstallFolder = "/etc/systemd/system/"; +// const char* SystemInstallFolder = "/etc/systemd/system/"; #endif // ZEN_PLATFORM_LINUX } // namespace @@ -887,156 +888,26 @@ StopService(std::string_view ServiceName) std::error_code InstallService(std::string_view ServiceName, const ServiceSpec& Spec) { - const std::string DaemonName = GetDaemonName(ServiceName); - std::string PList = BuildPlist(ServiceName, Spec.ExecutablePath, Spec.CommandLineOptions, DaemonName, true); + ZEN_UNUSED(ServiceName, Spec); + ZEN_NOT_IMPLEMENTED("linux service implementation incomplete"); - const std::filesystem::path PListPath = GetPListPath(DaemonName); - ZEN_INFO("Writing launchd plist to {}", PListPath.string()); - try - { - zen::WriteFile(PListPath, IoBuffer(IoBuffer::Wrap, PList.data(), PList.size())); - } - catch (const std::system_error& Ex) - { - return MakeErrorCode(Ex.code().value()); - } - - ZEN_INFO("Changing permissions to 644 for {}", PListPath.string()); - if (chmod(PListPath.string().c_str(), 0644) == -1) - { - return MakeErrorCodeFromLastError(); - } return {}; } std::error_code UninstallService(std::string_view ServiceName) { - const std::string DaemonName = GetDaemonName(ServiceName); - const std::filesystem::path PListPath = GetPListPath(DaemonName); - ZEN_INFO("Attempting to remove launchd plist from {}", PListPath.string()); - std::error_code Ec; - std::filesystem::remove(PListPath, Ec); - return Ec; + ZEN_UNUSED(ServiceName); + ZEN_NOT_IMPLEMENTED("linux service implementation incomplete"); + + return {}; } std::error_code QueryInstalledService(std::string_view ServiceName, ServiceInfo& OutInfo) { ZEN_UNUSED(ServiceName, OutInfo); - - OutInfo.Status = ServiceStatus::NotInstalled; - const std::string DaemonName = GetDaemonName(ServiceName); - - const std::filesystem::path PListPath = GetPListPath(DaemonName); - if (std::filesystem::is_regular_file(PListPath)) - { - OutInfo.Status = ServiceStatus::Stopped; - - { - // Parse plist :( - IoBuffer Buffer = ReadFile(PListPath).Flatten(); - MemoryView Data = Buffer.GetView(); - std::string PList((const char*)Data.GetData(), Data.GetSize()); - - enum class ParseMode - { - None, - ExpectingProgramArgumentsArray, - ExpectingProgramExecutablePath, - ExpectingCommandLineOption - }; - - ParseMode Mode = ParseMode::None; - - ForEachStrTok(PList, '\n', [&](std::string_view Line) { - switch (Mode) - { - case ParseMode::None: - { - if (Line.find("ProgramArguments") != std::string_view::npos) - { - Mode = ParseMode::ExpectingProgramArgumentsArray; - return true; - } - } - break; - case ParseMode::ExpectingProgramArgumentsArray: - { - if (Line.find("") != std::string_view::npos) - { - Mode = ParseMode::ExpectingProgramExecutablePath; - return true; - } - Mode = ParseMode::None; - } - break; - case ParseMode::ExpectingProgramExecutablePath: - { - if (std::string_view::size_type ArgStart = Line.find(""); ArgStart != std::string_view::npos) - { - ArgStart += 8; - if (std::string_view::size_type ArgEnd = Line.find("", ArgStart); ArgEnd != std::string_view::npos) - { - std::string_view ProgramString = Line.substr(ArgStart, ArgEnd - ArgStart); - OutInfo.Spec.ExecutablePath = ProgramString; - Mode = ParseMode::ExpectingCommandLineOption; - return true; - } - } - Mode = ParseMode::None; - } - break; - case ParseMode::ExpectingCommandLineOption: - { - if (std::string_view::size_type ArgStart = Line.find(""); ArgStart != std::string_view::npos) - { - Mode = ParseMode::None; - return true; - } - else if (std::string_view::size_type ArgStart = Line.find(""); ArgStart != std::string_view::npos) - { - ArgStart += 8; - if (std::string_view::size_type ArgEnd = Line.find("", ArgStart); ArgEnd != std::string_view::npos) - { - std::string_view ArgumentString = Line.substr(ArgStart, ArgEnd - ArgStart); - if (!OutInfo.Spec.CommandLineOptions.empty()) - { - OutInfo.Spec.CommandLineOptions += " "; - } - OutInfo.Spec.CommandLineOptions += ArgumentString; - return true; - } - } - Mode = ParseMode::None; - } - break; - } - return true; - }); - } - { - std::pair Res = ExecuteProgram(std::string("launchctl list ") + DaemonName); - if (Res.first == 0 && !Res.second.empty()) - { - ForEachStrTok(Res.second, '\n', [&](std::string_view Line) { - if (Line.find("\"PID\"") != std::string_view::npos) - { - std::string_view::size_type PidStart = Line.find('='); - std::string_view::size_type PidEnd = Line.find(';'); - std::string_view PidString = Line.substr(PidStart + 2, PidEnd - (PidStart + 2)); - if (ParseInt(PidString).has_value()) - { - OutInfo.Status = ServiceStatus::Running; - } - return false; - } - return true; - }); - // Parse installed info - } - } - } + ZEN_NOT_IMPLEMENTED("linux service implementation incomplete"); return {}; } @@ -1045,15 +916,7 @@ 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 Res = ExecuteProgram(std::string("launchctl bootstrap system ") + PListPath.string()); - if (Res.first != 0) - { - return MakeErrorCode(Res.first); - } + ZEN_NOT_IMPLEMENTED("linux service implementation incomplete"); return {}; } @@ -1062,16 +925,7 @@ 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 Res = ExecuteProgram(std::string("launchctl bootout system ") + PListPath.string()); - if (Res.first != 0) - { - return MakeErrorCode(Res.first); - } - + ZEN_NOT_IMPLEMENTED("linux service implementation incomplete"); return {}; } -- cgit v1.2.3 From dda538aa2b24a4e99d719e96e5a82f21889612ab Mon Sep 17 00:00:00 2001 From: Dan Engelbrecht Date: Mon, 13 Jan 2025 11:40:23 +0100 Subject: generate unit file --- src/zenutil/service.cpp | 74 +++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 71 insertions(+), 3 deletions(-) diff --git a/src/zenutil/service.cpp b/src/zenutil/service.cpp index 01ef66431..d31786835 100644 --- a/src/zenutil/service.cpp +++ b/src/zenutil/service.cpp @@ -12,11 +12,17 @@ #endif #if ZEN_PLATFORM_MAC # include +# include # include # include +#endif +#if ZEN_PLATFORM_LINUX +# include +# include -# include +# include +# include #endif namespace zen { @@ -228,7 +234,7 @@ namespace { "\n" "\n", DaemonName, - ExecutablePath.string(), + ExecutablePath, ProgramArguments.ToView(), ServiceName, ServiceName, @@ -354,6 +360,49 @@ namespace { #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) +{ + const std::filesystem::path SystemUnitFolder = "/etc/systemd/system/"; + return SystemUnitFolder / (UnitName + ".service"); +} + +std::string BuildUnitFile(std::string_view ServiceName, + const std::filesystem::path& ExecutablePath, + std::string_view CommandLineOptions, + std::string_view AliasName) +{ + #if 0 + ZEN_UNUSED(ServiceName, ExecutablePath, CommandLineOptions, AliasName); + return ""; + #else + return fmt::format("[Unit]\n" + "Description={}\n" + "Documentation=https://github.com/epicgames/zen\n" + "\n" + "DefaultDependencies=no\n" + "After=network.target\n" + "StartLimitIntervalSec=0\n" + "\n" + "[Service]\n" + "Type=simple\n" + "Restart=always\n" + "RestartSec=1\n" + "User=serviceuser\n" + "ExecStart={} {}\n" + "Restart=always\n" + "RuntimeDirectory={}\n" + "[Install]\n" + "Alias={}", + ServiceName, + ExecutablePath, CommandLineOptions, + ExecutablePath.parent_path(), + AliasName); + #endif +} + #endif // ZEN_PLATFORM_LINUX } // namespace @@ -889,7 +938,26 @@ std::error_code InstallService(std::string_view ServiceName, const ServiceSpec& Spec) { ZEN_UNUSED(ServiceName, Spec); - ZEN_NOT_IMPLEMENTED("linux service implementation incomplete"); + + 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()); + try + { + zen::WriteFile(ServiceUnitPath, IoBuffer(IoBuffer::Wrap, UnitFile.data(), UnitFile.size())); + } + catch (const std::system_error& Ex) + { + return MakeErrorCode(Ex.code().value()); + } + + ZEN_INFO("Changing permissions to 644 for {}", ServiceUnitPath.string()); + if (chmod(ServiceUnitPath.string().c_str(), 0644) == -1) + { + return MakeErrorCodeFromLastError(); + } return {}; } -- cgit v1.2.3 From afb0fb0a2c7934b62d61fc191bfbce3a7535fe5e Mon Sep 17 00:00:00 2001 From: Dan Engelbrecht Date: Mon, 13 Jan 2025 11:40:56 +0100 Subject: clang format --- src/zenutil/service.cpp | 80 +++++++++++++++++++++++++------------------------ 1 file changed, 41 insertions(+), 39 deletions(-) diff --git a/src/zenutil/service.cpp b/src/zenutil/service.cpp index d31786835..834d8b764 100644 --- a/src/zenutil/service.cpp +++ b/src/zenutil/service.cpp @@ -359,49 +359,51 @@ namespace { #endif // ZEN_PLATFORM_MAC #if ZEN_PLATFORM_LINUX -// const char* SystemInstallFolder = "/etc/systemd/system/"; + // const char* SystemInstallFolder = "/etc/systemd/system/"; -std::string GetUnitName(std::string_view ServiceName) { return fmt::format("com.epicgames.unreal.{}", ServiceName); } + std::string GetUnitName(std::string_view ServiceName) { return fmt::format("com.epicgames.unreal.{}", ServiceName); } -std::filesystem::path GetServiceUnitPath(const std::string& UnitName) -{ - const std::filesystem::path SystemUnitFolder = "/etc/systemd/system/"; - return SystemUnitFolder / (UnitName + ".service"); -} + std::filesystem::path GetServiceUnitPath(const std::string& UnitName) + { + const std::filesystem::path SystemUnitFolder = "/etc/systemd/system/"; + return SystemUnitFolder / (UnitName + ".service"); + } -std::string BuildUnitFile(std::string_view ServiceName, - const std::filesystem::path& ExecutablePath, - std::string_view CommandLineOptions, - std::string_view AliasName) -{ - #if 0 + std::string BuildUnitFile(std::string_view ServiceName, + const std::filesystem::path& ExecutablePath, + std::string_view CommandLineOptions, + std::string_view AliasName) + { +# if 0 ZEN_UNUSED(ServiceName, ExecutablePath, CommandLineOptions, AliasName); return ""; - #else - return fmt::format("[Unit]\n" - "Description={}\n" - "Documentation=https://github.com/epicgames/zen\n" - "\n" - "DefaultDependencies=no\n" - "After=network.target\n" - "StartLimitIntervalSec=0\n" - "\n" - "[Service]\n" - "Type=simple\n" - "Restart=always\n" - "RestartSec=1\n" - "User=serviceuser\n" - "ExecStart={} {}\n" - "Restart=always\n" - "RuntimeDirectory={}\n" - "[Install]\n" - "Alias={}", - ServiceName, - ExecutablePath, CommandLineOptions, - ExecutablePath.parent_path(), - AliasName); - #endif -} +# else + return fmt::format( + "[Unit]\n" + "Description={}\n" + "Documentation=https://github.com/epicgames/zen\n" + "\n" + "DefaultDependencies=no\n" + "After=network.target\n" + "StartLimitIntervalSec=0\n" + "\n" + "[Service]\n" + "Type=simple\n" + "Restart=always\n" + "RestartSec=1\n" + "User=serviceuser\n" + "ExecStart={} {}\n" + "Restart=always\n" + "RuntimeDirectory={}\n" + "[Install]\n" + "Alias={}", + ServiceName, + ExecutablePath, + CommandLineOptions, + ExecutablePath.parent_path(), + AliasName); +# endif + } #endif // ZEN_PLATFORM_LINUX } // namespace @@ -940,7 +942,7 @@ InstallService(std::string_view ServiceName, const ServiceSpec& Spec) ZEN_UNUSED(ServiceName, Spec); const std::string UnitName = GetUnitName(ServiceName); - std::string UnitFile = BuildUnitFile(ServiceName, Spec.ExecutablePath, Spec.CommandLineOptions, UnitName); + 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()); -- cgit v1.2.3 From 531c59032bbc46bc1f7284859fa8ff8c8b5ede61 Mon Sep 17 00:00:00 2001 From: Dan Engelbrecht Date: Wed, 15 Jan 2025 09:30:12 +0100 Subject: systemd unit file, incomplete --- src/zen/cmds/service_cmd.cpp | 2 +- src/zencore/process.cpp | 4 + src/zencore/thread.cpp | 2 +- src/zenserver/main.cpp | 13 ++ src/zenutil/service.cpp | 250 +++++++++++++++++++++------------------ 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 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 Res = ExecuteProgram(std::string("launchctl list ") + DaemonName); + std::pair 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 Res = ExecuteProgram(std::string("launchctl bootstrap system ") + PListPath.string()); + std::pair 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 Res = ExecuteProgram(std::string("launchctl bootout system ") + PListPath.string()); + std::pair 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 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 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 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 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 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"); -- cgit v1.2.3 From 53531a6b22dbb7b690db43964172e9d0f670c3c8 Mon Sep 17 00:00:00 2001 From: Dan Engelbrecht Date: Wed, 15 Jan 2025 10:17:02 +0100 Subject: clang format --- src/zencore/process.cpp | 2 +- src/zenserver/main.cpp | 5 ++++- src/zenutil/service.cpp | 25 +++++++++++++------------ src/zenutil/zenserverprocess.cpp | 2 +- 4 files changed, 19 insertions(+), 15 deletions(-) diff --git a/src/zencore/process.cpp b/src/zencore/process.cpp index 8049130da..8e903f05f 100644 --- a/src/zencore/process.cpp +++ b/src/zencore/process.cpp @@ -855,7 +855,7 @@ IsProcessRunning(int pid, std::error_code& OutEc) } else if (Error == EPERM) { - return true; // Running under a user we don't have access to, assume it is live + return true; // Running under a user we don't have access to, assume it is live } else { diff --git a/src/zenserver/main.cpp b/src/zenserver/main.cpp index 6bf369ca4..f35010866 100644 --- a/src/zenserver/main.cpp +++ b/src/zenserver/main.cpp @@ -313,7 +313,10 @@ ZenEntryPoint::Run() } catch (const std::system_error& e) { - ZEN_CRITICAL("Caught system error exception in main for process {}: {} ({})", zen::GetCurrentProcessId(), e.what(), e.code().value()); + ZEN_CRITICAL("Caught system error exception in main for process {}: {} ({})", + zen::GetCurrentProcessId(), + e.what(), + e.code().value()); RequestApplicationExit(1); } catch (const std::exception& e) diff --git a/src/zenutil/service.cpp b/src/zenutil/service.cpp index 820edf565..45874d1b5 100644 --- a/src/zenutil/service.cpp +++ b/src/zenutil/service.cpp @@ -280,13 +280,13 @@ namespace { } } - while(!Data.empty() && isspace(Data[Data.length() - 1])) + while (!Data.empty() && isspace(Data[Data.length() - 1])) { Data.pop_back(); } - int Res = -1; - int Status = pclose(Stream); + int Res = -1; + int Status = pclose(Stream); if (Status < 0) { return {Status, Data}; @@ -296,7 +296,7 @@ namespace { { Res = WEXITSTATUS(Status); } - if (Res != 0 && Res != (128+13)) + if (Res != 0 && Res != (128 + 13)) { return {Res, Data}; } @@ -883,10 +883,10 @@ 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::string UnitName = GetUnitName(ServiceName); const std::filesystem::path ServiceUnitPath = GetServiceUnitPath(UnitName); - std::string UnitFile = BuildUnitFile(ServiceName, Spec.ExecutablePath, Spec.CommandLineOptions, UnitName); + std::string UnitFile = BuildUnitFile(ServiceName, Spec.ExecutablePath, Spec.CommandLineOptions, UnitName); ZEN_DEBUG("Writing systemd unit file to {}", ServiceUnitPath.string()); try { @@ -923,7 +923,7 @@ InstallService(std::string_view ServiceName, const ServiceSpec& Spec) std::error_code UninstallService(std::string_view ServiceName) { - const std::string UnitName = GetUnitName(ServiceName); + const std::string UnitName = GetUnitName(ServiceName); const std::filesystem::path ServiceUnitPath = GetServiceUnitPath(UnitName); std::pair Res = ExecuteProgram(fmt::format("systemctl disable {}", UnitName)); @@ -955,7 +955,7 @@ UninstallService(std::string_view ServiceName) std::error_code QueryInstalledService(std::string_view ServiceName, ServiceInfo& OutInfo) { - const std::string UnitName = GetUnitName(ServiceName); + const std::string UnitName = GetUnitName(ServiceName); const std::filesystem::path ServiceUnitPath = GetServiceUnitPath(UnitName); OutInfo.Status = ServiceStatus::NotInstalled; @@ -973,7 +973,7 @@ QueryInstalledService(std::string_view ServiceName, ServiceInfo& OutInfo) } else { - ZEN_DEBUG("systemctl status failed with '{}" ({}), Res.second, Res.first); + ZEN_DEBUG("systemctl status failed with '{}"({}), Res.second, Res.first); } } @@ -988,7 +988,7 @@ StartService(std::string_view ServiceName) // 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::string UnitName = GetUnitName(ServiceName); const std::filesystem::path ServiceUnitPath = GetServiceUnitPath(UnitName); std::pair Res = ExecuteProgram(fmt::format("systemctl start {}", UnitName)); @@ -1004,9 +1004,10 @@ StartService(std::string_view ServiceName) std::error_code StopService(std::string_view ServiceName) { - // TODO: Stopping the service returns -1 from ExecuteProgram, maybe this ie expected as I have yet to successfully start the service using systemctl start + // 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::string UnitName = GetUnitName(ServiceName); const std::filesystem::path ServiceUnitPath = GetServiceUnitPath(UnitName); std::pair Res = ExecuteProgram(fmt::format("systemctl stop {}", UnitName)); diff --git a/src/zenutil/zenserverprocess.cpp b/src/zenutil/zenserverprocess.cpp index 3b7bf5ad4..0c0882311 100644 --- a/src/zenutil/zenserverprocess.cpp +++ b/src/zenutil/zenserverprocess.cpp @@ -167,7 +167,7 @@ ZenServerState::Initialize() ThrowLastError("Could not map view of Zen server state"); } #else -ZEN_INFO("{}", S_IRUSR | S_IWUSR | S_IXUSR); + 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) { -- cgit v1.2.3 From 715a895b5c23ed353a9cf77ceb6744a6d3e451e9 Mon Sep 17 00:00:00 2001 From: Dan Engelbrecht Date: Fri, 17 Jan 2025 09:52:48 +0100 Subject: fix positional arguments for zen service install on non-windows platforms --- src/zen/cmds/service_cmd.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/zen/cmds/service_cmd.cpp b/src/zen/cmds/service_cmd.cpp index 5e02db813..372fce5cb 100644 --- a/src/zen/cmds/service_cmd.cpp +++ b/src/zen/cmds/service_cmd.cpp @@ -179,8 +179,10 @@ ServiceCommand::ServiceCommand() fmt::format("Allow attempt to relauch command using an elevated"), cxxopts::value(m_AllowElevation), ""); -#endif // ZEN_PLATFORM_WINDOWS m_InstallOptions.parse_positional({"executable", "name", "display-name"}); +#else + m_InstallOptions.parse_positional({"executable", "name"}); +#endif // ZEN_PLATFORM_WINDOWS m_InstallOptions.positional_help("executable name display-name"); m_UninstallOptions.add_options()("h,help", "Print help"); -- cgit v1.2.3 From 849283ced7702986b173c27a98f834fd7e1adc5d Mon Sep 17 00:00:00 2001 From: Dan Engelbrecht Date: Fri, 17 Jan 2025 09:52:56 +0100 Subject: changelog --- CHANGELOG.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7f5e43ec8..998d5198f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,22 @@ ## +- Feature: Added command line tool `zen service` to do maintenance operations for running zenserver as a local service + - `zen service status` - Check the status of zenservice as local service, indidicating if it is installed, running and the options it was installed with + - `zen service install` - Install zenserver as a local service, this requires admin/sudo priviliges to execute properly + - `--executable` - Path to zenserver executable. Defaults to same path as `zen` command line executable that was issued but with filename replaced. + - `--name` - Name of the installed service. Defaults to `ZenServer` + - `--display-name` - Windows only, the user friendly display name of the service. Defaults to `Unreal Zen Storage Service` + - `--description` - Windows only, the user friendly description of the service + - `--allow-elevation` - Windows only, if command is not run with elevated priviliges, the command will attempt to relaunch itself with elevated priviliges + - `-- ` - All options given after the double dash will be added as command line arguments to the zenserver executable when starting the service + - `zen service uninstall` - Uninstall zenserver as a local service, this requires admin/sudo priviliges to execute properly + - `--name` - Name of the installed service. Defaults to `ZenServer` + - `--allow-elevation` - Windows only, if command is not run with elevated priviliges, the command will attempt to relaunch itself with elevated priviliges + - `zen service start` - Start the installed zenserver local service, this requires admin/sudo priviliges to execute properly + - `--name` - Name of the installed service. Defaults to `ZenServer` + - `--allow-elevation` - Windows only, if command is not run with elevated priviliges, the command will attempt to relaunch itself with elevated priviliges + - `zen service stop` - Stops the running installed zenserver local service, this requires admin/sudo priviliges to execute properly + - `--name` - Name of the installed service. Defaults to `ZenServer` + - `--allow-elevation` - Windows only, if command is not run with elevated priviliges, the command will attempt to relaunch itself with elevated priviliges - Bugfix: Fix parsing of workspace options in Lua config - Bugfix: Add missing Lua option for option `--gc-projectstore-duration-seconds` - Bugfix: Add missing Lua mapping option to `--statsd` command line option -- cgit v1.2.3 From 7e700445334d8d3269bca0bf446ff63211296dce Mon Sep 17 00:00:00 2001 From: Liam Mitchell Date: Wed, 19 Feb 2025 00:08:41 +0000 Subject: Linux compilation fixes --- src/zencore/include/zencore/memory/fmalloc.h | 2 ++ src/zencore/memory/mallocmimalloc.cpp | 2 ++ src/zencore/process.cpp | 1 - src/zenstore/chunking.cpp | 1 + src/zenutil/chunkrequests.cpp | 2 ++ src/zenutil/service.cpp | 2 +- 6 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/zencore/include/zencore/memory/fmalloc.h b/src/zencore/include/zencore/memory/fmalloc.h index aeb05b651..5b476429e 100644 --- a/src/zencore/include/zencore/memory/fmalloc.h +++ b/src/zencore/include/zencore/memory/fmalloc.h @@ -2,6 +2,8 @@ #pragma once +#include + #include namespace zen { diff --git a/src/zencore/memory/mallocmimalloc.cpp b/src/zencore/memory/mallocmimalloc.cpp index 1919af3bf..1f9aff404 100644 --- a/src/zencore/memory/mallocmimalloc.cpp +++ b/src/zencore/memory/mallocmimalloc.cpp @@ -1,5 +1,7 @@ // Copyright Epic Games, Inc. All Rights Reserved. +#include + #include #include #include diff --git a/src/zencore/process.cpp b/src/zencore/process.cpp index 8e903f05f..8dc86371e 100644 --- a/src/zencore/process.cpp +++ b/src/zencore/process.cpp @@ -24,7 +24,6 @@ # include # include # include -# include # include # include # include diff --git a/src/zenstore/chunking.cpp b/src/zenstore/chunking.cpp index 30edd322a..71f0a06e4 100644 --- a/src/zenstore/chunking.cpp +++ b/src/zenstore/chunking.cpp @@ -5,6 +5,7 @@ #include #include +#include namespace zen::detail { diff --git a/src/zenutil/chunkrequests.cpp b/src/zenutil/chunkrequests.cpp index 745363668..e28df02a8 100644 --- a/src/zenutil/chunkrequests.cpp +++ b/src/zenutil/chunkrequests.cpp @@ -1,5 +1,7 @@ // Copyright Epic Games, Inc. All Rights Reserved. +#include + #include #include diff --git a/src/zenutil/service.cpp b/src/zenutil/service.cpp index 45874d1b5..8d6b399ca 100644 --- a/src/zenutil/service.cpp +++ b/src/zenutil/service.cpp @@ -973,7 +973,7 @@ QueryInstalledService(std::string_view ServiceName, ServiceInfo& OutInfo) } else { - ZEN_DEBUG("systemctl status failed with '{}"({}), Res.second, Res.first); + ZEN_DEBUG("systemctl status failed with '{}'({})", Res.second, Res.first); } } -- cgit v1.2.3 From c49b0a053c5e28de1afa83600ebffd383766e38a Mon Sep 17 00:00:00 2001 From: Liam Mitchell Date: Thu, 27 Feb 2025 02:16:10 +0000 Subject: Implementation of service commands for Linux. --- src/zen/cmds/service_cmd.cpp | 9 +++++- src/zen/cmds/service_cmd.h | 1 + src/zencore-test/zencore-test.cpp | 5 +++ src/zencore/include/zencore/process.h | 4 +++ src/zencore/process.cpp | 7 +++-- src/zenhttp-test/zenhttp-test.cpp | 5 +++ src/zennet-test/zennet-test.cpp | 5 +++ src/zenserver-test/zenserver-test.cpp | 4 +++ src/zenserver/main.cpp | 4 +++ src/zenstore-test/zenstore-test.cpp | 5 +++ src/zenutil-test/zenutil-test.cpp | 5 +++ src/zenutil/include/zenutil/service.h | 1 + src/zenutil/service.cpp | 59 +++++++++++++++++++++++++++-------- src/zenutil/zenserverprocess.cpp | 14 ++++++++- 14 files changed, 110 insertions(+), 18 deletions(-) diff --git a/src/zen/cmds/service_cmd.cpp b/src/zen/cmds/service_cmd.cpp index 372fce5cb..b3872dae7 100644 --- a/src/zen/cmds/service_cmd.cpp +++ b/src/zen/cmds/service_cmd.cpp @@ -160,6 +160,13 @@ ServiceCommand::ServiceCommand() fmt::format("Service name, defaults to \"{}\"", m_ServiceName), cxxopts::value(m_ServiceName), ""); + + m_InstallOptions.add_option("", + "u", + "user", + "User to run service as, defaults to current user", + cxxopts::value(m_UserName), + ""); #if ZEN_PLATFORM_WINDOWS m_InstallOptions.add_option("", "d", @@ -340,7 +347,7 @@ ServiceCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) Ec = InstallService( m_ServiceName, ServiceSpec { - .ExecutablePath = m_ServerExecutable, .CommandLineOptions = GlobalOptions.PassthroughCommandLine + .ExecutablePath = m_ServerExecutable, .CommandLineOptions = GlobalOptions.PassthroughCommandLine, .UserName = m_UserName #if ZEN_PLATFORM_WINDOWS , .DisplayName = m_ServiceDisplayName, .Description = m_ServiceDescription diff --git a/src/zen/cmds/service_cmd.h b/src/zen/cmds/service_cmd.h index 4d370b29c..f88e8c25b 100644 --- a/src/zen/cmds/service_cmd.h +++ b/src/zen/cmds/service_cmd.h @@ -26,6 +26,7 @@ private: std::string m_Verb; // create, info, remove std::string m_ServiceName = "ZenServer"; + std::string m_UserName; bool m_AllowElevation = false; diff --git a/src/zencore-test/zencore-test.cpp b/src/zencore-test/zencore-test.cpp index 40cb51156..37ae7f587 100644 --- a/src/zencore-test/zencore-test.cpp +++ b/src/zencore-test/zencore-test.cpp @@ -12,6 +12,7 @@ #if ZEN_WITH_TESTS # define ZEN_TEST_WITH_RUNNER 1 # include +# include #endif int @@ -20,6 +21,10 @@ main([[maybe_unused]] int argc, [[maybe_unused]] char* argv[]) #if ZEN_WITH_TESTS zen::zencore_forcelinktests(); +#if ZEN_PLATFORM_LINUX + zen::IgnoreChildSignals(); +#endif + zen::logging::InitializeLogging(); zen::MaximizeOpenFileCount(); diff --git a/src/zencore/include/zencore/process.h b/src/zencore/include/zencore/process.h index 42b997c39..36c2a2481 100644 --- a/src/zencore/include/zencore/process.h +++ b/src/zencore/include/zencore/process.h @@ -101,6 +101,10 @@ int GetProcessId(CreateProcResult ProcId); std::filesystem::path GetProcessExecutablePath(int Pid, std::error_code& OutEc); std::error_code FindProcess(const std::filesystem::path& ExecutableImage, ProcessHandle& OutHandle); +#if ZEN_PLATFORM_LINUX +void IgnoreChildSignals(); +#endif + void process_forcelink(); // internal } // namespace zen diff --git a/src/zencore/process.cpp b/src/zencore/process.cpp index 8dc86371e..9f0c9578f 100644 --- a/src/zencore/process.cpp +++ b/src/zencore/process.cpp @@ -40,7 +40,9 @@ ZEN_THIRD_PARTY_INCLUDES_END namespace zen { #if ZEN_PLATFORM_LINUX -const bool bNoZombieChildren = []() { +void +IgnoreChildSignals() +{ // When a child process exits it is put into a zombie state until the parent // collects its result. This doesn't fit the Windows-like model that Zen uses // where there is a less strict familial model and no zombification. Ignoring @@ -51,8 +53,7 @@ const bool bNoZombieChildren = []() { sigemptyset(&Action.sa_mask); Action.sa_handler = SIG_IGN; sigaction(SIGCHLD, &Action, nullptr); - return true; -}(); +} static char GetPidStatus(int Pid, std::error_code& OutEc) diff --git a/src/zenhttp-test/zenhttp-test.cpp b/src/zenhttp-test/zenhttp-test.cpp index 49db1ba54..df395939b 100644 --- a/src/zenhttp-test/zenhttp-test.cpp +++ b/src/zenhttp-test/zenhttp-test.cpp @@ -8,6 +8,7 @@ #if ZEN_WITH_TESTS # define ZEN_TEST_WITH_RUNNER 1 # include +# include #endif int @@ -16,6 +17,10 @@ main([[maybe_unused]] int argc, [[maybe_unused]] char* argv[]) #if ZEN_WITH_TESTS zen::zenhttp_forcelinktests(); +#if ZEN_PLATFORM_LINUX + zen::IgnoreChildSignals(); +#endif + zen::logging::InitializeLogging(); zen::MaximizeOpenFileCount(); diff --git a/src/zennet-test/zennet-test.cpp b/src/zennet-test/zennet-test.cpp index 482d3c617..b45a5f807 100644 --- a/src/zennet-test/zennet-test.cpp +++ b/src/zennet-test/zennet-test.cpp @@ -9,6 +9,7 @@ #if ZEN_WITH_TESTS # define ZEN_TEST_WITH_RUNNER 1 # include +# include #endif int @@ -17,6 +18,10 @@ main([[maybe_unused]] int argc, [[maybe_unused]] char** argv) #if ZEN_WITH_TESTS zen::zennet_forcelinktests(); +#if ZEN_PLATFORM_LINUX + zen::IgnoreChildSignals(); +#endif + zen::logging::InitializeLogging(); zen::MaximizeOpenFileCount(); diff --git a/src/zenserver-test/zenserver-test.cpp b/src/zenserver-test/zenserver-test.cpp index 6259c0f37..5c245d8bb 100644 --- a/src/zenserver-test/zenserver-test.cpp +++ b/src/zenserver-test/zenserver-test.cpp @@ -100,6 +100,10 @@ main(int argc, char** argv) using namespace std::literals; using namespace zen; +#if ZEN_PLATFORM_LINUX + IgnoreChildSignals(); +#endif + zen::logging::InitializeLogging(); zen::logging::SetLogLevel(zen::logging::level::Debug); diff --git a/src/zenserver/main.cpp b/src/zenserver/main.cpp index f35010866..41e8d782c 100644 --- a/src/zenserver/main.cpp +++ b/src/zenserver/main.cpp @@ -396,6 +396,10 @@ main(int argc, char* argv[]) signal(SIGINT, utils::SignalCallbackHandler); signal(SIGTERM, utils::SignalCallbackHandler); +#if ZEN_PLATFORM_LINUX + IgnoreChildSignals(); +#endif + try { ZenServerOptions ServerOptions; diff --git a/src/zenstore-test/zenstore-test.cpp b/src/zenstore-test/zenstore-test.cpp index e5b312984..b86f6be15 100644 --- a/src/zenstore-test/zenstore-test.cpp +++ b/src/zenstore-test/zenstore-test.cpp @@ -9,6 +9,7 @@ #if ZEN_WITH_TESTS # define ZEN_TEST_WITH_RUNNER 1 # include +# include #endif int @@ -17,6 +18,10 @@ main([[maybe_unused]] int argc, [[maybe_unused]] char* argv[]) #if ZEN_WITH_TESTS zen::zenstore_forcelinktests(); +#if ZEN_PLATFORM_LINUX + zen::IgnoreChildSignals(); +#endif + zen::logging::InitializeLogging(); zen::MaximizeOpenFileCount(); diff --git a/src/zenutil-test/zenutil-test.cpp b/src/zenutil-test/zenutil-test.cpp index fadaf0995..a392ab058 100644 --- a/src/zenutil-test/zenutil-test.cpp +++ b/src/zenutil-test/zenutil-test.cpp @@ -9,6 +9,7 @@ #if ZEN_WITH_TESTS # define ZEN_TEST_WITH_RUNNER 1 # include +# include #endif int @@ -17,6 +18,10 @@ main([[maybe_unused]] int argc, [[maybe_unused]] char* argv[]) #if ZEN_WITH_TESTS zen::zenutil_forcelinktests(); +#if ZEN_PLATFORM_LINUX + zen::IgnoreChildSignals(); +#endif + zen::logging::InitializeLogging(); zen::MaximizeOpenFileCount(); diff --git a/src/zenutil/include/zenutil/service.h b/src/zenutil/include/zenutil/service.h index 492e5c80a..2798bcb1f 100644 --- a/src/zenutil/include/zenutil/service.h +++ b/src/zenutil/include/zenutil/service.h @@ -20,6 +20,7 @@ struct ServiceSpec { std::filesystem::path ExecutablePath; std::string CommandLineOptions; + std::string UserName; #if ZEN_PLATFORM_WINDOWS std::string DisplayName; std::string Description; 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 #include #include +#include #if ZEN_PLATFORM_WINDOWS # include @@ -23,6 +24,7 @@ # include # include +# include #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 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 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 Res = ExecuteProgram(fmt::format("systemctl status {}", UnitName)); + std::pair 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 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 { diff --git a/src/zenutil/zenserverprocess.cpp b/src/zenutil/zenserverprocess.cpp index 11fcce02f..0409cb976 100644 --- a/src/zenutil/zenserverprocess.cpp +++ b/src/zenutil/zenserverprocess.cpp @@ -168,10 +168,22 @@ ZenServerState::Initialize() } #else ZEN_INFO("{}", S_IRUSR | S_IWUSR | S_IXUSR); + ZEN_INFO("{}", geteuid()); 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"); + // Work around a potential issue if the service user is changed in certain configurations. + // If the sysctl 'fs.protected_regular' is set to 1 or 2 (default on many distros), + // we will be unable to open an existing shared memory object created by another user using O_CREAT, + // even if we have the correct permissions, or are running as root. If we destroy the existing + // shared memory object and retry, we'll be able to get past shm_open() so long as we have + // the appropriate permissions to create the shared memory object. + shm_unlink("/UnrealEngineZen"); + 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"); + } } fchmod(Fd, 0666); void* hMap = (void*)intptr_t(Fd); -- cgit v1.2.3 From 8857b1551e719478f50d914c34a1e63782045b86 Mon Sep 17 00:00:00 2001 From: Liam Mitchell Date: Tue, 4 Mar 2025 18:13:14 -0800 Subject: Initial implementation of service status reporting --- src/zenserver/main.cpp | 45 +++++++++++++++++++++++++++++++++++++++ src/zenserver/windows/service.cpp | 8 ------- src/zenserver/windows/service.h | 4 ++++ 3 files changed, 49 insertions(+), 8 deletions(-) diff --git a/src/zenserver/main.cpp b/src/zenserver/main.cpp index 41e8d782c..636e7f6c1 100644 --- a/src/zenserver/main.cpp +++ b/src/zenserver/main.cpp @@ -23,6 +23,8 @@ #include #include +#include + #include "config.h" #include "diag/logging.h" #include "sentryintegration.h" @@ -88,6 +90,43 @@ ZenEntryPoint::ZenEntryPoint(ZenServerOptions& ServerOptions) : m_ServerOptions( { } +void +ReportServiceStatus(ServiceStatus Status) +{ +#if ZEN_PLATFORM_WINDOWS + switch (Status) + { + case ServiceStatus::Starting: + ReportSvcStatus(SERVICE_START_PENDING, NO_ERROR, 3000); + break; + case ServiceStatus::Running: + ReportSvcStatus(SERVICE_RUNNING, NO_ERROR, 0); + break; + case ServiceStatus::Stopping: + ReportSvcStatus(SERVICE_STOP_PENDING, NO_ERROR, 0); + break; + case ServiceStatus::Stopped: + ReportSvcStatus(SERVICE_STOPPED, (DWORD)ApplicationExitCode(), 0); + break; + default: + break; + } +#elif ZEN_PLATFORM_LINUX + switch (Status) + { + case ServiceStatus::Running: + sd_notify(0, "READY=1"); + break; + case ServiceStatus::Stopping: + sd_notify(0, "STOPPING=1"); + break; + case ServiceStatus::Stopped: + sd_notifyf(0, "EXIT_STATUS=%d", ApplicationExitCode()); + break; + } +#endif +} + int ZenEntryPoint::Run() { @@ -281,6 +320,8 @@ ZenEntryPoint::Run() }}); auto CleanupShutdown = MakeGuard([&ShutdownEvent, &ShutdownThread] { + ReportServiceStatus(ServiceStatus::Stopping); + if (ShutdownEvent) { ShutdownEvent->Set(); @@ -302,6 +343,8 @@ ZenEntryPoint::Run() NamedEvent ParentEvent{m_ServerOptions.ChildId}; ParentEvent.Set(); } + + ReportServiceStatus(ServiceStatus::Running); }); Server.Run(); @@ -327,6 +370,8 @@ ZenEntryPoint::Run() ShutdownServerLogging(); + ReportServiceStatus(ServiceStatus::Stopped); + return ApplicationExitCode(); } diff --git a/src/zenserver/windows/service.cpp b/src/zenserver/windows/service.cpp index cb87df1f6..4be5e6205 100644 --- a/src/zenserver/windows/service.cpp +++ b/src/zenserver/windows/service.cpp @@ -21,7 +21,6 @@ HANDLE ghSvcStopEvent = NULL; void SvcInstall(void); -void ReportSvcStatus(DWORD, DWORD, DWORD); void SvcReportEvent(LPTSTR); WindowsService::WindowsService() @@ -222,14 +221,7 @@ WindowsService::SvcMain() return 1; } - // Report running status when initialization is complete. - - ReportSvcStatus(SERVICE_RUNNING, NO_ERROR, 0); - int ReturnCode = Run(); - - ReportSvcStatus(SERVICE_STOPPED, NO_ERROR, 0); - return ReturnCode; } diff --git a/src/zenserver/windows/service.h b/src/zenserver/windows/service.h index 7c9610983..ca0270a36 100644 --- a/src/zenserver/windows/service.h +++ b/src/zenserver/windows/service.h @@ -2,6 +2,8 @@ #pragma once +#include + class WindowsService { public: @@ -18,3 +20,5 @@ public: int SvcMain(); static void __stdcall SvcCtrlHandler(unsigned long); }; + +VOID ReportSvcStatus(DWORD dwCurrentState, DWORD dwWin32ExitCode, DWORD dwWaitHint); -- cgit v1.2.3 From 2b823aa2088587575f62784c150af75c2535618b Mon Sep 17 00:00:00 2001 From: Liam Mitchell Date: Wed, 5 Mar 2025 02:33:21 +0000 Subject: Update Linux service type and add libsystemd dependency --- src/zenserver/main.cpp | 4 ++++ src/zenutil/service.cpp | 2 +- xmake.lua | 4 ++++ 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/zenserver/main.cpp b/src/zenserver/main.cpp index 636e7f6c1..9ae54bdf1 100644 --- a/src/zenserver/main.cpp +++ b/src/zenserver/main.cpp @@ -34,6 +34,10 @@ # include "windows/service.h" #endif +#if ZEN_PLATFORM_LINUX +# include +#endif + ////////////////////////////////////////////////////////////////////////// // We don't have any doctest code in this file but this is needed to bring // in some shared code into the executable diff --git a/src/zenutil/service.cpp b/src/zenutil/service.cpp index c156b001c..ea7c2aae6 100644 --- a/src/zenutil/service.cpp +++ b/src/zenutil/service.cpp @@ -334,7 +334,7 @@ namespace { "StartLimitIntervalSec=0\n" "\n" "[Service]\n" - "Type=simple\n" + "Type=notify\n" "Restart=always\n" "RestartSec=1\n" "User={}\n" diff --git a/xmake.lua b/xmake.lua index c49a71901..2ea7bc639 100644 --- a/xmake.lua +++ b/xmake.lua @@ -37,6 +37,10 @@ if is_plat("windows") then add_requires("7z") end +if is_plat("linux") then + add_requires("vcpkg::libsystemd") +end + add_requires("vcpkg::mimalloc") if has_config("zensentry") then -- cgit v1.2.3 From 5f7a202aeb559aec9e98ed662229452bd2813e0a Mon Sep 17 00:00:00 2001 From: Liam Mitchell Date: Tue, 25 Mar 2025 17:30:46 -0700 Subject: Fix command line parsing when running service commands elevated --- src/zen/cmds/service_cmd.cpp | 4 ++-- src/zen/xmake.lua | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/zen/cmds/service_cmd.cpp b/src/zen/cmds/service_cmd.cpp index 386046e66..9eca10fc7 100644 --- a/src/zen/cmds/service_cmd.cpp +++ b/src/zen/cmds/service_cmd.cpp @@ -11,6 +11,7 @@ #if ZEN_PLATFORM_WINDOWS # include # include +# include #endif #if ZEN_PLATFORM_LINUX || ZEN_PLATFORM_MAC @@ -58,8 +59,7 @@ namespace { ExtendableWideStringBuilder<256> Parameters; std::filesystem::path ExecutablePath = GetRunningExecutablePath(); std::wstring CommandLine(GetCommandLine()); - std::string::size_type ExtraLength = CommandLine[0] == '\"' ? 2 : 0; - std::wstring CommandArguments = CommandLine.substr(ExecutablePath.string().length() + ExtraLength + 1); + std::wstring CommandArguments(PathGetArgs(CommandLine.data())); ZEN_CONSOLE("Attempting to run '{} {}' elevated...", ExecutablePath, WideToUtf8(CommandArguments)); diff --git a/src/zen/xmake.lua b/src/zen/xmake.lua index 78b2a3c2b..a755df374 100644 --- a/src/zen/xmake.lua +++ b/src/zen/xmake.lua @@ -17,7 +17,7 @@ target("zen") add_files("zen.rc") add_ldflags("/subsystem:console,5.02") add_ldflags("/LTCG") - add_links("crypt32", "wldap32", "Ws2_32") + add_links("crypt32", "wldap32", "Ws2_32", "Shlwapi") end if is_plat("macosx") then -- cgit v1.2.3 From ffe14640c54887d5daf4c1e76547c171e7b644e8 Mon Sep 17 00:00:00 2001 From: Liam Mitchell Date: Tue, 25 Mar 2025 17:33:05 -0700 Subject: Update return codes for service commands to provide more information to the caller --- src/zen/cmds/service_cmd.cpp | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/src/zen/cmds/service_cmd.cpp b/src/zen/cmds/service_cmd.cpp index 386046e66..fd4d3895e 100644 --- a/src/zen/cmds/service_cmd.cpp +++ b/src/zen/cmds/service_cmd.cpp @@ -92,6 +92,7 @@ namespace { else { ZEN_CONSOLE("Failed to run elevated, operation did not complete."); + ReturnCode = DWORD(-1); } return (int)ReturnCode; } @@ -272,6 +273,14 @@ FmtServiceInfo(const ServiceInfo& Info, std::string_view Prefix) return Result; } +enum class ServiceStatusReturnCode +{ + Running = 0, // successful return indicates proper installation and everything running smoothly + NotInstalled, + NotRunning, + UnknownError +}; + int ServiceCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) { @@ -304,12 +313,17 @@ ServiceCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) if (Ec) { ZEN_CONSOLE("Can't get information about service '{}'. Reason: '{}'", m_ServiceName, Ec.message()); - return 1; + return gsl::narrow(ServiceStatusReturnCode::UnknownError); } if (Info.Status == ServiceStatus::NotInstalled) { ZEN_CONSOLE("Service '{}' is not installed", m_ServiceName); - return 0; + return gsl::narrow(ServiceStatusReturnCode::NotInstalled); + } + else if (Info.Status != ServiceStatus::Running) + { + ZEN_CONSOLE("Service '{}' is not running", m_ServiceName); + return gsl::narrow(ServiceStatusReturnCode::NotRunning); } else { -- cgit v1.2.3 From ca949ab61d1540d5e6b92c6b5b53d5e3d02d5050 Mon Sep 17 00:00:00 2001 From: Liam Mitchell Date: Tue, 25 Mar 2025 17:37:18 -0700 Subject: Specify restart options for service manager, to avoid use of manual restart logic --- src/zenutil/service.cpp | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/zenutil/service.cpp b/src/zenutil/service.cpp index a2ff93efd..e1c5e17fa 100644 --- a/src/zenutil/service.cpp +++ b/src/zenutil/service.cpp @@ -449,6 +449,25 @@ InstallService(std::string_view ServiceName, const ServiceSpec& Spec) } } + // Actions defining what the service manager should do in the event of a zenserver crash. + // Attempt an immediate restart twice. If both restarts fail, stop trying. + // The attempt count will be reset based on the timeout specified in SERVICE_FAILURE_ACTIONS. + // If the service manages to survive for the length of that timeout, the reset attempt count + // will be reset and we will try restarting if we fail again. + SC_ACTION Actions[] = {{SC_ACTION_RESTART, 0}, {SC_ACTION_RESTART, 0}, {SC_ACTION_NONE, 0}}; + + SERVICE_FAILURE_ACTIONS FailureAction = { + 60, // if we haven't failed for one minute, assume the service is healthy and reset the failure count + NULL, // no reboot message - we don't want to reboot the whole system if zen dies + NULL, // no command to run on failure - just attempt restarting the service + ZEN_ARRAY_COUNT(Actions), + Actions}; + + if (!ChangeServiceConfig2(schService, SERVICE_CONFIG_FAILURE_ACTIONS, &FailureAction)) + { + return MakeErrorCodeFromLastError(); + } + CloseServiceHandle(schService); return {}; -- cgit v1.2.3 From 6362d82d2476d740968665e0b4f10750236d201e Mon Sep 17 00:00:00 2001 From: Liam Mitchell Date: Tue, 25 Mar 2025 18:02:11 -0700 Subject: Avoid leaking service handle in Windows service installation --- src/zenutil/service.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/zenutil/service.cpp b/src/zenutil/service.cpp index e1c5e17fa..760e52baf 100644 --- a/src/zenutil/service.cpp +++ b/src/zenutil/service.cpp @@ -436,6 +436,8 @@ InstallService(std::string_view ServiceName, const ServiceSpec& Spec) return MakeErrorCodeFromLastError(); } + auto _ = MakeGuard([schService]() { CloseServiceHandle(schService); }); + if (!Spec.Description.empty()) { ExtendableWideStringBuilder<128> DescriptionBuilder; @@ -468,8 +470,6 @@ InstallService(std::string_view ServiceName, const ServiceSpec& Spec) return MakeErrorCodeFromLastError(); } - CloseServiceHandle(schService); - return {}; } -- cgit v1.2.3 From aa3c95266ace162ed3ff83e71df50e2321a6364b Mon Sep 17 00:00:00 2001 From: Liam Mitchell Date: Thu, 27 Mar 2025 15:00:46 -0700 Subject: Fix naming of service handle close guard variable --- src/zenutil/service.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/zenutil/service.cpp b/src/zenutil/service.cpp index 760e52baf..6e68a01fa 100644 --- a/src/zenutil/service.cpp +++ b/src/zenutil/service.cpp @@ -436,7 +436,7 @@ InstallService(std::string_view ServiceName, const ServiceSpec& Spec) return MakeErrorCodeFromLastError(); } - auto _ = MakeGuard([schService]() { CloseServiceHandle(schService); }); + auto __ = MakeGuard([schService]() { CloseServiceHandle(schService); }); if (!Spec.Description.empty()) { -- cgit v1.2.3 From 2c6f3b6e0d23a327e695f535a34c7f6a56ecae1e Mon Sep 17 00:00:00 2001 From: Liam Mitchell Date: Wed, 16 Jul 2025 17:05:19 -0700 Subject: Add --full option to service install, which will handle stop/uninstall if necessary, and copy binaries to install location --- src/zen/cmds/service_cmd.cpp | 128 ++++++++++++++++++++++++++++++++++++++++--- src/zen/cmds/service_cmd.h | 2 + 2 files changed, 121 insertions(+), 9 deletions(-) diff --git a/src/zen/cmds/service_cmd.cpp b/src/zen/cmds/service_cmd.cpp index 91cce3ec1..f88e39c7f 100644 --- a/src/zen/cmds/service_cmd.cpp +++ b/src/zen/cmds/service_cmd.cpp @@ -162,6 +162,19 @@ ServiceCommand::ServiceCommand() cxxopts::value(m_ServiceName), ""); + m_InstallOptions.add_option("", + "", + "full", + fmt::format("Uninstall a running service and update service binaries before installing"), + cxxopts::value(m_FullInstall), + ""); + m_InstallOptions.add_option("", + "", + "install-path", + fmt::format("Path in which to install service binaries"), + cxxopts::value(m_InstallPath), + ""); + m_InstallOptions.add_option("", "u", "user", "User to run service as, defaults to current user", cxxopts::value(m_UserName), ""); #if ZEN_PLATFORM_WINDOWS m_InstallOptions.add_option("", @@ -333,19 +346,73 @@ ServiceCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) if (SubOption == &m_InstallOptions) { - ServiceInfo Info; - std::error_code Ec = QueryInstalledService(m_ServiceName, Info); - if (!Ec && Info.Status != ServiceStatus::NotInstalled) - { - ZEN_CONSOLE("Service '{}' already installed:\n{}", m_ServiceName, FmtServiceInfo(Info, " ")); - return 1; - } - if (!IsElevated()) { return RunElevated(m_AllowElevation); } + ServiceInfo Info; + std::error_code Ec = QueryInstalledService(m_ServiceName, Info); + if (!Ec && Info.Status != ServiceStatus::NotInstalled) + { + if (m_FullInstall) + { + if (Info.Status == ServiceStatus::Running) + { + Ec = StopService(m_ServiceName); + if (Ec) + { + ZEN_CONSOLE("Failed to stop service '{}' using '{}'. Reason: '{}'", + m_ServiceName, + m_ServerExecutable, + Ec.message()); + + return 1; + } + + int Timeout = 3000; // Wait up to 3 seconds for the service to fully stop before uninstalling + while (Timeout > 0) + { + Ec = QueryInstalledService(m_ServiceName, Info); + if (Ec) + { + ZEN_CONSOLE("Failed to wait for service to stop: '{}'", Ec.message()); + return 1; + } + + if (Info.Status == ServiceStatus::Stopped) + { + break; + } + + Sleep(100); + Timeout -= 100; + } + + if (Info.Status != ServiceStatus::Stopped) + { + ZEN_CONSOLE("Timed out waiting for service to stop"); + return 1; + } + } + + Ec = UninstallService(m_ServiceName); + if (Ec) + { + ZEN_CONSOLE("Failed to uninstall running service '{}' using '{}'. Reason: '{}'", + m_ServiceName, + m_ServerExecutable, + Ec.message()); + + return 1; + } + } + else + { + ZEN_CONSOLE("Service '{}' already installed:\n{}", m_ServiceName, FmtServiceInfo(Info, " ")); + return 1; + } + } if (m_ServerExecutable.empty()) { std::filesystem::path ExePath = zen::GetRunningExecutablePath(); @@ -353,7 +420,40 @@ ServiceCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) m_ServerExecutable = ExePath; } m_ServerExecutable = std::filesystem::absolute(m_ServerExecutable); - Ec = InstallService( + + if (m_FullInstall) + { + if (m_InstallPath.empty()) + { + throw zen::OptionParseException("--full requires --install-path to be specified"); + } + + std::filesystem::path ExePath = zen::GetRunningExecutablePath(); + std::filesystem::path DestinationExePath = m_InstallPath / ExePath.filename(); + std::filesystem::path DestinationServerPath = m_InstallPath / m_ServerExecutable.filename(); + + if (!std::filesystem::is_directory(m_InstallPath) && !CreateDirectories(m_InstallPath)) + { + ZEN_CONSOLE("Unable to create install directory '{}'", m_InstallPath); + return 1; + } + + if (!CopyFile(ExePath, DestinationExePath, {})) + { + ZEN_CONSOLE("Unable to copy executable from '{}' to '{}'", ExePath, DestinationExePath); + return 1; + } + + if (!CopyFile(m_ServerExecutable, DestinationServerPath, {})) + { + ZEN_CONSOLE("Unable to copy server executable from '{}' to '{}'", m_ServerExecutable, DestinationServerPath); + return 1; + } + + m_ServerExecutable = DestinationServerPath; + } + + Ec = InstallService( m_ServiceName, ServiceSpec { .ExecutablePath = m_ServerExecutable, .CommandLineOptions = GlobalOptions.PassthroughCommandLine, .UserName = m_UserName @@ -368,6 +468,16 @@ ServiceCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) return 1; } ZEN_CONSOLE("Installed service '{}' using '{}' successfully", m_ServiceName, m_ServerExecutable); + + if (m_FullInstall) + { + Ec = StartService(m_ServiceName); + if (Ec) + { + ZEN_CONSOLE("Failed to start service '{}'. Reason: '{}'", m_ServiceName, Ec.message()); + return 1; + } + } } if (SubOption == &m_UninstallOptions) diff --git a/src/zen/cmds/service_cmd.h b/src/zen/cmds/service_cmd.h index f88e8c25b..359e8e854 100644 --- a/src/zen/cmds/service_cmd.h +++ b/src/zen/cmds/service_cmd.h @@ -36,6 +36,8 @@ private: "install", "Install zenserver as a service. Arguments following \" -- \" will be added as parameters to the installed service."}; std::filesystem::path m_ServerExecutable; + std::filesystem::path m_InstallPath; + bool m_FullInstall = false; #if ZEN_PLATFORM_WINDOWS std::string m_ServiceDisplayName = "Unreal Zen Storage Service"; std::string m_ServiceDescription; -- cgit v1.2.3 From 0004a51433d8bc5e427178fe76c8a58e651870ad Mon Sep 17 00:00:00 2001 From: Liam Mitchell Date: Mon, 21 Jul 2025 15:44:41 -0700 Subject: Add PDBs and crashpad_handler to installed files during full service install --- src/zen/cmds/service_cmd.cpp | 35 ++++++++++++++++++++++++----------- 1 file changed, 24 insertions(+), 11 deletions(-) diff --git a/src/zen/cmds/service_cmd.cpp b/src/zen/cmds/service_cmd.cpp index f88e39c7f..b3f64e99f 100644 --- a/src/zen/cmds/service_cmd.cpp +++ b/src/zen/cmds/service_cmd.cpp @@ -428,7 +428,22 @@ ServiceCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) throw zen::OptionParseException("--full requires --install-path to be specified"); } - std::filesystem::path ExePath = zen::GetRunningExecutablePath(); + std::filesystem::path ExePath = zen::GetRunningExecutablePath(); + + std::filesystem::path FilesToCopy[] = { + ExePath, + m_ServerExecutable, +#if ZEN_PLATFORM_WINDOWS + ExePath.replace_extension("pdb"), + m_ServerExecutable.replace_extension("pdb"), + ExePath + .replace_filename("crashpad_handler.exe") +#endif +#if ZEN_PLATFORM_MAC + ExePath.replace_filename("crashpad_handler") +#endif + }; + std::filesystem::path DestinationExePath = m_InstallPath / ExePath.filename(); std::filesystem::path DestinationServerPath = m_InstallPath / m_ServerExecutable.filename(); @@ -438,19 +453,17 @@ ServiceCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) return 1; } - if (!CopyFile(ExePath, DestinationExePath, {})) - { - ZEN_CONSOLE("Unable to copy executable from '{}' to '{}'", ExePath, DestinationExePath); - return 1; - } - - if (!CopyFile(m_ServerExecutable, DestinationServerPath, {})) + for (const std::filesystem::path& File : FilesToCopy) { - ZEN_CONSOLE("Unable to copy server executable from '{}' to '{}'", m_ServerExecutable, DestinationServerPath); - return 1; + std::filesystem::path Destination = m_InstallPath / File.filename(); + if (!CopyFile(File, Destination, {})) + { + ZEN_CONSOLE("Failed to copy '{}' to '{}'", File, Destination); + return 1; + } } - m_ServerExecutable = DestinationServerPath; + m_ServerExecutable = m_InstallPath / m_ServerExecutable.filename(); } Ec = InstallService( -- cgit v1.2.3 From fb90f836a325770f597138f1339a930c81eeac7d Mon Sep 17 00:00:00 2001 From: Liam Mitchell Date: Mon, 21 Jul 2025 15:56:01 -0700 Subject: Increase service stop timeout to 30 seconds during full install --- src/zen/cmds/service_cmd.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/zen/cmds/service_cmd.cpp b/src/zen/cmds/service_cmd.cpp index b3f64e99f..2125e49f1 100644 --- a/src/zen/cmds/service_cmd.cpp +++ b/src/zen/cmds/service_cmd.cpp @@ -370,7 +370,7 @@ ServiceCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) return 1; } - int Timeout = 3000; // Wait up to 3 seconds for the service to fully stop before uninstalling + int Timeout = 30000; // Wait up to 30 seconds for the service to fully stop before uninstalling while (Timeout > 0) { Ec = QueryInstalledService(m_ServiceName, Info); -- cgit v1.2.3 From baa420dbc358cb48d26e0aa94a36196f737b3f04 Mon Sep 17 00:00:00 2001 From: Liam Mitchell Date: Fri, 25 Jul 2025 20:58:17 +0000 Subject: Fix permissions and ownership issues with service binary copy and remove unnecessary alias from unit file --- src/zen/cmds/service_cmd.cpp | 11 ++++++++++- src/zencore/filesystem.cpp | 2 ++ src/zenutil/service.cpp | 7 ++----- 3 files changed, 14 insertions(+), 6 deletions(-) diff --git a/src/zen/cmds/service_cmd.cpp b/src/zen/cmds/service_cmd.cpp index 2125e49f1..a734504b8 100644 --- a/src/zen/cmds/service_cmd.cpp +++ b/src/zen/cmds/service_cmd.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #if ZEN_PLATFORM_WINDOWS # include @@ -456,13 +457,21 @@ ServiceCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) for (const std::filesystem::path& File : FilesToCopy) { std::filesystem::path Destination = m_InstallPath / File.filename(); - if (!CopyFile(File, Destination, {})) + if (!CopyFile(File, Destination, {.EnableClone = false})) { ZEN_CONSOLE("Failed to copy '{}' to '{}'", File, Destination); return 1; } + + ZEN_INFO("Copied '{}' to '{}'", File, Destination); + + if (File.extension() != "pdb") + { + std::filesystem::permissions(Destination, std::filesystem::perms::owner_exec, std::filesystem::perm_options::add); + } } + m_ServerExecutable = m_InstallPath / m_ServerExecutable.filename(); } diff --git a/src/zencore/filesystem.cpp b/src/zencore/filesystem.cpp index 05e2bf049..783f01b6c 100644 --- a/src/zencore/filesystem.cpp +++ b/src/zencore/filesystem.cpp @@ -603,6 +603,8 @@ CopyFile(const std::filesystem::path& FromPath, const std::filesystem::path& ToP size_t FileSizeBytes = Stat.st_size; + fchown(ToFd, Stat.st_uid, Stat.st_gid); + // Copy impl const size_t BufferSize = Min(FileSizeBytes, 64u << 10); void* Buffer = malloc(BufferSize); diff --git a/src/zenutil/service.cpp b/src/zenutil/service.cpp index 6e68a01fa..25941bde1 100644 --- a/src/zenutil/service.cpp +++ b/src/zenutil/service.cpp @@ -321,7 +321,6 @@ namespace { std::string BuildUnitFile(std::string_view ServiceName, const std::filesystem::path& ExecutablePath, std::string_view CommandLineOptions, - std::string_view AliasName, std::string_view UserName) { return fmt::format( @@ -341,14 +340,12 @@ namespace { "ExecStart={} {}\n" "RuntimeDirectory={}\n" "[Install]\n" - "Alias={}\n" "WantedBy=multi-user.target", ServiceName, UserName, ExecutablePath, CommandLineOptions, - ExecutablePath.parent_path(), - AliasName); + ExecutablePath.parent_path()); } #endif // ZEN_PLATFORM_LINUX @@ -917,7 +914,7 @@ InstallService(std::string_view ServiceName, const ServiceSpec& Spec) UserName = UserResult.second; } - std::string UnitFile = BuildUnitFile(ServiceName, Spec.ExecutablePath, Spec.CommandLineOptions, UnitName, UserName); + std::string UnitFile = BuildUnitFile(ServiceName, Spec.ExecutablePath, Spec.CommandLineOptions, UserName); ZEN_DEBUG("Writing systemd unit file to {}", ServiceUnitPath.string()); try { -- cgit v1.2.3 From 346dcd7887ca9239248953f10bed0443bb7e96c2 Mon Sep 17 00:00:00 2001 From: Liam Mitchell Date: Fri, 25 Jul 2025 23:53:37 +0000 Subject: Upload vcpkg logs as artifacts on failure --- .github/workflows/validate.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml index 7487297f4..3b2f445d0 100644 --- a/.github/workflows/validate.yml +++ b/.github/workflows/validate.yml @@ -157,6 +157,12 @@ jobs: env: VCPKG_ROOT: ${{ github.workspace }}/.vcpkg + - name: Upload config logs + uses: actions/upload-artifact@v3 + with: + name: vcpkg-logs-${{ matrix.config }} + path: .vcpkg/buildtrees/**/*.log + - name: Build & Test if: ${{ matrix.config == 'debug' }} run: | -- cgit v1.2.3 From e35f5c9cb28260809fb7f087db061079ebc4cccd Mon Sep 17 00:00:00 2001 From: Liam Mitchell Date: Sat, 26 Jul 2025 00:06:12 +0000 Subject: Temporarily enable Validate jobs on push to de/zen-service-command for testing --- .github/workflows/validate.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml index 3b2f445d0..f87eda352 100644 --- a/.github/workflows/validate.yml +++ b/.github/workflows/validate.yml @@ -21,6 +21,7 @@ on: push: branches: - 'main' + - 'de/zen-service-command' paths-ignore: - 'VERSION.txt' - 'CHANGELOG.md' -- cgit v1.2.3 From a3871c7142d59724c7deb89f4e9d18c4772919e9 Mon Sep 17 00:00:00 2001 From: Liam Mitchell Date: Sat, 26 Jul 2025 00:08:58 +0000 Subject: Always upload vcpkg logs on failure --- .github/workflows/validate.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml index f87eda352..a35d1eeed 100644 --- a/.github/workflows/validate.yml +++ b/.github/workflows/validate.yml @@ -159,6 +159,7 @@ jobs: VCPKG_ROOT: ${{ github.workspace }}/.vcpkg - name: Upload config logs + if: ${{ (failure() || success()) }} uses: actions/upload-artifact@v3 with: name: vcpkg-logs-${{ matrix.config }} -- cgit v1.2.3 From c9f0ebda018b3196d90c2e07affc35722c03f2c4 Mon Sep 17 00:00:00 2001 From: Liam Mitchell Date: Tue, 29 Jul 2025 23:34:40 +0000 Subject: Install libc++-dev during CI in order to build libsystemd properly --- .github/workflows/validate.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml index a35d1eeed..81742fd94 100644 --- a/.github/workflows/validate.yml +++ b/.github/workflows/validate.yml @@ -152,6 +152,11 @@ jobs: ./bootstrap-vcpkg.sh cd .. + - name: Installing libc++ # This is not used for zen builds, but gperf/meson are used by some dependencies and they require it + run: | + sudo apt-get update + sudo apt-get -y install libc++-dev + - name: Config run: | ./scripts/ue_build_linux/ue_build.sh ./.tmp-ue-toolchain xmake config -v -y -m ${{ matrix.config }} --arch=${{ matrix.arch }} -- cgit v1.2.3 From 3141541886259534a6c46521e4a3be7f147afd9f Mon Sep 17 00:00:00 2001 From: Liam Mitchell Date: Wed, 30 Jul 2025 20:40:06 +0000 Subject: Use apt instead of apt-get for installing system packages --- .github/workflows/validate.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml index 81742fd94..cadaeb280 100644 --- a/.github/workflows/validate.yml +++ b/.github/workflows/validate.yml @@ -153,9 +153,10 @@ jobs: cd .. - name: Installing libc++ # This is not used for zen builds, but gperf/meson are used by some dependencies and they require it + shell: bash run: | - sudo apt-get update - sudo apt-get -y install libc++-dev + sudo apt update + sudo apt install -y libc++-dev - name: Config run: | -- cgit v1.2.3 From 157f21e39e0226228833965d5a1d125fce5d30b2 Mon Sep 17 00:00:00 2001 From: Liam Mitchell Date: Wed, 20 Aug 2025 23:35:02 +0000 Subject: Add pre-built systemd library and remove vcpkg dependency --- src/zenserver/main.cpp | 2 + src/zenserver/xmake.lua | 8 + thirdparty/systemd/include/systemd/_sd-common.h | 108 ++++ .../systemd/include/systemd/sd-bus-protocol.h | 108 ++++ thirdparty/systemd/include/systemd/sd-bus-vtable.h | 357 ++++++++++++++ thirdparty/systemd/include/systemd/sd-bus.h | 544 +++++++++++++++++++++ thirdparty/systemd/include/systemd/sd-daemon.h | 347 +++++++++++++ thirdparty/systemd/include/systemd/sd-device.h | 168 +++++++ thirdparty/systemd/include/systemd/sd-event.h | 188 +++++++ thirdparty/systemd/include/systemd/sd-gpt.h | 369 ++++++++++++++ thirdparty/systemd/include/systemd/sd-hwdb.h | 46 ++ thirdparty/systemd/include/systemd/sd-id128.h | 165 +++++++ thirdparty/systemd/include/systemd/sd-journal.h | 182 +++++++ thirdparty/systemd/include/systemd/sd-login.h | 270 ++++++++++ thirdparty/systemd/include/systemd/sd-messages.h | 280 +++++++++++ thirdparty/systemd/include/systemd/sd-path.h | 131 +++++ thirdparty/systemd/lib/libblkid.a | Bin 0 -> 938808 bytes thirdparty/systemd/lib/libcap.a | Bin 0 -> 59540 bytes thirdparty/systemd/lib/libcrypt.a | Bin 0 -> 302222 bytes thirdparty/systemd/lib/liblz4.a | Bin 0 -> 274226 bytes thirdparty/systemd/lib/liblzma.a | Bin 0 -> 365960 bytes thirdparty/systemd/lib/libmount.a | Bin 0 -> 934428 bytes thirdparty/systemd/lib/libpsx.a | Bin 0 -> 16878 bytes thirdparty/systemd/lib/libsystemd.a | Bin 0 -> 3418556 bytes thirdparty/systemd/lib/libzstd.a | Bin 0 -> 1331440 bytes xmake.lua | 4 - 26 files changed, 3273 insertions(+), 4 deletions(-) create mode 100644 thirdparty/systemd/include/systemd/_sd-common.h create mode 100644 thirdparty/systemd/include/systemd/sd-bus-protocol.h create mode 100644 thirdparty/systemd/include/systemd/sd-bus-vtable.h create mode 100644 thirdparty/systemd/include/systemd/sd-bus.h create mode 100644 thirdparty/systemd/include/systemd/sd-daemon.h create mode 100644 thirdparty/systemd/include/systemd/sd-device.h create mode 100644 thirdparty/systemd/include/systemd/sd-event.h create mode 100644 thirdparty/systemd/include/systemd/sd-gpt.h create mode 100644 thirdparty/systemd/include/systemd/sd-hwdb.h create mode 100644 thirdparty/systemd/include/systemd/sd-id128.h create mode 100644 thirdparty/systemd/include/systemd/sd-journal.h create mode 100644 thirdparty/systemd/include/systemd/sd-login.h create mode 100644 thirdparty/systemd/include/systemd/sd-messages.h create mode 100644 thirdparty/systemd/include/systemd/sd-path.h create mode 100644 thirdparty/systemd/lib/libblkid.a create mode 100644 thirdparty/systemd/lib/libcap.a create mode 100644 thirdparty/systemd/lib/libcrypt.a create mode 100644 thirdparty/systemd/lib/liblz4.a create mode 100644 thirdparty/systemd/lib/liblzma.a create mode 100644 thirdparty/systemd/lib/libmount.a create mode 100644 thirdparty/systemd/lib/libpsx.a create mode 100644 thirdparty/systemd/lib/libsystemd.a create mode 100644 thirdparty/systemd/lib/libzstd.a diff --git a/src/zenserver/main.cpp b/src/zenserver/main.cpp index 6bd098da9..f8f25877b 100644 --- a/src/zenserver/main.cpp +++ b/src/zenserver/main.cpp @@ -35,7 +35,9 @@ #endif #if ZEN_PLATFORM_LINUX +ZEN_THIRD_PARTY_INCLUDES_START # include +ZEN_THIRD_PARTY_INCLUDES_END #endif ////////////////////////////////////////////////////////////////////////// diff --git a/src/zenserver/xmake.lua b/src/zenserver/xmake.lua index 470fbd24e..7ea9d52cc 100644 --- a/src/zenserver/xmake.lua +++ b/src/zenserver/xmake.lua @@ -41,6 +41,14 @@ target("zenserver") add_ldflags("-framework SystemConfiguration") end + if is_plat("linux") then + add_includedirs("$(projectdir)/thirdparty/systemd/include") + add_linkdirs("$(projectdir)/thirdparty/systemd/lib") + add_links("systemd") + add_links("cap") + end + + add_options("compute") add_options("exec") diff --git a/thirdparty/systemd/include/systemd/_sd-common.h b/thirdparty/systemd/include/systemd/_sd-common.h new file mode 100644 index 000000000..d4381d90f --- /dev/null +++ b/thirdparty/systemd/include/systemd/_sd-common.h @@ -0,0 +1,108 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#ifndef foosdcommonhfoo +#define foosdcommonhfoo + +/*** + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +/* This is a private header; never even think of including this directly! */ + +#if defined(__INCLUDE_LEVEL__) && __INCLUDE_LEVEL__ <= 1 && !defined(__COVERITY__) +# error "Do not include _sd-common.h directly; it is a private header." +#endif + +typedef void (*_sd_destroy_t)(void *userdata); + +#ifndef _sd_printf_ +# if __GNUC__ >= 4 +# define _sd_printf_(a,b) __attribute__((__format__(printf, a, b))) +# else +# define _sd_printf_(a,b) +# endif +#endif + +#ifndef _sd_sentinel_ +# define _sd_sentinel_ __attribute__((__sentinel__)) +#endif + +#ifndef _sd_packed_ +# define _sd_packed_ __attribute__((__packed__)) +#endif + +#ifndef _sd_pure_ +# define _sd_pure_ __attribute__((__pure__)) +#endif + +/* Note that strictly speaking __deprecated__ has been available before GCC 6. However, starting with GCC 6 + * it also works on enum values, which we are interested in. Since this is a developer-facing feature anyway + * (as opposed to build engineer-facing), let's hence conditionalize this to gcc 6, given that the developers + * are probably going to use something newer anyway. */ +#ifndef _sd_deprecated_ +# if __GNUC__ >= 6 +# define _sd_deprecated_ __attribute__((__deprecated__)) +# else +# define _sd_deprecated_ +# endif +#endif + +#ifndef _SD_STRINGIFY +# define _SD_XSTRINGIFY(x) #x +# define _SD_STRINGIFY(x) _SD_XSTRINGIFY(x) +#endif + +#ifndef _SD_BEGIN_DECLARATIONS +# ifdef __cplusplus +# define _SD_BEGIN_DECLARATIONS \ + extern "C" { \ + struct _sd_useless_struct_to_allow_trailing_semicolon_ +# else +# define _SD_BEGIN_DECLARATIONS \ + struct _sd_useless_struct_to_allow_trailing_semicolon_ +# endif +#endif + +#ifndef _SD_END_DECLARATIONS +# ifdef __cplusplus +# define _SD_END_DECLARATIONS \ + } \ + struct _sd_useless_cpp_struct_to_allow_trailing_semicolon_ +# else +# define _SD_END_DECLARATIONS \ + struct _sd_useless_struct_to_allow_trailing_semicolon_ +# endif +#endif + +#ifndef _SD_ARRAY_STATIC +# if __STDC_VERSION__ >= 199901L && !defined(__cplusplus) +# define _SD_ARRAY_STATIC static +# else +# define _SD_ARRAY_STATIC +# endif +#endif + +#define _SD_DEFINE_POINTER_CLEANUP_FUNC(type, func) \ + static __inline__ void func##p(type **p) { \ + if (*p) \ + func(*p); \ + } \ + struct _sd_useless_struct_to_allow_trailing_semicolon_ + +/* The following macro should be used in all public enums, to force 64-bit wideness on them, so that we can + * freely extend them later on, without breaking compatibility. */ +#define _SD_ENUM_FORCE_S64(id) \ + _SD_##id##_INT64_MIN = INT64_MIN, \ + _SD_##id##_INT64_MAX = INT64_MAX + +#endif diff --git a/thirdparty/systemd/include/systemd/sd-bus-protocol.h b/thirdparty/systemd/include/systemd/sd-bus-protocol.h new file mode 100644 index 000000000..25c9ab335 --- /dev/null +++ b/thirdparty/systemd/include/systemd/sd-bus-protocol.h @@ -0,0 +1,108 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#ifndef foosdbusprotocolhfoo +#define foosdbusprotocolhfoo + +/*** + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include "_sd-common.h" + +_SD_BEGIN_DECLARATIONS; + +/* Types of message */ + +enum { + _SD_BUS_MESSAGE_TYPE_INVALID = 0, + SD_BUS_MESSAGE_METHOD_CALL, + SD_BUS_MESSAGE_METHOD_RETURN, + SD_BUS_MESSAGE_METHOD_ERROR, + SD_BUS_MESSAGE_SIGNAL, + _SD_BUS_MESSAGE_TYPE_MAX +}; + +/* Primitive types */ + +enum { + _SD_BUS_TYPE_INVALID = 0, + SD_BUS_TYPE_BYTE = 'y', + SD_BUS_TYPE_BOOLEAN = 'b', + SD_BUS_TYPE_INT16 = 'n', + SD_BUS_TYPE_UINT16 = 'q', + SD_BUS_TYPE_INT32 = 'i', + SD_BUS_TYPE_UINT32 = 'u', + SD_BUS_TYPE_INT64 = 'x', + SD_BUS_TYPE_UINT64 = 't', + SD_BUS_TYPE_DOUBLE = 'd', + SD_BUS_TYPE_STRING = 's', + SD_BUS_TYPE_OBJECT_PATH = 'o', + SD_BUS_TYPE_SIGNATURE = 'g', + SD_BUS_TYPE_UNIX_FD = 'h', + SD_BUS_TYPE_ARRAY = 'a', + SD_BUS_TYPE_VARIANT = 'v', + SD_BUS_TYPE_STRUCT = 'r', /* not actually used in signatures */ + SD_BUS_TYPE_STRUCT_BEGIN = '(', + SD_BUS_TYPE_STRUCT_END = ')', + SD_BUS_TYPE_DICT_ENTRY = 'e', /* not actually used in signatures */ + SD_BUS_TYPE_DICT_ENTRY_BEGIN = '{', + SD_BUS_TYPE_DICT_ENTRY_END = '}' +}; + +/* Well-known errors. Note that this is only a sanitized subset of the + * errors that the reference implementation generates. */ + +#define SD_BUS_ERROR_FAILED "org.freedesktop.DBus.Error.Failed" +#define SD_BUS_ERROR_NO_MEMORY "org.freedesktop.DBus.Error.NoMemory" +#define SD_BUS_ERROR_SERVICE_UNKNOWN "org.freedesktop.DBus.Error.ServiceUnknown" +#define SD_BUS_ERROR_NAME_HAS_NO_OWNER "org.freedesktop.DBus.Error.NameHasNoOwner" +#define SD_BUS_ERROR_NO_REPLY "org.freedesktop.DBus.Error.NoReply" +#define SD_BUS_ERROR_IO_ERROR "org.freedesktop.DBus.Error.IOError" +#define SD_BUS_ERROR_BAD_ADDRESS "org.freedesktop.DBus.Error.BadAddress" +#define SD_BUS_ERROR_NOT_SUPPORTED "org.freedesktop.DBus.Error.NotSupported" +#define SD_BUS_ERROR_LIMITS_EXCEEDED "org.freedesktop.DBus.Error.LimitsExceeded" +#define SD_BUS_ERROR_ACCESS_DENIED "org.freedesktop.DBus.Error.AccessDenied" +#define SD_BUS_ERROR_AUTH_FAILED "org.freedesktop.DBus.Error.AuthFailed" +#define SD_BUS_ERROR_NO_SERVER "org.freedesktop.DBus.Error.NoServer" +#define SD_BUS_ERROR_TIMEOUT "org.freedesktop.DBus.Error.Timeout" +#define SD_BUS_ERROR_NO_NETWORK "org.freedesktop.DBus.Error.NoNetwork" +#define SD_BUS_ERROR_ADDRESS_IN_USE "org.freedesktop.DBus.Error.AddressInUse" +#define SD_BUS_ERROR_DISCONNECTED "org.freedesktop.DBus.Error.Disconnected" +#define SD_BUS_ERROR_INVALID_ARGS "org.freedesktop.DBus.Error.InvalidArgs" +#define SD_BUS_ERROR_FILE_NOT_FOUND "org.freedesktop.DBus.Error.FileNotFound" +#define SD_BUS_ERROR_FILE_EXISTS "org.freedesktop.DBus.Error.FileExists" +#define SD_BUS_ERROR_UNKNOWN_METHOD "org.freedesktop.DBus.Error.UnknownMethod" +#define SD_BUS_ERROR_UNKNOWN_OBJECT "org.freedesktop.DBus.Error.UnknownObject" +#define SD_BUS_ERROR_UNKNOWN_INTERFACE "org.freedesktop.DBus.Error.UnknownInterface" +#define SD_BUS_ERROR_UNKNOWN_PROPERTY "org.freedesktop.DBus.Error.UnknownProperty" +#define SD_BUS_ERROR_PROPERTY_READ_ONLY "org.freedesktop.DBus.Error.PropertyReadOnly" +#define SD_BUS_ERROR_UNIX_PROCESS_ID_UNKNOWN "org.freedesktop.DBus.Error.UnixProcessIdUnknown" +#define SD_BUS_ERROR_INVALID_SIGNATURE "org.freedesktop.DBus.Error.InvalidSignature" +#define SD_BUS_ERROR_INCONSISTENT_MESSAGE "org.freedesktop.DBus.Error.InconsistentMessage" +#define SD_BUS_ERROR_TIMED_OUT "org.freedesktop.DBus.Error.TimedOut" +#define SD_BUS_ERROR_MATCH_RULE_NOT_FOUND "org.freedesktop.DBus.Error.MatchRuleNotFound" +#define SD_BUS_ERROR_MATCH_RULE_INVALID "org.freedesktop.DBus.Error.MatchRuleInvalid" +#define SD_BUS_ERROR_INTERACTIVE_AUTHORIZATION_REQUIRED "org.freedesktop.DBus.Error.InteractiveAuthorizationRequired" +#define SD_BUS_ERROR_INVALID_FILE_CONTENT "org.freedesktop.DBus.Error.InvalidFileContent" +#define SD_BUS_ERROR_SELINUX_SECURITY_CONTEXT_UNKNOWN "org.freedesktop.DBus.Error.SELinuxSecurityContextUnknown" +#define SD_BUS_ERROR_OBJECT_PATH_IN_USE "org.freedesktop.DBus.Error.ObjectPathInUse" + +/* https://dbus.freedesktop.org/doc/dbus-specification.html#message-protocol-marshaling-signature */ +#define SD_BUS_MAXIMUM_SIGNATURE_LENGTH 255 + +/* https://dbus.freedesktop.org/doc/dbus-specification.html#message-protocol-names */ +#define SD_BUS_MAXIMUM_NAME_LENGTH 255 + +_SD_END_DECLARATIONS; + +#endif diff --git a/thirdparty/systemd/include/systemd/sd-bus-vtable.h b/thirdparty/systemd/include/systemd/sd-bus-vtable.h new file mode 100644 index 000000000..d06c5c301 --- /dev/null +++ b/thirdparty/systemd/include/systemd/sd-bus-vtable.h @@ -0,0 +1,357 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#ifndef foosdbusvtablehfoo +#define foosdbusvtablehfoo + +/*** + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include "_sd-common.h" + +_SD_BEGIN_DECLARATIONS; + +typedef struct sd_bus_vtable sd_bus_vtable; + +#include "sd-bus.h" + +enum { + _SD_BUS_VTABLE_START = '<', + _SD_BUS_VTABLE_END = '>', + _SD_BUS_VTABLE_METHOD = 'M', + _SD_BUS_VTABLE_SIGNAL = 'S', + _SD_BUS_VTABLE_PROPERTY = 'P', + _SD_BUS_VTABLE_WRITABLE_PROPERTY = 'W' +}; + +__extension__ enum { + SD_BUS_VTABLE_DEPRECATED = 1ULL << 0, + SD_BUS_VTABLE_HIDDEN = 1ULL << 1, + SD_BUS_VTABLE_UNPRIVILEGED = 1ULL << 2, + SD_BUS_VTABLE_METHOD_NO_REPLY = 1ULL << 3, + SD_BUS_VTABLE_PROPERTY_CONST = 1ULL << 4, + SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE = 1ULL << 5, + SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION = 1ULL << 6, + SD_BUS_VTABLE_PROPERTY_EXPLICIT = 1ULL << 7, + SD_BUS_VTABLE_SENSITIVE = 1ULL << 8, /* covers both directions: method call + reply */ + SD_BUS_VTABLE_ABSOLUTE_OFFSET = 1ULL << 9, + _SD_BUS_VTABLE_CAPABILITY_MASK = 0xFFFFULL << 40 +}; + +#define SD_BUS_VTABLE_CAPABILITY(x) ((uint64_t) (((x)+1) & 0xFFFF) << 40) + +enum { + _SD_BUS_VTABLE_PARAM_NAMES = 1 << 0 +}; + +extern const unsigned sd_bus_object_vtable_format; + +/* Note: unused areas in the sd_bus_vtable[] array must be initialized to 0. The structure contains an embedded + * union, and the compiler is NOT required to initialize the unused areas of the union when the rest of the + * structure is initialized. Normally the array is defined as read-only data, in which case the linker places + * it in the BSS section, which is always fully initialized, so this is not a concern. But if the array is + * created on the stack or on the heap, care must be taken to initialize the unused areas, for examply by + * first memsetting the whole region to zero before filling the data in. */ + +struct sd_bus_vtable { + /* Please do not initialize this structure directly, use the + * macros below instead */ + + __extension__ uint8_t type:8; + __extension__ uint64_t flags:56; + union { + struct { + size_t element_size; + uint64_t features; + const unsigned *vtable_format_reference; + } start; + struct { + /* This field exists only to make sure we have something to initialize in + * SD_BUS_VTABLE_END in a way that is both compatible with pedantic versions of C and + * C++. It's unused otherwise. */ + size_t _reserved; + } end; + struct { + const char *member; + const char *signature; + const char *result; + sd_bus_message_handler_t handler; + size_t offset; + const char *names; + } method; + struct { + const char *member; + const char *signature; + const char *names; + } signal; + struct { + const char *member; + const char *signature; + sd_bus_property_get_t get; + sd_bus_property_set_t set; + size_t offset; + } property; + } x; +}; + +#define SD_BUS_VTABLE_START(_flags) \ + { \ + .type = _SD_BUS_VTABLE_START, \ + .flags = _flags, \ + .x = { \ + .start = { \ + .element_size = sizeof(sd_bus_vtable), \ + .features = _SD_BUS_VTABLE_PARAM_NAMES, \ + .vtable_format_reference = &sd_bus_object_vtable_format, \ + }, \ + }, \ + } + +/* helper macro to format method and signal parameters, one at a time */ +#define SD_BUS_PARAM(x) #x "\0" + +#define SD_BUS_METHOD_WITH_NAMES_OFFSET(_member, _signature, _in_names, _result, _out_names, _handler, _offset, _flags) \ + { \ + .type = _SD_BUS_VTABLE_METHOD, \ + .flags = _flags, \ + .x = { \ + .method = { \ + .member = _member, \ + .signature = _signature, \ + .result = _result, \ + .handler = _handler, \ + .offset = _offset, \ + .names = _in_names _out_names, \ + }, \ + }, \ + } +#define SD_BUS_METHOD_WITH_OFFSET(_member, _signature, _result, _handler, _offset, _flags) \ + SD_BUS_METHOD_WITH_NAMES_OFFSET(_member, _signature, "", _result, "", _handler, _offset, _flags) +#define SD_BUS_METHOD_WITH_NAMES(_member, _signature, _in_names, _result, _out_names, _handler, _flags) \ + SD_BUS_METHOD_WITH_NAMES_OFFSET(_member, _signature, _in_names, _result, _out_names, _handler, 0, _flags) +#define SD_BUS_METHOD(_member, _signature, _result, _handler, _flags) \ + SD_BUS_METHOD_WITH_NAMES_OFFSET(_member, _signature, "", _result, "", _handler, 0, _flags) + +#define SD_BUS_SIGNAL_WITH_NAMES(_member, _signature, _out_names, _flags) \ + { \ + .type = _SD_BUS_VTABLE_SIGNAL, \ + .flags = _flags, \ + .x = { \ + .signal = { \ + .member = _member, \ + .signature = _signature, \ + .names = _out_names, \ + }, \ + }, \ + } +#define SD_BUS_SIGNAL(_member, _signature, _flags) \ + SD_BUS_SIGNAL_WITH_NAMES(_member, _signature, "", _flags) + +#define SD_BUS_PROPERTY(_member, _signature, _get, _offset, _flags) \ + { \ + .type = _SD_BUS_VTABLE_PROPERTY, \ + .flags = _flags, \ + .x = { \ + .property = { \ + .member = _member, \ + .signature = _signature, \ + .get = _get, \ + .set = NULL, \ + .offset = _offset, \ + }, \ + }, \ + } + +#define SD_BUS_WRITABLE_PROPERTY(_member, _signature, _get, _set, _offset, _flags) \ + { \ + .type = _SD_BUS_VTABLE_WRITABLE_PROPERTY, \ + .flags = _flags, \ + .x = { \ + .property = { \ + .member = _member, \ + .signature = _signature, \ + .get = _get, \ + .set = _set, \ + .offset = _offset, \ + }, \ + }, \ + } + +#define SD_BUS_VTABLE_END \ + { \ + .type = _SD_BUS_VTABLE_END, \ + .flags = 0, \ + .x = { \ + .end = { \ + ._reserved = 0, \ + }, \ + }, \ + } + +#define _SD_ECHO(X) X +#define _SD_CONCAT(X) #X "\0" + +#define _SD_VARARGS_FOREACH_SEQ(_01, _02, _03, _04, _05, _06, _07, _08, _09, _10, \ + _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, \ + _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, \ + _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, \ + _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, \ + NAME, ...) NAME + +#define _SD_VARARGS_FOREACH_EVEN_00(FN) +#define _SD_VARARGS_FOREACH_EVEN_01(FN, X) FN(X) +#define _SD_VARARGS_FOREACH_EVEN_02(FN, X, Y) FN(X) +#define _SD_VARARGS_FOREACH_EVEN_04(FN, X, Y, ...) FN(X) _SD_VARARGS_FOREACH_EVEN_02(FN, __VA_ARGS__) +#define _SD_VARARGS_FOREACH_EVEN_06(FN, X, Y, ...) FN(X) _SD_VARARGS_FOREACH_EVEN_04(FN, __VA_ARGS__) +#define _SD_VARARGS_FOREACH_EVEN_08(FN, X, Y, ...) FN(X) _SD_VARARGS_FOREACH_EVEN_06(FN, __VA_ARGS__) +#define _SD_VARARGS_FOREACH_EVEN_10(FN, X, Y, ...) FN(X) _SD_VARARGS_FOREACH_EVEN_08(FN, __VA_ARGS__) +#define _SD_VARARGS_FOREACH_EVEN_12(FN, X, Y, ...) FN(X) _SD_VARARGS_FOREACH_EVEN_10(FN, __VA_ARGS__) +#define _SD_VARARGS_FOREACH_EVEN_14(FN, X, Y, ...) FN(X) _SD_VARARGS_FOREACH_EVEN_12(FN, __VA_ARGS__) +#define _SD_VARARGS_FOREACH_EVEN_16(FN, X, Y, ...) FN(X) _SD_VARARGS_FOREACH_EVEN_14(FN, __VA_ARGS__) +#define _SD_VARARGS_FOREACH_EVEN_18(FN, X, Y, ...) FN(X) _SD_VARARGS_FOREACH_EVEN_16(FN, __VA_ARGS__) +#define _SD_VARARGS_FOREACH_EVEN_20(FN, X, Y, ...) FN(X) _SD_VARARGS_FOREACH_EVEN_18(FN, __VA_ARGS__) +#define _SD_VARARGS_FOREACH_EVEN_22(FN, X, Y, ...) FN(X) _SD_VARARGS_FOREACH_EVEN_20(FN, __VA_ARGS__) +#define _SD_VARARGS_FOREACH_EVEN_24(FN, X, Y, ...) FN(X) _SD_VARARGS_FOREACH_EVEN_22(FN, __VA_ARGS__) +#define _SD_VARARGS_FOREACH_EVEN_26(FN, X, Y, ...) FN(X) _SD_VARARGS_FOREACH_EVEN_24(FN, __VA_ARGS__) +#define _SD_VARARGS_FOREACH_EVEN_28(FN, X, Y, ...) FN(X) _SD_VARARGS_FOREACH_EVEN_26(FN, __VA_ARGS__) +#define _SD_VARARGS_FOREACH_EVEN_30(FN, X, Y, ...) FN(X) _SD_VARARGS_FOREACH_EVEN_28(FN, __VA_ARGS__) +#define _SD_VARARGS_FOREACH_EVEN_32(FN, X, Y, ...) FN(X) _SD_VARARGS_FOREACH_EVEN_30(FN, __VA_ARGS__) +#define _SD_VARARGS_FOREACH_EVEN_34(FN, X, Y, ...) FN(X) _SD_VARARGS_FOREACH_EVEN_32(FN, __VA_ARGS__) +#define _SD_VARARGS_FOREACH_EVEN_36(FN, X, Y, ...) FN(X) _SD_VARARGS_FOREACH_EVEN_34(FN, __VA_ARGS__) +#define _SD_VARARGS_FOREACH_EVEN_38(FN, X, Y, ...) FN(X) _SD_VARARGS_FOREACH_EVEN_36(FN, __VA_ARGS__) +#define _SD_VARARGS_FOREACH_EVEN_40(FN, X, Y, ...) FN(X) _SD_VARARGS_FOREACH_EVEN_38(FN, __VA_ARGS__) +#define _SD_VARARGS_FOREACH_EVEN_42(FN, X, Y, ...) FN(X) _SD_VARARGS_FOREACH_EVEN_40(FN, __VA_ARGS__) +#define _SD_VARARGS_FOREACH_EVEN_44(FN, X, Y, ...) FN(X) _SD_VARARGS_FOREACH_EVEN_42(FN, __VA_ARGS__) +#define _SD_VARARGS_FOREACH_EVEN_46(FN, X, Y, ...) FN(X) _SD_VARARGS_FOREACH_EVEN_44(FN, __VA_ARGS__) +#define _SD_VARARGS_FOREACH_EVEN_48(FN, X, Y, ...) FN(X) _SD_VARARGS_FOREACH_EVEN_46(FN, __VA_ARGS__) +#define _SD_VARARGS_FOREACH_EVEN_50(FN, X, Y, ...) FN(X) _SD_VARARGS_FOREACH_EVEN_48(FN, __VA_ARGS__) + +#define _SD_VARARGS_FOREACH_EVEN(FN, ...) \ + _SD_VARARGS_FOREACH_SEQ(__VA_ARGS__, \ + _SD_VARARGS_FOREACH_EVEN_50, _SD_VARARGS_FOREACH_EVEN_49, \ + _SD_VARARGS_FOREACH_EVEN_48, _SD_VARARGS_FOREACH_EVEN_47, \ + _SD_VARARGS_FOREACH_EVEN_46, _SD_VARARGS_FOREACH_EVEN_45, \ + _SD_VARARGS_FOREACH_EVEN_44, _SD_VARARGS_FOREACH_EVEN_43, \ + _SD_VARARGS_FOREACH_EVEN_42, _SD_VARARGS_FOREACH_EVEN_41, \ + _SD_VARARGS_FOREACH_EVEN_40, _SD_VARARGS_FOREACH_EVEN_39, \ + _SD_VARARGS_FOREACH_EVEN_38, _SD_VARARGS_FOREACH_EVEN_37, \ + _SD_VARARGS_FOREACH_EVEN_36, _SD_VARARGS_FOREACH_EVEN_35, \ + _SD_VARARGS_FOREACH_EVEN_34, _SD_VARARGS_FOREACH_EVEN_33, \ + _SD_VARARGS_FOREACH_EVEN_32, _SD_VARARGS_FOREACH_EVEN_31, \ + _SD_VARARGS_FOREACH_EVEN_30, _SD_VARARGS_FOREACH_EVEN_29, \ + _SD_VARARGS_FOREACH_EVEN_28, _SD_VARARGS_FOREACH_EVEN_27, \ + _SD_VARARGS_FOREACH_EVEN_26, _SD_VARARGS_FOREACH_EVEN_25, \ + _SD_VARARGS_FOREACH_EVEN_24, _SD_VARARGS_FOREACH_EVEN_23, \ + _SD_VARARGS_FOREACH_EVEN_22, _SD_VARARGS_FOREACH_EVEN_21, \ + _SD_VARARGS_FOREACH_EVEN_20, _SD_VARARGS_FOREACH_EVEN_19, \ + _SD_VARARGS_FOREACH_EVEN_18, _SD_VARARGS_FOREACH_EVEN_17, \ + _SD_VARARGS_FOREACH_EVEN_16, _SD_VARARGS_FOREACH_EVEN_15, \ + _SD_VARARGS_FOREACH_EVEN_14, _SD_VARARGS_FOREACH_EVEN_13, \ + _SD_VARARGS_FOREACH_EVEN_12, _SD_VARARGS_FOREACH_EVEN_11, \ + _SD_VARARGS_FOREACH_EVEN_10, _SD_VARARGS_FOREACH_EVEN_09, \ + _SD_VARARGS_FOREACH_EVEN_08, _SD_VARARGS_FOREACH_EVEN_07, \ + _SD_VARARGS_FOREACH_EVEN_06, _SD_VARARGS_FOREACH_EVEN_05, \ + _SD_VARARGS_FOREACH_EVEN_04, _SD_VARARGS_FOREACH_EVEN_03, \ + _SD_VARARGS_FOREACH_EVEN_02, _SD_VARARGS_FOREACH_EVEN_01, \ + _SD_VARARGS_FOREACH_EVEN_00) \ + (FN, __VA_ARGS__) + +#define _SD_VARARGS_FOREACH_ODD_00(FN) +#define _SD_VARARGS_FOREACH_ODD_01(FN, X) +#define _SD_VARARGS_FOREACH_ODD_02(FN, X, Y) FN(Y) +#define _SD_VARARGS_FOREACH_ODD_04(FN, X, Y, ...) FN(Y) _SD_VARARGS_FOREACH_ODD_02(FN, __VA_ARGS__) +#define _SD_VARARGS_FOREACH_ODD_06(FN, X, Y, ...) FN(Y) _SD_VARARGS_FOREACH_ODD_04(FN, __VA_ARGS__) +#define _SD_VARARGS_FOREACH_ODD_08(FN, X, Y, ...) FN(Y) _SD_VARARGS_FOREACH_ODD_06(FN, __VA_ARGS__) +#define _SD_VARARGS_FOREACH_ODD_10(FN, X, Y, ...) FN(Y) _SD_VARARGS_FOREACH_ODD_08(FN, __VA_ARGS__) +#define _SD_VARARGS_FOREACH_ODD_12(FN, X, Y, ...) FN(Y) _SD_VARARGS_FOREACH_ODD_10(FN, __VA_ARGS__) +#define _SD_VARARGS_FOREACH_ODD_14(FN, X, Y, ...) FN(Y) _SD_VARARGS_FOREACH_ODD_12(FN, __VA_ARGS__) +#define _SD_VARARGS_FOREACH_ODD_16(FN, X, Y, ...) FN(Y) _SD_VARARGS_FOREACH_ODD_14(FN, __VA_ARGS__) +#define _SD_VARARGS_FOREACH_ODD_18(FN, X, Y, ...) FN(Y) _SD_VARARGS_FOREACH_ODD_16(FN, __VA_ARGS__) +#define _SD_VARARGS_FOREACH_ODD_20(FN, X, Y, ...) FN(Y) _SD_VARARGS_FOREACH_ODD_18(FN, __VA_ARGS__) +#define _SD_VARARGS_FOREACH_ODD_22(FN, X, Y, ...) FN(Y) _SD_VARARGS_FOREACH_ODD_20(FN, __VA_ARGS__) +#define _SD_VARARGS_FOREACH_ODD_24(FN, X, Y, ...) FN(Y) _SD_VARARGS_FOREACH_ODD_22(FN, __VA_ARGS__) +#define _SD_VARARGS_FOREACH_ODD_26(FN, X, Y, ...) FN(Y) _SD_VARARGS_FOREACH_ODD_24(FN, __VA_ARGS__) +#define _SD_VARARGS_FOREACH_ODD_28(FN, X, Y, ...) FN(Y) _SD_VARARGS_FOREACH_ODD_26(FN, __VA_ARGS__) +#define _SD_VARARGS_FOREACH_ODD_30(FN, X, Y, ...) FN(Y) _SD_VARARGS_FOREACH_ODD_28(FN, __VA_ARGS__) +#define _SD_VARARGS_FOREACH_ODD_32(FN, X, Y, ...) FN(Y) _SD_VARARGS_FOREACH_ODD_30(FN, __VA_ARGS__) +#define _SD_VARARGS_FOREACH_ODD_34(FN, X, Y, ...) FN(Y) _SD_VARARGS_FOREACH_ODD_32(FN, __VA_ARGS__) +#define _SD_VARARGS_FOREACH_ODD_36(FN, X, Y, ...) FN(Y) _SD_VARARGS_FOREACH_ODD_34(FN, __VA_ARGS__) +#define _SD_VARARGS_FOREACH_ODD_38(FN, X, Y, ...) FN(Y) _SD_VARARGS_FOREACH_ODD_36(FN, __VA_ARGS__) +#define _SD_VARARGS_FOREACH_ODD_40(FN, X, Y, ...) FN(Y) _SD_VARARGS_FOREACH_ODD_38(FN, __VA_ARGS__) +#define _SD_VARARGS_FOREACH_ODD_42(FN, X, Y, ...) FN(Y) _SD_VARARGS_FOREACH_ODD_40(FN, __VA_ARGS__) +#define _SD_VARARGS_FOREACH_ODD_44(FN, X, Y, ...) FN(Y) _SD_VARARGS_FOREACH_ODD_42(FN, __VA_ARGS__) +#define _SD_VARARGS_FOREACH_ODD_46(FN, X, Y, ...) FN(Y) _SD_VARARGS_FOREACH_ODD_44(FN, __VA_ARGS__) +#define _SD_VARARGS_FOREACH_ODD_48(FN, X, Y, ...) FN(Y) _SD_VARARGS_FOREACH_ODD_46(FN, __VA_ARGS__) +#define _SD_VARARGS_FOREACH_ODD_50(FN, X, Y, ...) FN(Y) _SD_VARARGS_FOREACH_ODD_48(FN, __VA_ARGS__) + +#define _SD_VARARGS_FOREACH_ODD(FN, ...) \ + _SD_VARARGS_FOREACH_SEQ(__VA_ARGS__, \ + _SD_VARARGS_FOREACH_ODD_50, _SD_VARARGS_FOREACH_ODD_49, \ + _SD_VARARGS_FOREACH_ODD_48, _SD_VARARGS_FOREACH_ODD_47, \ + _SD_VARARGS_FOREACH_ODD_46, _SD_VARARGS_FOREACH_ODD_45, \ + _SD_VARARGS_FOREACH_ODD_44, _SD_VARARGS_FOREACH_ODD_43, \ + _SD_VARARGS_FOREACH_ODD_42, _SD_VARARGS_FOREACH_ODD_41, \ + _SD_VARARGS_FOREACH_ODD_40, _SD_VARARGS_FOREACH_ODD_39, \ + _SD_VARARGS_FOREACH_ODD_38, _SD_VARARGS_FOREACH_ODD_37, \ + _SD_VARARGS_FOREACH_ODD_36, _SD_VARARGS_FOREACH_ODD_35, \ + _SD_VARARGS_FOREACH_ODD_34, _SD_VARARGS_FOREACH_ODD_33, \ + _SD_VARARGS_FOREACH_ODD_32, _SD_VARARGS_FOREACH_ODD_31, \ + _SD_VARARGS_FOREACH_ODD_30, _SD_VARARGS_FOREACH_ODD_29, \ + _SD_VARARGS_FOREACH_ODD_28, _SD_VARARGS_FOREACH_ODD_27, \ + _SD_VARARGS_FOREACH_ODD_26, _SD_VARARGS_FOREACH_ODD_25, \ + _SD_VARARGS_FOREACH_ODD_24, _SD_VARARGS_FOREACH_ODD_23, \ + _SD_VARARGS_FOREACH_ODD_22, _SD_VARARGS_FOREACH_ODD_21, \ + _SD_VARARGS_FOREACH_ODD_20, _SD_VARARGS_FOREACH_ODD_19, \ + _SD_VARARGS_FOREACH_ODD_18, _SD_VARARGS_FOREACH_ODD_17, \ + _SD_VARARGS_FOREACH_ODD_16, _SD_VARARGS_FOREACH_ODD_15, \ + _SD_VARARGS_FOREACH_ODD_14, _SD_VARARGS_FOREACH_ODD_13, \ + _SD_VARARGS_FOREACH_ODD_12, _SD_VARARGS_FOREACH_ODD_11, \ + _SD_VARARGS_FOREACH_ODD_10, _SD_VARARGS_FOREACH_ODD_09, \ + _SD_VARARGS_FOREACH_ODD_08, _SD_VARARGS_FOREACH_ODD_07, \ + _SD_VARARGS_FOREACH_ODD_06, _SD_VARARGS_FOREACH_ODD_05, \ + _SD_VARARGS_FOREACH_ODD_04, _SD_VARARGS_FOREACH_ODD_03, \ + _SD_VARARGS_FOREACH_ODD_02, _SD_VARARGS_FOREACH_ODD_01, \ + _SD_VARARGS_FOREACH_ODD_00) \ + (FN, __VA_ARGS__) + +#define SD_BUS_ARGS(...) __VA_ARGS__ +#define SD_BUS_RESULT(...) __VA_ARGS__ + +#define SD_BUS_NO_ARGS SD_BUS_ARGS(NULL) +#define SD_BUS_NO_RESULT SD_BUS_RESULT(NULL) + +#define SD_BUS_METHOD_WITH_ARGS(_member, _args, _result, _handler, _flags) \ + SD_BUS_METHOD_WITH_NAMES(_member, \ + _SD_VARARGS_FOREACH_EVEN(_SD_ECHO, _args), \ + _SD_VARARGS_FOREACH_ODD(_SD_CONCAT, _args), \ + _SD_VARARGS_FOREACH_EVEN(_SD_ECHO, _result), \ + _SD_VARARGS_FOREACH_ODD(_SD_CONCAT, _result) "\0", \ + _handler, _flags) + +#define SD_BUS_METHOD_WITH_ARGS_OFFSET(_member, _args, _result, _handler, _offset, _flags) \ + SD_BUS_METHOD_WITH_NAMES_OFFSET(_member, \ + _SD_VARARGS_FOREACH_EVEN(_SD_ECHO, _args), \ + _SD_VARARGS_FOREACH_ODD(_SD_CONCAT, _args), \ + _SD_VARARGS_FOREACH_EVEN(_SD_ECHO, _result), \ + _SD_VARARGS_FOREACH_ODD(_SD_CONCAT, _result) "\0", \ + _handler, _offset, _flags) + +#define SD_BUS_SIGNAL_WITH_ARGS(_member, _args, _flags) \ + SD_BUS_SIGNAL_WITH_NAMES(_member, \ + _SD_VARARGS_FOREACH_EVEN(_SD_ECHO, _args), \ + _SD_VARARGS_FOREACH_ODD(_SD_CONCAT, _args) "\0", \ + _flags) + +_SD_END_DECLARATIONS; + +#endif diff --git a/thirdparty/systemd/include/systemd/sd-bus.h b/thirdparty/systemd/include/systemd/sd-bus.h new file mode 100644 index 000000000..f1a331199 --- /dev/null +++ b/thirdparty/systemd/include/systemd/sd-bus.h @@ -0,0 +1,544 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#ifndef foosdbushfoo +#define foosdbushfoo + +/*** + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include + +#include "sd-event.h" +#include "sd-id128.h" + +#include "_sd-common.h" + +_SD_BEGIN_DECLARATIONS; + +#define SD_BUS_DEFAULT ((sd_bus *) 1) +#define SD_BUS_DEFAULT_USER ((sd_bus *) 2) +#define SD_BUS_DEFAULT_SYSTEM ((sd_bus *) 3) + +/* Types */ + +typedef struct sd_bus sd_bus; +typedef struct sd_bus_message sd_bus_message; +typedef struct sd_bus_slot sd_bus_slot; +typedef struct sd_bus_creds sd_bus_creds; +typedef struct sd_bus_track sd_bus_track; + +typedef struct { + const char *name; + const char *message; + int _need_free; +} sd_bus_error; + +typedef struct { + const char *name; + int code; +} sd_bus_error_map; + +/* Flags */ + +__extension__ enum { + SD_BUS_CREDS_PID = 1ULL << 0, + SD_BUS_CREDS_TID = 1ULL << 1, + SD_BUS_CREDS_PPID = 1ULL << 2, + SD_BUS_CREDS_UID = 1ULL << 3, + SD_BUS_CREDS_EUID = 1ULL << 4, + SD_BUS_CREDS_SUID = 1ULL << 5, + SD_BUS_CREDS_FSUID = 1ULL << 6, + SD_BUS_CREDS_GID = 1ULL << 7, + SD_BUS_CREDS_EGID = 1ULL << 8, + SD_BUS_CREDS_SGID = 1ULL << 9, + SD_BUS_CREDS_FSGID = 1ULL << 10, + SD_BUS_CREDS_SUPPLEMENTARY_GIDS = 1ULL << 11, + SD_BUS_CREDS_COMM = 1ULL << 12, + SD_BUS_CREDS_TID_COMM = 1ULL << 13, + SD_BUS_CREDS_EXE = 1ULL << 14, + SD_BUS_CREDS_CMDLINE = 1ULL << 15, + SD_BUS_CREDS_CGROUP = 1ULL << 16, + SD_BUS_CREDS_UNIT = 1ULL << 17, + SD_BUS_CREDS_SLICE = 1ULL << 18, + SD_BUS_CREDS_USER_UNIT = 1ULL << 19, + SD_BUS_CREDS_USER_SLICE = 1ULL << 20, + SD_BUS_CREDS_SESSION = 1ULL << 21, + SD_BUS_CREDS_OWNER_UID = 1ULL << 22, + SD_BUS_CREDS_EFFECTIVE_CAPS = 1ULL << 23, + SD_BUS_CREDS_PERMITTED_CAPS = 1ULL << 24, + SD_BUS_CREDS_INHERITABLE_CAPS = 1ULL << 25, + SD_BUS_CREDS_BOUNDING_CAPS = 1ULL << 26, + SD_BUS_CREDS_SELINUX_CONTEXT = 1ULL << 27, + SD_BUS_CREDS_AUDIT_SESSION_ID = 1ULL << 28, + SD_BUS_CREDS_AUDIT_LOGIN_UID = 1ULL << 29, + SD_BUS_CREDS_TTY = 1ULL << 30, + SD_BUS_CREDS_UNIQUE_NAME = 1ULL << 31, + SD_BUS_CREDS_WELL_KNOWN_NAMES = 1ULL << 32, + SD_BUS_CREDS_DESCRIPTION = 1ULL << 33, + SD_BUS_CREDS_PIDFD = 1ULL << 34, + SD_BUS_CREDS_AUGMENT = 1ULL << 63, /* special flag, if on sd-bus will augment creds struct, in a potentially race-full way. */ + _SD_BUS_CREDS_ALL = (1ULL << 35) -1 +}; + +__extension__ enum { + SD_BUS_NAME_REPLACE_EXISTING = 1ULL << 0, + SD_BUS_NAME_ALLOW_REPLACEMENT = 1ULL << 1, + SD_BUS_NAME_QUEUE = 1ULL << 2 +}; + +__extension__ enum { + SD_BUS_MESSAGE_DUMP_WITH_HEADER = 1ULL << 0, + SD_BUS_MESSAGE_DUMP_SUBTREE_ONLY = 1ULL << 1, + _SD_BUS_MESSAGE_DUMP_KNOWN_FLAGS = SD_BUS_MESSAGE_DUMP_WITH_HEADER | SD_BUS_MESSAGE_DUMP_SUBTREE_ONLY +}; + +/* Callbacks */ + +typedef int (*sd_bus_message_handler_t)(sd_bus_message *m, void *userdata, sd_bus_error *ret_error); +typedef int (*sd_bus_property_get_t) (sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *reply, void *userdata, sd_bus_error *ret_error); +typedef int (*sd_bus_property_set_t) (sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *value, void *userdata, sd_bus_error *ret_error); +typedef int (*sd_bus_object_find_t) (sd_bus *bus, const char *path, const char *interface, void *userdata, void **ret_found, sd_bus_error *ret_error); +typedef int (*sd_bus_node_enumerator_t) (sd_bus *bus, const char *prefix, void *userdata, char ***ret_nodes, sd_bus_error *ret_error); +typedef int (*sd_bus_track_handler_t) (sd_bus_track *track, void *userdata); +typedef _sd_destroy_t sd_bus_destroy_t; + +#include "sd-bus-protocol.h" +#include "sd-bus-vtable.h" + +/* Naming */ + +int sd_bus_interface_name_is_valid(const char *p); +int sd_bus_service_name_is_valid(const char *p); +int sd_bus_member_name_is_valid(const char *p); +int sd_bus_object_path_is_valid(const char *p); + +/* Connections */ + +int sd_bus_default(sd_bus **ret); +int sd_bus_default_user(sd_bus **ret); +int sd_bus_default_system(sd_bus **ret); + +int sd_bus_open(sd_bus **ret); +int sd_bus_open_with_description(sd_bus **ret, const char *description); +int sd_bus_open_user(sd_bus **ret); +int sd_bus_open_user_with_description(sd_bus **ret, const char *description); +int sd_bus_open_user_machine(sd_bus **ret, const char *machine); +int sd_bus_open_system(sd_bus **ret); +int sd_bus_open_system_with_description(sd_bus **ret, const char *description); +int sd_bus_open_system_remote(sd_bus **ret, const char *host); +int sd_bus_open_system_machine(sd_bus **ret, const char *machine); + +int sd_bus_new(sd_bus **ret); + +int sd_bus_set_address(sd_bus *bus, const char *address); +int sd_bus_set_fd(sd_bus *bus, int input_fd, int output_fd); +int sd_bus_set_exec(sd_bus *bus, const char *path, char *const *argv); +int sd_bus_get_address(sd_bus *bus, const char **address); +int sd_bus_set_bus_client(sd_bus *bus, int b); +int sd_bus_is_bus_client(sd_bus *bus); +int sd_bus_set_server(sd_bus *bus, int b, sd_id128_t bus_id); +int sd_bus_is_server(sd_bus *bus); +int sd_bus_set_anonymous(sd_bus *bus, int b); +int sd_bus_is_anonymous(sd_bus *bus); +int sd_bus_set_trusted(sd_bus *bus, int b); +int sd_bus_is_trusted(sd_bus *bus); +int sd_bus_set_monitor(sd_bus *bus, int b); +int sd_bus_is_monitor(sd_bus *bus); +int sd_bus_set_description(sd_bus *bus, const char *description); +int sd_bus_get_description(sd_bus *bus, const char **description); +int sd_bus_negotiate_creds(sd_bus *bus, int b, uint64_t creds_mask); +int sd_bus_negotiate_timestamp(sd_bus *bus, int b); +int sd_bus_negotiate_fds(sd_bus *bus, int b); +int sd_bus_can_send(sd_bus *bus, char type); +int sd_bus_get_creds_mask(sd_bus *bus, uint64_t *creds_mask); +int sd_bus_set_allow_interactive_authorization(sd_bus *bus, int b); +int sd_bus_get_allow_interactive_authorization(sd_bus *bus); +int sd_bus_set_exit_on_disconnect(sd_bus *bus, int b); +int sd_bus_get_exit_on_disconnect(sd_bus *bus); +int sd_bus_set_close_on_exit(sd_bus *bus, int b); +int sd_bus_get_close_on_exit(sd_bus *bus); +int sd_bus_set_watch_bind(sd_bus *bus, int b); +int sd_bus_get_watch_bind(sd_bus *bus); +int sd_bus_set_connected_signal(sd_bus *bus, int b); +int sd_bus_get_connected_signal(sd_bus *bus); +int sd_bus_set_sender(sd_bus *bus, const char *sender); +int sd_bus_get_sender(sd_bus *bus, const char **ret); + +int sd_bus_start(sd_bus *bus); + +int sd_bus_try_close(sd_bus *bus) _sd_deprecated_; +void sd_bus_close(sd_bus *bus); + +sd_bus* sd_bus_ref(sd_bus *bus); +sd_bus* sd_bus_unref(sd_bus *bus); +sd_bus* sd_bus_close_unref(sd_bus *bus); +sd_bus* sd_bus_flush_close_unref(sd_bus *bus); + +void sd_bus_default_flush_close(void); + +int sd_bus_is_open(sd_bus *bus); +int sd_bus_is_ready(sd_bus *bus); + +int sd_bus_get_bus_id(sd_bus *bus, sd_id128_t *id); +int sd_bus_get_scope(sd_bus *bus, const char **scope); +int sd_bus_get_tid(sd_bus *bus, pid_t *tid); +int sd_bus_get_owner_creds(sd_bus *bus, uint64_t creds_mask, sd_bus_creds **ret); + +int sd_bus_send(sd_bus *bus, sd_bus_message *m, uint64_t *cookie); +int sd_bus_send_to(sd_bus *bus, sd_bus_message *m, const char *destination, uint64_t *cookie); +int sd_bus_call(sd_bus *bus, sd_bus_message *m, uint64_t usec, sd_bus_error *ret_error, sd_bus_message **reply); +int sd_bus_call_async(sd_bus *bus, sd_bus_slot **slot, sd_bus_message *m, sd_bus_message_handler_t callback, void *userdata, uint64_t usec); + +int sd_bus_get_fd(sd_bus *bus); +int sd_bus_get_events(sd_bus *bus); +int sd_bus_get_timeout(sd_bus *bus, uint64_t *timeout_usec); +int sd_bus_process(sd_bus *bus, sd_bus_message **r); +int sd_bus_process_priority(sd_bus *bus, int64_t max_priority, sd_bus_message **r) _sd_deprecated_; +int sd_bus_wait(sd_bus *bus, uint64_t timeout_usec); +int sd_bus_flush(sd_bus *bus); +int sd_bus_enqueue_for_read(sd_bus *bus, sd_bus_message *m); + +sd_bus_slot* sd_bus_get_current_slot(sd_bus *bus); +sd_bus_message* sd_bus_get_current_message(sd_bus *bus); +sd_bus_message_handler_t sd_bus_get_current_handler(sd_bus *bus); +void* sd_bus_get_current_userdata(sd_bus *bus); + +int sd_bus_attach_event(sd_bus *bus, sd_event *e, int priority); +int sd_bus_detach_event(sd_bus *bus); +sd_event* sd_bus_get_event(sd_bus *bus); + +int sd_bus_get_n_queued_read(sd_bus *bus, uint64_t *ret); +int sd_bus_get_n_queued_write(sd_bus *bus, uint64_t *ret); + +int sd_bus_set_method_call_timeout(sd_bus *bus, uint64_t usec); +int sd_bus_get_method_call_timeout(sd_bus *bus, uint64_t *ret); + +int sd_bus_add_filter(sd_bus *bus, sd_bus_slot **slot, sd_bus_message_handler_t callback, void *userdata); +int sd_bus_add_match(sd_bus *bus, sd_bus_slot **slot, const char *match, sd_bus_message_handler_t callback, void *userdata); +int sd_bus_add_match_async(sd_bus *bus, sd_bus_slot **slot, const char *match, sd_bus_message_handler_t callback, sd_bus_message_handler_t install_callback, void *userdata); +int sd_bus_add_object(sd_bus *bus, sd_bus_slot **slot, const char *path, sd_bus_message_handler_t callback, void *userdata); +int sd_bus_add_fallback(sd_bus *bus, sd_bus_slot **slot, const char *prefix, sd_bus_message_handler_t callback, void *userdata); +int sd_bus_add_object_vtable(sd_bus *bus, sd_bus_slot **slot, const char *path, const char *interface, const sd_bus_vtable *vtable, void *userdata); +int sd_bus_add_fallback_vtable(sd_bus *bus, sd_bus_slot **slot, const char *prefix, const char *interface, const sd_bus_vtable *vtable, sd_bus_object_find_t find, void *userdata); +int sd_bus_add_node_enumerator(sd_bus *bus, sd_bus_slot **slot, const char *path, sd_bus_node_enumerator_t callback, void *userdata); +int sd_bus_add_object_manager(sd_bus *bus, sd_bus_slot **slot, const char *path); + +/* Slot object */ + +sd_bus_slot* sd_bus_slot_ref(sd_bus_slot *slot); +sd_bus_slot* sd_bus_slot_unref(sd_bus_slot *slot); + +sd_bus* sd_bus_slot_get_bus(sd_bus_slot *slot); +void* sd_bus_slot_get_userdata(sd_bus_slot *slot); +void* sd_bus_slot_set_userdata(sd_bus_slot *slot, void *userdata); +int sd_bus_slot_set_description(sd_bus_slot *slot, const char *description); +int sd_bus_slot_get_description(sd_bus_slot *slot, const char **description); +int sd_bus_slot_get_floating(sd_bus_slot *slot); +int sd_bus_slot_set_floating(sd_bus_slot *slot, int b); +int sd_bus_slot_set_destroy_callback(sd_bus_slot *s, sd_bus_destroy_t callback); +int sd_bus_slot_get_destroy_callback(sd_bus_slot *s, sd_bus_destroy_t *callback); + +sd_bus_message* sd_bus_slot_get_current_message(sd_bus_slot *slot); +sd_bus_message_handler_t sd_bus_slot_get_current_handler(sd_bus_slot *slot); +void* sd_bus_slot_get_current_userdata(sd_bus_slot *slot); + +/* Message object */ + +int sd_bus_message_new(sd_bus *bus, sd_bus_message **m, uint8_t type); +int sd_bus_message_new_signal(sd_bus *bus, sd_bus_message **m, const char *path, const char *interface, const char *member); +int sd_bus_message_new_signal_to(sd_bus *bus, sd_bus_message **m, const char *destination, const char *path, const char *interface, const char *member); +int sd_bus_message_new_method_call(sd_bus *bus, sd_bus_message **m, const char *destination, const char *path, const char *interface, const char *member); +int sd_bus_message_new_method_return(sd_bus_message *call, sd_bus_message **m); +int sd_bus_message_new_method_error(sd_bus_message *call, sd_bus_message **m, const sd_bus_error *e); +int sd_bus_message_new_method_errorf(sd_bus_message *call, sd_bus_message **m, const char *name, const char *format, ...) _sd_printf_(4, 5); +int sd_bus_message_new_method_errno(sd_bus_message *call, sd_bus_message **m, int error, const sd_bus_error *e); +int sd_bus_message_new_method_errnof(sd_bus_message *call, sd_bus_message **m, int error, const char *format, ...) _sd_printf_(4, 5); + +sd_bus_message* sd_bus_message_ref(sd_bus_message *m); +sd_bus_message* sd_bus_message_unref(sd_bus_message *m); + +int sd_bus_message_seal(sd_bus_message *m, uint64_t cookie, uint64_t timeout_usec); + +int sd_bus_message_get_type(sd_bus_message *m, uint8_t *type); +int sd_bus_message_get_cookie(sd_bus_message *m, uint64_t *cookie); +int sd_bus_message_get_reply_cookie(sd_bus_message *m, uint64_t *cookie); +int sd_bus_message_get_priority(sd_bus_message *m, int64_t *priority) _sd_deprecated_; + +int sd_bus_message_get_expect_reply(sd_bus_message *m); +int sd_bus_message_get_auto_start(sd_bus_message *m); +int sd_bus_message_get_allow_interactive_authorization(sd_bus_message *m); + +const char* sd_bus_message_get_signature(sd_bus_message *m, int complete); +const char* sd_bus_message_get_path(sd_bus_message *m); +const char* sd_bus_message_get_interface(sd_bus_message *m); +const char* sd_bus_message_get_member(sd_bus_message *m); +const char* sd_bus_message_get_destination(sd_bus_message *m); +const char* sd_bus_message_get_sender(sd_bus_message *m); +const sd_bus_error* sd_bus_message_get_error(sd_bus_message *m); +int sd_bus_message_get_errno(sd_bus_message *m); + +int sd_bus_message_get_monotonic_usec(sd_bus_message *m, uint64_t *usec); +int sd_bus_message_get_realtime_usec(sd_bus_message *m, uint64_t *usec); +int sd_bus_message_get_seqnum(sd_bus_message *m, uint64_t *seqnum); + +sd_bus* sd_bus_message_get_bus(sd_bus_message *m); +sd_bus_creds* sd_bus_message_get_creds(sd_bus_message *m); /* do not unref the result */ + +int sd_bus_message_is_signal(sd_bus_message *m, const char *interface, const char *member); +int sd_bus_message_is_method_call(sd_bus_message *m, const char *interface, const char *member); +int sd_bus_message_is_method_error(sd_bus_message *m, const char *name); +int sd_bus_message_is_empty(sd_bus_message *m); +int sd_bus_message_has_signature(sd_bus_message *m, const char *signature); + +int sd_bus_message_set_expect_reply(sd_bus_message *m, int b); +int sd_bus_message_set_auto_start(sd_bus_message *m, int b); +int sd_bus_message_set_allow_interactive_authorization(sd_bus_message *m, int b); + +int sd_bus_message_set_destination(sd_bus_message *m, const char *destination); +int sd_bus_message_set_sender(sd_bus_message *m, const char *sender); +int sd_bus_message_set_priority(sd_bus_message *m, int64_t priority) _sd_deprecated_; + +int sd_bus_message_append(sd_bus_message *m, const char *types, ...); +int sd_bus_message_appendv(sd_bus_message *m, const char *types, va_list ap); +int sd_bus_message_append_basic(sd_bus_message *m, char type, const void *p); +int sd_bus_message_append_array(sd_bus_message *m, char type, const void *ptr, size_t size); +int sd_bus_message_append_array_space(sd_bus_message *m, char type, size_t size, void **ptr); +int sd_bus_message_append_array_iovec(sd_bus_message *m, char type, const struct iovec *iov, unsigned n); +int sd_bus_message_append_array_memfd(sd_bus_message *m, char type, int memfd, uint64_t offset, uint64_t size); +int sd_bus_message_append_string_space(sd_bus_message *m, size_t size, char **s); +int sd_bus_message_append_string_iovec(sd_bus_message *m, const struct iovec *iov, unsigned n); +int sd_bus_message_append_string_memfd(sd_bus_message *m, int memfd, uint64_t offset, uint64_t size); +int sd_bus_message_append_strv(sd_bus_message *m, char **l); +int sd_bus_message_open_container(sd_bus_message *m, char type, const char *contents); +int sd_bus_message_close_container(sd_bus_message *m); +int sd_bus_message_copy(sd_bus_message *m, sd_bus_message *source, int all); + +int sd_bus_message_read(sd_bus_message *m, const char *types, ...); +int sd_bus_message_readv(sd_bus_message *m, const char *types, va_list ap); +int sd_bus_message_read_basic(sd_bus_message *m, char type, void *p); +int sd_bus_message_read_array(sd_bus_message *m, char type, const void **ptr, size_t *size); +int sd_bus_message_read_strv(sd_bus_message *m, char ***l); /* free the result! */ +int sd_bus_message_read_strv_extend(sd_bus_message *m, char ***l); +int sd_bus_message_skip(sd_bus_message *m, const char *types); +int sd_bus_message_enter_container(sd_bus_message *m, char type, const char *contents); +int sd_bus_message_exit_container(sd_bus_message *m); +int sd_bus_message_peek_type(sd_bus_message *m, char *type, const char **contents); +int sd_bus_message_verify_type(sd_bus_message *m, char type, const char *contents); +int sd_bus_message_at_end(sd_bus_message *m, int complete); +int sd_bus_message_rewind(sd_bus_message *m, int complete); +int sd_bus_message_sensitive(sd_bus_message *m); + +int sd_bus_message_dump(sd_bus_message *m, FILE *f, uint64_t flags); + +/* Bus management */ + +int sd_bus_get_unique_name(sd_bus *bus, const char **unique); +int sd_bus_request_name(sd_bus *bus, const char *name, uint64_t flags); +int sd_bus_request_name_async(sd_bus *bus, sd_bus_slot **ret_slot, const char *name, uint64_t flags, sd_bus_message_handler_t callback, void *userdata); +int sd_bus_release_name(sd_bus *bus, const char *name); +int sd_bus_release_name_async(sd_bus *bus, sd_bus_slot **ret_slot, const char *name, sd_bus_message_handler_t callback, void *userdata); +int sd_bus_list_names(sd_bus *bus, char ***acquired, char ***activatable); /* free the results */ +int sd_bus_get_name_creds(sd_bus *bus, const char *name, uint64_t mask, sd_bus_creds **creds); /* unref the result! */ +int sd_bus_get_name_machine_id(sd_bus *bus, const char *name, sd_id128_t *machine); + +/* Convenience calls */ + +int sd_bus_message_send(sd_bus_message *m); +int sd_bus_call_methodv(sd_bus *bus, const char *destination, const char *path, const char *interface, const char *member, sd_bus_error *ret_error, sd_bus_message **reply, const char *types, va_list ap); +int sd_bus_call_method(sd_bus *bus, const char *destination, const char *path, const char *interface, const char *member, sd_bus_error *ret_error, sd_bus_message **reply, const char *types, ...); +int sd_bus_call_method_asyncv(sd_bus *bus, sd_bus_slot **slot, const char *destination, const char *path, const char *interface, const char *member, sd_bus_message_handler_t callback, void *userdata, const char *types, va_list ap); +int sd_bus_call_method_async(sd_bus *bus, sd_bus_slot **slot, const char *destination, const char *path, const char *interface, const char *member, sd_bus_message_handler_t callback, void *userdata, const char *types, ...); +int sd_bus_get_property(sd_bus *bus, const char *destination, const char *path, const char *interface, const char *member, sd_bus_error *ret_error, sd_bus_message **reply, const char *type); +int sd_bus_get_property_trivial(sd_bus *bus, const char *destination, const char *path, const char *interface, const char *member, sd_bus_error *ret_error, char type, void *ret_ptr); +int sd_bus_get_property_string(sd_bus *bus, const char *destination, const char *path, const char *interface, const char *member, sd_bus_error *ret_error, char **ret); /* free the result! */ +int sd_bus_get_property_strv(sd_bus *bus, const char *destination, const char *path, const char *interface, const char *member, sd_bus_error *ret_error, char ***ret); /* free the result! */ +int sd_bus_set_propertyv(sd_bus *bus, const char *destination, const char *path, const char *interface, const char *member, sd_bus_error *ret_error, const char *type, va_list ap); +int sd_bus_set_property(sd_bus *bus, const char *destination, const char *path, const char *interface, const char *member, sd_bus_error *ret_error, const char *type, ...); + +int sd_bus_reply_method_returnv(sd_bus_message *call, const char *types, va_list ap); +int sd_bus_reply_method_return(sd_bus_message *call, const char *types, ...); +int sd_bus_reply_method_error(sd_bus_message *call, const sd_bus_error *e); +int sd_bus_reply_method_errorfv(sd_bus_message *call, const char *name, const char *format, va_list ap) _sd_printf_(3, 0); +int sd_bus_reply_method_errorf(sd_bus_message *call, const char *name, const char *format, ...) _sd_printf_(3, 4); +int sd_bus_reply_method_errno(sd_bus_message *call, int error, const sd_bus_error *e); +int sd_bus_reply_method_errnofv(sd_bus_message *call, int error, const char *format, va_list ap) _sd_printf_(3, 0); +int sd_bus_reply_method_errnof(sd_bus_message *call, int error, const char *format, ...) _sd_printf_(3, 4); + +int sd_bus_emit_signalv(sd_bus *bus, const char *path, const char *interface, const char *member, const char *types, va_list ap); +int sd_bus_emit_signal(sd_bus *bus, const char *path, const char *interface, const char *member, const char *types, ...); +int sd_bus_emit_signal_tov(sd_bus *bus, const char *destination, const char *path, const char *interface, const char *member, const char *types, va_list ap); +int sd_bus_emit_signal_to(sd_bus *bus, const char *destination, const char *path, const char *interface, const char *member, const char *types, ...); + +int sd_bus_emit_properties_changed_strv(sd_bus *bus, const char *path, const char *interface, char **names); +int sd_bus_emit_properties_changed(sd_bus *bus, const char *path, const char *interface, const char *name, ...) _sd_sentinel_; + +int sd_bus_emit_object_added(sd_bus *bus, const char *path); +int sd_bus_emit_object_removed(sd_bus *bus, const char *path); +int sd_bus_emit_interfaces_added_strv(sd_bus *bus, const char *path, char **interfaces); +int sd_bus_emit_interfaces_added(sd_bus *bus, const char *path, const char *interface, ...) _sd_sentinel_; +int sd_bus_emit_interfaces_removed_strv(sd_bus *bus, const char *path, char **interfaces); +int sd_bus_emit_interfaces_removed(sd_bus *bus, const char *path, const char *interface, ...) _sd_sentinel_; + +int sd_bus_query_sender_creds(sd_bus_message *m, uint64_t mask, sd_bus_creds **creds); +int sd_bus_query_sender_privilege(sd_bus_message *m, int capability); + +int sd_bus_match_signal(sd_bus *bus, sd_bus_slot **ret, const char *sender, const char *path, const char *interface, const char *member, sd_bus_message_handler_t callback, void *userdata); +int sd_bus_match_signal_async(sd_bus *bus, sd_bus_slot **ret, const char *sender, const char *path, const char *interface, const char *member, sd_bus_message_handler_t match_callback, sd_bus_message_handler_t add_callback, void *userdata); + +/* Credential handling */ + +int sd_bus_creds_new_from_pid(sd_bus_creds **ret, pid_t pid, uint64_t creds_mask); +int sd_bus_creds_new_from_pidfd(sd_bus_creds **ret, int pidfd, uint64_t creds_mask); +sd_bus_creds* sd_bus_creds_ref(sd_bus_creds *c); +sd_bus_creds* sd_bus_creds_unref(sd_bus_creds *c); +uint64_t sd_bus_creds_get_mask(const sd_bus_creds *c); +uint64_t sd_bus_creds_get_augmented_mask(const sd_bus_creds *c); + +int sd_bus_creds_get_pid(sd_bus_creds *c, pid_t *pid); +int sd_bus_creds_get_pidfd_dup(sd_bus_creds *c, int *ret_fd); +int sd_bus_creds_get_ppid(sd_bus_creds *c, pid_t *ppid); +int sd_bus_creds_get_tid(sd_bus_creds *c, pid_t *tid); +int sd_bus_creds_get_uid(sd_bus_creds *c, uid_t *uid); +int sd_bus_creds_get_euid(sd_bus_creds *c, uid_t *euid); +int sd_bus_creds_get_suid(sd_bus_creds *c, uid_t *suid); +int sd_bus_creds_get_fsuid(sd_bus_creds *c, uid_t *fsuid); +int sd_bus_creds_get_gid(sd_bus_creds *c, gid_t *gid); +int sd_bus_creds_get_egid(sd_bus_creds *c, gid_t *egid); +int sd_bus_creds_get_sgid(sd_bus_creds *c, gid_t *sgid); +int sd_bus_creds_get_fsgid(sd_bus_creds *c, gid_t *fsgid); +int sd_bus_creds_get_supplementary_gids(sd_bus_creds *c, const gid_t **gids); +int sd_bus_creds_get_comm(sd_bus_creds *c, const char **comm); +int sd_bus_creds_get_tid_comm(sd_bus_creds *c, const char **comm); +int sd_bus_creds_get_exe(sd_bus_creds *c, const char **exe); +int sd_bus_creds_get_cmdline(sd_bus_creds *c, char ***cmdline); +int sd_bus_creds_get_cgroup(sd_bus_creds *c, const char **cgroup); +int sd_bus_creds_get_unit(sd_bus_creds *c, const char **unit); +int sd_bus_creds_get_slice(sd_bus_creds *c, const char **slice); +int sd_bus_creds_get_user_unit(sd_bus_creds *c, const char **unit); +int sd_bus_creds_get_user_slice(sd_bus_creds *c, const char **slice); +int sd_bus_creds_get_session(sd_bus_creds *c, const char **session); +int sd_bus_creds_get_owner_uid(sd_bus_creds *c, uid_t *uid); +int sd_bus_creds_has_effective_cap(sd_bus_creds *c, int capability); +int sd_bus_creds_has_permitted_cap(sd_bus_creds *c, int capability); +int sd_bus_creds_has_inheritable_cap(sd_bus_creds *c, int capability); +int sd_bus_creds_has_bounding_cap(sd_bus_creds *c, int capability); +int sd_bus_creds_get_selinux_context(sd_bus_creds *c, const char **context); +int sd_bus_creds_get_audit_session_id(sd_bus_creds *c, uint32_t *sessionid); +int sd_bus_creds_get_audit_login_uid(sd_bus_creds *c, uid_t *loginuid); +int sd_bus_creds_get_tty(sd_bus_creds *c, const char **tty); +int sd_bus_creds_get_unique_name(sd_bus_creds *c, const char **name); +int sd_bus_creds_get_well_known_names(sd_bus_creds *c, char ***names); +int sd_bus_creds_get_description(sd_bus_creds *c, const char **name); + +/* Error structures */ + +#define SD_BUS_ERROR_MAKE_CONST(name, message) ((const sd_bus_error) {(name), (message), 0}) +#define SD_BUS_ERROR_NULL SD_BUS_ERROR_MAKE_CONST(NULL, NULL) + +void sd_bus_error_free(sd_bus_error *e); +int sd_bus_error_set(sd_bus_error *e, const char *name, const char *message); +int sd_bus_error_setf(sd_bus_error *e, const char *name, const char *format, ...) _sd_printf_(3, 4); +int sd_bus_error_setfv(sd_bus_error *e, const char *name, const char *format, va_list ap) _sd_printf_(3,0); + +int sd_bus_error_set_const(sd_bus_error *e, const char *name, const char *message); +int sd_bus_error_set_errno(sd_bus_error *e, int error); +int sd_bus_error_set_errnof(sd_bus_error *e, int error, const char *format, ...) _sd_printf_(3, 4); +int sd_bus_error_set_errnofv(sd_bus_error *e, int error, const char *format, va_list ap) _sd_printf_(3,0); +int sd_bus_error_get_errno(const sd_bus_error *e); +int sd_bus_error_copy(sd_bus_error *dest, const sd_bus_error *e); +int sd_bus_error_move(sd_bus_error *dest, sd_bus_error *e); +int sd_bus_error_is_set(const sd_bus_error *e); +int sd_bus_error_has_name(const sd_bus_error *e, const char *name); +int sd_bus_error_has_names_sentinel(const sd_bus_error *e, ...) _sd_sentinel_; +#define sd_bus_error_has_names(e, ...) sd_bus_error_has_names_sentinel(e, __VA_ARGS__, NULL) + +#define SD_BUS_ERROR_MAP(_name, _code) \ + { \ + .name = _name, \ + .code = _code, \ + } +#define SD_BUS_ERROR_MAP_END \ + { \ + .name = NULL, \ + .code = - 'x', \ + } + +int sd_bus_error_add_map(const sd_bus_error_map *map); + +/* Auxiliary macros */ + +#define SD_BUS_MESSAGE_APPEND_ID128(x) 16, \ + (x).bytes[0], (x).bytes[1], (x).bytes[2], (x).bytes[3], \ + (x).bytes[4], (x).bytes[5], (x).bytes[6], (x).bytes[7], \ + (x).bytes[8], (x).bytes[9], (x).bytes[10], (x).bytes[11], \ + (x).bytes[12], (x).bytes[13], (x).bytes[14], (x).bytes[15] + +#define SD_BUS_MESSAGE_READ_ID128(x) 16, \ + &(x).bytes[0], &(x).bytes[1], &(x).bytes[2], &(x).bytes[3], \ + &(x).bytes[4], &(x).bytes[5], &(x).bytes[6], &(x).bytes[7], \ + &(x).bytes[8], &(x).bytes[9], &(x).bytes[10], &(x).bytes[11], \ + &(x).bytes[12], &(x).bytes[13], &(x).bytes[14], &(x).bytes[15] + +/* Label escaping */ + +int sd_bus_path_encode(const char *prefix, const char *external_id, char **ret_path); +int sd_bus_path_encode_many(char **out, const char *path_template, ...); +int sd_bus_path_decode(const char *path, const char *prefix, char **ret_external_id); +int sd_bus_path_decode_many(const char *path, const char *path_template, ...); + +/* Tracking peers */ + +int sd_bus_track_new(sd_bus *bus, sd_bus_track **track, sd_bus_track_handler_t handler, void *userdata); +sd_bus_track* sd_bus_track_ref(sd_bus_track *track); +sd_bus_track* sd_bus_track_unref(sd_bus_track *track); + +sd_bus* sd_bus_track_get_bus(sd_bus_track *track); +void* sd_bus_track_get_userdata(sd_bus_track *track); +void* sd_bus_track_set_userdata(sd_bus_track *track, void *userdata); + +int sd_bus_track_add_sender(sd_bus_track *track, sd_bus_message *m); +int sd_bus_track_remove_sender(sd_bus_track *track, sd_bus_message *m); +int sd_bus_track_add_name(sd_bus_track *track, const char *name); +int sd_bus_track_remove_name(sd_bus_track *track, const char *name); + +int sd_bus_track_set_recursive(sd_bus_track *track, int b); +int sd_bus_track_get_recursive(sd_bus_track *track); + +unsigned sd_bus_track_count(sd_bus_track *track); +int sd_bus_track_count_sender(sd_bus_track *track, sd_bus_message *m); +int sd_bus_track_count_name(sd_bus_track *track, const char *name); + +const char* sd_bus_track_contains(sd_bus_track *track, const char *name); +const char* sd_bus_track_first(sd_bus_track *track); +const char* sd_bus_track_next(sd_bus_track *track); + +int sd_bus_track_set_destroy_callback(sd_bus_track *s, sd_bus_destroy_t callback); +int sd_bus_track_get_destroy_callback(sd_bus_track *s, sd_bus_destroy_t *ret); + +/* Define helpers so that __attribute__((cleanup(sd_bus_unrefp))) and similar may be used. */ +_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_bus, sd_bus_unref); +_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_bus, sd_bus_close_unref); +_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_bus, sd_bus_flush_close_unref); +_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_bus_slot, sd_bus_slot_unref); +_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_bus_message, sd_bus_message_unref); +_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_bus_creds, sd_bus_creds_unref); +_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_bus_track, sd_bus_track_unref); + +_SD_END_DECLARATIONS; + +#endif diff --git a/thirdparty/systemd/include/systemd/sd-daemon.h b/thirdparty/systemd/include/systemd/sd-daemon.h new file mode 100644 index 000000000..595b6f337 --- /dev/null +++ b/thirdparty/systemd/include/systemd/sd-daemon.h @@ -0,0 +1,347 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#ifndef foosddaemonhfoo +#define foosddaemonhfoo + +/*** + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include + +#include "_sd-common.h" + +_SD_BEGIN_DECLARATIONS; + +/* + The following functionality is provided: + + - Support for logging with log levels on stderr + - File descriptor passing for socket-based activation + - Daemon startup and status notification + - Detection of systemd boots + + See sd-daemon(3) for more information. +*/ + +/* + Log levels for usage on stderr: + + fprintf(stderr, SD_NOTICE "Hello World!\n"); + + This is similar to printk() usage in the kernel. +*/ +#define SD_EMERG "<0>" /* system is unusable */ +#define SD_ALERT "<1>" /* action must be taken immediately */ +#define SD_CRIT "<2>" /* critical conditions */ +#define SD_ERR "<3>" /* error conditions */ +#define SD_WARNING "<4>" /* warning conditions */ +#define SD_NOTICE "<5>" /* normal but significant condition */ +#define SD_INFO "<6>" /* informational */ +#define SD_DEBUG "<7>" /* debug-level messages */ + +/* The first passed file descriptor is fd 3 */ +#define SD_LISTEN_FDS_START 3 + +/* + Returns how many file descriptors have been passed, or a negative + errno code on failure. Optionally, removes the $LISTEN_FDS and + $LISTEN_PID file descriptors from the environment (recommended, but + problematic in threaded environments). If r is the return value of + this function you'll find the file descriptors passed as fds + SD_LISTEN_FDS_START to SD_LISTEN_FDS_START+r-1. Returns a negative + errno style error code on failure. This function call ensures that + the FD_CLOEXEC flag is set for the passed file descriptors, to make + sure they are not passed on to child processes. If FD_CLOEXEC shall + not be set, the caller needs to unset it after this call for all file + descriptors that are used. + + See sd_listen_fds(3) for more information. +*/ +int sd_listen_fds(int unset_environment); + +int sd_listen_fds_with_names(int unset_environment, char ***names); + +/* + Helper call for identifying a passed file descriptor. Returns 1 if + the file descriptor is a FIFO in the file system stored under the + specified path, 0 otherwise. If path is NULL a path name check will + not be done and the call only verifies if the file descriptor + refers to a FIFO. Returns a negative errno style error code on + failure. + + See sd_is_fifo(3) for more information. +*/ +int sd_is_fifo(int fd, const char *path); + +/* + Helper call for identifying a passed file descriptor. Returns 1 if + the file descriptor is a special character device on the file + system stored under the specified path, 0 otherwise. + If path is NULL a path name check will not be done and the call + only verifies if the file descriptor refers to a special character. + Returns a negative errno style error code on failure. + + See sd_is_special(3) for more information. +*/ +int sd_is_special(int fd, const char *path); + +/* + Helper call for identifying a passed file descriptor. Returns 1 if + the file descriptor is a socket of the specified family (AF_INET, + ...) and type (SOCK_DGRAM, SOCK_STREAM, ...), 0 otherwise. If + family is 0 a socket family check will not be done. If type is 0 a + socket type check will not be done and the call only verifies if + the file descriptor refers to a socket. If listening is > 0 it is + verified that the socket is in listening mode. (i.e. listen() has + been called) If listening is == 0 it is verified that the socket is + not in listening mode. If listening is < 0 no listening mode check + is done. Returns a negative errno style error code on failure. + + See sd_is_socket(3) for more information. +*/ +int sd_is_socket(int fd, int family, int type, int listening); + +/* + Helper call for identifying a passed file descriptor. Returns 1 if + the file descriptor is an Internet socket, of the specified family + (either AF_INET or AF_INET6) and the specified type (SOCK_DGRAM, + SOCK_STREAM, ...), 0 otherwise. If version is 0 a protocol version + check is not done. If type is 0 a socket type check will not be + done. If port is 0 a socket port check will not be done. The + listening flag is used the same way as in sd_is_socket(). Returns a + negative errno style error code on failure. + + See sd_is_socket_inet(3) for more information. +*/ +int sd_is_socket_inet(int fd, int family, int type, int listening, uint16_t port); + +/* + Helper call for identifying a passed file descriptor. Returns 1 if the + file descriptor is an Internet socket of the specified type + (SOCK_DGRAM, SOCK_STREAM, ...), and if the address of the socket is + the same as the address specified by addr. The listening flag is used + the same way as in sd_is_socket(). Returns a negative errno style + error code on failure. + + See sd_is_socket_sockaddr(3) for more information. +*/ +int sd_is_socket_sockaddr(int fd, int type, const struct sockaddr* addr, unsigned addr_len, int listening); + +/* + Helper call for identifying a passed file descriptor. Returns 1 if + the file descriptor is an AF_UNIX socket of the specified type + (SOCK_DGRAM, SOCK_STREAM, ...) and path, 0 otherwise. If type is 0 + a socket type check will not be done. If path is NULL a socket path + check will not be done. For normal AF_UNIX sockets set length to + 0. For abstract namespace sockets set length to the length of the + socket name (including the initial 0 byte), and pass the full + socket path in path (including the initial 0 byte). The listening + flag is used the same way as in sd_is_socket(). Returns a negative + errno style error code on failure. + + See sd_is_socket_unix(3) for more information. +*/ +int sd_is_socket_unix(int fd, int type, int listening, const char *path, size_t length); + +/* + Helper call for identifying a passed file descriptor. Returns 1 if + the file descriptor is a POSIX Message Queue of the specified name, + 0 otherwise. If path is NULL a message queue name check is not + done. Returns a negative errno style error code on failure. + + See sd_is_mq(3) for more information. +*/ +int sd_is_mq(int fd, const char *path); + +/* + Informs systemd about changed daemon state. This takes a number of + newline separated environment-style variable assignments in a + string. The following variables are known: + + MAINPID=... The main PID of a daemon, in case systemd did not + fork off the process itself. Example: "MAINPID=4711" + + READY=1 Tells systemd that daemon startup or daemon reload + is finished (only relevant for services of Type=notify). + The passed argument is a boolean "1" or "0". Since there + is little value in signaling non-readiness the only + value daemons should send is "READY=1". + + RELOADING=1 Tell systemd that the daemon began reloading its + configuration. When the configuration has been + reloaded completely, READY=1 should be sent to inform + systemd about this. + + STOPPING=1 Tells systemd that the daemon is about to go down. + + STATUS=... Passes a single-line status string back to systemd + that describes the daemon state. This is free-form + and can be used for various purposes: general state + feedback, fsck-like programs could pass completion + percentages and failing programs could pass a human + readable error message. Example: "STATUS=Completed + 66% of file system check..." + + NOTIFYACCESS=... + Reset the access to the service status notification socket. + Example: "NOTIFYACCESS=main" + + ERRNO=... If a daemon fails, the errno-style error code, + formatted as string. Example: "ERRNO=2" for ENOENT. + + BUSERROR=... If a daemon fails, the D-Bus error-style error + code. Example: "BUSERROR=org.freedesktop.DBus.Error.TimedOut" + + WATCHDOG=1 Tells systemd to update the watchdog timestamp. + Services using this feature should do this in + regular intervals. A watchdog framework can use the + timestamps to detect failed services. Also see + sd_watchdog_enabled() below. + + WATCHDOG_USEC=... + Reset watchdog_usec value during runtime. + To reset watchdog_usec value, start the service again. + Example: "WATCHDOG_USEC=20000000" + + FDSTORE=1 Store the file descriptors passed along with the + message in the per-service file descriptor store, + and pass them to the main process again on next + invocation. This variable is only supported with + sd_pid_notify_with_fds(). + + FDSTOREREMOVE=1 + Remove one or more file descriptors from the file + descriptor store, identified by the name specified + in FDNAME=, see below. + + FDNAME= A name to assign to new file descriptors stored in the + file descriptor store, or the name of the file descriptors + to remove in case of FDSTOREREMOVE=1. + + Daemons can choose to send additional variables. However, it is + recommended to prefix variable names not listed above with X_. + + Returns a negative errno-style error code on failure. Returns > 0 + if systemd could be notified, 0 if it couldn't possibly because + systemd is not running. + + Example: When a daemon finished starting up, it could issue this + call to notify systemd about it: + + sd_notify(0, "READY=1"); + + See sd_notifyf() for more complete examples. + + See sd_notify(3) for more information. +*/ +int sd_notify(int unset_environment, const char *state); + +/* + Similar to sd_notify() but takes a format string. + + Example 1: A daemon could send the following after initialization: + + sd_notifyf(0, "READY=1\n" + "STATUS=Processing requests...\n" + "MAINPID=%lu", + (unsigned long) getpid()); + + Example 2: A daemon could send the following shortly before + exiting, on failure: + + sd_notifyf(0, "STATUS=Failed to start up: %s\n" + "ERRNO=%i", + strerror_r(errnum, (char[1024]){}, 1024), + errnum); + + See sd_notifyf(3) for more information. +*/ +int sd_notifyf(int unset_environment, const char *format, ...) _sd_printf_(2,3); + +/* + Similar to sd_notify(), but send the message on behalf of another + process, if the appropriate permissions are available. +*/ +int sd_pid_notify(pid_t pid, int unset_environment, const char *state); + +/* + Similar to sd_notifyf(), but send the message on behalf of another + process, if the appropriate permissions are available. +*/ +int sd_pid_notifyf(pid_t pid, int unset_environment, const char *format, ...) _sd_printf_(3,4); + +/* + Similar to sd_pid_notify(), but also passes the specified fd array + to the service manager for storage. This is particularly useful for + FDSTORE=1 messages. +*/ +int sd_pid_notify_with_fds(pid_t pid, int unset_environment, const char *state, const int *fds, unsigned n_fds); + +/* + Combination of sd_pid_notifyf() and sd_pid_notify_with_fds() +*/ +int sd_pid_notifyf_with_fds(pid_t pid, int unset_environment, const int *fds, size_t n_fds, const char *format, ...) _sd_printf_(5,6); + +/* + Returns > 0 if synchronization with systemd succeeded. Returns < 0 + on error. Returns 0 if $NOTIFY_SOCKET was not set. Note that the + timeout parameter of this function call takes the timeout in μs, and + will be passed to ppoll(2), hence the behaviour will be similar to + ppoll(2). This function can be called after sending a status message + to systemd, if one needs to synchronize against reception of the + status messages sent before this call is made. Therefore, this + cannot be used to know if the status message was processed + successfully, but to only synchronize against its consumption. +*/ +int sd_notify_barrier(int unset_environment, uint64_t timeout); + +/* + Just like sd_notify_barrier() but also takes a PID to send the barrier message from. +*/ +int sd_pid_notify_barrier(pid_t pid, int unset_environment, uint64_t timeout); + +/* + Returns > 0 if the system was booted with systemd. Returns < 0 on + error. Returns 0 if the system was not booted with systemd. Note + that all of the functions above handle non-systemd boots just + fine. You should NOT protect them with a call to this function. Also + note that this function checks whether the system, not the user + session is controlled by systemd. However the functions above work + for both user and system services. + + See sd_booted(3) for more information. +*/ +int sd_booted(void); + +/* + Returns > 0 if the service manager expects watchdog keep-alive + events to be sent regularly via sd_notify(0, "WATCHDOG=1"). Returns + 0 if it does not expect this. If the usec argument is non-NULL + returns the watchdog timeout in μs after which the service manager + will act on a process that has not sent a watchdog keep alive + message. This function is useful to implement services that + recognize automatically if they are being run under supervision of + systemd with WatchdogSec= set. It is recommended for clients to + generate keep-alive pings via sd_notify(0, "WATCHDOG=1") every half + of the returned time. + + See sd_watchdog_enabled(3) for more information. +*/ +int sd_watchdog_enabled(int unset_environment, uint64_t *usec); + +_SD_END_DECLARATIONS; + +#endif diff --git a/thirdparty/systemd/include/systemd/sd-device.h b/thirdparty/systemd/include/systemd/sd-device.h new file mode 100644 index 000000000..b67ec0f34 --- /dev/null +++ b/thirdparty/systemd/include/systemd/sd-device.h @@ -0,0 +1,168 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#ifndef foosddevicehfoo +#define foosddevicehfoo + +/*** + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include + +#include "sd-event.h" +#include "sd-id128.h" + +#include "_sd-common.h" + +_SD_BEGIN_DECLARATIONS; + +typedef struct sd_device sd_device; +typedef struct sd_device_enumerator sd_device_enumerator; +typedef struct sd_device_monitor sd_device_monitor; + +__extension__ typedef enum sd_device_action_t { + SD_DEVICE_ADD, + SD_DEVICE_REMOVE, + SD_DEVICE_CHANGE, + SD_DEVICE_MOVE, + SD_DEVICE_ONLINE, + SD_DEVICE_OFFLINE, + SD_DEVICE_BIND, + SD_DEVICE_UNBIND, + _SD_DEVICE_ACTION_MAX, + _SD_DEVICE_ACTION_INVALID = -EINVAL, + _SD_ENUM_FORCE_S64(DEVICE_ACTION) +} sd_device_action_t; + +/* callback */ + +typedef int (*sd_device_monitor_handler_t)(sd_device_monitor *m, sd_device *device, void *userdata); + +/* device */ + +sd_device *sd_device_ref(sd_device *device); +sd_device *sd_device_unref(sd_device *device); + +int sd_device_new_from_syspath(sd_device **ret, const char *syspath); +int sd_device_new_from_devnum(sd_device **ret, char type, dev_t devnum); +int sd_device_new_from_subsystem_sysname(sd_device **ret, const char *subsystem, const char *sysname); +int sd_device_new_from_device_id(sd_device **ret, const char *id); +int sd_device_new_from_stat_rdev(sd_device **ret, const struct stat *st); +int sd_device_new_from_devname(sd_device **ret, const char *devname); +int sd_device_new_from_path(sd_device **ret, const char *path); +int sd_device_new_from_ifname(sd_device **ret, const char *ifname); +int sd_device_new_from_ifindex(sd_device **ret, int ifindex); + +int sd_device_new_child(sd_device **ret, sd_device *device, const char *suffix); + +int sd_device_get_parent(sd_device *child, sd_device **ret); +int sd_device_get_parent_with_subsystem_devtype(sd_device *child, const char *subsystem, const char *devtype, sd_device **ret); + +int sd_device_get_syspath(sd_device *device, const char **ret); +int sd_device_get_subsystem(sd_device *device, const char **ret); +int sd_device_get_devtype(sd_device *device, const char **ret); +int sd_device_get_devnum(sd_device *device, dev_t *devnum); +int sd_device_get_ifindex(sd_device *device, int *ifindex); +int sd_device_get_driver(sd_device *device, const char **ret); +int sd_device_get_devpath(sd_device *device, const char **ret); +int sd_device_get_devname(sd_device *device, const char **ret); +int sd_device_get_sysname(sd_device *device, const char **ret); +int sd_device_get_sysnum(sd_device *device, const char **ret); +int sd_device_get_action(sd_device *device, sd_device_action_t *ret); +int sd_device_get_seqnum(sd_device *device, uint64_t *ret); +int sd_device_get_diskseq(sd_device *device, uint64_t *ret); + +int sd_device_get_is_initialized(sd_device *device); +int sd_device_get_usec_initialized(sd_device *device, uint64_t *ret); +int sd_device_get_usec_since_initialized(sd_device *device, uint64_t *ret); + +const char *sd_device_get_tag_first(sd_device *device); +const char *sd_device_get_tag_next(sd_device *device); +const char *sd_device_get_current_tag_first(sd_device *device); +const char *sd_device_get_current_tag_next(sd_device *device); +const char *sd_device_get_devlink_first(sd_device *device); +const char *sd_device_get_devlink_next(sd_device *device); +const char *sd_device_get_property_first(sd_device *device, const char **value); +const char *sd_device_get_property_next(sd_device *device, const char **value); +const char *sd_device_get_sysattr_first(sd_device *device); +const char *sd_device_get_sysattr_next(sd_device *device); +sd_device *sd_device_get_child_first(sd_device *device, const char **ret_suffix); +sd_device *sd_device_get_child_next(sd_device *device, const char **ret_suffix); + +int sd_device_has_tag(sd_device *device, const char *tag); +int sd_device_has_current_tag(sd_device *device, const char *tag); +int sd_device_get_property_value(sd_device *device, const char *key, const char **value); +int sd_device_get_trigger_uuid(sd_device *device, sd_id128_t *ret); +int sd_device_get_sysattr_value(sd_device *device, const char *sysattr, const char **_value); + +int sd_device_set_sysattr_value(sd_device *device, const char *sysattr, const char *value); +int sd_device_set_sysattr_valuef(sd_device *device, const char *sysattr, const char *format, ...) _sd_printf_(3, 4); +int sd_device_trigger(sd_device *device, sd_device_action_t action); +int sd_device_trigger_with_uuid(sd_device *device, sd_device_action_t action, sd_id128_t *ret_uuid); +int sd_device_open(sd_device *device, int flags); + +/* device enumerator */ + +int sd_device_enumerator_new(sd_device_enumerator **ret); +sd_device_enumerator *sd_device_enumerator_ref(sd_device_enumerator *enumerator); +sd_device_enumerator *sd_device_enumerator_unref(sd_device_enumerator *enumerator); + +sd_device *sd_device_enumerator_get_device_first(sd_device_enumerator *enumerator); +sd_device *sd_device_enumerator_get_device_next(sd_device_enumerator *enumerator); +sd_device *sd_device_enumerator_get_subsystem_first(sd_device_enumerator *enumerator); +sd_device *sd_device_enumerator_get_subsystem_next(sd_device_enumerator *enumerator); + +int sd_device_enumerator_add_match_subsystem(sd_device_enumerator *enumerator, const char *subsystem, int match); +int sd_device_enumerator_add_match_sysattr(sd_device_enumerator *enumerator, const char *sysattr, const char *value, int match); +int sd_device_enumerator_add_match_property(sd_device_enumerator *enumerator, const char *property, const char *value); +int sd_device_enumerator_add_match_property_required(sd_device_enumerator *enumerator, const char *property, const char *value); +int sd_device_enumerator_add_match_sysname(sd_device_enumerator *enumerator, const char *sysname); +int sd_device_enumerator_add_nomatch_sysname(sd_device_enumerator *enumerator, const char *sysname); +int sd_device_enumerator_add_match_tag(sd_device_enumerator *enumerator, const char *tag); +int sd_device_enumerator_add_match_parent(sd_device_enumerator *enumerator, sd_device *parent); +int sd_device_enumerator_allow_uninitialized(sd_device_enumerator *enumerator); + +/* device monitor */ + +int sd_device_monitor_new(sd_device_monitor **ret); +sd_device_monitor *sd_device_monitor_ref(sd_device_monitor *m); +sd_device_monitor *sd_device_monitor_unref(sd_device_monitor *m); + +int sd_device_monitor_set_receive_buffer_size(sd_device_monitor *m, size_t size); +int sd_device_monitor_attach_event(sd_device_monitor *m, sd_event *event); +int sd_device_monitor_detach_event(sd_device_monitor *m); +sd_event *sd_device_monitor_get_event(sd_device_monitor *m); +sd_event_source *sd_device_monitor_get_event_source(sd_device_monitor *m); +int sd_device_monitor_set_description(sd_device_monitor *m, const char *description); +int sd_device_monitor_get_description(sd_device_monitor *m, const char **ret); +int sd_device_monitor_start(sd_device_monitor *m, sd_device_monitor_handler_t callback, void *userdata); +int sd_device_monitor_stop(sd_device_monitor *m); + +int sd_device_monitor_filter_add_match_subsystem_devtype(sd_device_monitor *m, const char *subsystem, const char *devtype); +int sd_device_monitor_filter_add_match_tag(sd_device_monitor *m, const char *tag); +int sd_device_monitor_filter_add_match_sysattr(sd_device_monitor *m, const char *sysattr, const char *value, int match); +int sd_device_monitor_filter_add_match_parent(sd_device_monitor *m, sd_device *device, int match); +int sd_device_monitor_filter_update(sd_device_monitor *m); +int sd_device_monitor_filter_remove(sd_device_monitor *m); + +_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_device, sd_device_unref); +_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_device_enumerator, sd_device_enumerator_unref); +_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_device_monitor, sd_device_monitor_unref); + +_SD_END_DECLARATIONS; + +#endif diff --git a/thirdparty/systemd/include/systemd/sd-event.h b/thirdparty/systemd/include/systemd/sd-event.h new file mode 100644 index 000000000..a876add00 --- /dev/null +++ b/thirdparty/systemd/include/systemd/sd-event.h @@ -0,0 +1,188 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#ifndef foosdeventhfoo +#define foosdeventhfoo + +/*** + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "_sd-common.h" + +/* + Why is this better than pure epoll? + + - Supports event source prioritization + - Scales better with a large number of time events because it does not require one timerfd each + - Automatically tries to coalesce timer events system-wide + - Handles signals, child PIDs, inotify events + - Supports systemd-style automatic watchdog event generation +*/ + +_SD_BEGIN_DECLARATIONS; + +#define SD_EVENT_DEFAULT ((sd_event *) 1) + +typedef struct sd_event sd_event; +typedef struct sd_event_source sd_event_source; + +enum { + SD_EVENT_OFF = 0, + SD_EVENT_ON = 1, + SD_EVENT_ONESHOT = -1 +}; + +enum { + SD_EVENT_INITIAL, + SD_EVENT_ARMED, + SD_EVENT_PENDING, + SD_EVENT_RUNNING, + SD_EVENT_EXITING, + SD_EVENT_FINISHED, + SD_EVENT_PREPARING +}; + +enum { + /* And everything in-between and outside is good too */ + SD_EVENT_PRIORITY_IMPORTANT = -100, + SD_EVENT_PRIORITY_NORMAL = 0, + SD_EVENT_PRIORITY_IDLE = 100 +}; + +#define SD_EVENT_SIGNAL_PROCMASK (1 << 30) + +typedef int (*sd_event_handler_t)(sd_event_source *s, void *userdata); +typedef int (*sd_event_io_handler_t)(sd_event_source *s, int fd, uint32_t revents, void *userdata); +typedef int (*sd_event_time_handler_t)(sd_event_source *s, uint64_t usec, void *userdata); +typedef int (*sd_event_signal_handler_t)(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata); +#if defined _GNU_SOURCE || (defined _POSIX_C_SOURCE && _POSIX_C_SOURCE >= 199309L) +typedef int (*sd_event_child_handler_t)(sd_event_source *s, const siginfo_t *si, void *userdata); +#else +typedef void* sd_event_child_handler_t; +#endif +typedef int (*sd_event_inotify_handler_t)(sd_event_source *s, const struct inotify_event *event, void *userdata); +typedef _sd_destroy_t sd_event_destroy_t; + +int sd_event_default(sd_event **e); + +int sd_event_new(sd_event **e); +sd_event* sd_event_ref(sd_event *e); +sd_event* sd_event_unref(sd_event *e); + +int sd_event_add_io(sd_event *e, sd_event_source **s, int fd, uint32_t events, sd_event_io_handler_t callback, void *userdata); +int sd_event_add_time(sd_event *e, sd_event_source **s, clockid_t clock, uint64_t usec, uint64_t accuracy, sd_event_time_handler_t callback, void *userdata); +int sd_event_add_time_relative(sd_event *e, sd_event_source **s, clockid_t clock, uint64_t usec, uint64_t accuracy, sd_event_time_handler_t callback, void *userdata); +int sd_event_add_signal(sd_event *e, sd_event_source **s, int sig, sd_event_signal_handler_t callback, void *userdata); +int sd_event_add_child(sd_event *e, sd_event_source **s, pid_t pid, int options, sd_event_child_handler_t callback, void *userdata); +int sd_event_add_child_pidfd(sd_event *e, sd_event_source **s, int pidfd, int options, sd_event_child_handler_t callback, void *userdata); +int sd_event_add_inotify(sd_event *e, sd_event_source **s, const char *path, uint32_t mask, sd_event_inotify_handler_t callback, void *userdata); +int sd_event_add_inotify_fd(sd_event *e, sd_event_source **s, int fd, uint32_t mask, sd_event_inotify_handler_t callback, void *userdata); +int sd_event_add_defer(sd_event *e, sd_event_source **s, sd_event_handler_t callback, void *userdata); +int sd_event_add_post(sd_event *e, sd_event_source **s, sd_event_handler_t callback, void *userdata); +int sd_event_add_exit(sd_event *e, sd_event_source **s, sd_event_handler_t callback, void *userdata); +int sd_event_add_memory_pressure(sd_event *e, sd_event_source **s, sd_event_handler_t callback, void *userdata); + +int sd_event_prepare(sd_event *e); +int sd_event_wait(sd_event *e, uint64_t usec); +int sd_event_dispatch(sd_event *e); +int sd_event_run(sd_event *e, uint64_t usec); +int sd_event_loop(sd_event *e); +int sd_event_exit(sd_event *e, int code); + +int sd_event_now(sd_event *e, clockid_t clock, uint64_t *usec); + +int sd_event_get_fd(sd_event *e); +int sd_event_get_state(sd_event *e); +int sd_event_get_tid(sd_event *e, pid_t *tid); +int sd_event_get_exit_code(sd_event *e, int *code); +int sd_event_set_watchdog(sd_event *e, int b); +int sd_event_get_watchdog(sd_event *e); +int sd_event_get_iteration(sd_event *e, uint64_t *ret); +int sd_event_set_signal_exit(sd_event *e, int b); + +sd_event_source* sd_event_source_ref(sd_event_source *s); +sd_event_source* sd_event_source_unref(sd_event_source *s); +sd_event_source* sd_event_source_disable_unref(sd_event_source *s); + +sd_event *sd_event_source_get_event(sd_event_source *s); +void* sd_event_source_get_userdata(sd_event_source *s); +void* sd_event_source_set_userdata(sd_event_source *s, void *userdata); + +int sd_event_source_set_description(sd_event_source *s, const char *description); +int sd_event_source_get_description(sd_event_source *s, const char **description); +int sd_event_source_set_prepare(sd_event_source *s, sd_event_handler_t callback); +int sd_event_source_get_pending(sd_event_source *s); +int sd_event_source_get_priority(sd_event_source *s, int64_t *priority); +int sd_event_source_set_priority(sd_event_source *s, int64_t priority); +int sd_event_source_get_enabled(sd_event_source *s, int *enabled); +int sd_event_source_set_enabled(sd_event_source *s, int enabled); +int sd_event_source_get_io_fd(sd_event_source *s); +int sd_event_source_set_io_fd(sd_event_source *s, int fd); +int sd_event_source_get_io_fd_own(sd_event_source *s); +int sd_event_source_set_io_fd_own(sd_event_source *s, int own); +int sd_event_source_get_io_events(sd_event_source *s, uint32_t* events); +int sd_event_source_set_io_events(sd_event_source *s, uint32_t events); +int sd_event_source_get_io_revents(sd_event_source *s, uint32_t* revents); +int sd_event_source_get_time(sd_event_source *s, uint64_t *usec); +int sd_event_source_set_time(sd_event_source *s, uint64_t usec); +int sd_event_source_set_time_relative(sd_event_source *s, uint64_t usec); +int sd_event_source_get_time_accuracy(sd_event_source *s, uint64_t *usec); +int sd_event_source_set_time_accuracy(sd_event_source *s, uint64_t usec); +int sd_event_source_get_time_clock(sd_event_source *s, clockid_t *clock); +int sd_event_source_get_signal(sd_event_source *s); +int sd_event_source_get_child_pid(sd_event_source *s, pid_t *pid); +int sd_event_source_get_child_pidfd(sd_event_source *s); +int sd_event_source_get_child_pidfd_own(sd_event_source *s); +int sd_event_source_set_child_pidfd_own(sd_event_source *s, int own); +int sd_event_source_get_child_process_own(sd_event_source *s); +int sd_event_source_set_child_process_own(sd_event_source *s, int own); +#if defined _GNU_SOURCE || (defined _POSIX_C_SOURCE && _POSIX_C_SOURCE >= 199309L) +int sd_event_source_send_child_signal(sd_event_source *s, int sig, const siginfo_t *si, unsigned flags); +#else +int sd_event_source_send_child_signal(sd_event_source *s, int sig, const void *si, unsigned flags); +#endif +int sd_event_source_get_inotify_mask(sd_event_source *s, uint32_t *ret); +int sd_event_source_get_inotify_path(sd_event_source *s, const char **ret); +int sd_event_source_set_memory_pressure_type(sd_event_source *e, const char *ty); +int sd_event_source_set_memory_pressure_period(sd_event_source *s, uint64_t threshold_usec, uint64_t window_usec); +int sd_event_source_set_destroy_callback(sd_event_source *s, sd_event_destroy_t callback); +int sd_event_source_get_destroy_callback(sd_event_source *s, sd_event_destroy_t *ret); +int sd_event_source_get_floating(sd_event_source *s); +int sd_event_source_set_floating(sd_event_source *s, int b); +int sd_event_source_get_exit_on_failure(sd_event_source *s); +int sd_event_source_set_exit_on_failure(sd_event_source *s, int b); +int sd_event_source_set_ratelimit(sd_event_source *s, uint64_t interval_usec, unsigned burst); +int sd_event_source_get_ratelimit(sd_event_source *s, uint64_t *ret_interval_usec, unsigned *ret_burst); +int sd_event_source_is_ratelimited(sd_event_source *s); +int sd_event_source_set_ratelimit_expire_callback(sd_event_source *s, sd_event_handler_t callback); +int sd_event_source_leave_ratelimit(sd_event_source *s); + +int sd_event_trim_memory(void); + +/* Define helpers so that __attribute__((cleanup(sd_event_unrefp))) and similar may be used. */ +_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_event, sd_event_unref); +_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_event_source, sd_event_source_unref); +_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_event_source, sd_event_source_disable_unref); + +_SD_END_DECLARATIONS; + +#endif diff --git a/thirdparty/systemd/include/systemd/sd-gpt.h b/thirdparty/systemd/include/systemd/sd-gpt.h new file mode 100644 index 000000000..7ffa57a94 --- /dev/null +++ b/thirdparty/systemd/include/systemd/sd-gpt.h @@ -0,0 +1,369 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#ifndef foosdgpthfoo +#define foosdgpthfoo + +/*** + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include "sd-id128.h" + +#include "_sd-common.h" + +_SD_BEGIN_DECLARATIONS; + +#define SD_GPT_ROOT_ALPHA SD_ID128_MAKE(65,23,f8,ae,3e,b1,4e,2a,a0,5a,18,b6,95,ae,65,6f) +#define SD_GPT_ROOT_ARC SD_ID128_MAKE(d2,7f,46,ed,29,19,4c,b8,bd,25,95,31,f3,c1,65,34) +#define SD_GPT_ROOT_ARM SD_ID128_MAKE(69,da,d7,10,2c,e4,4e,3c,b1,6c,21,a1,d4,9a,be,d3) +#define SD_GPT_ROOT_ARM64 SD_ID128_MAKE(b9,21,b0,45,1d,f0,41,c3,af,44,4c,6f,28,0d,3f,ae) +#define SD_GPT_ROOT_IA64 SD_ID128_MAKE(99,3d,8d,3d,f8,0e,42,25,85,5a,9d,af,8e,d7,ea,97) +#define SD_GPT_ROOT_LOONGARCH64 SD_ID128_MAKE(77,05,58,00,79,2c,4f,94,b3,9a,98,c9,1b,76,2b,b6) +#define SD_GPT_ROOT_MIPS SD_ID128_MAKE(e9,43,45,44,6e,2c,47,cc,ba,e2,12,d6,de,af,b4,4c) +#define SD_GPT_ROOT_MIPS64 SD_ID128_MAKE(d1,13,af,76,80,ef,41,b4,bd,b6,0c,ff,4d,3d,4a,25) +#define SD_GPT_ROOT_MIPS_LE SD_ID128_MAKE(37,c5,8c,8a,d9,13,41,56,a2,5f,48,b1,b6,4e,07,f0) +#define SD_GPT_ROOT_MIPS64_LE SD_ID128_MAKE(70,0b,da,43,7a,34,45,07,b1,79,ee,b9,3d,7a,7c,a3) +#define SD_GPT_ROOT_PARISC SD_ID128_MAKE(1a,ac,db,3b,54,44,41,38,bd,9e,e5,c2,23,9b,23,46) +#define SD_GPT_ROOT_PPC SD_ID128_MAKE(1d,e3,f1,ef,fa,98,47,b5,8d,cd,4a,86,0a,65,4d,78) +#define SD_GPT_ROOT_PPC64 SD_ID128_MAKE(91,2a,de,1d,a8,39,49,13,89,64,a1,0e,ee,08,fb,d2) +#define SD_GPT_ROOT_PPC64_LE SD_ID128_MAKE(c3,1c,45,e6,3f,39,41,2e,80,fb,48,09,c4,98,05,99) +#define SD_GPT_ROOT_RISCV32 SD_ID128_MAKE(60,d5,a7,fe,8e,7d,43,5c,b7,14,3d,d8,16,21,44,e1) +#define SD_GPT_ROOT_RISCV64 SD_ID128_MAKE(72,ec,70,a6,cf,74,40,e6,bd,49,4b,da,08,e8,f2,24) +#define SD_GPT_ROOT_S390 SD_ID128_MAKE(08,a7,ac,ea,62,4c,4a,20,91,e8,6e,0f,a6,7d,23,f9) +#define SD_GPT_ROOT_S390X SD_ID128_MAKE(5e,ea,d9,a9,fe,09,4a,1e,a1,d7,52,0d,00,53,13,06) +#define SD_GPT_ROOT_TILEGX SD_ID128_MAKE(c5,0c,dd,70,38,62,4c,c3,90,e1,80,9a,8c,93,ee,2c) +#define SD_GPT_ROOT_X86 SD_ID128_MAKE(44,47,95,40,f2,97,41,b2,9a,f7,d1,31,d5,f0,45,8a) +#define SD_GPT_ROOT_X86_64 SD_ID128_MAKE(4f,68,bc,e3,e8,cd,4d,b1,96,e7,fb,ca,f9,84,b7,09) +#define SD_GPT_USR_ALPHA SD_ID128_MAKE(e1,8c,f0,8c,33,ec,4c,0d,82,46,c6,c6,fb,3d,a0,24) +#define SD_GPT_USR_ARC SD_ID128_MAKE(79,78,a6,83,63,16,49,22,bb,ee,38,bf,f5,a2,fe,cc) +#define SD_GPT_USR_ARM SD_ID128_MAKE(7d,03,59,a3,02,b3,4f,0a,86,5c,65,44,03,e7,06,25) +#define SD_GPT_USR_ARM64 SD_ID128_MAKE(b0,e0,10,50,ee,5f,43,90,94,9a,91,01,b1,71,04,e9) +#define SD_GPT_USR_IA64 SD_ID128_MAKE(43,01,d2,a6,4e,3b,4b,2a,bb,94,9e,0b,2c,42,25,ea) +#define SD_GPT_USR_LOONGARCH64 SD_ID128_MAKE(e6,11,c7,02,57,5c,4c,be,9a,46,43,4f,a0,bf,7e,3f) +#define SD_GPT_USR_MIPS SD_ID128_MAKE(77,3b,2a,bc,2a,99,43,98,8b,f5,03,ba,ac,40,d0,2b) +#define SD_GPT_USR_MIPS64 SD_ID128_MAKE(57,e1,39,58,73,31,43,65,8e,6e,35,ee,ee,17,c6,1b) +#define SD_GPT_USR_MIPS_LE SD_ID128_MAKE(0f,48,68,e9,99,52,47,06,97,9f,3e,d3,a4,73,e9,47) +#define SD_GPT_USR_MIPS64_LE SD_ID128_MAKE(c9,7c,1f,32,ba,06,40,b4,9f,22,23,60,61,b0,8a,a8) +#define SD_GPT_USR_PARISC SD_ID128_MAKE(dc,4a,44,80,69,17,42,62,a4,ec,db,93,84,94,9f,25) +#define SD_GPT_USR_PPC SD_ID128_MAKE(7d,14,fe,c5,cc,71,41,5d,9d,6c,06,bf,0b,3c,3e,af) +#define SD_GPT_USR_PPC64 SD_ID128_MAKE(2c,97,39,e2,f0,68,46,b3,9f,d0,01,c5,a9,af,bc,ca) +#define SD_GPT_USR_PPC64_LE SD_ID128_MAKE(15,bb,03,af,77,e7,4d,4a,b1,2b,c0,d0,84,f7,49,1c) +#define SD_GPT_USR_RISCV32 SD_ID128_MAKE(b9,33,fb,22,5c,3f,4f,91,af,90,e2,bb,0f,a5,07,02) +#define SD_GPT_USR_RISCV64 SD_ID128_MAKE(be,ae,c3,4b,84,42,43,9b,a4,0b,98,43,81,ed,09,7d) +#define SD_GPT_USR_S390 SD_ID128_MAKE(cd,0f,86,9b,d0,fb,4c,a0,b1,41,9e,a8,7c,c7,8d,66) +#define SD_GPT_USR_S390X SD_ID128_MAKE(8a,4f,57,70,50,aa,4e,d3,87,4a,99,b7,10,db,6f,ea) +#define SD_GPT_USR_TILEGX SD_ID128_MAKE(55,49,70,29,c7,c1,44,cc,aa,39,81,5e,d1,55,86,30) +#define SD_GPT_USR_X86 SD_ID128_MAKE(75,25,0d,76,8c,c6,45,8e,bd,66,bd,47,cc,81,a8,12) +#define SD_GPT_USR_X86_64 SD_ID128_MAKE(84,84,68,0c,95,21,48,c6,9c,11,b0,72,06,56,f6,9e) + +/* Verity partitions for the root partitions above (we only define them for the root and /usr partitions, + * because only they are commonly read-only and hence suitable for verity). */ +#define SD_GPT_ROOT_ALPHA_VERITY SD_ID128_MAKE(fc,56,d9,e9,e6,e5,4c,06,be,32,e7,44,07,ce,09,a5) +#define SD_GPT_ROOT_ARC_VERITY SD_ID128_MAKE(24,b2,d9,75,0f,97,45,21,af,a1,cd,53,1e,42,1b,8d) +#define SD_GPT_ROOT_ARM_VERITY SD_ID128_MAKE(73,86,cd,f2,20,3c,47,a9,a4,98,f2,ec,ce,45,a2,d6) +#define SD_GPT_ROOT_ARM64_VERITY SD_ID128_MAKE(df,33,00,ce,d6,9f,4c,92,97,8c,9b,fb,0f,38,d8,20) +#define SD_GPT_ROOT_IA64_VERITY SD_ID128_MAKE(86,ed,10,d5,b6,07,45,bb,89,57,d3,50,f2,3d,05,71) +#define SD_GPT_ROOT_LOONGARCH64_VERITY SD_ID128_MAKE(f3,39,3b,22,e9,af,46,13,a9,48,9d,3b,fb,d0,c5,35) +#define SD_GPT_ROOT_MIPS_VERITY SD_ID128_MAKE(7a,43,07,99,f7,11,4c,7e,8e,5b,1d,68,5b,d4,86,07) +#define SD_GPT_ROOT_MIPS64_VERITY SD_ID128_MAKE(57,95,36,f8,6a,33,40,55,a9,5a,df,2d,5e,2c,42,a8) +#define SD_GPT_ROOT_MIPS_LE_VERITY SD_ID128_MAKE(d7,d1,50,d2,2a,04,4a,33,8f,12,16,65,12,05,ff,7b) +#define SD_GPT_ROOT_MIPS64_LE_VERITY SD_ID128_MAKE(16,b4,17,f8,3e,06,4f,57,8d,d2,9b,52,32,f4,1a,a6) +#define SD_GPT_ROOT_PARISC_VERITY SD_ID128_MAKE(d2,12,a4,30,fb,c5,49,f9,a9,83,a7,fe,ef,2b,8d,0e) +#define SD_GPT_ROOT_PPC64_LE_VERITY SD_ID128_MAKE(90,6b,d9,44,45,89,4a,ae,a4,e4,dd,98,39,17,44,6a) +#define SD_GPT_ROOT_PPC64_VERITY SD_ID128_MAKE(92,25,a9,a3,3c,19,4d,89,b4,f6,ee,ff,88,f1,76,31) +#define SD_GPT_ROOT_PPC_VERITY SD_ID128_MAKE(98,cf,e6,49,15,88,46,dc,b2,f0,ad,d1,47,42,49,25) +#define SD_GPT_ROOT_RISCV32_VERITY SD_ID128_MAKE(ae,02,53,be,11,67,40,07,ac,68,43,92,6c,14,c5,de) +#define SD_GPT_ROOT_RISCV64_VERITY SD_ID128_MAKE(b6,ed,55,82,44,0b,42,09,b8,da,5f,f7,c4,19,ea,3d) +#define SD_GPT_ROOT_S390_VERITY SD_ID128_MAKE(7a,c6,3b,47,b2,5c,46,3b,8d,f8,b4,a9,4e,6c,90,e1) +#define SD_GPT_ROOT_S390X_VERITY SD_ID128_MAKE(b3,25,bf,be,c7,be,4a,b8,83,57,13,9e,65,2d,2f,6b) +#define SD_GPT_ROOT_TILEGX_VERITY SD_ID128_MAKE(96,60,61,ec,28,e4,4b,2e,b4,a5,1f,0a,82,5a,1d,84) +#define SD_GPT_ROOT_X86_64_VERITY SD_ID128_MAKE(2c,73,57,ed,eb,d2,46,d9,ae,c1,23,d4,37,ec,2b,f5) +#define SD_GPT_ROOT_X86_VERITY SD_ID128_MAKE(d1,3c,5d,3b,b5,d1,42,2a,b2,9f,94,54,fd,c8,9d,76) +#define SD_GPT_USR_ALPHA_VERITY SD_ID128_MAKE(8c,ce,0d,25,c0,d0,4a,44,bd,87,46,33,1b,f1,df,67) +#define SD_GPT_USR_ARC_VERITY SD_ID128_MAKE(fc,a0,59,8c,d8,80,45,91,8c,16,4e,da,05,c7,34,7c) +#define SD_GPT_USR_ARM_VERITY SD_ID128_MAKE(c2,15,d7,51,7b,cd,46,49,be,90,66,27,49,0a,4c,05) +#define SD_GPT_USR_ARM64_VERITY SD_ID128_MAKE(6e,11,a4,e7,fb,ca,4d,ed,b9,e9,e1,a5,12,bb,66,4e) +#define SD_GPT_USR_IA64_VERITY SD_ID128_MAKE(6a,49,1e,03,3b,e7,45,45,8e,38,83,32,0e,0e,a8,80) +#define SD_GPT_USR_LOONGARCH64_VERITY SD_ID128_MAKE(f4,6b,2c,26,59,ae,48,f0,91,06,c5,0e,d4,7f,67,3d) +#define SD_GPT_USR_MIPS_VERITY SD_ID128_MAKE(6e,5a,1b,c8,d2,23,49,b7,bc,a8,37,a5,fc,ce,b9,96) +#define SD_GPT_USR_MIPS64_VERITY SD_ID128_MAKE(81,cf,9d,90,74,58,4d,f4,8d,cf,c8,a3,a4,04,f0,9b) +#define SD_GPT_USR_MIPS_LE_VERITY SD_ID128_MAKE(46,b9,8d,8d,b5,5c,4e,8f,aa,b3,37,fc,a7,f8,07,52) +#define SD_GPT_USR_MIPS64_LE_VERITY SD_ID128_MAKE(3c,3d,61,fe,b5,f3,41,4d,bb,71,87,39,a6,94,a4,ef) +#define SD_GPT_USR_PARISC_VERITY SD_ID128_MAKE(58,43,d6,18,ec,37,48,d7,9f,12,ce,a8,e0,87,68,b2) +#define SD_GPT_USR_PPC64_LE_VERITY SD_ID128_MAKE(ee,2b,99,83,21,e8,41,53,86,d9,b6,90,1a,54,d1,ce) +#define SD_GPT_USR_PPC64_VERITY SD_ID128_MAKE(bd,b5,28,a5,a2,59,47,5f,a8,7d,da,53,fa,73,6a,07) +#define SD_GPT_USR_PPC_VERITY SD_ID128_MAKE(df,76,5d,00,27,0e,49,e5,bc,75,f4,7b,b2,11,8b,09) +#define SD_GPT_USR_RISCV32_VERITY SD_ID128_MAKE(cb,1e,e4,e3,8c,d0,41,36,a0,a4,aa,61,a3,2e,87,30) +#define SD_GPT_USR_RISCV64_VERITY SD_ID128_MAKE(8f,10,56,be,9b,05,47,c4,81,d6,be,53,12,8e,5b,54) +#define SD_GPT_USR_S390_VERITY SD_ID128_MAKE(b6,63,c6,18,e7,bc,4d,6d,90,aa,11,b7,56,bb,17,97) +#define SD_GPT_USR_S390X_VERITY SD_ID128_MAKE(31,74,1c,c4,1a,2a,41,11,a5,81,e0,0b,44,7d,2d,06) +#define SD_GPT_USR_TILEGX_VERITY SD_ID128_MAKE(2f,b4,bf,56,07,fa,42,da,81,32,6b,13,9f,20,26,ae) +#define SD_GPT_USR_X86_64_VERITY SD_ID128_MAKE(77,ff,5f,63,e7,b6,46,33,ac,f4,15,65,b8,64,c0,e6) +#define SD_GPT_USR_X86_VERITY SD_ID128_MAKE(8f,46,1b,0d,14,ee,4e,81,9a,a9,04,9b,6f,b9,7a,bd) + +/* PKCS#7 Signatures for the Verity Root Hashes */ +#define SD_GPT_ROOT_ALPHA_VERITY_SIG SD_ID128_MAKE(d4,64,95,b7,a0,53,41,4f,80,f7,70,0c,99,92,1e,f8) +#define SD_GPT_ROOT_ARC_VERITY_SIG SD_ID128_MAKE(14,3a,70,ba,cb,d3,4f,06,91,9f,6c,05,68,3a,78,bc) +#define SD_GPT_ROOT_ARM_VERITY_SIG SD_ID128_MAKE(42,b0,45,5f,eb,11,49,1d,98,d3,56,14,5b,a9,d0,37) +#define SD_GPT_ROOT_ARM64_VERITY_SIG SD_ID128_MAKE(6d,b6,9d,e6,29,f4,47,58,a7,a5,96,21,90,f0,0c,e3) +#define SD_GPT_ROOT_IA64_VERITY_SIG SD_ID128_MAKE(e9,8b,36,ee,32,ba,48,82,9b,12,0c,e1,46,55,f4,6a) +#define SD_GPT_ROOT_LOONGARCH64_VERITY_SIG SD_ID128_MAKE(5a,fb,67,eb,ec,c8,4f,85,ae,8e,ac,1e,7c,50,e7,d0) +#define SD_GPT_ROOT_MIPS_VERITY_SIG SD_ID128_MAKE(bb,a2,10,a2,9c,5d,45,ee,9e,87,ff,2c,cb,d0,02,d0) +#define SD_GPT_ROOT_MIPS64_VERITY_SIG SD_ID128_MAKE(43,ce,94,d4,0f,3d,49,99,82,50,b9,de,af,d9,8e,6e) +#define SD_GPT_ROOT_MIPS_LE_VERITY_SIG SD_ID128_MAKE(c9,19,cc,1f,44,56,4e,ff,91,8c,f7,5e,94,52,5c,a5) +#define SD_GPT_ROOT_MIPS64_LE_VERITY_SIG SD_ID128_MAKE(90,4e,58,ef,5c,65,4a,31,9c,57,6a,f5,fc,7c,5d,e7) +#define SD_GPT_ROOT_PARISC_VERITY_SIG SD_ID128_MAKE(15,de,61,70,65,d3,43,1c,91,6e,b0,dc,d8,39,3f,25) +#define SD_GPT_ROOT_PPC64_LE_VERITY_SIG SD_ID128_MAKE(d4,a2,36,e7,e8,73,4c,07,bf,1d,bf,6c,f7,f1,c3,c6) +#define SD_GPT_ROOT_PPC64_VERITY_SIG SD_ID128_MAKE(f5,e2,c2,0c,45,b2,4f,fa,bc,e9,2a,60,73,7e,1a,af) +#define SD_GPT_ROOT_PPC_VERITY_SIG SD_ID128_MAKE(1b,31,b5,aa,ad,d9,46,3a,b2,ed,bd,46,7f,c8,57,e7) +#define SD_GPT_ROOT_RISCV32_VERITY_SIG SD_ID128_MAKE(3a,11,2a,75,87,29,43,80,b4,cf,76,4d,79,93,44,48) +#define SD_GPT_ROOT_RISCV64_VERITY_SIG SD_ID128_MAKE(ef,e0,f0,87,ea,8d,44,69,82,1a,4c,2a,96,a8,38,6a) +#define SD_GPT_ROOT_S390_VERITY_SIG SD_ID128_MAKE(34,82,38,8e,42,54,43,5a,a2,41,76,6a,06,5f,99,60) +#define SD_GPT_ROOT_S390X_VERITY_SIG SD_ID128_MAKE(c8,01,87,a5,73,a3,49,1a,90,1a,01,7c,3f,a9,53,e9) +#define SD_GPT_ROOT_TILEGX_VERITY_SIG SD_ID128_MAKE(b3,67,14,39,97,b0,4a,53,90,f7,2d,5a,8f,3a,d4,7b) +#define SD_GPT_ROOT_X86_64_VERITY_SIG SD_ID128_MAKE(41,09,2b,05,9f,c8,45,23,99,4f,2d,ef,04,08,b1,76) +#define SD_GPT_ROOT_X86_VERITY_SIG SD_ID128_MAKE(59,96,fc,05,10,9c,48,de,80,8b,23,fa,08,30,b6,76) +#define SD_GPT_USR_ALPHA_VERITY_SIG SD_ID128_MAKE(5c,6e,1c,76,07,6a,45,7a,a0,fe,f3,b4,cd,21,ce,6e) +#define SD_GPT_USR_ARC_VERITY_SIG SD_ID128_MAKE(94,f9,a9,a1,99,71,42,7a,a4,00,50,cb,29,7f,0f,35) +#define SD_GPT_USR_ARM_VERITY_SIG SD_ID128_MAKE(d7,ff,81,2f,37,d1,49,02,a8,10,d7,6b,a5,7b,97,5a) +#define SD_GPT_USR_ARM64_VERITY_SIG SD_ID128_MAKE(c2,3c,e4,ff,44,bd,4b,00,b2,d4,b4,1b,34,19,e0,2a) +#define SD_GPT_USR_IA64_VERITY_SIG SD_ID128_MAKE(8d,e5,8b,c2,2a,43,46,0d,b1,4e,a7,6e,4a,17,b4,7f) +#define SD_GPT_USR_LOONGARCH64_VERITY_SIG SD_ID128_MAKE(b0,24,f3,15,d3,30,44,4c,84,61,44,bb,de,52,4e,99) +#define SD_GPT_USR_MIPS_VERITY_SIG SD_ID128_MAKE(97,ae,15,8d,f2,16,49,7b,80,57,f7,f9,05,77,0f,54) +#define SD_GPT_USR_MIPS64_VERITY_SIG SD_ID128_MAKE(05,81,6c,e2,dd,40,4a,c6,a6,1d,37,d3,2d,c1,ba,7d) +#define SD_GPT_USR_MIPS_LE_VERITY_SIG SD_ID128_MAKE(3e,23,ca,0b,a4,bc,4b,4e,80,87,5a,b6,a2,6a,a8,a9) +#define SD_GPT_USR_MIPS64_LE_VERITY_SIG SD_ID128_MAKE(f2,c2,c7,ee,ad,cc,43,51,b5,c6,ee,98,16,b6,6e,16) +#define SD_GPT_USR_PARISC_VERITY_SIG SD_ID128_MAKE(45,0d,d7,d1,32,24,45,ec,9c,f2,a4,3a,34,6d,71,ee) +#define SD_GPT_USR_PPC64_LE_VERITY_SIG SD_ID128_MAKE(c8,bf,bd,1e,26,8e,45,21,8b,ba,bf,31,4c,39,95,57) +#define SD_GPT_USR_PPC64_VERITY_SIG SD_ID128_MAKE(0b,88,88,63,d7,f8,4d,9e,97,66,23,9f,ce,4d,58,af) +#define SD_GPT_USR_PPC_VERITY_SIG SD_ID128_MAKE(70,07,89,1d,d3,71,4a,80,86,a4,5c,b8,75,b9,30,2e) +#define SD_GPT_USR_RISCV32_VERITY_SIG SD_ID128_MAKE(c3,83,6a,13,31,37,45,ba,b5,83,b1,6c,50,fe,5e,b4) +#define SD_GPT_USR_RISCV64_VERITY_SIG SD_ID128_MAKE(d2,f9,00,0a,7a,18,45,3f,b5,cd,4d,32,f7,7a,7b,32) +#define SD_GPT_USR_S390_VERITY_SIG SD_ID128_MAKE(17,44,0e,4f,a8,d0,46,7f,a4,6e,39,12,ae,6e,f2,c5) +#define SD_GPT_USR_S390X_VERITY_SIG SD_ID128_MAKE(3f,32,48,16,66,7b,46,ae,86,ee,9b,0c,0c,6c,11,b4) +#define SD_GPT_USR_TILEGX_VERITY_SIG SD_ID128_MAKE(4e,de,75,e2,6c,cc,4c,c8,b9,c7,70,33,4b,08,75,10) +#define SD_GPT_USR_X86_64_VERITY_SIG SD_ID128_MAKE(e7,bb,33,fb,06,cf,4e,81,82,73,e5,43,b4,13,e2,e2) +#define SD_GPT_USR_X86_VERITY_SIG SD_ID128_MAKE(97,4a,71,c0,de,41,43,c3,be,5d,5c,5c,cd,1a,d2,c0) + +#define SD_GPT_ESP SD_ID128_MAKE(c1,2a,73,28,f8,1f,11,d2,ba,4b,00,a0,c9,3e,c9,3b) +#define SD_GPT_ESP_STR SD_ID128_MAKE_UUID_STR(c1,2a,73,28,f8,1f,11,d2,ba,4b,00,a0,c9,3e,c9,3b) +#define SD_GPT_XBOOTLDR SD_ID128_MAKE(bc,13,c2,ff,59,e6,42,62,a3,52,b2,75,fd,6f,71,72) +#define SD_GPT_XBOOTLDR_STR SD_ID128_MAKE_UUID_STR(bc,13,c2,ff,59,e6,42,62,a3,52,b2,75,fd,6f,71,72) +#define SD_GPT_SWAP SD_ID128_MAKE(06,57,fd,6d,a4,ab,43,c4,84,e5,09,33,c8,4b,4f,4f) +#define SD_GPT_SWAP_STR SD_ID128_MAKE_UUID_STR(06,57,fd,6d,a4,ab,43,c4,84,e5,09,33,c8,4b,4f,4f) +#define SD_GPT_HOME SD_ID128_MAKE(93,3a,c7,e1,2e,b4,4f,13,b8,44,0e,14,e2,ae,f9,15) +#define SD_GPT_HOME_STR SD_ID128_MAKE_UUID_STR(93,3a,c7,e1,2e,b4,4f,13,b8,44,0e,14,e2,ae,f9,15) +#define SD_GPT_SRV SD_ID128_MAKE(3b,8f,84,25,20,e0,4f,3b,90,7f,1a,25,a7,6f,98,e8) +#define SD_GPT_SRV_STR SD_ID128_MAKE_UUID_STR(3b,8f,84,25,20,e0,4f,3b,90,7f,1a,25,a7,6f,98,e8) +#define SD_GPT_VAR SD_ID128_MAKE(4d,21,b0,16,b5,34,45,c2,a9,fb,5c,16,e0,91,fd,2d) +#define SD_GPT_VAR_STR SD_ID128_MAKE_UUID_STR(4d,21,b0,16,b5,34,45,c2,a9,fb,5c,16,e0,91,fd,2d) +#define SD_GPT_TMP SD_ID128_MAKE(7e,c6,f5,57,3b,c5,4a,ca,b2,93,16,ef,5d,f6,39,d1) +#define SD_GPT_TMP_STR SD_ID128_MAKE_UUID_STR(7e,c6,f5,57,3b,c5,4a,ca,b2,93,16,ef,5d,f6,39,d1) +#define SD_GPT_USER_HOME SD_ID128_MAKE(77,3f,91,ef,66,d4,49,b5,bd,83,d6,83,bf,40,ad,16) +#define SD_GPT_USER_HOME_STR SD_ID128_MAKE_UUID_STR(77,3f,91,ef,66,d4,49,b5,bd,83,d6,83,bf,40,ad,16) +#define SD_GPT_LINUX_GENERIC SD_ID128_MAKE(0f,c6,3d,af,84,83,47,72,8e,79,3d,69,d8,47,7d,e4) +#define SD_GPT_LINUX_GENERIC_STR SD_ID128_MAKE_UUID_STR(0f,c6,3d,af,84,83,47,72,8e,79,3d,69,d8,47,7d,e4) + +/* Maintain same order as above */ +#if defined(__alpha__) +# define SD_GPT_ROOT_NATIVE SD_GPT_ROOT_ALPHA +# define SD_GPT_ROOT_NATIVE_VERITY SD_GPT_ROOT_ALPHA_VERITY +# define SD_GPT_ROOT_NATIVE_VERITY_SIG SD_GPT_ROOT_ALPHA_VERITY_SIG +# define SD_GPT_USR_NATIVE SD_GPT_USR_ALPHA +# define SD_GPT_USR_NATIVE_VERITY SD_GPT_USR_ALPHA_VERITY +# define SD_GPT_USR_NATIVE_VERITY_SIG SD_GPT_USR_ALPHA_VERITY_SIG + +#elif defined(__arc__) +# define SD_GPT_ROOT_NATIVE SD_GPT_ROOT_ARC +# define SD_GPT_ROOT_NATIVE_VERITY SD_GPT_ROOT_ARC_VERITY +# define SD_GPT_ROOT_NATIVE_VERITY_SIG SD_GPT_ROOT_ARC_VERITY_SIG +# define SD_GPT_USR_NATIVE SD_GPT_USR_ARC +# define SD_GPT_USR_NATIVE_VERITY SD_GPT_USR_ARC_VERITY +# define SD_GPT_USR_NATIVE_VERITY_SIG SD_GPT_USR_ARC_VERITY_SIG + +#elif defined(__aarch64__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ +# define SD_GPT_ROOT_NATIVE SD_GPT_ROOT_ARM64 +# define SD_GPT_ROOT_SECONDARY SD_GPT_ROOT_ARM +# define SD_GPT_ROOT_NATIVE_VERITY SD_GPT_ROOT_ARM64_VERITY +# define SD_GPT_ROOT_SECONDARY_VERITY SD_GPT_ROOT_ARM_VERITY +# define SD_GPT_ROOT_NATIVE_VERITY_SIG SD_GPT_ROOT_ARM64_VERITY_SIG +# define SD_GPT_ROOT_SECONDARY_VERITY_SIG SD_GPT_ROOT_ARM_VERITY_SIG +# define SD_GPT_USR_NATIVE SD_GPT_USR_ARM64 +# define SD_GPT_USR_SECONDARY SD_GPT_USR_ARM +# define SD_GPT_USR_NATIVE_VERITY SD_GPT_USR_ARM64_VERITY +# define SD_GPT_USR_SECONDARY_VERITY SD_GPT_USR_ARM_VERITY +# define SD_GPT_USR_NATIVE_VERITY_SIG SD_GPT_USR_ARM64_VERITY_SIG +# define SD_GPT_USR_SECONDARY_VERITY_SIG SD_GPT_USR_ARM_VERITY_SIG +#elif defined(__arm__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ +# define SD_GPT_ROOT_NATIVE SD_GPT_ROOT_ARM +# define SD_GPT_ROOT_NATIVE_VERITY SD_GPT_ROOT_ARM_VERITY +# define SD_GPT_ROOT_NATIVE_VERITY_SIG SD_GPT_ROOT_ARM_VERITY_SIG +# define SD_GPT_USR_NATIVE SD_GPT_USR_ARM +# define SD_GPT_USR_NATIVE_VERITY SD_GPT_USR_ARM_VERITY +# define SD_GPT_USR_NATIVE_VERITY_SIG SD_GPT_USR_ARM_VERITY_SIG + +#elif defined(__ia64__) +# define SD_GPT_ROOT_NATIVE SD_GPT_ROOT_IA64 +# define SD_GPT_ROOT_NATIVE_VERITY SD_GPT_ROOT_IA64_VERITY +# define SD_GPT_ROOT_NATIVE_VERITY_SIG SD_GPT_ROOT_IA64_VERITY_SIG +# define SD_GPT_USR_NATIVE SD_GPT_USR_IA64 +# define SD_GPT_USR_NATIVE_VERITY SD_GPT_USR_IA64_VERITY +# define SD_GPT_USR_NATIVE_VERITY_SIG SD_GPT_USR_IA64_VERITY_SIG + +#elif defined(__loongarch_lp64) +# define SD_GPT_ROOT_NATIVE SD_GPT_ROOT_LOONGARCH64 +# define SD_GPT_ROOT_NATIVE_VERITY SD_GPT_ROOT_LOONGARCH64_VERITY +# define SD_GPT_ROOT_NATIVE_VERITY_SIG SD_GPT_ROOT_LOONGARCH64_VERITY_SIG +# define SD_GPT_USR_NATIVE SD_GPT_USR_LOONGARCH64 +# define SD_GPT_USR_NATIVE_VERITY SD_GPT_USR_LOONGARCH64_VERITY +# define SD_GPT_USR_NATIVE_VERITY_SIG SD_GPT_USR_LOONGARCH64_VERITY_SIG + +#elif defined(__mips__) && !defined(__mips64) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ +# define SD_GPT_ROOT_NATIVE SD_GPT_ROOT_MIPS +# define SD_GPT_ROOT_NATIVE_VERITY SD_GPT_ROOT_MIPS_VERITY +# define SD_GPT_ROOT_NATIVE_VERITY_SIG SD_GPT_ROOT_MIPS_VERITY_SIG +# define SD_GPT_USR_NATIVE SD_GPT_USR_MIPS +# define SD_GPT_USR_NATIVE_VERITY SD_GPT_USR_MIPS_VERITY +# define SD_GPT_USR_NATIVE_VERITY_SIG SD_GPT_USR_MIPS_VERITY_SIG +#elif defined(__mips64) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ +# define SD_GPT_ROOT_NATIVE SD_GPT_ROOT_MIPS64 +# define SD_GPT_ROOT_NATIVE_VERITY SD_GPT_ROOT_MIPS64_VERITY +# define SD_GPT_ROOT_NATIVE_VERITY_SIG SD_GPT_ROOT_MIPS64_VERITY_SIG +# define SD_GPT_USR_NATIVE SD_GPT_USR_MIPS64 +# define SD_GPT_USR_NATIVE_VERITY SD_GPT_USR_MIPS64_VERITY +# define SD_GPT_USR_NATIVE_VERITY_SIG SD_GPT_USR_MIPS64_VERITY_SIG + +#elif defined(__mips__) && !defined(__mips64) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ +# define SD_GPT_ROOT_NATIVE SD_GPT_ROOT_MIPS_LE +# define SD_GPT_ROOT_NATIVE_VERITY SD_GPT_ROOT_MIPS_LE_VERITY +# define SD_GPT_ROOT_NATIVE_VERITY_SIG SD_GPT_ROOT_MIPS_LE_VERITY_SIG +# define SD_GPT_USR_NATIVE SD_GPT_USR_MIPS_LE +# define SD_GPT_USR_NATIVE_VERITY SD_GPT_USR_MIPS_LE_VERITY +# define SD_GPT_USR_NATIVE_VERITY_SIG SD_GPT_USR_MIPS_LE_VERITY_SIG +#elif defined(__mips64) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ +# define SD_GPT_ROOT_NATIVE SD_GPT_ROOT_MIPS64_LE +# define SD_GPT_ROOT_NATIVE_VERITY SD_GPT_ROOT_MIPS64_LE_VERITY +# define SD_GPT_ROOT_NATIVE_VERITY_SIG SD_GPT_ROOT_MIPS64_LE_VERITY_SIG +# define SD_GPT_USR_NATIVE SD_GPT_USR_MIPS64_LE +# define SD_GPT_USR_NATIVE_VERITY SD_GPT_USR_MIPS64_LE_VERITY +# define SD_GPT_USR_NATIVE_VERITY_SIG SD_GPT_USR_MIPS64_LE_VERITY_SIG + +#elif defined(__parisc__) +# define SD_GPT_ROOT_NATIVE SD_GPT_ROOT_PARISC +# define SD_GPT_ROOT_NATIVE_VERITY SD_GPT_ROOT_PARISC_VERITY +# define SD_GPT_ROOT_NATIVE_VERITY_SIG SD_GPT_ROOT_PARISC_VERITY_SIG +# define SD_GPT_USR_NATIVE SD_GPT_USR_PARISC +# define SD_GPT_USR_NATIVE_VERITY SD_GPT_USR_PARISC_VERITY +# define SD_GPT_USR_NATIVE_VERITY_SIG SD_GPT_USR_PARISC_VERITY_SIG + +#elif defined(__powerpc__) && defined(__PPC64__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ +# define SD_GPT_ROOT_NATIVE SD_GPT_ROOT_PPC64_LE +# define SD_GPT_ROOT_NATIVE_VERITY SD_GPT_ROOT_PPC64_LE_VERITY +# define SD_GPT_ROOT_NATIVE_VERITY_SIG SD_GPT_ROOT_PPC64_LE_VERITY_SIG +# define SD_GPT_USR_NATIVE SD_GPT_USR_PPC64_LE +# define SD_GPT_USR_NATIVE_VERITY SD_GPT_USR_PPC64_LE_VERITY +# define SD_GPT_USR_NATIVE_VERITY_SIG SD_GPT_USR_PPC64_LE_VERITY_SIG +#elif defined(__powerpc__) && defined(__powerpc64__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ +# define SD_GPT_ROOT_NATIVE SD_GPT_ROOT_PPC64 +# define SD_GPT_ROOT_NATIVE_VERITY SD_GPT_ROOT_PPC64_VERITY +# define SD_GPT_ROOT_NATIVE_VERITY_SIG SD_GPT_ROOT_PPC64_VERITY_SIG +# define SD_GPT_USR_NATIVE SD_GPT_USR_PPC64 +# define SD_GPT_USR_NATIVE_VERITY SD_GPT_USR_PPC64_VERITY +# define SD_GPT_USR_NATIVE_VERITY_SIG SD_GPT_USR_PPC64_VERITY_SIG +#elif defined(__powerpc__) +# define SD_GPT_ROOT_NATIVE SD_GPT_ROOT_PPC +# define SD_GPT_ROOT_NATIVE_VERITY SD_GPT_ROOT_PPC_VERITY +# define SD_GPT_ROOT_NATIVE_VERITY_SIG SD_GPT_ROOT_PPC_VERITY_SIG +# define SD_GPT_USR_NATIVE SD_GPT_USR_PPC +# define SD_GPT_USR_NATIVE_VERITY SD_GPT_USR_PPC_VERITY +# define SD_GPT_USR_NATIVE_VERITY_SIG SD_GPT_USR_PPC_VERITY_SIG + +#elif defined(__riscv) && __riscv_xlen == 32 +# define SD_GPT_ROOT_NATIVE SD_GPT_ROOT_RISCV32 +# define SD_GPT_ROOT_NATIVE_VERITY SD_GPT_ROOT_RISCV32_VERITY +# define SD_GPT_ROOT_NATIVE_VERITY_SIG SD_GPT_ROOT_RISCV32_VERITY_SIG +# define SD_GPT_USR_NATIVE SD_GPT_USR_RISCV32 +# define SD_GPT_USR_NATIVE_VERITY SD_GPT_USR_RISCV32_VERITY +# define SD_GPT_USR_NATIVE_VERITY_SIG SD_GPT_USR_RISCV32_VERITY_SIG +#elif defined(__riscv) && __riscv_xlen == 64 +# define SD_GPT_ROOT_NATIVE SD_GPT_ROOT_RISCV64 +# define SD_GPT_ROOT_NATIVE_VERITY SD_GPT_ROOT_RISCV64_VERITY +# define SD_GPT_ROOT_NATIVE_VERITY_SIG SD_GPT_ROOT_RISCV64_VERITY_SIG +# define SD_GPT_USR_NATIVE SD_GPT_USR_RISCV64 +# define SD_GPT_USR_NATIVE_VERITY SD_GPT_USR_RISCV64_VERITY +# define SD_GPT_USR_NATIVE_VERITY_SIG SD_GPT_USR_RISCV64_VERITY_SIG + +#elif defined(__s390__) && !defined(__s390x__) +# define SD_GPT_ROOT_NATIVE SD_GPT_ROOT_S390 +# define SD_GPT_ROOT_NATIVE_VERITY SD_GPT_ROOT_S390_VERITY +# define SD_GPT_ROOT_NATIVE_VERITY_SIG SD_GPT_ROOT_S390_VERITY_SIG +# define SD_GPT_USR_NATIVE SD_GPT_USR_S390 +# define SD_GPT_USR_NATIVE_VERITY SD_GPT_USR_S390_VERITY +# define SD_GPT_USR_NATIVE_VERITY_SIG SD_GPT_USR_S390_VERITY_SIG + +#elif defined(__s390x__) +# define SD_GPT_ROOT_NATIVE SD_GPT_ROOT_S390X +# define SD_GPT_ROOT_NATIVE_VERITY SD_GPT_ROOT_S390X_VERITY +# define SD_GPT_ROOT_NATIVE_VERITY_SIG SD_GPT_ROOT_S390X_VERITY_SIG +# define SD_GPT_USR_NATIVE SD_GPT_USR_S390X +# define SD_GPT_USR_NATIVE_VERITY SD_GPT_USR_S390X_VERITY +# define SD_GPT_USR_NATIVE_VERITY_SIG SD_GPT_USR_S390X_VERITY_SIG + +#elif defined(__tilegx__) +# define SD_GPT_ROOT_NATIVE SD_GPT_ROOT_TILEGX +# define SD_GPT_ROOT_NATIVE_VERITY SD_GPT_ROOT_TILEGX_VERITY +# define SD_GPT_ROOT_NATIVE_VERITY_SIG SD_GPT_ROOT_TILEGX_VERITY_SIG +# define SD_GPT_USR_NATIVE SD_GPT_USR_TILEGX +# define SD_GPT_USR_NATIVE_VERITY SD_GPT_USR_TILEGX_VERITY +# define SD_GPT_USR_NATIVE_VERITY_SIG SD_GPT_USR_TILEGX_VERITY_SIG + +#elif defined(__x86_64__) +# define SD_GPT_ROOT_NATIVE SD_GPT_ROOT_X86_64 +# define SD_GPT_ROOT_SECONDARY SD_GPT_ROOT_X86 +# define SD_GPT_ROOT_NATIVE_VERITY SD_GPT_ROOT_X86_64_VERITY +# define SD_GPT_ROOT_SECONDARY_VERITY SD_GPT_ROOT_X86_VERITY +# define SD_GPT_ROOT_NATIVE_VERITY_SIG SD_GPT_ROOT_X86_64_VERITY_SIG +# define SD_GPT_ROOT_SECONDARY_VERITY_SIG SD_GPT_ROOT_X86_VERITY_SIG +# define SD_GPT_USR_NATIVE SD_GPT_USR_X86_64 +# define SD_GPT_USR_SECONDARY SD_GPT_USR_X86 +# define SD_GPT_USR_NATIVE_VERITY SD_GPT_USR_X86_64_VERITY +# define SD_GPT_USR_SECONDARY_VERITY SD_GPT_USR_X86_VERITY +# define SD_GPT_USR_NATIVE_VERITY_SIG SD_GPT_USR_X86_64_VERITY_SIG +# define SD_GPT_USR_SECONDARY_VERITY_SIG SD_GPT_USR_X86_VERITY_SIG +#elif defined(__i386__) +# define SD_GPT_ROOT_NATIVE SD_GPT_ROOT_X86 +# define SD_GPT_ROOT_NATIVE_VERITY SD_GPT_ROOT_X86_VERITY +# define SD_GPT_ROOT_NATIVE_VERITY_SIG SD_GPT_ROOT_X86_VERITY_SIG +# define SD_GPT_USR_NATIVE SD_GPT_USR_X86 +# define SD_GPT_USR_NATIVE_VERITY SD_GPT_USR_X86_VERITY +# define SD_GPT_USR_NATIVE_VERITY_SIG SD_GPT_USR_X86_VERITY_SIG +#endif + +/* Partition attributes defined by the UEFI specification. */ +#define SD_GPT_FLAG_REQUIRED_PARTITION (UINT64_C(1) << 0) +#define SD_GPT_FLAG_NO_BLOCK_IO_PROTOCOL (UINT64_C(1) << 1) +#define SD_GPT_FLAG_LEGACY_BIOS_BOOTABLE (UINT64_C(1) << 2) + +/* Flags we recognize on the root, usr, xbootldr, swap, home, srv, var, tmp partitions when doing + * auto-discovery. + * + * The first two happen to be identical to what Microsoft defines for its own Basic Data Partitions + * in "winioctl.h": GPT_BASIC_DATA_ATTRIBUTE_READ_ONLY, GPT_BASIC_DATA_ATTRIBUTE_NO_DRIVE_LETTER. + */ +#define SD_GPT_FLAG_READ_ONLY (UINT64_C(1) << 60) +#define SD_GPT_FLAG_NO_AUTO (UINT64_C(1) << 63) +#define SD_GPT_FLAG_GROWFS (UINT64_C(1) << 59) + +_SD_END_DECLARATIONS; + +#endif diff --git a/thirdparty/systemd/include/systemd/sd-hwdb.h b/thirdparty/systemd/include/systemd/sd-hwdb.h new file mode 100644 index 000000000..ff880f15d --- /dev/null +++ b/thirdparty/systemd/include/systemd/sd-hwdb.h @@ -0,0 +1,46 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#ifndef foosdhwdbhfoo +#define foosdhwdbhfoo + +/*** + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include "_sd-common.h" + +_SD_BEGIN_DECLARATIONS; + +typedef struct sd_hwdb sd_hwdb; + +sd_hwdb *sd_hwdb_ref(sd_hwdb *hwdb); +sd_hwdb *sd_hwdb_unref(sd_hwdb *hwdb); + +int sd_hwdb_new(sd_hwdb **ret); +int sd_hwdb_new_from_path(const char *path, sd_hwdb **ret); + +int sd_hwdb_get(sd_hwdb *hwdb, const char *modalias, const char *key, const char **value); + +int sd_hwdb_seek(sd_hwdb *hwdb, const char *modalias); +int sd_hwdb_enumerate(sd_hwdb *hwdb, const char **key, const char **value); + +/* the inverse condition avoids ambiguity of dangling 'else' after the macro */ +#define SD_HWDB_FOREACH_PROPERTY(hwdb, modalias, key, value) \ + if (sd_hwdb_seek(hwdb, modalias) < 0) { } \ + else while (sd_hwdb_enumerate(hwdb, &(key), &(value)) > 0) + +_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_hwdb, sd_hwdb_unref); + +_SD_END_DECLARATIONS; + +#endif diff --git a/thirdparty/systemd/include/systemd/sd-id128.h b/thirdparty/systemd/include/systemd/sd-id128.h new file mode 100644 index 000000000..a9210526b --- /dev/null +++ b/thirdparty/systemd/include/systemd/sd-id128.h @@ -0,0 +1,165 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#ifndef foosdid128hfoo +#define foosdid128hfoo + +/*** + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include + +#include "_sd-common.h" + +_SD_BEGIN_DECLARATIONS; + +/* 128-bit ID APIs. See sd-id128(3) for more information. */ + +typedef union sd_id128 sd_id128_t; + +union sd_id128 { + uint8_t bytes[16]; + uint64_t qwords[2]; +}; + +#define SD_ID128_STRING_MAX 33U +#define SD_ID128_UUID_STRING_MAX 37U + +char *sd_id128_to_string(sd_id128_t id, char s[_SD_ARRAY_STATIC SD_ID128_STRING_MAX]); +char *sd_id128_to_uuid_string(sd_id128_t id, char s[_SD_ARRAY_STATIC SD_ID128_UUID_STRING_MAX]); +int sd_id128_from_string(const char *s, sd_id128_t *ret); + +#define SD_ID128_TO_STRING(id) sd_id128_to_string((id), (char[SD_ID128_STRING_MAX]) {}) +#define SD_ID128_TO_UUID_STRING(id) sd_id128_to_uuid_string((id), (char[SD_ID128_UUID_STRING_MAX]) {}) + +int sd_id128_randomize(sd_id128_t *ret); + +int sd_id128_get_machine(sd_id128_t *ret); +int sd_id128_get_boot(sd_id128_t *ret); +int sd_id128_get_invocation(sd_id128_t *ret); + +int sd_id128_get_app_specific(sd_id128_t base, sd_id128_t app_id, sd_id128_t *ret); +int sd_id128_get_machine_app_specific(sd_id128_t app_id, sd_id128_t *ret); +int sd_id128_get_boot_app_specific(sd_id128_t app_id, sd_id128_t *ret); +int sd_id128_get_invocation_app_specific(sd_id128_t app_id, sd_id128_t *ret); + +#define SD_ID128_ARRAY(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15) \ + { .bytes = { 0x##v0, 0x##v1, 0x##v2, 0x##v3, 0x##v4, 0x##v5, 0x##v6, 0x##v7, \ + 0x##v8, 0x##v9, 0x##v10, 0x##v11, 0x##v12, 0x##v13, 0x##v14, 0x##v15 }} + +#define SD_ID128_MAKE(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15) \ + ((const sd_id128_t) SD_ID128_ARRAY(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15)) + +/* Note that SD_ID128_FORMAT_VAL will evaluate the passed argument 16 + * times. It is hence not a good idea to call this macro with an + * expensive function as parameter or an expression with side + * effects */ + +#define SD_ID128_FORMAT_STR "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x" +#define SD_ID128_FORMAT_VAL(x) (x).bytes[0], (x).bytes[1], (x).bytes[2], (x).bytes[3], (x).bytes[4], (x).bytes[5], (x).bytes[6], (x).bytes[7], (x).bytes[8], (x).bytes[9], (x).bytes[10], (x).bytes[11], (x).bytes[12], (x).bytes[13], (x).bytes[14], (x).bytes[15] + +/* Like SD_ID128_FORMAT_STR, but formats as UUID, not in plain format (Strictly Big Endian byte order, + * i.e. treats everything as RFC4122 Variant 1 UUIDs, even if variant says otherwise, but matching other + * Linux userspace behaviour.) */ +#define SD_ID128_UUID_FORMAT_STR "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x" + +#define SD_ID128_CONST_STR(x) \ + ((const char[SD_ID128_STRING_MAX]) { \ + ((x).bytes[0] >> 4) >= 10 ? 'a' + ((x).bytes[0] >> 4) - 10 : '0' + ((x).bytes[0] >> 4), \ + ((x).bytes[0] & 15) >= 10 ? 'a' + ((x).bytes[0] & 15) - 10 : '0' + ((x).bytes[0] & 15), \ + ((x).bytes[1] >> 4) >= 10 ? 'a' + ((x).bytes[1] >> 4) - 10 : '0' + ((x).bytes[1] >> 4), \ + ((x).bytes[1] & 15) >= 10 ? 'a' + ((x).bytes[1] & 15) - 10 : '0' + ((x).bytes[1] & 15), \ + ((x).bytes[2] >> 4) >= 10 ? 'a' + ((x).bytes[2] >> 4) - 10 : '0' + ((x).bytes[2] >> 4), \ + ((x).bytes[2] & 15) >= 10 ? 'a' + ((x).bytes[2] & 15) - 10 : '0' + ((x).bytes[2] & 15), \ + ((x).bytes[3] >> 4) >= 10 ? 'a' + ((x).bytes[3] >> 4) - 10 : '0' + ((x).bytes[3] >> 4), \ + ((x).bytes[3] & 15) >= 10 ? 'a' + ((x).bytes[3] & 15) - 10 : '0' + ((x).bytes[3] & 15), \ + ((x).bytes[4] >> 4) >= 10 ? 'a' + ((x).bytes[4] >> 4) - 10 : '0' + ((x).bytes[4] >> 4), \ + ((x).bytes[4] & 15) >= 10 ? 'a' + ((x).bytes[4] & 15) - 10 : '0' + ((x).bytes[4] & 15), \ + ((x).bytes[5] >> 4) >= 10 ? 'a' + ((x).bytes[5] >> 4) - 10 : '0' + ((x).bytes[5] >> 4), \ + ((x).bytes[5] & 15) >= 10 ? 'a' + ((x).bytes[5] & 15) - 10 : '0' + ((x).bytes[5] & 15), \ + ((x).bytes[6] >> 4) >= 10 ? 'a' + ((x).bytes[6] >> 4) - 10 : '0' + ((x).bytes[6] >> 4), \ + ((x).bytes[6] & 15) >= 10 ? 'a' + ((x).bytes[6] & 15) - 10 : '0' + ((x).bytes[6] & 15), \ + ((x).bytes[7] >> 4) >= 10 ? 'a' + ((x).bytes[7] >> 4) - 10 : '0' + ((x).bytes[7] >> 4), \ + ((x).bytes[7] & 15) >= 10 ? 'a' + ((x).bytes[7] & 15) - 10 : '0' + ((x).bytes[7] & 15), \ + ((x).bytes[8] >> 4) >= 10 ? 'a' + ((x).bytes[8] >> 4) - 10 : '0' + ((x).bytes[8] >> 4), \ + ((x).bytes[8] & 15) >= 10 ? 'a' + ((x).bytes[8] & 15) - 10 : '0' + ((x).bytes[8] & 15), \ + ((x).bytes[9] >> 4) >= 10 ? 'a' + ((x).bytes[9] >> 4) - 10 : '0' + ((x).bytes[9] >> 4), \ + ((x).bytes[9] & 15) >= 10 ? 'a' + ((x).bytes[9] & 15) - 10 : '0' + ((x).bytes[9] & 15), \ + ((x).bytes[10] >> 4) >= 10 ? 'a' + ((x).bytes[10] >> 4) - 10 : '0' + ((x).bytes[10] >> 4), \ + ((x).bytes[10] & 15) >= 10 ? 'a' + ((x).bytes[10] & 15) - 10 : '0' + ((x).bytes[10] & 15), \ + ((x).bytes[11] >> 4) >= 10 ? 'a' + ((x).bytes[11] >> 4) - 10 : '0' + ((x).bytes[11] >> 4), \ + ((x).bytes[11] & 15) >= 10 ? 'a' + ((x).bytes[11] & 15) - 10 : '0' + ((x).bytes[11] & 15), \ + ((x).bytes[12] >> 4) >= 10 ? 'a' + ((x).bytes[12] >> 4) - 10 : '0' + ((x).bytes[12] >> 4), \ + ((x).bytes[12] & 15) >= 10 ? 'a' + ((x).bytes[12] & 15) - 10 : '0' + ((x).bytes[12] & 15), \ + ((x).bytes[13] >> 4) >= 10 ? 'a' + ((x).bytes[13] >> 4) - 10 : '0' + ((x).bytes[13] >> 4), \ + ((x).bytes[13] & 15) >= 10 ? 'a' + ((x).bytes[13] & 15) - 10 : '0' + ((x).bytes[13] & 15), \ + ((x).bytes[14] >> 4) >= 10 ? 'a' + ((x).bytes[14] >> 4) - 10 : '0' + ((x).bytes[14] >> 4), \ + ((x).bytes[14] & 15) >= 10 ? 'a' + ((x).bytes[14] & 15) - 10 : '0' + ((x).bytes[14] & 15), \ + ((x).bytes[15] >> 4) >= 10 ? 'a' + ((x).bytes[15] >> 4) - 10 : '0' + ((x).bytes[15] >> 4), \ + ((x).bytes[15] & 15) >= 10 ? 'a' + ((x).bytes[15] & 15) - 10 : '0' + ((x).bytes[15] & 15), \ + 0 }) + +#define SD_ID128_MAKE_STR(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p) \ + #a #b #c #d #e #f #g #h #i #j #k #l #m #n #o #p + +#define SD_ID128_MAKE_UUID_STR(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p) \ + #a #b #c #d "-" #e #f "-" #g #h "-" #i #j "-" #k #l #m #n #o #p + +_sd_pure_ static __inline__ int sd_id128_equal(sd_id128_t a, sd_id128_t b) { + return a.qwords[0] == b.qwords[0] && a.qwords[1] == b.qwords[1]; +} + +int sd_id128_string_equal(const char *s, sd_id128_t id); + +_sd_pure_ static __inline__ int sd_id128_is_null(sd_id128_t a) { + return a.qwords[0] == 0 && a.qwords[1] == 0; +} + +_sd_pure_ static __inline__ int sd_id128_is_allf(sd_id128_t a) { + return a.qwords[0] == UINT64_C(0xFFFFFFFFFFFFFFFF) && a.qwords[1] == UINT64_C(0xFFFFFFFFFFFFFFFF); +} + +#define SD_ID128_NULL ((const sd_id128_t) { .qwords = { 0, 0 }}) +#define SD_ID128_ALLF ((const sd_id128_t) { .qwords = { UINT64_C(0xFFFFFFFFFFFFFFFF), UINT64_C(0xFFFFFFFFFFFFFFFF) }}) + +_sd_pure_ static __inline__ int sd_id128_in_setv(sd_id128_t a, va_list ap) { + for (;;) { + sd_id128_t b = va_arg(ap, sd_id128_t); + + if (sd_id128_is_null(b)) + return 0; + + if (sd_id128_equal(a, b)) + return 1; + } +} + +_sd_pure_ static __inline__ int sd_id128_in_set_sentinel(sd_id128_t a, ...) { + va_list ap; + int r; + + va_start(ap, a); + r = sd_id128_in_setv(a, ap); + va_end(ap); + + return r; +} + +#define sd_id128_in_set(a, ...) \ + sd_id128_in_set_sentinel(a, ##__VA_ARGS__, SD_ID128_NULL) + +_SD_END_DECLARATIONS; + +#endif diff --git a/thirdparty/systemd/include/systemd/sd-journal.h b/thirdparty/systemd/include/systemd/sd-journal.h new file mode 100644 index 000000000..7434051ce --- /dev/null +++ b/thirdparty/systemd/include/systemd/sd-journal.h @@ -0,0 +1,182 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#ifndef foosdjournalhfoo +#define foosdjournalhfoo + +/*** + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include + +#include "sd-id128.h" + +#include "_sd-common.h" + +/* Journal APIs. See sd-journal(3) for more information. */ + +_SD_BEGIN_DECLARATIONS; + +/* Write to daemon */ +int sd_journal_print(int priority, const char *format, ...) _sd_printf_(2, 3); +int sd_journal_printv(int priority, const char *format, va_list ap) _sd_printf_(2, 0); +int sd_journal_send(const char *format, ...) _sd_printf_(1, 0) _sd_sentinel_; +int sd_journal_sendv(const struct iovec *iov, int n); +int sd_journal_perror(const char *message); + +/* Used by the macros below. You probably don't want to call this directly. */ +int sd_journal_print_with_location(int priority, const char *file, const char *line, const char *func, const char *format, ...) _sd_printf_(5, 6); +int sd_journal_printv_with_location(int priority, const char *file, const char *line, const char *func, const char *format, va_list ap) _sd_printf_(5, 0); +int sd_journal_send_with_location(const char *file, const char *line, const char *func, const char *format, ...) _sd_printf_(4, 0) _sd_sentinel_; +int sd_journal_sendv_with_location(const char *file, const char *line, const char *func, const struct iovec *iov, int n); +int sd_journal_perror_with_location(const char *file, const char *line, const char *func, const char *message); + +/* implicitly add code location to messages sent, if this is enabled */ +#ifndef SD_JOURNAL_SUPPRESS_LOCATION + +#define sd_journal_print(priority, ...) sd_journal_print_with_location(priority, "CODE_FILE=" __FILE__, "CODE_LINE=" _SD_STRINGIFY(__LINE__), __func__, __VA_ARGS__) +#define sd_journal_printv(priority, format, ap) sd_journal_printv_with_location(priority, "CODE_FILE=" __FILE__, "CODE_LINE=" _SD_STRINGIFY(__LINE__), __func__, format, ap) +#define sd_journal_send(...) sd_journal_send_with_location("CODE_FILE=" __FILE__, "CODE_LINE=" _SD_STRINGIFY(__LINE__), __func__, __VA_ARGS__) +#define sd_journal_sendv(iovec, n) sd_journal_sendv_with_location("CODE_FILE=" __FILE__, "CODE_LINE=" _SD_STRINGIFY(__LINE__), __func__, iovec, n) +#define sd_journal_perror(message) sd_journal_perror_with_location("CODE_FILE=" __FILE__, "CODE_LINE=" _SD_STRINGIFY(__LINE__), __func__, message) + +#endif + +int sd_journal_stream_fd(const char *identifier, int priority, int level_prefix); +int sd_journal_stream_fd_with_namespace(const char *name_space, const char *identifier, int priority, int level_prefix); + +/* Browse journal stream */ + +typedef struct sd_journal sd_journal; + +/* Open flags */ +enum { + SD_JOURNAL_LOCAL_ONLY = 1 << 0, + SD_JOURNAL_RUNTIME_ONLY = 1 << 1, + SD_JOURNAL_SYSTEM = 1 << 2, + SD_JOURNAL_CURRENT_USER = 1 << 3, + SD_JOURNAL_OS_ROOT = 1 << 4, + SD_JOURNAL_ALL_NAMESPACES = 1 << 5, /* Show all namespaces, not just the default or specified one */ + SD_JOURNAL_INCLUDE_DEFAULT_NAMESPACE = 1 << 6, /* Show default namespace in addition to specified one */ + SD_JOURNAL_TAKE_DIRECTORY_FD = 1 << 7, /* sd_journal_open_directory_fd() will take ownership of the provided file descriptor. */ + SD_JOURNAL_ASSUME_IMMUTABLE = 1 << 8, /* Assume the opened journal files are immutable. Journal entries added later may be ignored. */ + + SD_JOURNAL_SYSTEM_ONLY _sd_deprecated_ = SD_JOURNAL_SYSTEM /* old name */ +}; + +/* Wakeup event types */ +enum { + SD_JOURNAL_NOP, + SD_JOURNAL_APPEND, + SD_JOURNAL_INVALIDATE +}; + +int sd_journal_open(sd_journal **ret, int flags); +int sd_journal_open_namespace(sd_journal **ret, const char *name_space, int flags); +int sd_journal_open_directory(sd_journal **ret, const char *path, int flags); +int sd_journal_open_directory_fd(sd_journal **ret, int fd, int flags); +int sd_journal_open_files(sd_journal **ret, const char **paths, int flags); +int sd_journal_open_files_fd(sd_journal **ret, int fds[], unsigned n_fds, int flags); +int sd_journal_open_container(sd_journal **ret, const char *machine, int flags) _sd_deprecated_; +void sd_journal_close(sd_journal *j); + +int sd_journal_previous(sd_journal *j); +int sd_journal_next(sd_journal *j); +int sd_journal_step_one(sd_journal *j, int advanced); + +int sd_journal_previous_skip(sd_journal *j, uint64_t skip); +int sd_journal_next_skip(sd_journal *j, uint64_t skip); + +int sd_journal_get_realtime_usec(sd_journal *j, uint64_t *ret); +int sd_journal_get_monotonic_usec(sd_journal *j, uint64_t *ret, sd_id128_t *ret_boot_id); +int sd_journal_get_seqnum(sd_journal *j, uint64_t *ret_seqnum, sd_id128_t *ret_seqnum_id); + +int sd_journal_set_data_threshold(sd_journal *j, size_t sz); +int sd_journal_get_data_threshold(sd_journal *j, size_t *sz); + +int sd_journal_get_data(sd_journal *j, const char *field, const void **data, size_t *l); +int sd_journal_enumerate_data(sd_journal *j, const void **data, size_t *l); +int sd_journal_enumerate_available_data(sd_journal *j, const void **data, size_t *l); +void sd_journal_restart_data(sd_journal *j); + +int sd_journal_add_match(sd_journal *j, const void *data, size_t size); +int sd_journal_add_disjunction(sd_journal *j); +int sd_journal_add_conjunction(sd_journal *j); +void sd_journal_flush_matches(sd_journal *j); + +int sd_journal_seek_head(sd_journal *j); +int sd_journal_seek_tail(sd_journal *j); +int sd_journal_seek_monotonic_usec(sd_journal *j, sd_id128_t boot_id, uint64_t usec); +int sd_journal_seek_realtime_usec(sd_journal *j, uint64_t usec); +int sd_journal_seek_cursor(sd_journal *j, const char *cursor); + +int sd_journal_get_cursor(sd_journal *j, char **cursor); +int sd_journal_test_cursor(sd_journal *j, const char *cursor); + +int sd_journal_get_cutoff_realtime_usec(sd_journal *j, uint64_t *from, uint64_t *to); +int sd_journal_get_cutoff_monotonic_usec(sd_journal *j, const sd_id128_t boot_id, uint64_t *from, uint64_t *to); + +int sd_journal_get_usage(sd_journal *j, uint64_t *bytes); + +int sd_journal_query_unique(sd_journal *j, const char *field); +int sd_journal_enumerate_unique(sd_journal *j, const void **data, size_t *l); +int sd_journal_enumerate_available_unique(sd_journal *j, const void **data, size_t *l); +void sd_journal_restart_unique(sd_journal *j); + +int sd_journal_enumerate_fields(sd_journal *j, const char **field); +void sd_journal_restart_fields(sd_journal *j); + +int sd_journal_get_fd(sd_journal *j); +int sd_journal_get_events(sd_journal *j); +int sd_journal_get_timeout(sd_journal *j, uint64_t *timeout_usec); +int sd_journal_process(sd_journal *j); +int sd_journal_wait(sd_journal *j, uint64_t timeout_usec); +int sd_journal_reliable_fd(sd_journal *j); + +int sd_journal_get_catalog(sd_journal *j, char **text); +int sd_journal_get_catalog_for_message_id(sd_id128_t id, char **text); + +int sd_journal_has_runtime_files(sd_journal *j); +int sd_journal_has_persistent_files(sd_journal *j); + +/* The inverse condition avoids ambiguity of dangling 'else' after the macro */ +#define SD_JOURNAL_FOREACH(j) \ + if (sd_journal_seek_head(j) < 0) { } \ + else while (sd_journal_next(j) > 0) + +/* The inverse condition avoids ambiguity of dangling 'else' after the macro */ +#define SD_JOURNAL_FOREACH_BACKWARDS(j) \ + if (sd_journal_seek_tail(j) < 0) { } \ + else while (sd_journal_previous(j) > 0) + +/* Iterate through all available data fields of the current journal entry */ +#define SD_JOURNAL_FOREACH_DATA(j, data, l) \ + for (sd_journal_restart_data(j); sd_journal_enumerate_available_data((j), &(data), &(l)) > 0; ) + +/* Iterate through all available values of a specific field */ +#define SD_JOURNAL_FOREACH_UNIQUE(j, data, l) \ + for (sd_journal_restart_unique(j); sd_journal_enumerate_available_unique((j), &(data), &(l)) > 0; ) + +/* Iterate through all known field names */ +#define SD_JOURNAL_FOREACH_FIELD(j, field) \ + for (sd_journal_restart_fields(j); sd_journal_enumerate_fields((j), &(field)) > 0; ) + +_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_journal, sd_journal_close); + +_SD_END_DECLARATIONS; + +#endif diff --git a/thirdparty/systemd/include/systemd/sd-login.h b/thirdparty/systemd/include/systemd/sd-login.h new file mode 100644 index 000000000..c84f2c06b --- /dev/null +++ b/thirdparty/systemd/include/systemd/sd-login.h @@ -0,0 +1,270 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#ifndef foosdloginhfoo +#define foosdloginhfoo + +/*** + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include + +#include "_sd-common.h" + +/* + * A few points: + * + * Instead of returning an empty string array or empty uid array, we + * may return NULL. + * + * Free the data the library returns with libc free(). String arrays + * are NULL terminated, and you need to free the array itself, in + * addition to the strings contained. + * + * We return error codes as negative errno, kernel-style. On success, we + * return 0 or positive. + * + * These functions access data in /proc, /sys/fs/cgroup, and /run. All + * of these are virtual file systems; therefore, accesses are + * relatively cheap. + * + * See sd-login(3) for more information. + */ + +_SD_BEGIN_DECLARATIONS; + +/* Get session from PID. Note that 'shared' processes of a user are + * not attached to a session, but only attached to a user. This will + * return an error for system processes and 'shared' processes of a + * user. */ +int sd_pid_get_session(pid_t pid, char **session); + +/* Get UID of the owner of the session of the PID (or in case the + * process is a 'shared' user process, the UID of that user is + * returned). This will not return the UID of the process, but rather + * the UID of the owner of the cgroup that the process is in. This will + * return an error for system processes. */ +int sd_pid_get_owner_uid(pid_t pid, uid_t *uid); + +/* Get systemd non-slice unit (i.e. service) name from PID, for system + * services. This will return an error for non-service processes. */ +int sd_pid_get_unit(pid_t pid, char **unit); + +/* Get systemd non-slice unit (i.e. service) name from PID, for user + * services. This will return an error for non-user-service + * processes. */ +int sd_pid_get_user_unit(pid_t pid, char **unit); + +/* Get slice name from PID. */ +int sd_pid_get_slice(pid_t pid, char **slice); + +/* Get user slice name from PID. */ +int sd_pid_get_user_slice(pid_t pid, char **slice); + +/* Get machine name from PID, for processes assigned to a VM or + * container. This will return an error for non-machine processes. */ +int sd_pid_get_machine_name(pid_t pid, char **machine); + +/* Get the control group from a PID, relative to the root of the + * hierarchy. */ +int sd_pid_get_cgroup(pid_t pid, char **cgroup); + +/* Equivalent to the corresponding sd_pid_get* functions, but take a + * PIDFD instead of a PID, to ensure there can be no possible PID + * recycle issues before/after the calls. */ +int sd_pidfd_get_session(int pidfd, char **session); +int sd_pidfd_get_owner_uid(int pidfd, uid_t *uid); +int sd_pidfd_get_unit(int pidfd, char **unit); +int sd_pidfd_get_user_unit(int pidfd, char **unit); +int sd_pidfd_get_slice(int pidfd, char **slice); +int sd_pidfd_get_user_slice(int pidfd, char **slice); +int sd_pidfd_get_machine_name(int pidfd, char **machine); +int sd_pidfd_get_cgroup(int pidfd, char **cgroup); + +/* Similar to sd_pid_get_session(), but retrieves data about the peer + * of a connected AF_UNIX socket */ +int sd_peer_get_session(int fd, char **session); + +/* Similar to sd_pid_get_owner_uid(), but retrieves data about the peer of + * a connected AF_UNIX socket */ +int sd_peer_get_owner_uid(int fd, uid_t *uid); + +/* Similar to sd_pid_get_unit(), but retrieves data about the peer of + * a connected AF_UNIX socket */ +int sd_peer_get_unit(int fd, char **unit); + +/* Similar to sd_pid_get_user_unit(), but retrieves data about the peer of + * a connected AF_UNIX socket */ +int sd_peer_get_user_unit(int fd, char **unit); + +/* Similar to sd_pid_get_slice(), but retrieves data about the peer of + * a connected AF_UNIX socket */ +int sd_peer_get_slice(int fd, char **slice); + +/* Similar to sd_pid_get_user_slice(), but retrieves data about the peer of + * a connected AF_UNIX socket */ +int sd_peer_get_user_slice(int fd, char **slice); + +/* Similar to sd_pid_get_machine_name(), but retrieves data about the + * peer of a connected AF_UNIX socket */ +int sd_peer_get_machine_name(int fd, char **machine); + +/* Similar to sd_pid_get_cgroup(), but retrieves data about the peer + * of a connected AF_UNIX socket. */ +int sd_peer_get_cgroup(int fd, char **cgroup); + +/* Get state from UID. Possible states: offline, lingering, online, active, closing */ +int sd_uid_get_state(uid_t uid, char **state); + +/* Return primary session of user, if there is any */ +int sd_uid_get_display(uid_t uid, char **session); + +/* Determine the login time of user */ +int sd_uid_get_login_time(uid_t uid, uint64_t *usec); + +/* Return 1 if UID has session on seat. If require_active is true, this will + * look for active sessions only. */ +int sd_uid_is_on_seat(uid_t uid, int require_active, const char *seat); + +/* Return sessions of user. If require_active is true, this will look for + * active sessions only. Returns the number of sessions. + * If sessions is NULL, this will just return the number of sessions. */ +int sd_uid_get_sessions(uid_t uid, int require_active, char ***sessions); + +/* Return seats of user is on. If require_active is true, this will look for + * active seats only. Returns the number of seats. + * If seats is NULL, this will just return the number of seats. */ +int sd_uid_get_seats(uid_t uid, int require_active, char ***seats); + +/* Return 1 if the session is active. */ +int sd_session_is_active(const char *session); + +/* Return 1 if the session is remote. */ +int sd_session_is_remote(const char *session); + +/* Get state from session. Possible states: online, active, closing. + * This function is a more generic version of sd_session_is_active(). */ +int sd_session_get_state(const char *session, char **state); + +/* Determine user ID of session */ +int sd_session_get_uid(const char *session, uid_t *uid); + +/* Determine username of session */ +int sd_session_get_username(const char *session, char **username); + +/* Determine seat of session */ +int sd_session_get_seat(const char *session, char **seat); + +/* Determine the start time of session */ +int sd_session_get_start_time(const char *session, uint64_t *usec); + +/* Determine the (PAM) service name this session was registered by. */ +int sd_session_get_service(const char *session, char **service); + +/* Determine the type of this session, i.e. one of "tty", "x11", "wayland", "mir" or "unspecified". */ +int sd_session_get_type(const char *session, char **type); + +/* Determine the class of this session, i.e. one of "user", "greeter" or "lock-screen". */ +int sd_session_get_class(const char *session, char **clazz); + +/* Determine the desktop brand of this session, i.e. something like "GNOME", "KDE" or "systemd-console". */ +int sd_session_get_desktop(const char *session, char **desktop); + +/* Determine the X11 display of this session. */ +int sd_session_get_display(const char *session, char **display); + +/* Determine the leader process of this session. */ +int sd_session_get_leader(const char *session, pid_t *leader); + +/* Determine the remote host of this session. */ +int sd_session_get_remote_host(const char *session, char **remote_host); + +/* Determine the remote user of this session (if provided by PAM). */ +int sd_session_get_remote_user(const char *session, char **remote_user); + +/* Determine the TTY of this session. */ +int sd_session_get_tty(const char *session, char **display); + +/* Determine the VT number of this session. */ +int sd_session_get_vt(const char *session, unsigned *vtnr); + +/* Return active session and user of seat */ +int sd_seat_get_active(const char *seat, char **session, uid_t *uid); + +/* Return sessions and users on seat. Returns number of sessions. + * If sessions is NULL, this returns only the number of sessions. */ +int sd_seat_get_sessions( + const char *seat, + char ***ret_sessions, + uid_t **ret_uids, + unsigned *ret_n_uids); + +/* Return whether the seat is multi-session capable */ +int sd_seat_can_multi_session(const char *seat) _sd_deprecated_; + +/* Return whether the seat is TTY capable, i.e. suitable for showing console UIs */ +int sd_seat_can_tty(const char *seat); + +/* Return whether the seat is graphics capable, i.e. suitable for showing graphical UIs */ +int sd_seat_can_graphical(const char *seat); + +/* Return the class of machine */ +int sd_machine_get_class(const char *machine, char **clazz); + +/* Return the list if host-side network interface indices of a machine */ +int sd_machine_get_ifindices(const char *machine, int **ret_ifindices); + +/* Get all seats, store in *seats. Returns the number of seats. If + * seats is NULL, this only returns the number of seats. */ +int sd_get_seats(char ***seats); + +/* Get all sessions, store in *sessions. Returns the number of + * sessions. If sessions is NULL, this only returns the number of sessions. */ +int sd_get_sessions(char ***sessions); + +/* Get all logged in users, store in *users. Returns the number of + * users. If users is NULL, this only returns the number of users. */ +int sd_get_uids(uid_t **users); + +/* Get all running virtual machines/containers */ +int sd_get_machine_names(char ***machines); + +/* Monitor object */ +typedef struct sd_login_monitor sd_login_monitor; + +/* Create a new monitor. Category must be NULL, "seat", "session", + * "uid", or "machine" to get monitor events for the specific category + * (or all). */ +int sd_login_monitor_new(const char *category, sd_login_monitor** ret); + +/* Destroys the passed monitor. Returns NULL. */ +sd_login_monitor* sd_login_monitor_unref(sd_login_monitor *m); + +/* Flushes the monitor */ +int sd_login_monitor_flush(sd_login_monitor *m); + +/* Get FD from monitor */ +int sd_login_monitor_get_fd(sd_login_monitor *m); + +/* Get poll() mask to monitor */ +int sd_login_monitor_get_events(sd_login_monitor *m); + +/* Get timeout for poll(), as usec value relative to CLOCK_MONOTONIC's epoch */ +int sd_login_monitor_get_timeout(sd_login_monitor *m, uint64_t *timeout_usec); + +_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_login_monitor, sd_login_monitor_unref); + +_SD_END_DECLARATIONS; + +#endif diff --git a/thirdparty/systemd/include/systemd/sd-messages.h b/thirdparty/systemd/include/systemd/sd-messages.h new file mode 100644 index 000000000..16e9986be --- /dev/null +++ b/thirdparty/systemd/include/systemd/sd-messages.h @@ -0,0 +1,280 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#ifndef foosdmessageshfoo +#define foosdmessageshfoo + +/*** + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include "sd-id128.h" + +#include "_sd-common.h" + +_SD_BEGIN_DECLARATIONS; + +/* Hey! If you add a new message here, you *must* also update the message catalog with an appropriate explanation */ + +/* And if you add a new ID here, make sure to generate a random one with "systemd-id128 new". Do not use any + * other IDs, and do not count them up manually. */ + +#define SD_MESSAGE_JOURNAL_START SD_ID128_MAKE(f7,73,79,a8,49,0b,40,8b,be,5f,69,40,50,5a,77,7b) +#define SD_MESSAGE_JOURNAL_START_STR SD_ID128_MAKE_STR(f7,73,79,a8,49,0b,40,8b,be,5f,69,40,50,5a,77,7b) +#define SD_MESSAGE_JOURNAL_STOP SD_ID128_MAKE(d9,3f,b3,c9,c2,4d,45,1a,97,ce,a6,15,ce,59,c0,0b) +#define SD_MESSAGE_JOURNAL_STOP_STR SD_ID128_MAKE_STR(d9,3f,b3,c9,c2,4d,45,1a,97,ce,a6,15,ce,59,c0,0b) +#define SD_MESSAGE_JOURNAL_DROPPED SD_ID128_MAKE(a5,96,d6,fe,7b,fa,49,94,82,8e,72,30,9e,95,d6,1e) +#define SD_MESSAGE_JOURNAL_DROPPED_STR SD_ID128_MAKE_STR(a5,96,d6,fe,7b,fa,49,94,82,8e,72,30,9e,95,d6,1e) +#define SD_MESSAGE_JOURNAL_MISSED SD_ID128_MAKE(e9,bf,28,e6,e8,34,48,1b,b6,f4,8f,54,8a,d1,36,06) +#define SD_MESSAGE_JOURNAL_MISSED_STR SD_ID128_MAKE_STR(e9,bf,28,e6,e8,34,48,1b,b6,f4,8f,54,8a,d1,36,06) +#define SD_MESSAGE_JOURNAL_USAGE SD_ID128_MAKE(ec,38,7f,57,7b,84,4b,8f,a9,48,f3,3c,ad,9a,75,e6) +#define SD_MESSAGE_JOURNAL_USAGE_STR SD_ID128_MAKE_STR(ec,38,7f,57,7b,84,4b,8f,a9,48,f3,3c,ad,9a,75,e6) + +#define SD_MESSAGE_COREDUMP SD_ID128_MAKE(fc,2e,22,bc,6e,e6,47,b6,b9,07,29,ab,34,a2,50,b1) +#define SD_MESSAGE_COREDUMP_STR SD_ID128_MAKE_STR(fc,2e,22,bc,6e,e6,47,b6,b9,07,29,ab,34,a2,50,b1) +#define SD_MESSAGE_TRUNCATED_CORE SD_ID128_MAKE(5a,ad,d8,e9,54,dc,4b,1a,8c,95,4d,63,fd,9e,11,37) +#define SD_MESSAGE_TRUNCATED_CORE_STR SD_ID128_MAKE_STR(5a,ad,d8,e9,54,dc,4b,1a,8c,95,4d,63,fd,9e,11,37) +#define SD_MESSAGE_BACKTRACE SD_ID128_MAKE(1f,4e,0a,44,a8,86,49,93,9a,ae,a3,4f,c6,da,8c,95) +#define SD_MESSAGE_BACKTRACE_STR SD_ID128_MAKE_STR(1f,4e,0a,44,a8,86,49,93,9a,ae,a3,4f,c6,da,8c,95) + +#define SD_MESSAGE_SESSION_START SD_ID128_MAKE(8d,45,62,0c,1a,43,48,db,b1,74,10,da,57,c6,0c,66) +#define SD_MESSAGE_SESSION_START_STR SD_ID128_MAKE_STR(8d,45,62,0c,1a,43,48,db,b1,74,10,da,57,c6,0c,66) +#define SD_MESSAGE_SESSION_STOP SD_ID128_MAKE(33,54,93,94,24,b4,45,6d,98,02,ca,83,33,ed,42,4a) +#define SD_MESSAGE_SESSION_STOP_STR SD_ID128_MAKE_STR(33,54,93,94,24,b4,45,6d,98,02,ca,83,33,ed,42,4a) +#define SD_MESSAGE_SEAT_START SD_ID128_MAKE(fc,be,fc,5d,a2,3d,42,80,93,f9,7c,82,a9,29,0f,7b) +#define SD_MESSAGE_SEAT_START_STR SD_ID128_MAKE_STR(fc,be,fc,5d,a2,3d,42,80,93,f9,7c,82,a9,29,0f,7b) +#define SD_MESSAGE_SEAT_STOP SD_ID128_MAKE(e7,85,2b,fe,46,78,4e,d0,ac,cd,e0,4b,c8,64,c2,d5) +#define SD_MESSAGE_SEAT_STOP_STR SD_ID128_MAKE_STR(e7,85,2b,fe,46,78,4e,d0,ac,cd,e0,4b,c8,64,c2,d5) +#define SD_MESSAGE_MACHINE_START SD_ID128_MAKE(24,d8,d4,45,25,73,40,24,96,06,83,81,a6,31,2d,f2) +#define SD_MESSAGE_MACHINE_START_STR SD_ID128_MAKE_STR(24,d8,d4,45,25,73,40,24,96,06,83,81,a6,31,2d,f2) +#define SD_MESSAGE_MACHINE_STOP SD_ID128_MAKE(58,43,2b,d3,ba,ce,47,7c,b5,14,b5,63,81,b8,a7,58) +#define SD_MESSAGE_MACHINE_STOP_STR SD_ID128_MAKE_STR(58,43,2b,d3,ba,ce,47,7c,b5,14,b5,63,81,b8,a7,58) + +#define SD_MESSAGE_TIME_CHANGE SD_ID128_MAKE(c7,a7,87,07,9b,35,4e,aa,a9,e7,7b,37,18,93,cd,27) +#define SD_MESSAGE_TIME_CHANGE_STR SD_ID128_MAKE_STR(c7,a7,87,07,9b,35,4e,aa,a9,e7,7b,37,18,93,cd,27) +#define SD_MESSAGE_TIMEZONE_CHANGE SD_ID128_MAKE(45,f8,2f,4a,ef,7a,4b,bf,94,2c,e8,61,d1,f2,09,90) +#define SD_MESSAGE_TIMEZONE_CHANGE_STR SD_ID128_MAKE_STR(45,f8,2f,4a,ef,7a,4b,bf,94,2c,e8,61,d1,f2,09,90) + +#define SD_MESSAGE_TAINTED SD_ID128_MAKE(50,87,6a,9d,b0,0f,4c,40,bd,e1,a2,ad,38,1c,3a,1b) +#define SD_MESSAGE_TAINTED_STR SD_ID128_MAKE_STR(50,87,6a,9d,b0,0f,4c,40,bd,e1,a2,ad,38,1c,3a,1b) +#define SD_MESSAGE_STARTUP_FINISHED SD_ID128_MAKE(b0,7a,24,9c,d0,24,41,4a,82,dd,00,cd,18,13,78,ff) +#define SD_MESSAGE_STARTUP_FINISHED_STR SD_ID128_MAKE_STR(b0,7a,24,9c,d0,24,41,4a,82,dd,00,cd,18,13,78,ff) +#define SD_MESSAGE_USER_STARTUP_FINISHED SD_ID128_MAKE(ee,d0,0a,68,ff,d8,4e,31,88,21,05,fd,97,3a,bd,d1) +#define SD_MESSAGE_USER_STARTUP_FINISHED_STR SD_ID128_MAKE_STR(ee,d0,0a,68,ff,d8,4e,31,88,21,05,fd,97,3a,bd,d1) + +#define SD_MESSAGE_SLEEP_START SD_ID128_MAKE(6b,bd,95,ee,97,79,41,e4,97,c4,8b,e2,7c,25,41,28) +#define SD_MESSAGE_SLEEP_START_STR SD_ID128_MAKE_STR(6b,bd,95,ee,97,79,41,e4,97,c4,8b,e2,7c,25,41,28) +#define SD_MESSAGE_SLEEP_STOP SD_ID128_MAKE(88,11,e6,df,2a,8e,40,f5,8a,94,ce,a2,6f,8e,bf,14) +#define SD_MESSAGE_SLEEP_STOP_STR SD_ID128_MAKE_STR(88,11,e6,df,2a,8e,40,f5,8a,94,ce,a2,6f,8e,bf,14) + +#define SD_MESSAGE_SHUTDOWN SD_ID128_MAKE(98,26,88,66,d1,d5,4a,49,9c,4e,98,92,1d,93,bc,40) +#define SD_MESSAGE_SHUTDOWN_STR SD_ID128_MAKE_STR(98,26,88,66,d1,d5,4a,49,9c,4e,98,92,1d,93,bc,40) + +#define SD_MESSAGE_FACTORY_RESET SD_ID128_MAKE(c1,4a,af,76,ec,28,4a,5f,a1,f1,05,f8,8d,fb,06,1c) +#define SD_MESSAGE_FACTORY_RESET_STR SD_ID128_MAKE_STR(c1,4a,af,76,ec,28,4a,5f,a1,f1,05,f8,8d,fb,06,1c) + +#define SD_MESSAGE_CRASH_EXIT SD_ID128_MAKE(d9,ec,5e,95,e4,b6,46,aa,ae,a2,fd,05,21,4e,db,da) +#define SD_MESSAGE_CRASH_EXIT_STR SD_ID128_MAKE_STR(d9,ec,5e,95,e4,b6,46,aa,ae,a2,fd,05,21,4e,db,da) +#define SD_MESSAGE_CRASH_FAILED SD_ID128_MAKE(3e,d0,16,3e,86,8a,44,17,ab,8b,9e,21,04,07,a9,6c) +#define SD_MESSAGE_CRASH_FAILED_STR SD_ID128_MAKE_STR(3e,d0,16,3e,86,8a,44,17,ab,8b,9e,21,04,07,a9,6c) +#define SD_MESSAGE_CRASH_FREEZE SD_ID128_MAKE(64,5c,73,55,37,63,4a,e0,a3,2b,15,a7,c6,cb,a7,d4) +#define SD_MESSAGE_CRASH_FREEZE_STR SD_ID128_MAKE_STR(64,5c,73,55,37,63,4a,e0,a3,2b,15,a7,c6,cb,a7,d4) + +#define SD_MESSAGE_CRASH_NO_COREDUMP SD_ID128_MAKE(5a,dd,b3,a0,6a,73,4d,33,96,b7,94,bf,98,fb,2d,01) +#define SD_MESSAGE_CRASH_NO_COREDUMP_STR SD_ID128_MAKE_STR(5a,dd,b3,a0,6a,73,4d,33,96,b7,94,bf,98,fb,2d,01) +#define SD_MESSAGE_CRASH_NO_FORK SD_ID128_MAKE(5c,9e,98,de,4a,b9,4c,6a,9d,04,d0,ad,79,3b,d9,03) +#define SD_MESSAGE_CRASH_NO_FORK_STR SD_ID128_MAKE_STR(5c,9e,98,de,4a,b9,4c,6a,9d,04,d0,ad,79,3b,d9,03) +#define SD_MESSAGE_CRASH_UNKNOWN_SIGNAL SD_ID128_MAKE(5e,6f,1f,5e,4d,b6,4a,0e,ae,e3,36,82,49,d2,0b,94) +#define SD_MESSAGE_CRASH_UNKNOWN_SIGNAL_STR SD_ID128_MAKE_STR(5e,6f,1f,5e,4d,b6,4a,0e,ae,e3,36,82,49,d2,0b,94) +#define SD_MESSAGE_CRASH_SYSTEMD_SIGNAL SD_ID128_MAKE(83,f8,4b,35,ee,26,4f,74,a3,89,6a,97,17,af,34,cb) +#define SD_MESSAGE_CRASH_SYSTEMD_SIGNAL_STR SD_ID128_MAKE_STR(83,f8,4b,35,ee,26,4f,74,a3,89,6a,97,17,af,34,cb) +#define SD_MESSAGE_CRASH_PROCESS_SIGNAL SD_ID128_MAKE(3a,73,a9,8b,af,5b,4b,19,99,29,e3,22,6c,0b,e7,83) +#define SD_MESSAGE_CRASH_PROCESS_SIGNAL_STR SD_ID128_MAKE_STR(3a,73,a9,8b,af,5b,4b,19,99,29,e3,22,6c,0b,e7,83) +#define SD_MESSAGE_CRASH_WAITPID_FAILED SD_ID128_MAKE(2e,d1,8d,4f,78,ca,47,f0,a9,bc,25,27,1c,26,ad,b4) +#define SD_MESSAGE_CRASH_WAITPID_FAILED_STR SD_ID128_MAKE_STR(2e,d1,8d,4f,78,ca,47,f0,a9,bc,25,27,1c,26,ad,b4) +#define SD_MESSAGE_CRASH_COREDUMP_FAILED SD_ID128_MAKE(56,b1,cd,96,f2,42,46,c5,b6,07,66,6f,da,95,23,56) +#define SD_MESSAGE_CRASH_COREDUMP_FAILED_STR SD_ID128_MAKE_STR(56,b1,cd,96,f2,42,46,c5,b6,07,66,6f,da,95,23,56) +#define SD_MESSAGE_CRASH_COREDUMP_PID SD_ID128_MAKE(4a,c7,56,6d,4d,75,48,f4,98,1f,62,9a,28,f0,f8,29) +#define SD_MESSAGE_CRASH_COREDUMP_PID_STR SD_ID128_MAKE_STR(4a,c7,56,6d,4d,75,48,f4,98,1f,62,9a,28,f0,f8,29) +#define SD_MESSAGE_CRASH_SHELL_FORK_FAILED SD_ID128_MAKE(38,e8,b1,e0,39,ad,46,92,91,b1,8b,44,c5,53,a5,b7) +#define SD_MESSAGE_CRASH_SHELL_FORK_FAILED_STR SD_ID128_MAKE_STR(38,e8,b1,e0,39,ad,46,92,91,b1,8b,44,c5,53,a5,b7) +#define SD_MESSAGE_CRASH_EXECLE_FAILED SD_ID128_MAKE(87,27,29,b4,7d,be,47,3e,b7,68,cc,ec,d4,77,be,da) +#define SD_MESSAGE_CRASH_EXECLE_FAILED_STR SD_ID128_MAKE_STR(87,27,29,b4,7d,be,47,3e,b7,68,cc,ec,d4,77,be,da) + +#define SD_MESSAGE_SELINUX_FAILED SD_ID128_MAKE(65,8a,67,ad,c1,c9,40,b3,b3,31,6e,7e,86,28,83,4a) +#define SD_MESSAGE_SELINUX_FAILED_STR SD_ID128_MAKE_STR(65,8a,67,ad,c1,c9,40,b3,b3,31,6e,7e,86,28,83,4a) + +#define SD_MESSAGE_BATTERY_LOW_WARNING SD_ID128_MAKE(e6,f4,56,bd,92,00,4d,95,80,16,0b,22,07,55,51,86) +#define SD_MESSAGE_BATTERY_LOW_WARNING_STR SD_ID128_MAKE_STR(e6,f4,56,bd,92,00,4d,95,80,16,0b,22,07,55,51,86) +#define SD_MESSAGE_BATTERY_LOW_POWEROFF SD_ID128_MAKE(26,74,37,d3,3f,dd,41,09,9a,d7,62,21,cc,24,a3,35) +#define SD_MESSAGE_BATTERY_LOW_POWEROFF_STR SD_ID128_MAKE_STR(26,74,37,d3,3f,dd,41,09,9a,d7,62,21,cc,24,a3,35) + +#define SD_MESSAGE_CORE_MAINLOOP_FAILED SD_ID128_MAKE(79,e0,5b,67,bc,45,45,d1,92,2f,e4,71,07,ee,60,c5) +#define SD_MESSAGE_CORE_MAINLOOP_FAILED_STR SD_ID128_MAKE_STR(79,e0,5b,67,bc,45,45,d1,92,2f,e4,71,07,ee,60,c5) +#define SD_MESSAGE_CORE_NO_XDGDIR_PATH SD_ID128_MAKE(db,b1,36,b1,0e,f4,45,7b,a4,7a,79,5d,62,f1,08,c9) +#define SD_MESSAGE_CORE_NO_XDGDIR_PATH_STR SD_ID128_MAKE_STR(db,b1,36,b1,0e,f4,45,7b,a4,7a,79,5d,62,f1,08,c9) +#define SD_MESSAGE_CORE_CAPABILITY_BOUNDING_USER SD_ID128_MAKE(ed,15,8c,2d,f8,88,4f,a5,84,ee,ad,2d,90,2c,10,32) +#define SD_MESSAGE_CORE_CAPABILITY_BOUNDING_USER_STR SD_ID128_MAKE_STR(ed,15,8c,2d,f8,88,4f,a5,84,ee,ad,2d,90,2c,10,32) +#define SD_MESSAGE_CORE_CAPABILITY_BOUNDING SD_ID128_MAKE(42,69,5b,50,0d,f0,48,29,8b,ee,37,15,9c,aa,9f,2e) +#define SD_MESSAGE_CORE_CAPABILITY_BOUNDING_STR SD_ID128_MAKE_STR(42,69,5b,50,0d,f0,48,29,8b,ee,37,15,9c,aa,9f,2e) +#define SD_MESSAGE_CORE_DISABLE_PRIVILEGES SD_ID128_MAKE(bf,c2,43,07,24,ab,44,49,97,35,b4,f9,4c,ca,92,95) +#define SD_MESSAGE_CORE_DISABLE_PRIVILEGES_STR SD_ID128_MAKE_STR(bf,c2,43,07,24,ab,44,49,97,35,b4,f9,4c,ca,92,95) +#define SD_MESSAGE_CORE_START_TARGET_FAILED SD_ID128_MAKE(59,28,8a,f5,23,be,43,a2,8d,49,4e,41,e2,6e,45,10) +#define SD_MESSAGE_CORE_START_TARGET_FAILED_STR SD_ID128_MAKE_STR(59,28,8a,f5,23,be,43,a2,8d,49,4e,41,e2,6e,45,10) +#define SD_MESSAGE_CORE_ISOLATE_TARGET_FAILED SD_ID128_MAKE(68,9b,4f,cc,97,b4,48,6e,a5,da,92,db,69,c9,e3,14) +#define SD_MESSAGE_CORE_ISOLATE_TARGET_FAILED_STR SD_ID128_MAKE_STR(68,9b,4f,cc,97,b4,48,6e,a5,da,92,db,69,c9,e3,14) +#define SD_MESSAGE_CORE_FD_SET_FAILED SD_ID128_MAKE(5e,d8,36,f1,76,6f,4a,8a,9f,c5,da,45,aa,e2,3b,29) +#define SD_MESSAGE_CORE_FD_SET_FAILED_STR SD_ID128_MAKE_STR(5e,d8,36,f1,76,6f,4a,8a,9f,c5,da,45,aa,e2,3b,29) +#define SD_MESSAGE_CORE_PID1_ENVIRONMENT SD_ID128_MAKE(6a,40,fb,fb,d2,ba,4b,8d,b0,2f,b4,0c,9c,d0,90,d7) +#define SD_MESSAGE_CORE_PID1_ENVIRONMENT_STR SD_ID128_MAKE_STR(6a,40,fb,fb,d2,ba,4b,8d,b0,2f,b4,0c,9c,d0,90,d7) +#define SD_MESSAGE_CORE_MANAGER_ALLOCATE SD_ID128_MAKE(0e,54,47,09,84,ac,41,96,89,74,3d,95,7a,11,9e,2e) +#define SD_MESSAGE_CORE_MANAGER_ALLOCATE_STR SD_ID128_MAKE_STR(0e,54,47,09,84,ac,41,96,89,74,3d,95,7a,11,9e,2e) + +#define SD_MESSAGE_SMACK_FAILED_WRITE SD_ID128_MAKE(d6,7f,a9,f8,47,aa,4b,04,8a,2a,e3,35,35,33,1a,db) +#define SD_MESSAGE_SMACK_FAILED_WRITE_STR SD_ID128_MAKE_STR(d6,7f,a9,f8,47,aa,4b,04,8a,2a,e3,35,35,33,1a,db) + +#define SD_MESSAGE_SHUTDOWN_ERROR SD_ID128_MAKE(af,55,a6,f7,5b,54,44,31,b7,26,49,f3,6f,f6,d6,2c) +#define SD_MESSAGE_SHUTDOWN_ERROR_STR SD_ID128_MAKE_STR(af,55,a6,f7,5b,54,44,31,b7,26,49,f3,6f,f6,d6,2c) + +#define SD_MESSAGE_VALGRIND_HELPER_FORK SD_ID128_MAKE(d1,8e,03,39,ef,b2,4a,06,8d,9c,10,60,22,10,48,c2) +#define SD_MESSAGE_VALGRIND_HELPER_FORK_STR SD_ID128_MAKE_STR(d1,8e,03,39,ef,b2,4a,06,8d,9c,10,60,22,10,48,c2) + +/* The messages below are actually about jobs, not really about units, the macros are misleadingly named. + * Moreover SD_MESSAGE_UNIT_FAILED is not actually about a failing unit but about a failed start job. A job + * either finishes with SD_MESSAGE_UNIT_STARTED or with SD_MESSAGE_UNIT_FAILED hence. */ +#define SD_MESSAGE_UNIT_STARTING SD_ID128_MAKE(7d,49,58,e8,42,da,4a,75,8f,6c,1c,dc,7b,36,dc,c5) +#define SD_MESSAGE_UNIT_STARTING_STR SD_ID128_MAKE_STR(7d,49,58,e8,42,da,4a,75,8f,6c,1c,dc,7b,36,dc,c5) +#define SD_MESSAGE_UNIT_STARTED SD_ID128_MAKE(39,f5,34,79,d3,a0,45,ac,8e,11,78,62,48,23,1f,bf) +#define SD_MESSAGE_UNIT_STARTED_STR SD_ID128_MAKE_STR(39,f5,34,79,d3,a0,45,ac,8e,11,78,62,48,23,1f,bf) +#define SD_MESSAGE_UNIT_FAILED SD_ID128_MAKE(be,02,cf,68,55,d2,42,8b,a4,0d,f7,e9,d0,22,f0,3d) +#define SD_MESSAGE_UNIT_FAILED_STR SD_ID128_MAKE_STR(be,02,cf,68,55,d2,42,8b,a4,0d,f7,e9,d0,22,f0,3d) +#define SD_MESSAGE_UNIT_STOPPING SD_ID128_MAKE(de,5b,42,6a,63,be,47,a7,b6,ac,3e,aa,c8,2e,2f,6f) +#define SD_MESSAGE_UNIT_STOPPING_STR SD_ID128_MAKE_STR(de,5b,42,6a,63,be,47,a7,b6,ac,3e,aa,c8,2e,2f,6f) +#define SD_MESSAGE_UNIT_STOPPED SD_ID128_MAKE(9d,1a,aa,27,d6,01,40,bd,96,36,54,38,aa,d2,02,86) +#define SD_MESSAGE_UNIT_STOPPED_STR SD_ID128_MAKE_STR(9d,1a,aa,27,d6,01,40,bd,96,36,54,38,aa,d2,02,86) +#define SD_MESSAGE_UNIT_RELOADING SD_ID128_MAKE(d3,4d,03,7f,ff,18,47,e6,ae,66,9a,37,0e,69,47,25) +#define SD_MESSAGE_UNIT_RELOADING_STR SD_ID128_MAKE_STR(d3,4d,03,7f,ff,18,47,e6,ae,66,9a,37,0e,69,47,25) +#define SD_MESSAGE_UNIT_RELOADED SD_ID128_MAKE(7b,05,eb,c6,68,38,42,22,ba,a8,88,11,79,cf,da,54) +#define SD_MESSAGE_UNIT_RELOADED_STR SD_ID128_MAKE_STR(7b,05,eb,c6,68,38,42,22,ba,a8,88,11,79,cf,da,54) + +#define SD_MESSAGE_UNIT_RESTART_SCHEDULED SD_ID128_MAKE(5e,b0,34,94,b6,58,48,70,a5,36,b3,37,29,08,09,b3) +#define SD_MESSAGE_UNIT_RESTART_SCHEDULED_STR SD_ID128_MAKE_STR(5e,b0,34,94,b6,58,48,70,a5,36,b3,37,29,08,09,b3) + +#define SD_MESSAGE_UNIT_RESOURCES SD_ID128_MAKE(ae,8f,7b,86,6b,03,47,b9,af,31,fe,1c,80,b1,27,c0) +#define SD_MESSAGE_UNIT_RESOURCES_STR SD_ID128_MAKE_STR(ae,8f,7b,86,6b,03,47,b9,af,31,fe,1c,80,b1,27,c0) + +#define SD_MESSAGE_UNIT_SUCCESS SD_ID128_MAKE(7a,d2,d1,89,f7,e9,4e,70,a3,8c,78,13,54,91,24,48) +#define SD_MESSAGE_UNIT_SUCCESS_STR SD_ID128_MAKE_STR(7a,d2,d1,89,f7,e9,4e,70,a3,8c,78,13,54,91,24,48) +#define SD_MESSAGE_UNIT_SKIPPED SD_ID128_MAKE(0e,42,84,a0,ca,ca,4b,fc,81,c0,bb,67,86,97,26,73) +#define SD_MESSAGE_UNIT_SKIPPED_STR SD_ID128_MAKE_STR(0e,42,84,a0,ca,ca,4b,fc,81,c0,bb,67,86,97,26,73) +#define SD_MESSAGE_UNIT_FAILURE_RESULT SD_ID128_MAKE(d9,b3,73,ed,55,a6,4f,eb,82,42,e0,2d,be,79,a4,9c) +#define SD_MESSAGE_UNIT_FAILURE_RESULT_STR SD_ID128_MAKE_STR(d9,b3,73,ed,55,a6,4f,eb,82,42,e0,2d,be,79,a4,9c) + +#define SD_MESSAGE_SPAWN_FAILED SD_ID128_MAKE(64,12,57,65,1c,1b,4e,c9,a8,62,4d,7a,40,a9,e1,e7) +#define SD_MESSAGE_SPAWN_FAILED_STR SD_ID128_MAKE_STR(64,12,57,65,1c,1b,4e,c9,a8,62,4d,7a,40,a9,e1,e7) + +#define SD_MESSAGE_UNIT_PROCESS_EXIT SD_ID128_MAKE(98,e3,22,20,3f,7a,4e,d2,90,d0,9f,e0,3c,09,fe,15) +#define SD_MESSAGE_UNIT_PROCESS_EXIT_STR SD_ID128_MAKE_STR(98,e3,22,20,3f,7a,4e,d2,90,d0,9f,e0,3c,09,fe,15) + +#define SD_MESSAGE_FORWARD_SYSLOG_MISSED SD_ID128_MAKE(00,27,22,9c,a0,64,41,81,a7,6c,4e,92,45,8a,fa,2e) +#define SD_MESSAGE_FORWARD_SYSLOG_MISSED_STR SD_ID128_MAKE_STR(00,27,22,9c,a0,64,41,81,a7,6c,4e,92,45,8a,fa,2e) + +#define SD_MESSAGE_OVERMOUNTING SD_ID128_MAKE(1d,ee,03,69,c7,fc,47,36,b7,09,9b,38,ec,b4,6e,e7) +#define SD_MESSAGE_OVERMOUNTING_STR SD_ID128_MAKE_STR(1d,ee,03,69,c7,fc,47,36,b7,09,9b,38,ec,b4,6e,e7) + +#define SD_MESSAGE_UNIT_OOMD_KILL SD_ID128_MAKE(d9,89,61,1b,15,e4,4c,9d,bf,31,e3,c8,12,56,e4,ed) +#define SD_MESSAGE_UNIT_OOMD_KILL_STR SD_ID128_MAKE_STR(d9,89,61,1b,15,e4,4c,9d,bf,31,e3,c8,12,56,e4,ed) + +#define SD_MESSAGE_UNIT_OUT_OF_MEMORY SD_ID128_MAKE(fe,6f,aa,94,e7,77,46,63,a0,da,52,71,78,91,d8,ef) +#define SD_MESSAGE_UNIT_OUT_OF_MEMORY_STR SD_ID128_MAKE_STR(fe,6f,aa,94,e7,77,46,63,a0,da,52,71,78,91,d8,ef) + +#define SD_MESSAGE_LID_OPENED SD_ID128_MAKE(b7,2e,a4,a2,88,15,45,a0,b5,0e,20,0e,55,b9,b0,6f) +#define SD_MESSAGE_LID_OPENED_STR SD_ID128_MAKE_STR(b7,2e,a4,a2,88,15,45,a0,b5,0e,20,0e,55,b9,b0,6f) +#define SD_MESSAGE_LID_CLOSED SD_ID128_MAKE(b7,2e,a4,a2,88,15,45,a0,b5,0e,20,0e,55,b9,b0,70) +#define SD_MESSAGE_LID_CLOSED_STR SD_ID128_MAKE_STR(b7,2e,a4,a2,88,15,45,a0,b5,0e,20,0e,55,b9,b0,70) +#define SD_MESSAGE_SYSTEM_DOCKED SD_ID128_MAKE(f5,f4,16,b8,62,07,4b,28,92,7a,48,c3,ba,7d,51,ff) +#define SD_MESSAGE_SYSTEM_DOCKED_STR SD_ID128_MAKE_STR(f5,f4,16,b8,62,07,4b,28,92,7a,48,c3,ba,7d,51,ff) +#define SD_MESSAGE_SYSTEM_UNDOCKED SD_ID128_MAKE(51,e1,71,bd,58,52,48,56,81,10,14,4c,51,7c,ca,53) +#define SD_MESSAGE_SYSTEM_UNDOCKED_STR SD_ID128_MAKE_STR(51,e1,71,bd,58,52,48,56,81,10,14,4c,51,7c,ca,53) +#define SD_MESSAGE_POWER_KEY SD_ID128_MAKE(b7,2e,a4,a2,88,15,45,a0,b5,0e,20,0e,55,b9,b0,71) +#define SD_MESSAGE_POWER_KEY_STR SD_ID128_MAKE_STR(b7,2e,a4,a2,88,15,45,a0,b5,0e,20,0e,55,b9,b0,71) +#define SD_MESSAGE_POWER_KEY_LONG_PRESS SD_ID128_MAKE(3e,01,17,10,1e,b2,43,c1,b9,a5,0d,b3,49,4a,b1,0b) +#define SD_MESSAGE_POWER_KEY_LONG_PRESS_STR SD_ID128_MAKE_STR(3e,01,17,10,1e,b2,43,c1,b9,a5,0d,b3,49,4a,b1,0b) +#define SD_MESSAGE_REBOOT_KEY SD_ID128_MAKE(9f,a9,d2,c0,12,13,4e,c3,85,45,1f,fe,31,6f,97,d0) +#define SD_MESSAGE_REBOOT_KEY_STR SD_ID128_MAKE_STR(9f,a9,d2,c0,12,13,4e,c3,85,45,1f,fe,31,6f,97,d0) +#define SD_MESSAGE_REBOOT_KEY_LONG_PRESS SD_ID128_MAKE(f1,c5,9a,58,c9,d9,43,66,89,65,c3,37,ca,ec,59,75) +#define SD_MESSAGE_REBOOT_KEY_LONG_PRESS_STR SD_ID128_MAKE_STR(f1,c5,9a,58,c9,d9,43,66,89,65,c3,37,ca,ec,59,75) +#define SD_MESSAGE_SUSPEND_KEY SD_ID128_MAKE(b7,2e,a4,a2,88,15,45,a0,b5,0e,20,0e,55,b9,b0,72) +#define SD_MESSAGE_SUSPEND_KEY_STR SD_ID128_MAKE_STR(b7,2e,a4,a2,88,15,45,a0,b5,0e,20,0e,55,b9,b0,72) +#define SD_MESSAGE_SUSPEND_KEY_LONG_PRESS SD_ID128_MAKE(bf,da,f6,d3,12,ab,40,07,bc,1f,e4,0a,15,df,78,e8) +#define SD_MESSAGE_SUSPEND_KEY_LONG_PRESS_STR SD_ID128_MAKE_STR(bf,da,f6,d3,12,ab,40,07,bc,1f,e4,0a,15,df,78,e8) +#define SD_MESSAGE_HIBERNATE_KEY SD_ID128_MAKE(b7,2e,a4,a2,88,15,45,a0,b5,0e,20,0e,55,b9,b0,73) +#define SD_MESSAGE_HIBERNATE_KEY_STR SD_ID128_MAKE_STR(b7,2e,a4,a2,88,15,45,a0,b5,0e,20,0e,55,b9,b0,73) +#define SD_MESSAGE_HIBERNATE_KEY_LONG_PRESS SD_ID128_MAKE(16,78,36,df,6f,7f,42,8e,98,14,72,27,b2,dc,89,45) +#define SD_MESSAGE_HIBERNATE_KEY_LONG_PRESS_STR SD_ID128_MAKE_STR(16,78,36,df,6f,7f,42,8e,98,14,72,27,b2,dc,89,45) + +#define SD_MESSAGE_INVALID_CONFIGURATION SD_ID128_MAKE(c7,72,d2,4e,9a,88,4c,be,b9,ea,12,62,5c,30,6c,01) +#define SD_MESSAGE_INVALID_CONFIGURATION_STR SD_ID128_MAKE_STR(c7,72,d2,4e,9a,88,4c,be,b9,ea,12,62,5c,30,6c,01) + +#define SD_MESSAGE_DNSSEC_FAILURE SD_ID128_MAKE(16,75,d7,f1,72,17,40,98,b1,10,8b,f8,c7,dc,8f,5d) +#define SD_MESSAGE_DNSSEC_FAILURE_STR SD_ID128_MAKE_STR(16,75,d7,f1,72,17,40,98,b1,10,8b,f8,c7,dc,8f,5d) +#define SD_MESSAGE_DNSSEC_TRUST_ANCHOR_REVOKED SD_ID128_MAKE(4d,44,08,cf,d0,d1,44,85,91,84,d1,e6,5d,7c,8a,65) +#define SD_MESSAGE_DNSSEC_TRUST_ANCHOR_REVOKED_STR SD_ID128_MAKE_STR(4d,44,08,cf,d0,d1,44,85,91,84,d1,e6,5d,7c,8a,65) +#define SD_MESSAGE_DNSSEC_DOWNGRADE SD_ID128_MAKE(36,db,2d,fa,5a,90,45,e1,bd,4a,f5,f9,3e,1c,f0,57) +#define SD_MESSAGE_DNSSEC_DOWNGRADE_STR SD_ID128_MAKE_STR(36,db,2d,fa,5a,90,45,e1,bd,4a,f5,f9,3e,1c,f0,57) + +#define SD_MESSAGE_UNSAFE_USER_NAME SD_ID128_MAKE(b6,1f,da,c6,12,e9,4b,91,82,28,5b,99,88,43,06,1f) +#define SD_MESSAGE_UNSAFE_USER_NAME_STR SD_ID128_MAKE_STR(b6,1f,da,c6,12,e9,4b,91,82,28,5b,99,88,43,06,1f) + +#define SD_MESSAGE_MOUNT_POINT_PATH_NOT_SUITABLE SD_ID128_MAKE(1b,3b,b9,40,37,f0,4b,bf,81,02,8e,13,5a,12,d2,93) +#define SD_MESSAGE_MOUNT_POINT_PATH_NOT_SUITABLE_STR SD_ID128_MAKE_STR(1b,3b,b9,40,37,f0,4b,bf,81,02,8e,13,5a,12,d2,93) +#define SD_MESSAGE_DEVICE_PATH_NOT_SUITABLE SD_ID128_MAKE(01,01,90,13,8f,49,4e,29,a0,ef,66,69,74,95,31,aa) +#define SD_MESSAGE_DEVICE_PATH_NOT_SUITABLE_STR SD_ID128_MAKE_STR(01,01,90,13,8f,49,4e,29,a0,ef,66,69,74,95,31,aa) + +#define SD_MESSAGE_NOBODY_USER_UNSUITABLE SD_ID128_MAKE(b4,80,32,5f,9c,39,4a,7b,80,2c,23,1e,51,a2,75,2c) +#define SD_MESSAGE_NOBODY_USER_UNSUITABLE_STR SD_ID128_MAKE_STR(b4,80,32,5f,9c,39,4a,7b,80,2c,23,1e,51,a2,75,2c) + +#define SD_MESSAGE_SYSTEMD_UDEV_SETTLE_DEPRECATED SD_ID128_MAKE(1c,04,54,c1,bd,22,41,e0,ac,6f,ef,b4,bc,63,14,33) +#define SD_MESSAGE_SYSTEMD_UDEV_SETTLE_DEPRECATED_STR SD_ID128_MAKE_STR(1c,04,54,c1,bd,22,41,e0,ac,6f,ef,b4,bc,63,14,33) + +#define SD_MESSAGE_TIME_SYNC SD_ID128_MAKE(7c,8a,41,f3,7b,76,49,41,a0,e1,78,0b,1b,e2,f0,37) +#define SD_MESSAGE_TIME_SYNC_STR SD_ID128_MAKE_STR(7c,8a,41,f3,7b,76,49,41,a0,e1,78,0b,1b,e2,f0,37) + +#define SD_MESSAGE_TIME_BUMP SD_ID128_MAKE(7d,b7,3c,8a,f0,d9,4e,eb,82,2a,e0,43,23,fe,6a,b6) +#define SD_MESSAGE_TIME_BUMP_STR SD_ID128_MAKE_STR(7d,b7,3c,8a,f0,d9,4e,eb,82,2a,e0,43,23,fe,6a,b6) + +#define SD_MESSAGE_SHUTDOWN_SCHEDULED SD_ID128_MAKE(9e,70,66,27,9d,c8,40,3d,a7,9c,e4,b1,a6,90,64,b2) +#define SD_MESSAGE_SHUTDOWN_SCHEDULED_STR SD_ID128_MAKE_STR(9e,70,66,27,9d,c8,40,3d,a7,9c,e4,b1,a6,90,64,b2) + +#define SD_MESSAGE_SHUTDOWN_CANCELED SD_ID128_MAKE(24,9f,6f,b9,e6,e2,42,8c,96,f3,f0,87,56,81,ff,a3) +#define SD_MESSAGE_SHUTDOWN_CANCELED_STR SD_ID128_MAKE_STR(24,9f,6f,b9,e6,e2,42,8c,96,f3,f0,87,56,81,ff,a3) + +#define SD_MESSAGE_TPM_PCR_EXTEND SD_ID128_MAKE(3f,7d,5e,f3,e5,4f,43,02,b4,f0,b1,43,bb,27,0c,ab) +#define SD_MESSAGE_TPM_PCR_EXTEND_STR SD_ID128_MAKE_STR(3f,7d,5e,f3,e5,4f,43,02,b4,f0,b1,43,bb,27,0c,ab) + +#define SD_MESSAGE_MEMORY_TRIM SD_ID128_MAKE(f9,b0,be,46,5a,d5,40,d0,85,0a,d3,21,72,d5,7c,21) +#define SD_MESSAGE_MEMORY_TRIM_STR SD_ID128_MAKE_STR(f9,b0,be,46,5a,d5,40,d0,85,0a,d3,21,72,d5,7c,21) + +#define SD_MESSAGE_SYSV_GENERATOR_DEPRECATED SD_ID128_MAKE(a8,fa,8d,ac,db,1d,44,3e,95,03,b8,be,36,7a,6a,db) +#define SD_MESSAGE_SYSV_GENERATOR_DEPRECATED_STR SD_ID128_MAKE_STR(a8,fa,8d,ac,db,1d,44,3e,95,03,b8,be,36,7a,6a,db) + +#define SD_MESSAGE_PORTABLE_ATTACHED SD_ID128_MAKE(18,7c,62,eb,1e,7f,46,3b,b5,30,39,4f,52,cb,09,0f) +#define SD_MESSAGE_PORTABLE_ATTACHED_STR SD_ID128_MAKE_STR(18,7c,62,eb,1e,7f,46,3b,b5,30,39,4f,52,cb,09,0f) +#define SD_MESSAGE_PORTABLE_DETACHED SD_ID128_MAKE(76,c5,c7,54,d6,28,49,0d,8e,cb,a4,c9,d0,42,11,2b) +#define SD_MESSAGE_PORTABLE_DETACHED_STR SD_ID128_MAKE_STR(76,c5,c7,54,d6,28,49,0d,8e,cb,a4,c9,d0,42,11,2b) + +#define SD_MESSAGE_SRK_ENROLLMENT_NEEDS_AUTHORIZATION SD_ID128_MAKE(ad,70,89,f9,28,ac,4f,7e,a0,0c,07,45,7d,47,ba,8a) +#define SD_MESSAGE_SRK_ENROLLMENT_NEEDS_AUTHORIZATION_STR SD_ID128_MAKE_STR(ad,70,89,f9,28,ac,4f,7e,a0,0c,07,45,7d,47,ba,8a) + +_SD_END_DECLARATIONS; + +#endif diff --git a/thirdparty/systemd/include/systemd/sd-path.h b/thirdparty/systemd/include/systemd/sd-path.h new file mode 100644 index 000000000..fcd90aa96 --- /dev/null +++ b/thirdparty/systemd/include/systemd/sd-path.h @@ -0,0 +1,131 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#ifndef foosdpathhfoo +#define foosdpathhfoo + +/*** + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include + +#include "_sd-common.h" + +_SD_BEGIN_DECLARATIONS; + +enum { + /* Temporary files */ + SD_PATH_TEMPORARY, + SD_PATH_TEMPORARY_LARGE, + + /* Vendor supplied data */ + SD_PATH_SYSTEM_BINARIES, + SD_PATH_SYSTEM_INCLUDE, + SD_PATH_SYSTEM_LIBRARY_PRIVATE, + SD_PATH_SYSTEM_LIBRARY_ARCH, + SD_PATH_SYSTEM_SHARED, + SD_PATH_SYSTEM_CONFIGURATION_FACTORY, + SD_PATH_SYSTEM_STATE_FACTORY, + + /* System configuration, runtime, state, ... */ + SD_PATH_SYSTEM_CONFIGURATION, + SD_PATH_SYSTEM_RUNTIME, + SD_PATH_SYSTEM_RUNTIME_LOGS, + SD_PATH_SYSTEM_STATE_PRIVATE, + SD_PATH_SYSTEM_STATE_LOGS, + SD_PATH_SYSTEM_STATE_CACHE, + SD_PATH_SYSTEM_STATE_SPOOL, + + /* Vendor supplied data */ + SD_PATH_USER_BINARIES, + SD_PATH_USER_LIBRARY_PRIVATE, + SD_PATH_USER_LIBRARY_ARCH, + SD_PATH_USER_SHARED, + + /* User configuration, state, runtime ... */ + SD_PATH_USER_CONFIGURATION, + SD_PATH_USER_RUNTIME, + SD_PATH_USER_STATE_CACHE, + /* → SD_PATH_USER_STATE_PRIVATE is added at the bottom */ + + /* User resources */ + SD_PATH_USER, /* $HOME itself */ + SD_PATH_USER_DOCUMENTS, + SD_PATH_USER_MUSIC, + SD_PATH_USER_PICTURES, + SD_PATH_USER_VIDEOS, + SD_PATH_USER_DOWNLOAD, + SD_PATH_USER_PUBLIC, + SD_PATH_USER_TEMPLATES, + SD_PATH_USER_DESKTOP, + + /* Search paths */ + SD_PATH_SEARCH_BINARIES, + SD_PATH_SEARCH_BINARIES_DEFAULT, + SD_PATH_SEARCH_LIBRARY_PRIVATE, + SD_PATH_SEARCH_LIBRARY_ARCH, + SD_PATH_SEARCH_SHARED, + SD_PATH_SEARCH_CONFIGURATION_FACTORY, + SD_PATH_SEARCH_STATE_FACTORY, + SD_PATH_SEARCH_CONFIGURATION, + + /* Various systemd paths, generally mirroring systemd.pc — Except we drop the "dir" suffix (and + * replaces "path" by "search"), since this API is about dirs/paths anyway, and contains "path" + * already in the prefix */ + SD_PATH_SYSTEMD_UTIL, + + SD_PATH_SYSTEMD_SYSTEM_UNIT, + SD_PATH_SYSTEMD_SYSTEM_PRESET, + SD_PATH_SYSTEMD_SYSTEM_CONF, + SD_PATH_SYSTEMD_USER_UNIT, + SD_PATH_SYSTEMD_USER_PRESET, + SD_PATH_SYSTEMD_USER_CONF, + + SD_PATH_SYSTEMD_SEARCH_SYSTEM_UNIT, + SD_PATH_SYSTEMD_SEARCH_USER_UNIT, + + SD_PATH_SYSTEMD_SYSTEM_GENERATOR, + SD_PATH_SYSTEMD_USER_GENERATOR, + SD_PATH_SYSTEMD_SEARCH_SYSTEM_GENERATOR, + SD_PATH_SYSTEMD_SEARCH_USER_GENERATOR, + + SD_PATH_SYSTEMD_SLEEP, + SD_PATH_SYSTEMD_SHUTDOWN, + + SD_PATH_TMPFILES, + SD_PATH_SYSUSERS, + SD_PATH_SYSCTL, + SD_PATH_BINFMT, + SD_PATH_MODULES_LOAD, + SD_PATH_CATALOG, + + /* systemd-networkd search paths */ + SD_PATH_SYSTEMD_SEARCH_NETWORK, + + /* systemd environment generators */ + SD_PATH_SYSTEMD_SYSTEM_ENVIRONMENT_GENERATOR, + SD_PATH_SYSTEMD_USER_ENVIRONMENT_GENERATOR, + SD_PATH_SYSTEMD_SEARCH_SYSTEM_ENVIRONMENT_GENERATOR, + SD_PATH_SYSTEMD_SEARCH_USER_ENVIRONMENT_GENERATOR, + + SD_PATH_USER_STATE_PRIVATE, + + _SD_PATH_MAX +}; + +int sd_path_lookup(uint64_t type, const char *suffix, char **path); +int sd_path_lookup_strv(uint64_t type, const char *suffix, char ***paths); + +_SD_END_DECLARATIONS; + +#endif diff --git a/thirdparty/systemd/lib/libblkid.a b/thirdparty/systemd/lib/libblkid.a new file mode 100644 index 000000000..d0bcf9191 Binary files /dev/null and b/thirdparty/systemd/lib/libblkid.a differ diff --git a/thirdparty/systemd/lib/libcap.a b/thirdparty/systemd/lib/libcap.a new file mode 100644 index 000000000..f82526191 Binary files /dev/null and b/thirdparty/systemd/lib/libcap.a differ diff --git a/thirdparty/systemd/lib/libcrypt.a b/thirdparty/systemd/lib/libcrypt.a new file mode 100644 index 000000000..42ab1a2be Binary files /dev/null and b/thirdparty/systemd/lib/libcrypt.a differ diff --git a/thirdparty/systemd/lib/liblz4.a b/thirdparty/systemd/lib/liblz4.a new file mode 100644 index 000000000..4354b76f0 Binary files /dev/null and b/thirdparty/systemd/lib/liblz4.a differ diff --git a/thirdparty/systemd/lib/liblzma.a b/thirdparty/systemd/lib/liblzma.a new file mode 100644 index 000000000..25c9cc2fd Binary files /dev/null and b/thirdparty/systemd/lib/liblzma.a differ diff --git a/thirdparty/systemd/lib/libmount.a b/thirdparty/systemd/lib/libmount.a new file mode 100644 index 000000000..8b2f8237a Binary files /dev/null and b/thirdparty/systemd/lib/libmount.a differ diff --git a/thirdparty/systemd/lib/libpsx.a b/thirdparty/systemd/lib/libpsx.a new file mode 100644 index 000000000..e40f6b852 Binary files /dev/null and b/thirdparty/systemd/lib/libpsx.a differ diff --git a/thirdparty/systemd/lib/libsystemd.a b/thirdparty/systemd/lib/libsystemd.a new file mode 100644 index 000000000..059667f0b Binary files /dev/null and b/thirdparty/systemd/lib/libsystemd.a differ diff --git a/thirdparty/systemd/lib/libzstd.a b/thirdparty/systemd/lib/libzstd.a new file mode 100644 index 000000000..8bbce4b3b Binary files /dev/null and b/thirdparty/systemd/lib/libzstd.a differ diff --git a/xmake.lua b/xmake.lua index cbae98c04..46c403c11 100644 --- a/xmake.lua +++ b/xmake.lua @@ -40,10 +40,6 @@ if is_plat("windows") then add_requires("7z") end -if is_plat("linux") then - add_requires("vcpkg::libsystemd") -end - -- If we're using the UE cross-compile toolchain, we need to ensure we link statically -- against the toolchain libc++ and libc++abi, as the system ones can differ in ABI -- leading to nasty crashes -- cgit v1.2.3 From 30deab979c4be80c577837ab77d88ab59ca85120 Mon Sep 17 00:00:00 2001 From: Liam Mitchell Date: Wed, 20 Aug 2025 23:42:23 +0000 Subject: Remove libc++ installation from validate workflow --- .github/workflows/validate.yml | 6 ------ 1 file changed, 6 deletions(-) diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml index cadaeb280..a35d1eeed 100644 --- a/.github/workflows/validate.yml +++ b/.github/workflows/validate.yml @@ -152,12 +152,6 @@ jobs: ./bootstrap-vcpkg.sh cd .. - - name: Installing libc++ # This is not used for zen builds, but gperf/meson are used by some dependencies and they require it - shell: bash - run: | - sudo apt update - sudo apt install -y libc++-dev - - name: Config run: | ./scripts/ue_build_linux/ue_build.sh ./.tmp-ue-toolchain xmake config -v -y -m ${{ matrix.config }} --arch=${{ matrix.arch }} -- cgit v1.2.3 From 8d8d23acb100e34915fc2caddb0e5efbdbb1419e Mon Sep 17 00:00:00 2001 From: Liam Mitchell Date: Wed, 20 Aug 2025 23:49:43 +0000 Subject: Fix for MacOS compile failure --- src/zenutil/service.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/zenutil/service.cpp b/src/zenutil/service.cpp index 25941bde1..ab9553cf4 100644 --- a/src/zenutil/service.cpp +++ b/src/zenutil/service.cpp @@ -882,11 +882,13 @@ StopService(std::string_view ServiceName) const std::string DaemonName = GetDaemonName(ServiceName); const std::filesystem::path PListPath = GetPListPath(DaemonName); + /* std::pair Res = ExecuteProgram(fmt::format("launchctl bootout system ", PListPath.)); if (Res.first != 0) { return MakeErrorCode(Res.first); } + */ return {}; } -- cgit v1.2.3 From 9603a1ece6deb833149206bcb23b9b52253958d6 Mon Sep 17 00:00:00 2001 From: Liam Mitchell Date: Thu, 21 Aug 2025 00:07:10 +0000 Subject: Add missing headers to Mac process.cpp --- src/zencore/process.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/zencore/process.cpp b/src/zencore/process.cpp index f62731d94..8dc251c26 100644 --- a/src/zencore/process.cpp +++ b/src/zencore/process.cpp @@ -31,6 +31,8 @@ #if ZEN_PLATFORM_MAC # include +# include +# include #endif ZEN_THIRD_PARTY_INCLUDES_START -- cgit v1.2.3 From e6a5a2afda3b33d218440d7311a24f61a9400b3d Mon Sep 17 00:00:00 2001 From: Liam Mitchell Date: Wed, 20 Aug 2025 17:15:19 -0700 Subject: Fix trailing whitespace in service_cmd.cpp --- src/zen/cmds/service_cmd.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/zen/cmds/service_cmd.cpp b/src/zen/cmds/service_cmd.cpp index a734504b8..3847c423d 100644 --- a/src/zen/cmds/service_cmd.cpp +++ b/src/zen/cmds/service_cmd.cpp @@ -471,7 +471,6 @@ ServiceCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) } } - m_ServerExecutable = m_InstallPath / m_ServerExecutable.filename(); } -- cgit v1.2.3 From ce01022f8e51774cf39d8469bc14d0440f56839d Mon Sep 17 00:00:00 2001 From: Liam Mitchell Date: Thu, 21 Aug 2025 00:19:53 +0000 Subject: Use correct sysctl headers on Mac --- src/zencore/process.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/zencore/process.cpp b/src/zencore/process.cpp index 8dc251c26..0b25d14f4 100644 --- a/src/zencore/process.cpp +++ b/src/zencore/process.cpp @@ -31,7 +31,7 @@ #if ZEN_PLATFORM_MAC # include -# include +# include # include #endif -- cgit v1.2.3 From c2bac0c4103e1af53a5f6ab74e5567f5a5191b93 Mon Sep 17 00:00:00 2001 From: Liam Mitchell Date: Thu, 21 Aug 2025 23:30:03 +0000 Subject: Ignore unused variable warnings on platforms where ReportServiceStatus is unimplemented --- src/zenserver/main.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/zenserver/main.cpp b/src/zenserver/main.cpp index f8f25877b..76e022017 100644 --- a/src/zenserver/main.cpp +++ b/src/zenserver/main.cpp @@ -131,6 +131,7 @@ ReportServiceStatus(ServiceStatus Status) break; } #endif + (void)Status; } int -- cgit v1.2.3 From 8b199731a983e2cd7a04775f54c26127f9b8be48 Mon Sep 17 00:00:00 2001 From: Liam Mitchell Date: Thu, 21 Aug 2025 23:45:36 +0000 Subject: Update changelog --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 080638520..bf8fb6d23 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,8 @@ - `zen service install` - Install zenserver as a local service, this requires admin/sudo priviliges to execute properly - `--executable` - Path to zenserver executable. Defaults to same path as `zen` command line executable that was issued but with filename replaced. - `--name` - Name of the installed service. Defaults to `ZenServer` + - `--full` - If necessary, will uninstall a currently running service, then copy updated Zen binaries and debugging information to the location specified by `--install-path` and start the service. Requires `--install-path` to be specified. + - `--install-path` - Path to install service binaries. Only necessary in conjunction with `--full`. - `--display-name` - Windows only, the user friendly display name of the service. Defaults to `Unreal Zen Storage Service` - `--description` - Windows only, the user friendly description of the service - `--allow-elevation` - Windows only, if command is not run with elevated priviliges, the command will attempt to relaunch itself with elevated priviliges -- cgit v1.2.3 From 9fdaad481829b8a5b24c221fcfbe3547ca0f33a5 Mon Sep 17 00:00:00 2001 From: Liam Mitchell Date: Thu, 21 Aug 2025 23:51:51 +0000 Subject: Fix changelog merge issues --- CHANGELOG.md | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bf8fb6d23..c2fb70ad1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -193,7 +193,6 @@ - Improvement: Bumped the default number of blocks to search during upload to 10000 (from 5000). ## 5.6.1 ->>>>>>> main - Bugfix: GetModificationTickFromPath and CopyFile now works correctly on Windows/Mac - Bugfix: Handling of quotes and quotes with leading backslash for command line parsing - UE-231677 - Improvement: When logging with a epoch time prefix, the milliseconds/fraction is now correct. We now also set the epoch to the process spawn time rather than the time when the logger is created @@ -345,24 +344,6 @@ - `--verbose` ## 5.5.18 -- Feature: Added command line tool `zen service` to do maintenance operations for running zenserver as a local service - - `zen service status` - Check the status of zenservice as local service, indidicating if it is installed, running and the options it was installed with - - `zen service install` - Install zenserver as a local service, this requires admin/sudo priviliges to execute properly - - `--executable` - Path to zenserver executable. Defaults to same path as `zen` command line executable that was issued but with filename replaced. - - `--name` - Name of the installed service. Defaults to `ZenServer` - - `--display-name` - Windows only, the user friendly display name of the service. Defaults to `Unreal Zen Storage Service` - - `--description` - Windows only, the user friendly description of the service - - `--allow-elevation` - Windows only, if command is not run with elevated priviliges, the command will attempt to relaunch itself with elevated priviliges - - `-- ` - All options given after the double dash will be added as command line arguments to the zenserver executable when starting the service - - `zen service uninstall` - Uninstall zenserver as a local service, this requires admin/sudo priviliges to execute properly - - `--name` - Name of the installed service. Defaults to `ZenServer` - - `--allow-elevation` - Windows only, if command is not run with elevated priviliges, the command will attempt to relaunch itself with elevated priviliges - - `zen service start` - Start the installed zenserver local service, this requires admin/sudo priviliges to execute properly - - `--name` - Name of the installed service. Defaults to `ZenServer` - - `--allow-elevation` - Windows only, if command is not run with elevated priviliges, the command will attempt to relaunch itself with elevated priviliges - - `zen service stop` - Stops the running installed zenserver local service, this requires admin/sudo priviliges to execute properly - - `--name` - Name of the installed service. Defaults to `ZenServer` - - `--allow-elevation` - Windows only, if command is not run with elevated priviliges, the command will attempt to relaunch itself with elevated priviliges - Bugfix: Fix parsing of workspace options in Lua config - Bugfix: Add missing Lua option for option `--gc-projectstore-duration-seconds` - Bugfix: Add missing Lua mapping option to `--statsd` command line option -- cgit v1.2.3 From a799c748ee576f58f5b5a707d11a962ea3d6e958 Mon Sep 17 00:00:00 2001 From: Liam Mitchell Date: Fri, 22 Aug 2025 00:00:19 +0000 Subject: Remove workflow hacks for CI debugging --- .github/workflows/validate.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml index a35d1eeed..5a898b730 100644 --- a/.github/workflows/validate.yml +++ b/.github/workflows/validate.yml @@ -21,7 +21,6 @@ on: push: branches: - 'main' - - 'de/zen-service-command' paths-ignore: - 'VERSION.txt' - 'CHANGELOG.md' -- cgit v1.2.3 From b905b6d1d94c2a09c268603935991ee3c018c700 Mon Sep 17 00:00:00 2001 From: Liam Mitchell Date: Fri, 22 Aug 2025 22:29:14 +0000 Subject: Move ReportServiceStatus to zenutil and remove extraneous logging --- src/zenserver/main.cpp | 50 ----------------------------------- src/zenserver/xmake.lua | 8 ------ src/zenutil/include/zenutil/service.h | 2 ++ src/zenutil/service.cpp | 43 ++++++++++++++++++++++++++++++ src/zenutil/xmake.lua | 8 ++++++ 5 files changed, 53 insertions(+), 58 deletions(-) diff --git a/src/zenserver/main.cpp b/src/zenserver/main.cpp index 4ea4ee87e..e3c88f8bb 100644 --- a/src/zenserver/main.cpp +++ b/src/zenserver/main.cpp @@ -34,12 +34,6 @@ # include "windows/service.h" #endif -#if ZEN_PLATFORM_LINUX -ZEN_THIRD_PARTY_INCLUDES_START -# include -ZEN_THIRD_PARTY_INCLUDES_END -#endif - ////////////////////////////////////////////////////////////////////////// // We don't have any doctest code in this file but this is needed to bring // in some shared code into the executable @@ -96,43 +90,6 @@ ZenEntryPoint::ZenEntryPoint(ZenServerOptions& ServerOptions) : m_ServerOptions( { } -void -ReportServiceStatus(ServiceStatus Status) -{ -#if ZEN_PLATFORM_WINDOWS - switch (Status) - { - case ServiceStatus::Starting: - ReportSvcStatus(SERVICE_START_PENDING, NO_ERROR, 3000); - break; - case ServiceStatus::Running: - ReportSvcStatus(SERVICE_RUNNING, NO_ERROR, 0); - break; - case ServiceStatus::Stopping: - ReportSvcStatus(SERVICE_STOP_PENDING, NO_ERROR, 0); - break; - case ServiceStatus::Stopped: - ReportSvcStatus(SERVICE_STOPPED, (DWORD)ApplicationExitCode(), 0); - break; - default: - break; - } -#elif ZEN_PLATFORM_LINUX - switch (Status) - { - case ServiceStatus::Running: - sd_notify(0, "READY=1"); - break; - case ServiceStatus::Stopping: - sd_notify(0, "STOPPING=1"); - break; - case ServiceStatus::Stopped: - sd_notifyf(0, "EXIT_STATUS=%d", ApplicationExitCode()); - break; - } -#endif - (void)Status; -} int ZenEntryPoint::Run() @@ -160,11 +117,8 @@ 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(); auto NotifyReady = [&] { @@ -238,8 +192,6 @@ ZenEntryPoint::Run() } } - ZEN_INFO("Preparing lock file"); - std::error_code Ec; std::filesystem::path LockFilePath = m_ServerOptions.DataDir / ".lock"; @@ -253,7 +205,6 @@ ZenEntryPoint::Run() .ExecutablePath = GetRunningExecutablePath()}); }; - ZEN_INFO("m_LockFile.Create"); m_LockFile.Create(LockFilePath, MakeLockData(false), Ec); if (Ec) @@ -269,7 +220,6 @@ ZenEntryPoint::Run() } } - ZEN_INFO("InitializeServerLogging"); InitializeServerLogging(m_ServerOptions); ZEN_INFO("Command line: {}", m_ServerOptions.CommandLine); diff --git a/src/zenserver/xmake.lua b/src/zenserver/xmake.lua index 7ea9d52cc..470fbd24e 100644 --- a/src/zenserver/xmake.lua +++ b/src/zenserver/xmake.lua @@ -41,14 +41,6 @@ target("zenserver") add_ldflags("-framework SystemConfiguration") end - if is_plat("linux") then - add_includedirs("$(projectdir)/thirdparty/systemd/include") - add_linkdirs("$(projectdir)/thirdparty/systemd/lib") - add_links("systemd") - add_links("cap") - end - - add_options("compute") add_options("exec") diff --git a/src/zenutil/include/zenutil/service.h b/src/zenutil/include/zenutil/service.h index 2798bcb1f..a8bb868b3 100644 --- a/src/zenutil/include/zenutil/service.h +++ b/src/zenutil/include/zenutil/service.h @@ -53,4 +53,6 @@ std::error_code QueryInstalledService(std::string_view ServiceName, ServiceInfo& std::error_code StartService(std::string_view ServiceName); std::error_code StopService(std::string_view ServiceName); +void ReportServiceStatus(ServiceStatus Status); + } // namespace zen diff --git a/src/zenutil/service.cpp b/src/zenutil/service.cpp index ab9553cf4..bf2d7d630 100644 --- a/src/zenutil/service.cpp +++ b/src/zenutil/service.cpp @@ -25,11 +25,54 @@ # include # include # include + +ZEN_THIRD_PARTY_INCLUDES_START +# include +ZEN_THIRD_PARTY_INCLUDES_END + #endif namespace zen { using namespace std::literals; +void +ReportServiceStatus(ServiceStatus Status) +{ +#if ZEN_PLATFORM_WINDOWS + switch (Status) + { + case ServiceStatus::Starting: + ReportSvcStatus(SERVICE_START_PENDING, NO_ERROR, 3000); + break; + case ServiceStatus::Running: + ReportSvcStatus(SERVICE_RUNNING, NO_ERROR, 0); + break; + case ServiceStatus::Stopping: + ReportSvcStatus(SERVICE_STOP_PENDING, NO_ERROR, 0); + break; + case ServiceStatus::Stopped: + ReportSvcStatus(SERVICE_STOPPED, (DWORD)ApplicationExitCode(), 0); + break; + default: + break; + } +#elif ZEN_PLATFORM_LINUX + switch (Status) + { + case ServiceStatus::Running: + sd_notify(0, "READY=1"); + break; + case ServiceStatus::Stopping: + sd_notify(0, "STOPPING=1"); + break; + case ServiceStatus::Stopped: + sd_notifyf(0, "EXIT_STATUS=%d", ApplicationExitCode()); + break; + } +#endif + (void)Status; +} + namespace { #if ZEN_PLATFORM_WINDOWS diff --git a/src/zenutil/xmake.lua b/src/zenutil/xmake.lua index 3d95651f2..6d87aefcc 100644 --- a/src/zenutil/xmake.lua +++ b/src/zenutil/xmake.lua @@ -8,3 +8,11 @@ target('zenutil') add_includedirs("include", {public=true}) add_deps("zencore", "zenhttp") add_packages("vcpkg::robin-map", "vcpkg::spdlog") + + if is_plat("linux") then + add_includedirs("$(projectdir)/thirdparty/systemd/include") + add_linkdirs("$(projectdir)/thirdparty/systemd/lib") + add_links("systemd") + add_links("cap") + end + -- cgit v1.2.3 From 623f5bbd39e24a8a26203c02bafc3800d74d1db0 Mon Sep 17 00:00:00 2001 From: Liam Mitchell Date: Fri, 22 Aug 2025 15:51:32 -0700 Subject: Move windows service utilities to zenutil and fix clang-format errors --- src/zenserver/main.cpp | 3 +- src/zenserver/windows/service.cpp | 640 -------------------------- src/zenserver/windows/service.h | 24 - src/zenutil/include/zenutil/windows/service.h | 24 + src/zenutil/service.cpp | 1 + src/zenutil/windows/service.cpp | 639 +++++++++++++++++++++++++ 6 files changed, 665 insertions(+), 666 deletions(-) delete mode 100644 src/zenserver/windows/service.cpp delete mode 100644 src/zenserver/windows/service.h create mode 100644 src/zenutil/include/zenutil/windows/service.h create mode 100644 src/zenutil/windows/service.cpp diff --git a/src/zenserver/main.cpp b/src/zenserver/main.cpp index e3c88f8bb..6b22e1a0f 100644 --- a/src/zenserver/main.cpp +++ b/src/zenserver/main.cpp @@ -31,7 +31,7 @@ #if ZEN_PLATFORM_WINDOWS # include -# include "windows/service.h" +# include #endif ////////////////////////////////////////////////////////////////////////// @@ -90,7 +90,6 @@ ZenEntryPoint::ZenEntryPoint(ZenServerOptions& ServerOptions) : m_ServerOptions( { } - int ZenEntryPoint::Run() { diff --git a/src/zenserver/windows/service.cpp b/src/zenserver/windows/service.cpp deleted file mode 100644 index 4be5e6205..000000000 --- a/src/zenserver/windows/service.cpp +++ /dev/null @@ -1,640 +0,0 @@ -// Copyright Epic Games, Inc. All Rights Reserved. - -#include "service.h" - -#include - -#if ZEN_PLATFORM_WINDOWS - -# include -# include - -# include -# include -# include - -# define SVCNAME L"Zen Store" - -SERVICE_STATUS gSvcStatus; -SERVICE_STATUS_HANDLE gSvcStatusHandle; -HANDLE ghSvcStopEvent = NULL; - -void SvcInstall(void); - -void SvcReportEvent(LPTSTR); - -WindowsService::WindowsService() -{ -} - -WindowsService::~WindowsService() -{ -} - -// -// Purpose: -// Installs a service in the SCM database -// -// Parameters: -// None -// -// Return value: -// None -// -VOID -WindowsService::Install() -{ - SC_HANDLE schSCManager; - SC_HANDLE schService; - TCHAR szPath[MAX_PATH]; - - if (!GetModuleFileName(NULL, szPath, MAX_PATH)) - { - printf("Cannot install service (%d)\n", GetLastError()); - return; - } - - // Get a handle to the SCM database. - - schSCManager = OpenSCManager(NULL, // local computer - NULL, // ServicesActive database - SC_MANAGER_ALL_ACCESS); // full access rights - - if (NULL == schSCManager) - { - printf("OpenSCManager failed (%d)\n", GetLastError()); - return; - } - - // Create the service - - schService = CreateService(schSCManager, // SCM database - SVCNAME, // name of service - SVCNAME, // 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 - szPath, // 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) - { - printf("CreateService failed (%d)\n", GetLastError()); - CloseServiceHandle(schSCManager); - return; - } - else - printf("Service installed successfully\n"); - - CloseServiceHandle(schService); - CloseServiceHandle(schSCManager); -} - -void -WindowsService::Delete() -{ - SC_HANDLE schSCManager; - SC_HANDLE schService; - - // Get a handle to the SCM database. - - schSCManager = OpenSCManager(NULL, // local computer - NULL, // ServicesActive database - SC_MANAGER_ALL_ACCESS); // full access rights - - if (NULL == schSCManager) - { - printf("OpenSCManager failed (%d)\n", GetLastError()); - return; - } - - // Get a handle to the service. - - schService = OpenService(schSCManager, // SCM database - SVCNAME, // name of service - DELETE); // need delete access - - if (schService == NULL) - { - printf("OpenService failed (%d)\n", GetLastError()); - CloseServiceHandle(schSCManager); - return; - } - - // Delete the service. - - if (!DeleteService(schService)) - { - printf("DeleteService failed (%d)\n", GetLastError()); - } - else - printf("Service deleted successfully\n"); - - CloseServiceHandle(schService); - CloseServiceHandle(schSCManager); -} - -WindowsService* gSvc; - -void WINAPI -CallMain(DWORD, LPSTR*) -{ - gSvc->SvcMain(); -} - -int -WindowsService::ServiceMain() -{ - zen::SetCurrentThreadName("svc-main"); - - gSvc = this; - - SERVICE_TABLE_ENTRY DispatchTable[] = {{(LPWSTR)SVCNAME, (LPSERVICE_MAIN_FUNCTION)&CallMain}, {NULL, NULL}}; - - // This call returns when the service has stopped. - // The process should simply terminate when the call returns. - - if (!StartServiceCtrlDispatcher(DispatchTable)) - { - const DWORD dwError = zen::GetLastError(); - - if (dwError == ERROR_FAILED_SERVICE_CONTROLLER_CONNECT) - { - // Not actually running as a service - gSvc = nullptr; - - zen::SetIsInteractiveSession(true); - - return Run(); - } - else - { - zen::ThrowSystemError(dwError, "StartServiceCtrlDispatcher failed"); - } - } - - zen::SetIsInteractiveSession(false); - - return zen::ApplicationExitCode(); -} - -int -WindowsService::SvcMain() -{ - // Register the handler function for the service - - gSvcStatusHandle = RegisterServiceCtrlHandler(SVCNAME, SvcCtrlHandler); - - if (!gSvcStatusHandle) - { - SvcReportEvent((LPTSTR)TEXT("RegisterServiceCtrlHandler")); - - return 1; - } - - // These SERVICE_STATUS members remain as set here - - gSvcStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS; - gSvcStatus.dwServiceSpecificExitCode = 0; - - // Report initial status to the SCM - - ReportSvcStatus(SERVICE_START_PENDING, NO_ERROR, 3000); - - // Create an event. The control handler function, SvcCtrlHandler, - // signals this event when it receives the stop control code. - - ghSvcStopEvent = CreateEvent(NULL, // default security attributes - TRUE, // manual reset event - FALSE, // not signaled - NULL); // no name - - if (ghSvcStopEvent == NULL) - { - ReportSvcStatus(SERVICE_STOPPED, GetLastError(), 0); - - return 1; - } - - int ReturnCode = Run(); - return ReturnCode; -} - -// -// Purpose: -// Retrieves and displays the current service configuration. -// -// Parameters: -// None -// -// Return value: -// None -// -void -DoQuerySvc() -{ - SC_HANDLE schSCManager{}; - SC_HANDLE schService{}; - LPQUERY_SERVICE_CONFIG lpsc{}; - LPSERVICE_DESCRIPTION lpsd{}; - DWORD dwBytesNeeded{}, cbBufSize{}, dwError{}; - - // Get a handle to the SCM database. - - schSCManager = OpenSCManager(NULL, // local computer - NULL, // ServicesActive database - SC_MANAGER_ALL_ACCESS); // full access rights - - if (NULL == schSCManager) - { - printf("OpenSCManager failed (%d)\n", GetLastError()); - return; - } - - // Get a handle to the service. - - schService = OpenService(schSCManager, // SCM database - SVCNAME, // name of service - SERVICE_QUERY_CONFIG); // need query config access - - if (schService == NULL) - { - printf("OpenService failed (%d)\n", GetLastError()); - CloseServiceHandle(schSCManager); - return; - } - - // Get the configuration information. - - if (!QueryServiceConfig(schService, NULL, 0, &dwBytesNeeded)) - { - dwError = GetLastError(); - if (ERROR_INSUFFICIENT_BUFFER == dwError) - { - cbBufSize = dwBytesNeeded; - lpsc = (LPQUERY_SERVICE_CONFIG)LocalAlloc(LMEM_FIXED, cbBufSize); - } - else - { - printf("QueryServiceConfig failed (%d)", dwError); - goto cleanup; - } - } - - if (!QueryServiceConfig(schService, lpsc, cbBufSize, &dwBytesNeeded)) - { - printf("QueryServiceConfig failed (%d)", GetLastError()); - goto cleanup; - } - - if (!QueryServiceConfig2(schService, SERVICE_CONFIG_DESCRIPTION, NULL, 0, &dwBytesNeeded)) - { - dwError = GetLastError(); - if (ERROR_INSUFFICIENT_BUFFER == dwError) - { - cbBufSize = dwBytesNeeded; - lpsd = (LPSERVICE_DESCRIPTION)LocalAlloc(LMEM_FIXED, cbBufSize); - } - else - { - printf("QueryServiceConfig2 failed (%d)", dwError); - goto cleanup; - } - } - - if (!QueryServiceConfig2(schService, SERVICE_CONFIG_DESCRIPTION, (LPBYTE)lpsd, cbBufSize, &dwBytesNeeded)) - { - printf("QueryServiceConfig2 failed (%d)", GetLastError()); - goto cleanup; - } - - // Print the configuration information. - - _tprintf(TEXT("%s configuration: \n"), SVCNAME); - _tprintf(TEXT(" Type: 0x%x\n"), lpsc->dwServiceType); - _tprintf(TEXT(" Start Type: 0x%x\n"), lpsc->dwStartType); - _tprintf(TEXT(" Error Control: 0x%x\n"), lpsc->dwErrorControl); - _tprintf(TEXT(" Binary path: %s\n"), lpsc->lpBinaryPathName); - _tprintf(TEXT(" Account: %s\n"), lpsc->lpServiceStartName); - - if (lpsd->lpDescription != NULL && lstrcmp(lpsd->lpDescription, TEXT("")) != 0) - _tprintf(TEXT(" Description: %s\n"), lpsd->lpDescription); - if (lpsc->lpLoadOrderGroup != NULL && lstrcmp(lpsc->lpLoadOrderGroup, TEXT("")) != 0) - _tprintf(TEXT(" Load order group: %s\n"), lpsc->lpLoadOrderGroup); - if (lpsc->dwTagId != 0) - _tprintf(TEXT(" Tag ID: %d\n"), lpsc->dwTagId); - if (lpsc->lpDependencies != NULL && lstrcmp(lpsc->lpDependencies, TEXT("")) != 0) - _tprintf(TEXT(" Dependencies: %s\n"), lpsc->lpDependencies); - - LocalFree(lpsc); - LocalFree(lpsd); - -cleanup: - CloseServiceHandle(schService); - CloseServiceHandle(schSCManager); -} - -// -// Purpose: -// Disables the service. -// -// Parameters: -// None -// -// Return value: -// None -// -void -DoDisableSvc() -{ - SC_HANDLE schSCManager; - SC_HANDLE schService; - - // Get a handle to the SCM database. - - schSCManager = OpenSCManager(NULL, // local computer - NULL, // ServicesActive database - SC_MANAGER_ALL_ACCESS); // full access rights - - if (NULL == schSCManager) - { - printf("OpenSCManager failed (%d)\n", GetLastError()); - return; - } - - // Get a handle to the service. - - schService = OpenService(schSCManager, // SCM database - SVCNAME, // name of service - SERVICE_CHANGE_CONFIG); // need change config access - - if (schService == NULL) - { - printf("OpenService failed (%d)\n", GetLastError()); - CloseServiceHandle(schSCManager); - return; - } - - // Change the service start type. - - if (!ChangeServiceConfig(schService, // handle of service - SERVICE_NO_CHANGE, // service type: no change - SERVICE_DISABLED, // service start type - SERVICE_NO_CHANGE, // error control: no change - NULL, // binary path: no change - NULL, // load order group: no change - NULL, // tag ID: no change - NULL, // dependencies: no change - NULL, // account name: no change - NULL, // password: no change - NULL)) // display name: no change - { - printf("ChangeServiceConfig failed (%d)\n", GetLastError()); - } - else - printf("Service disabled successfully.\n"); - - CloseServiceHandle(schService); - CloseServiceHandle(schSCManager); -} - -// -// Purpose: -// Enables the service. -// -// Parameters: -// None -// -// Return value: -// None -// -VOID __stdcall DoEnableSvc() -{ - SC_HANDLE schSCManager; - SC_HANDLE schService; - - // Get a handle to the SCM database. - - schSCManager = OpenSCManager(NULL, // local computer - NULL, // ServicesActive database - SC_MANAGER_ALL_ACCESS); // full access rights - - if (NULL == schSCManager) - { - printf("OpenSCManager failed (%d)\n", GetLastError()); - return; - } - - // Get a handle to the service. - - schService = OpenService(schSCManager, // SCM database - SVCNAME, // name of service - SERVICE_CHANGE_CONFIG); // need change config access - - if (schService == NULL) - { - printf("OpenService failed (%d)\n", GetLastError()); - CloseServiceHandle(schSCManager); - return; - } - - // Change the service start type. - - if (!ChangeServiceConfig(schService, // handle of service - SERVICE_NO_CHANGE, // service type: no change - SERVICE_DEMAND_START, // service start type - SERVICE_NO_CHANGE, // error control: no change - NULL, // binary path: no change - NULL, // load order group: no change - NULL, // tag ID: no change - NULL, // dependencies: no change - NULL, // account name: no change - NULL, // password: no change - NULL)) // display name: no change - { - printf("ChangeServiceConfig failed (%d)\n", GetLastError()); - } - else - printf("Service enabled successfully.\n"); - - CloseServiceHandle(schService); - CloseServiceHandle(schSCManager); -} -// -// Purpose: -// Updates the service description to "This is a test description". -// -// Parameters: -// None -// -// Return value: -// None -// -void -DoUpdateSvcDesc() -{ - SC_HANDLE schSCManager; - SC_HANDLE schService; - SERVICE_DESCRIPTION sd; - TCHAR szDesc[] = TEXT("This is a test description"); - - // Get a handle to the SCM database. - - schSCManager = OpenSCManager(NULL, // local computer - NULL, // ServicesActive database - SC_MANAGER_ALL_ACCESS); // full access rights - - if (NULL == schSCManager) - { - printf("OpenSCManager failed (%d)\n", GetLastError()); - return; - } - - // Get a handle to the service. - - schService = OpenService(schSCManager, // SCM database - SVCNAME, // name of service - SERVICE_CHANGE_CONFIG); // need change config access - - if (schService == NULL) - { - printf("OpenService failed (%d)\n", GetLastError()); - CloseServiceHandle(schSCManager); - return; - } - - // Change the service description. - - sd.lpDescription = szDesc; - - if (!ChangeServiceConfig2(schService, // handle to service - SERVICE_CONFIG_DESCRIPTION, // change: description - &sd)) // new description - { - printf("ChangeServiceConfig2 failed\n"); - } - else - printf("Service description updated successfully.\n"); - - CloseServiceHandle(schService); - CloseServiceHandle(schSCManager); -} - -// -// Purpose: -// Sets the current service status and reports it to the SCM. -// -// Parameters: -// dwCurrentState - The current state (see SERVICE_STATUS) -// dwWin32ExitCode - The system error code -// dwWaitHint - Estimated time for pending operation, -// in milliseconds -// -// Return value: -// None -// -VOID -ReportSvcStatus(DWORD dwCurrentState, DWORD dwWin32ExitCode, DWORD dwWaitHint) -{ - static DWORD dwCheckPoint = 1; - - // Fill in the SERVICE_STATUS structure. - - gSvcStatus.dwCurrentState = dwCurrentState; - gSvcStatus.dwWin32ExitCode = dwWin32ExitCode; - gSvcStatus.dwWaitHint = dwWaitHint; - - if (dwCurrentState == SERVICE_START_PENDING) - gSvcStatus.dwControlsAccepted = 0; - else - gSvcStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP; - - if ((dwCurrentState == SERVICE_RUNNING) || (dwCurrentState == SERVICE_STOPPED)) - gSvcStatus.dwCheckPoint = 0; - else - gSvcStatus.dwCheckPoint = dwCheckPoint++; - - // Report the status of the service to the SCM. - SetServiceStatus(gSvcStatusHandle, &gSvcStatus); -} - -void -WindowsService::SvcCtrlHandler(DWORD dwCtrl) -{ - // Handle the requested control code. - // - // Called by SCM whenever a control code is sent to the service - // using the ControlService function. - - switch (dwCtrl) - { - case SERVICE_CONTROL_STOP: - ReportSvcStatus(SERVICE_STOP_PENDING, NO_ERROR, 0); - - // Signal the service to stop. - - SetEvent(ghSvcStopEvent); - zen::RequestApplicationExit(0); - - ReportSvcStatus(gSvcStatus.dwCurrentState, NO_ERROR, 0); - return; - - case SERVICE_CONTROL_INTERROGATE: - break; - - default: - break; - } -} - -// -// Purpose: -// Logs messages to the event log -// -// Parameters: -// szFunction - name of function that failed -// -// Return value: -// None -// -// Remarks: -// The service must have an entry in the Application event log. -// -VOID -SvcReportEvent(LPTSTR szFunction) -{ - ZEN_UNUSED(szFunction); - - // HANDLE hEventSource; - // LPCTSTR lpszStrings[2]; - // TCHAR Buffer[80]; - - // hEventSource = RegisterEventSource(NULL, SVCNAME); - - // if (NULL != hEventSource) - //{ - // StringCchPrintf(Buffer, 80, TEXT("%s failed with %d"), szFunction, GetLastError()); - - // lpszStrings[0] = SVCNAME; - // lpszStrings[1] = Buffer; - - // ReportEvent(hEventSource, // event log handle - // EVENTLOG_ERROR_TYPE, // event type - // 0, // event category - // SVC_ERROR, // event identifier - // NULL, // no security identifier - // 2, // size of lpszStrings array - // 0, // no binary data - // lpszStrings, // array of strings - // NULL); // no binary data - - // DeregisterEventSource(hEventSource); - //} -} - -#endif // ZEN_PLATFORM_WINDOWS diff --git a/src/zenserver/windows/service.h b/src/zenserver/windows/service.h deleted file mode 100644 index ca0270a36..000000000 --- a/src/zenserver/windows/service.h +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright Epic Games, Inc. All Rights Reserved. - -#pragma once - -#include - -class WindowsService -{ -public: - WindowsService(); - ~WindowsService(); - - virtual int Run() = 0; - - int ServiceMain(); - - static void Install(); - static void Delete(); - - int SvcMain(); - static void __stdcall SvcCtrlHandler(unsigned long); -}; - -VOID ReportSvcStatus(DWORD dwCurrentState, DWORD dwWin32ExitCode, DWORD dwWaitHint); diff --git a/src/zenutil/include/zenutil/windows/service.h b/src/zenutil/include/zenutil/windows/service.h new file mode 100644 index 000000000..ca0270a36 --- /dev/null +++ b/src/zenutil/include/zenutil/windows/service.h @@ -0,0 +1,24 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +#pragma once + +#include + +class WindowsService +{ +public: + WindowsService(); + ~WindowsService(); + + virtual int Run() = 0; + + int ServiceMain(); + + static void Install(); + static void Delete(); + + int SvcMain(); + static void __stdcall SvcCtrlHandler(unsigned long); +}; + +VOID ReportSvcStatus(DWORD dwCurrentState, DWORD dwWin32ExitCode, DWORD dwWaitHint); diff --git a/src/zenutil/service.cpp b/src/zenutil/service.cpp index bf2d7d630..e4a9a951e 100644 --- a/src/zenutil/service.cpp +++ b/src/zenutil/service.cpp @@ -10,6 +10,7 @@ #if ZEN_PLATFORM_WINDOWS # include +# include #endif #if ZEN_PLATFORM_MAC # include diff --git a/src/zenutil/windows/service.cpp b/src/zenutil/windows/service.cpp new file mode 100644 index 000000000..b76ed7a66 --- /dev/null +++ b/src/zenutil/windows/service.cpp @@ -0,0 +1,639 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +#include + +#if ZEN_PLATFORM_WINDOWS +# include + +# include +# include + +# include +# include +# include + +# define SVCNAME L"Zen Store" + +SERVICE_STATUS gSvcStatus; +SERVICE_STATUS_HANDLE gSvcStatusHandle; +HANDLE ghSvcStopEvent = NULL; + +void SvcInstall(void); + +void SvcReportEvent(LPTSTR); + +WindowsService::WindowsService() +{ +} + +WindowsService::~WindowsService() +{ +} + +// +// Purpose: +// Installs a service in the SCM database +// +// Parameters: +// None +// +// Return value: +// None +// +VOID +WindowsService::Install() +{ + SC_HANDLE schSCManager; + SC_HANDLE schService; + TCHAR szPath[MAX_PATH]; + + if (!GetModuleFileName(NULL, szPath, MAX_PATH)) + { + printf("Cannot install service (%d)\n", GetLastError()); + return; + } + + // Get a handle to the SCM database. + + schSCManager = OpenSCManager(NULL, // local computer + NULL, // ServicesActive database + SC_MANAGER_ALL_ACCESS); // full access rights + + if (NULL == schSCManager) + { + printf("OpenSCManager failed (%d)\n", GetLastError()); + return; + } + + // Create the service + + schService = CreateService(schSCManager, // SCM database + SVCNAME, // name of service + SVCNAME, // 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 + szPath, // 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) + { + printf("CreateService failed (%d)\n", GetLastError()); + CloseServiceHandle(schSCManager); + return; + } + else + printf("Service installed successfully\n"); + + CloseServiceHandle(schService); + CloseServiceHandle(schSCManager); +} + +void +WindowsService::Delete() +{ + SC_HANDLE schSCManager; + SC_HANDLE schService; + + // Get a handle to the SCM database. + + schSCManager = OpenSCManager(NULL, // local computer + NULL, // ServicesActive database + SC_MANAGER_ALL_ACCESS); // full access rights + + if (NULL == schSCManager) + { + printf("OpenSCManager failed (%d)\n", GetLastError()); + return; + } + + // Get a handle to the service. + + schService = OpenService(schSCManager, // SCM database + SVCNAME, // name of service + DELETE); // need delete access + + if (schService == NULL) + { + printf("OpenService failed (%d)\n", GetLastError()); + CloseServiceHandle(schSCManager); + return; + } + + // Delete the service. + + if (!DeleteService(schService)) + { + printf("DeleteService failed (%d)\n", GetLastError()); + } + else + printf("Service deleted successfully\n"); + + CloseServiceHandle(schService); + CloseServiceHandle(schSCManager); +} + +WindowsService* gSvc; + +void WINAPI +CallMain(DWORD, LPSTR*) +{ + gSvc->SvcMain(); +} + +int +WindowsService::ServiceMain() +{ + zen::SetCurrentThreadName("svc-main"); + + gSvc = this; + + SERVICE_TABLE_ENTRY DispatchTable[] = {{(LPWSTR)SVCNAME, (LPSERVICE_MAIN_FUNCTION)&CallMain}, {NULL, NULL}}; + + // This call returns when the service has stopped. + // The process should simply terminate when the call returns. + + if (!StartServiceCtrlDispatcher(DispatchTable)) + { + const DWORD dwError = zen::GetLastError(); + + if (dwError == ERROR_FAILED_SERVICE_CONTROLLER_CONNECT) + { + // Not actually running as a service + gSvc = nullptr; + + zen::SetIsInteractiveSession(true); + + return Run(); + } + else + { + zen::ThrowSystemError(dwError, "StartServiceCtrlDispatcher failed"); + } + } + + zen::SetIsInteractiveSession(false); + + return zen::ApplicationExitCode(); +} + +int +WindowsService::SvcMain() +{ + // Register the handler function for the service + + gSvcStatusHandle = RegisterServiceCtrlHandler(SVCNAME, SvcCtrlHandler); + + if (!gSvcStatusHandle) + { + SvcReportEvent((LPTSTR)TEXT("RegisterServiceCtrlHandler")); + + return 1; + } + + // These SERVICE_STATUS members remain as set here + + gSvcStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS; + gSvcStatus.dwServiceSpecificExitCode = 0; + + // Report initial status to the SCM + + ReportSvcStatus(SERVICE_START_PENDING, NO_ERROR, 3000); + + // Create an event. The control handler function, SvcCtrlHandler, + // signals this event when it receives the stop control code. + + ghSvcStopEvent = CreateEvent(NULL, // default security attributes + TRUE, // manual reset event + FALSE, // not signaled + NULL); // no name + + if (ghSvcStopEvent == NULL) + { + ReportSvcStatus(SERVICE_STOPPED, GetLastError(), 0); + + return 1; + } + + int ReturnCode = Run(); + return ReturnCode; +} + +// +// Purpose: +// Retrieves and displays the current service configuration. +// +// Parameters: +// None +// +// Return value: +// None +// +void +DoQuerySvc() +{ + SC_HANDLE schSCManager{}; + SC_HANDLE schService{}; + LPQUERY_SERVICE_CONFIG lpsc{}; + LPSERVICE_DESCRIPTION lpsd{}; + DWORD dwBytesNeeded{}, cbBufSize{}, dwError{}; + + // Get a handle to the SCM database. + + schSCManager = OpenSCManager(NULL, // local computer + NULL, // ServicesActive database + SC_MANAGER_ALL_ACCESS); // full access rights + + if (NULL == schSCManager) + { + printf("OpenSCManager failed (%d)\n", GetLastError()); + return; + } + + // Get a handle to the service. + + schService = OpenService(schSCManager, // SCM database + SVCNAME, // name of service + SERVICE_QUERY_CONFIG); // need query config access + + if (schService == NULL) + { + printf("OpenService failed (%d)\n", GetLastError()); + CloseServiceHandle(schSCManager); + return; + } + + // Get the configuration information. + + if (!QueryServiceConfig(schService, NULL, 0, &dwBytesNeeded)) + { + dwError = GetLastError(); + if (ERROR_INSUFFICIENT_BUFFER == dwError) + { + cbBufSize = dwBytesNeeded; + lpsc = (LPQUERY_SERVICE_CONFIG)LocalAlloc(LMEM_FIXED, cbBufSize); + } + else + { + printf("QueryServiceConfig failed (%d)", dwError); + goto cleanup; + } + } + + if (!QueryServiceConfig(schService, lpsc, cbBufSize, &dwBytesNeeded)) + { + printf("QueryServiceConfig failed (%d)", GetLastError()); + goto cleanup; + } + + if (!QueryServiceConfig2(schService, SERVICE_CONFIG_DESCRIPTION, NULL, 0, &dwBytesNeeded)) + { + dwError = GetLastError(); + if (ERROR_INSUFFICIENT_BUFFER == dwError) + { + cbBufSize = dwBytesNeeded; + lpsd = (LPSERVICE_DESCRIPTION)LocalAlloc(LMEM_FIXED, cbBufSize); + } + else + { + printf("QueryServiceConfig2 failed (%d)", dwError); + goto cleanup; + } + } + + if (!QueryServiceConfig2(schService, SERVICE_CONFIG_DESCRIPTION, (LPBYTE)lpsd, cbBufSize, &dwBytesNeeded)) + { + printf("QueryServiceConfig2 failed (%d)", GetLastError()); + goto cleanup; + } + + // Print the configuration information. + + _tprintf(TEXT("%s configuration: \n"), SVCNAME); + _tprintf(TEXT(" Type: 0x%x\n"), lpsc->dwServiceType); + _tprintf(TEXT(" Start Type: 0x%x\n"), lpsc->dwStartType); + _tprintf(TEXT(" Error Control: 0x%x\n"), lpsc->dwErrorControl); + _tprintf(TEXT(" Binary path: %s\n"), lpsc->lpBinaryPathName); + _tprintf(TEXT(" Account: %s\n"), lpsc->lpServiceStartName); + + if (lpsd->lpDescription != NULL && lstrcmp(lpsd->lpDescription, TEXT("")) != 0) + _tprintf(TEXT(" Description: %s\n"), lpsd->lpDescription); + if (lpsc->lpLoadOrderGroup != NULL && lstrcmp(lpsc->lpLoadOrderGroup, TEXT("")) != 0) + _tprintf(TEXT(" Load order group: %s\n"), lpsc->lpLoadOrderGroup); + if (lpsc->dwTagId != 0) + _tprintf(TEXT(" Tag ID: %d\n"), lpsc->dwTagId); + if (lpsc->lpDependencies != NULL && lstrcmp(lpsc->lpDependencies, TEXT("")) != 0) + _tprintf(TEXT(" Dependencies: %s\n"), lpsc->lpDependencies); + + LocalFree(lpsc); + LocalFree(lpsd); + +cleanup: + CloseServiceHandle(schService); + CloseServiceHandle(schSCManager); +} + +// +// Purpose: +// Disables the service. +// +// Parameters: +// None +// +// Return value: +// None +// +void +DoDisableSvc() +{ + SC_HANDLE schSCManager; + SC_HANDLE schService; + + // Get a handle to the SCM database. + + schSCManager = OpenSCManager(NULL, // local computer + NULL, // ServicesActive database + SC_MANAGER_ALL_ACCESS); // full access rights + + if (NULL == schSCManager) + { + printf("OpenSCManager failed (%d)\n", GetLastError()); + return; + } + + // Get a handle to the service. + + schService = OpenService(schSCManager, // SCM database + SVCNAME, // name of service + SERVICE_CHANGE_CONFIG); // need change config access + + if (schService == NULL) + { + printf("OpenService failed (%d)\n", GetLastError()); + CloseServiceHandle(schSCManager); + return; + } + + // Change the service start type. + + if (!ChangeServiceConfig(schService, // handle of service + SERVICE_NO_CHANGE, // service type: no change + SERVICE_DISABLED, // service start type + SERVICE_NO_CHANGE, // error control: no change + NULL, // binary path: no change + NULL, // load order group: no change + NULL, // tag ID: no change + NULL, // dependencies: no change + NULL, // account name: no change + NULL, // password: no change + NULL)) // display name: no change + { + printf("ChangeServiceConfig failed (%d)\n", GetLastError()); + } + else + printf("Service disabled successfully.\n"); + + CloseServiceHandle(schService); + CloseServiceHandle(schSCManager); +} + +// +// Purpose: +// Enables the service. +// +// Parameters: +// None +// +// Return value: +// None +// +VOID __stdcall DoEnableSvc() +{ + SC_HANDLE schSCManager; + SC_HANDLE schService; + + // Get a handle to the SCM database. + + schSCManager = OpenSCManager(NULL, // local computer + NULL, // ServicesActive database + SC_MANAGER_ALL_ACCESS); // full access rights + + if (NULL == schSCManager) + { + printf("OpenSCManager failed (%d)\n", GetLastError()); + return; + } + + // Get a handle to the service. + + schService = OpenService(schSCManager, // SCM database + SVCNAME, // name of service + SERVICE_CHANGE_CONFIG); // need change config access + + if (schService == NULL) + { + printf("OpenService failed (%d)\n", GetLastError()); + CloseServiceHandle(schSCManager); + return; + } + + // Change the service start type. + + if (!ChangeServiceConfig(schService, // handle of service + SERVICE_NO_CHANGE, // service type: no change + SERVICE_DEMAND_START, // service start type + SERVICE_NO_CHANGE, // error control: no change + NULL, // binary path: no change + NULL, // load order group: no change + NULL, // tag ID: no change + NULL, // dependencies: no change + NULL, // account name: no change + NULL, // password: no change + NULL)) // display name: no change + { + printf("ChangeServiceConfig failed (%d)\n", GetLastError()); + } + else + printf("Service enabled successfully.\n"); + + CloseServiceHandle(schService); + CloseServiceHandle(schSCManager); +} +// +// Purpose: +// Updates the service description to "This is a test description". +// +// Parameters: +// None +// +// Return value: +// None +// +void +DoUpdateSvcDesc() +{ + SC_HANDLE schSCManager; + SC_HANDLE schService; + SERVICE_DESCRIPTION sd; + TCHAR szDesc[] = TEXT("This is a test description"); + + // Get a handle to the SCM database. + + schSCManager = OpenSCManager(NULL, // local computer + NULL, // ServicesActive database + SC_MANAGER_ALL_ACCESS); // full access rights + + if (NULL == schSCManager) + { + printf("OpenSCManager failed (%d)\n", GetLastError()); + return; + } + + // Get a handle to the service. + + schService = OpenService(schSCManager, // SCM database + SVCNAME, // name of service + SERVICE_CHANGE_CONFIG); // need change config access + + if (schService == NULL) + { + printf("OpenService failed (%d)\n", GetLastError()); + CloseServiceHandle(schSCManager); + return; + } + + // Change the service description. + + sd.lpDescription = szDesc; + + if (!ChangeServiceConfig2(schService, // handle to service + SERVICE_CONFIG_DESCRIPTION, // change: description + &sd)) // new description + { + printf("ChangeServiceConfig2 failed\n"); + } + else + printf("Service description updated successfully.\n"); + + CloseServiceHandle(schService); + CloseServiceHandle(schSCManager); +} + +// +// Purpose: +// Sets the current service status and reports it to the SCM. +// +// Parameters: +// dwCurrentState - The current state (see SERVICE_STATUS) +// dwWin32ExitCode - The system error code +// dwWaitHint - Estimated time for pending operation, +// in milliseconds +// +// Return value: +// None +// +VOID +ReportSvcStatus(DWORD dwCurrentState, DWORD dwWin32ExitCode, DWORD dwWaitHint) +{ + static DWORD dwCheckPoint = 1; + + // Fill in the SERVICE_STATUS structure. + + gSvcStatus.dwCurrentState = dwCurrentState; + gSvcStatus.dwWin32ExitCode = dwWin32ExitCode; + gSvcStatus.dwWaitHint = dwWaitHint; + + if (dwCurrentState == SERVICE_START_PENDING) + gSvcStatus.dwControlsAccepted = 0; + else + gSvcStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP; + + if ((dwCurrentState == SERVICE_RUNNING) || (dwCurrentState == SERVICE_STOPPED)) + gSvcStatus.dwCheckPoint = 0; + else + gSvcStatus.dwCheckPoint = dwCheckPoint++; + + // Report the status of the service to the SCM. + SetServiceStatus(gSvcStatusHandle, &gSvcStatus); +} + +void +WindowsService::SvcCtrlHandler(DWORD dwCtrl) +{ + // Handle the requested control code. + // + // Called by SCM whenever a control code is sent to the service + // using the ControlService function. + + switch (dwCtrl) + { + case SERVICE_CONTROL_STOP: + ReportSvcStatus(SERVICE_STOP_PENDING, NO_ERROR, 0); + + // Signal the service to stop. + + SetEvent(ghSvcStopEvent); + zen::RequestApplicationExit(0); + + ReportSvcStatus(gSvcStatus.dwCurrentState, NO_ERROR, 0); + return; + + case SERVICE_CONTROL_INTERROGATE: + break; + + default: + break; + } +} + +// +// Purpose: +// Logs messages to the event log +// +// Parameters: +// szFunction - name of function that failed +// +// Return value: +// None +// +// Remarks: +// The service must have an entry in the Application event log. +// +VOID +SvcReportEvent(LPTSTR szFunction) +{ + ZEN_UNUSED(szFunction); + + // HANDLE hEventSource; + // LPCTSTR lpszStrings[2]; + // TCHAR Buffer[80]; + + // hEventSource = RegisterEventSource(NULL, SVCNAME); + + // if (NULL != hEventSource) + //{ + // StringCchPrintf(Buffer, 80, TEXT("%s failed with %d"), szFunction, GetLastError()); + + // lpszStrings[0] = SVCNAME; + // lpszStrings[1] = Buffer; + + // ReportEvent(hEventSource, // event log handle + // EVENTLOG_ERROR_TYPE, // event type + // 0, // event category + // SVC_ERROR, // event identifier + // NULL, // no security identifier + // 2, // size of lpszStrings array + // 0, // no binary data + // lpszStrings, // array of strings + // NULL); // no binary data + + // DeregisterEventSource(hEventSource); + //} +} + +#endif // ZEN_PLATFORM_WINDOWS -- cgit v1.2.3