aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/zen/cmds/service_cmd.cpp150
-rw-r--r--src/zen/cmds/service_cmd.h2
-rw-r--r--src/zencore/filesystem.cpp2
-rw-r--r--src/zenutil/service.cpp7
4 files changed, 147 insertions, 14 deletions
diff --git a/src/zen/cmds/service_cmd.cpp b/src/zen/cmds/service_cmd.cpp
index 91cce3ec1..a734504b8 100644
--- a/src/zen/cmds/service_cmd.cpp
+++ b/src/zen/cmds/service_cmd.cpp
@@ -7,6 +7,7 @@
#include <zencore/logging.h>
#include <zencore/zencore.h>
#include <zenutil/service.h>
+#include <filesystem>
#if ZEN_PLATFORM_WINDOWS
# include <zencore/windows.h>
@@ -162,6 +163,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 +347,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 = 30000; // Wait up to 30 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 +421,61 @@ 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 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();
+
+ if (!std::filesystem::is_directory(m_InstallPath) && !CreateDirectories(m_InstallPath))
+ {
+ ZEN_CONSOLE("Unable to create install directory '{}'", m_InstallPath);
+ return 1;
+ }
+
+ for (const std::filesystem::path& File : FilesToCopy)
+ {
+ std::filesystem::path Destination = m_InstallPath / File.filename();
+ 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();
+ }
+
+ Ec = InstallService(
m_ServiceName,
ServiceSpec {
.ExecutablePath = m_ServerExecutable, .CommandLineOptions = GlobalOptions.PassthroughCommandLine, .UserName = m_UserName
@@ -368,6 +490,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;
diff --git a/src/zencore/filesystem.cpp b/src/zencore/filesystem.cpp
index 46337ffc8..8327838c9 100644
--- a/src/zencore/filesystem.cpp
+++ b/src/zencore/filesystem.cpp
@@ -900,6 +900,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
{