aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorLiam Mitchell <[email protected]>2025-07-16 17:05:19 -0700
committerLiam Mitchell <[email protected]>2025-07-16 17:09:41 -0700
commit2c6f3b6e0d23a327e695f535a34c7f6a56ecae1e (patch)
tree1218f56c2dfd6ec9a98f5e8b12daaff1918df293 /src
parentFix naming of service handle close guard variable (diff)
downloadzen-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.cpp128
-rw-r--r--src/zen/cmds/service_cmd.h2
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;