diff options
| author | Liam Mitchell <[email protected]> | 2025-07-16 17:05:19 -0700 |
|---|---|---|
| committer | Liam Mitchell <[email protected]> | 2025-07-16 17:09:41 -0700 |
| commit | 2c6f3b6e0d23a327e695f535a34c7f6a56ecae1e (patch) | |
| tree | 1218f56c2dfd6ec9a98f5e8b12daaff1918df293 /src | |
| parent | Fix naming of service handle close guard variable (diff) | |
| download | zen-2c6f3b6e0d23a327e695f535a34c7f6a56ecae1e.tar.xz zen-2c6f3b6e0d23a327e695f535a34c7f6a56ecae1e.zip | |
Add --full option to service install, which will handle stop/uninstall if necessary, and copy binaries to install location
Diffstat (limited to 'src')
| -rw-r--r-- | src/zen/cmds/service_cmd.cpp | 128 | ||||
| -rw-r--r-- | 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), "<name>"); + m_InstallOptions.add_option("", + "", + "full", + fmt::format("Uninstall a running service and update service binaries before installing"), + cxxopts::value(m_FullInstall), + "<full>"); + m_InstallOptions.add_option("", + "", + "install-path", + fmt::format("Path in which to install service binaries"), + cxxopts::value(m_InstallPath), + "<install-path>"); + m_InstallOptions.add_option("", "u", "user", "User to run service as, defaults to current user", cxxopts::value(m_UserName), "<user>"); #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; |