namespace Tgstation.Server.Host.Service.Wix.Extensions { using System; using System.IO; using System.ServiceProcess; using Tgstation.Server.Host.Common; using WixToolset.Dtf.WindowsInstaller; /// /// Extension methods for the .msi installer. /// public static class InstallationExtensions { /// /// Package name /// /// As much as I'd like to use Tgstation.Server.Common.Constants.CanonicalPackageName here, attempting to reference it makes VS go crazy with fake errors. const string CanonicalPackageName = "tgstation-server"; /// /// Attempts to detach stop the existing tgstation-server service if it exists. /// /// The installer . /// The of the custom action. [CustomAction] public static ActionResult DetachStopTgsServiceIfRunning(Session session) { if (session == null) throw new ArgumentNullException(nameof(session)); try { session.Log("Begin DetachStopTgsServiceIfRunning"); ServiceController serviceController = null; session.Log($"Searching for {CanonicalPackageName} service..."); try { foreach (var controller in ServiceController.GetServices()) if (controller.ServiceName == CanonicalPackageName) serviceController = controller; else controller.Dispose(); if (serviceController == null || serviceController.Status != ServiceControllerStatus.Running) { session.Log($"{CanonicalPackageName} service not found. Continuing."); return ActionResult.Success; } var commandId = PipeCommands.GetServiceCommandId( PipeCommands.CommandDetachingShutdown) .Value; session.Log($"{CanonicalPackageName} service found. Sending command \"{PipeCommands.CommandDetachingShutdown}\" ({commandId})..."); serviceController.ExecuteCommand(commandId); session.Log($"Command sent. Waiting for {CanonicalPackageName} service to stop..."); serviceController.WaitForStatus(ServiceControllerStatus.Stopped, TimeSpan.FromMinutes(1)); var stopped = serviceController.Status == ServiceControllerStatus.Stopped; session.Log($"{CanonicalPackageName} stopped {(stopped ? String.Empty : "un")}successfully."); return stopped ? ActionResult.Success : ActionResult.NotExecuted; } finally { serviceController?.Dispose(); } } catch (Exception ex) { session.Log($"Exception in DetachStopTgsServiceIfRunning:{Environment.NewLine}{ex}"); return ActionResult.Failure; } } /// /// Attempts to copy the initial config to the production config if necessary. /// /// The installer . /// The of the custom action. [CustomAction] public static ActionResult ApplyProductionAppsettingsIfNonExistant(Session session) { if (session == null) throw new ArgumentNullException(nameof(session)); try { session.Log("Begin ApplyProductionAppsettingsIfNonExistant"); var programDataDirectory = Path.Combine( Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), CanonicalPackageName); var initialAppSettingsPath = Path.Combine(programDataDirectory, "appsettings.Initial.yml"); var productionAppSettingsPath = Path.Combine(programDataDirectory, "appsettings.Production.yml"); var initialAppSettingsExists = File.Exists(initialAppSettingsPath); var productionAppSettingsExists = File.Exists(productionAppSettingsPath); if (productionAppSettingsExists) session.Log("appsettings.Production.yml present"); else session.Log("appsettings.Production.yml NOT present"); if (!initialAppSettingsExists) session.Log("appsettings.Initial.yml NOT present!"); else { session.Log("appsettings.Initial.yml present"); if (!productionAppSettingsExists) { session.Log("Copying initial settings to production settings..."); File.Copy(initialAppSettingsPath, productionAppSettingsPath); return ActionResult.Success; } } return ActionResult.NotExecuted; } catch (Exception ex) { session.Log($"Exception in ApplyProductionAppsettingsIfNonExistant:{Environment.NewLine}{ex}"); return ActionResult.Failure; } } } }