diff options
| author | Dan Engelbrecht <[email protected]> | 2024-08-27 17:09:55 +0200 |
|---|---|---|
| committer | Dan Engelbrecht <[email protected]> | 2025-01-08 10:01:23 +0100 |
| commit | 1747a513dc9dccd5a60e76daebd59cfba4a536d9 (patch) | |
| tree | f46af5db5c13cbca9c072030ff04e54ba0e1bede /src | |
| parent | 5.5.17-pre1 (diff) | |
| download | zen-1747a513dc9dccd5a60e76daebd59cfba4a536d9.tar.xz zen-1747a513dc9dccd5a60e76daebd59cfba4a536d9.zip | |
zen `service` command
Diffstat (limited to 'src')
| -rw-r--r-- | src/zen/cmds/service_cmd.cpp | 308 | ||||
| -rw-r--r-- | src/zen/cmds/service_cmd.h | 48 | ||||
| -rw-r--r-- | src/zen/zen.cpp | 3 | ||||
| -rw-r--r-- | src/zenutil/include/zenutil/service.h | 38 | ||||
| -rw-r--r-- | src/zenutil/service.cpp | 393 |
5 files changed, 790 insertions, 0 deletions
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 <zencore/filesystem.h> +#include <zencore/fmtutils.h> +#include <zencore/logging.h> +#include <zencore/zencore.h> +#include <zenutil/service.h> + +#if ZEN_PLATFORM_WINDOWS +# include <zencore/windows.h> +# include <shellapi.h> +#endif + +ZEN_THIRD_PARTY_INCLUDES_START +#include <gsl/gsl-lite.hpp> +ZEN_THIRD_PARTY_INCLUDES_END + +#include <memory> + +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), + "<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), + "<name>"); + 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), "<path>"); + m_InstallOptions.add_option("", + "n", + "name", + fmt::format("Service name, defaults to \"{}\"", m_ServiceName), + cxxopts::value(m_ServiceName), + "<name>"); + m_InstallOptions.add_option("", + "d", + "display-name", + fmt::format("Service display, defaults to \"{}\"", m_ServiceDisplayName), + cxxopts::value(m_ServiceDisplayName), + "<display-name>"); + m_InstallOptions.add_option("", + "", + "description", + fmt::format("Service description", m_ServiceDescription), + cxxopts::value(m_ServiceDescription), + "<description>"); + 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), + "<name>"); + 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), + "<name>"); + 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), + "<name>"); + 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<char*> 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<int>(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 <filesystem> + +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 <filesystem> + +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 <zenutil/service.h> + +#include <zencore/except.h> +#include <zencore/scopeguard.h> +#include <zencore/zencore.h> + +#if ZEN_PLATFORM_WINDOWS +# include <zencore/windows.h> +#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<wchar_t*>(DescriptionBuilder.c_str()); + if (!ChangeServiceConfig2(schService, SERVICE_CONFIG_DESCRIPTION, &Description)) + { + return MakeErrorCodeFromLastError(); + } + } + + CloseServiceHandle(schService); + + return {}; +} + +std::error_code +UninstallService(std::string_view ServiceName) +{ + // Get a handle to the SCM database. + SC_HANDLE schSCManager = OpenSCManager(NULL, // local computer + NULL, // ServicesActive database + SC_MANAGER_ALL_ACCESS); // full access rights + + if (NULL == schSCManager) + { + return MakeErrorCodeFromLastError(); + } + + auto _ = MakeGuard([schSCManager]() { CloseServiceHandle(schSCManager); }); + + // Get a handle to the service. + + ExtendableWideStringBuilder<128> Name; + Utf8ToWide(ServiceName, Name); + + SC_HANDLE schService = OpenService(schSCManager, // SCM database + Name.c_str(), // name of service + DELETE); // need delete access + + if (schService == NULL) + { + DWORD Error = ::GetLastError(); + if (Error == ERROR_SERVICE_DOES_NOT_EXIST) + { + return {}; + } + return MakeErrorCode(Error); + } + auto __ = MakeGuard([schService]() { CloseServiceHandle(schService); }); + + // Delete the service. + + if (!DeleteService(schService)) + { + return MakeErrorCodeFromLastError(); + } + + return {}; +} + +std::error_code +QueryInstalledService(std::string_view ServiceName, ServiceInfo& OutInfo) +{ + // Get a handle to the SCM database. + SC_HANDLE schSCManager = OpenSCManager(NULL, // local computer + NULL, // ServicesActive database + SC_MANAGER_CONNECT); // standard access rights + + if (NULL == schSCManager) + { + return MakeErrorCodeFromLastError(); + } + + auto _ = MakeGuard([schSCManager]() { CloseServiceHandle(schSCManager); }); + + // Get a handle to the service. + + ExtendableWideStringBuilder<128> Name; + Utf8ToWide(ServiceName, Name); + + SC_HANDLE schService = OpenService(schSCManager, // SCM database + Name.c_str(), // name of service + SERVICE_QUERY_STATUS | SERVICE_QUERY_CONFIG); // need delete access + + if (schService == NULL) + { + DWORD Error = ::GetLastError(); + if (Error == ERROR_SERVICE_DOES_NOT_EXIST) + { + OutInfo.Status = ServiceStatus::NotInstalled; + return {}; + } + return MakeErrorCode(Error); + } + auto __ = MakeGuard([schService]() { CloseServiceHandle(schService); }); + + std::vector<std::uint8_t> Buffer(8192); + QUERY_SERVICE_CONFIG* ServiceConfig = reinterpret_cast<QUERY_SERVICE_CONFIG*>(Buffer.data()); + DWORD BytesNeeded = 0; + if (!QueryServiceConfig(schService, ServiceConfig, (DWORD)Buffer.size(), &BytesNeeded)) + { + return MakeErrorCodeFromLastError(); + } + + OutInfo.ExecutablePath = std::filesystem::path(ServiceConfig->lpBinaryPathName); + OutInfo.DisplayName = WideToUtf8(ServiceConfig->lpDisplayName); + + SERVICE_STATUS ServiceStatus; + if (!::QueryServiceStatus(schService, &ServiceStatus)) + { + return MakeErrorCodeFromLastError(); + } + + switch (ServiceStatus.dwCurrentState) + { + case SERVICE_STOPPED: + OutInfo.Status = ServiceStatus::Stopped; + break; + case SERVICE_START_PENDING: + OutInfo.Status = ServiceStatus::Starting; + break; + case SERVICE_STOP_PENDING: + OutInfo.Status = ServiceStatus::Stopping; + break; + case SERVICE_RUNNING: + OutInfo.Status = ServiceStatus::Running; + break; + case SERVICE_CONTINUE_PENDING: + OutInfo.Status = ServiceStatus::Resuming; + break; + case SERVICE_PAUSE_PENDING: + OutInfo.Status = ServiceStatus::Pausing; + break; + case SERVICE_PAUSED: + OutInfo.Status = ServiceStatus::Paused; + break; + default: + throw std::runtime_error(fmt::format("Unknown service status for '{}': {}", ServiceName, ServiceStatus.dwCurrentState)); + } + + if (!QueryServiceConfig2(schService, SERVICE_CONFIG_DESCRIPTION, Buffer.data(), (DWORD)Buffer.size(), &BytesNeeded)) + { + DWORD Error = ::GetLastError(); + if (Error == ERROR_INSUFFICIENT_BUFFER) + { + Buffer.resize((size_t)BytesNeeded); + if (!QueryServiceConfig2(schService, SERVICE_CONFIG_DESCRIPTION, Buffer.data(), (DWORD)Buffer.size(), &BytesNeeded)) + { + return MakeErrorCodeFromLastError(); + } + } + else + { + return MakeErrorCode(Error); + } + } + SERVICE_DESCRIPTION* Description = (SERVICE_DESCRIPTION*)Buffer.data(); + if (Description->lpDescription != NULL) + { + OutInfo.Description = WideToUtf8(std::wstring(Description->lpDescription)); + } + + return {}; +} + +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 |