diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/zen/cmds/service_cmd.cpp | 150 | ||||
| -rw-r--r-- | src/zen/cmds/service_cmd.h | 2 | ||||
| -rw-r--r-- | src/zencore/filesystem.cpp | 2 | ||||
| -rw-r--r-- | src/zenutil/service.cpp | 7 |
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 { |