mirror of https://github.com/winsw/winsw
Refactor service configuration
parent
94954e076f
commit
efc3e34f6d
|
@ -1,90 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.ServiceProcess;
|
||||
using System.Xml;
|
||||
|
||||
namespace WinSW.Configuration
|
||||
{
|
||||
/// <summary>
|
||||
/// Default WinSW settings
|
||||
/// </summary>
|
||||
public sealed class DefaultWinSWSettings : IWinSWConfiguration
|
||||
{
|
||||
public string FullPath => throw new InvalidOperationException(nameof(this.FullPath) + " must be specified.");
|
||||
|
||||
public string Id => throw new InvalidOperationException(nameof(this.Id) + " must be specified.");
|
||||
|
||||
public string Caption => string.Empty;
|
||||
|
||||
public string Description => string.Empty;
|
||||
|
||||
public string Executable => throw new InvalidOperationException(nameof(this.Executable) + " must be specified.");
|
||||
|
||||
public bool HideWindow => false;
|
||||
|
||||
public string ExecutablePath => Process.GetCurrentProcess().MainModule.FileName;
|
||||
|
||||
// Installation
|
||||
public bool AllowServiceAcountLogonRight => false;
|
||||
|
||||
public string? ServiceAccountPassword => null;
|
||||
|
||||
public string? ServiceAccountUserName => null;
|
||||
|
||||
public Native.SC_ACTION[] FailureActions => new Native.SC_ACTION[0];
|
||||
|
||||
public TimeSpan ResetFailureAfter => TimeSpan.FromDays(1);
|
||||
|
||||
// Executable management
|
||||
public string Arguments => string.Empty;
|
||||
|
||||
public string? StartArguments => null;
|
||||
|
||||
public string? StopExecutable => null;
|
||||
|
||||
public string? StopArguments => null;
|
||||
|
||||
public string WorkingDirectory => Path.GetDirectoryName(this.FullPath)!;
|
||||
|
||||
public ProcessPriorityClass Priority => ProcessPriorityClass.Normal;
|
||||
|
||||
public TimeSpan StopTimeout => TimeSpan.FromSeconds(15);
|
||||
|
||||
// Service management
|
||||
public ServiceStartMode StartMode => ServiceStartMode.Automatic;
|
||||
|
||||
public bool DelayedAutoStart => false;
|
||||
|
||||
public bool Preshutdown => false;
|
||||
|
||||
public string[] ServiceDependencies => new string[0];
|
||||
|
||||
public bool Interactive => false;
|
||||
|
||||
// Logging
|
||||
public string LogDirectory => Path.GetDirectoryName(this.ExecutablePath)!;
|
||||
|
||||
public string LogMode => "append";
|
||||
|
||||
public bool OutFileDisabled => false;
|
||||
|
||||
public bool ErrFileDisabled => false;
|
||||
|
||||
public string OutFilePattern => ".out.log";
|
||||
|
||||
public string ErrFilePattern => ".err.log";
|
||||
|
||||
// Environment
|
||||
public List<Download> Downloads => new List<Download>(0);
|
||||
|
||||
public Dictionary<string, string> EnvironmentVariables => new Dictionary<string, string>(0);
|
||||
|
||||
// Misc
|
||||
public bool BeepOnShutdown => false;
|
||||
|
||||
// Extensions
|
||||
public XmlNode? ExtensionsConfiguration => null;
|
||||
}
|
||||
}
|
|
@ -1,76 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.ServiceProcess;
|
||||
using System.Xml;
|
||||
|
||||
namespace WinSW.Configuration
|
||||
{
|
||||
// TODO: Document the parameters && refactor
|
||||
public interface IWinSWConfiguration
|
||||
{
|
||||
string FullPath { get; }
|
||||
|
||||
string Id { get; }
|
||||
|
||||
string Caption { get; }
|
||||
|
||||
string Description { get; }
|
||||
|
||||
string Executable { get; }
|
||||
|
||||
string ExecutablePath { get; }
|
||||
|
||||
bool HideWindow { get; }
|
||||
|
||||
// Installation
|
||||
bool AllowServiceAcountLogonRight { get; }
|
||||
|
||||
string? ServiceAccountPassword { get; }
|
||||
|
||||
string? ServiceAccountUserName { get; }
|
||||
|
||||
Native.SC_ACTION[] FailureActions { get; }
|
||||
|
||||
TimeSpan ResetFailureAfter { get; }
|
||||
|
||||
// Executable management
|
||||
string Arguments { get; }
|
||||
|
||||
string? StartArguments { get; }
|
||||
|
||||
string? StopExecutable { get; }
|
||||
|
||||
string? StopArguments { get; }
|
||||
|
||||
string WorkingDirectory { get; }
|
||||
|
||||
ProcessPriorityClass Priority { get; }
|
||||
|
||||
TimeSpan StopTimeout { get; }
|
||||
|
||||
// Service management
|
||||
ServiceStartMode StartMode { get; }
|
||||
|
||||
string[] ServiceDependencies { get; }
|
||||
|
||||
bool Interactive { get; }
|
||||
|
||||
// Logging
|
||||
string LogDirectory { get; }
|
||||
|
||||
// TODO: replace by enum
|
||||
string LogMode { get; }
|
||||
|
||||
// Environment
|
||||
List<Download> Downloads { get; }
|
||||
|
||||
Dictionary<string, string> EnvironmentVariables { get; }
|
||||
|
||||
// Misc
|
||||
bool BeepOnShutdown { get; }
|
||||
|
||||
// Extensions
|
||||
XmlNode? ExtensionsConfiguration { get; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,90 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.ServiceProcess;
|
||||
using System.Xml;
|
||||
|
||||
namespace WinSW.Configuration
|
||||
{
|
||||
/// <summary>
|
||||
/// Default WinSW settings
|
||||
/// </summary>
|
||||
public abstract class ServiceConfig
|
||||
{
|
||||
public abstract string FullPath { get; }
|
||||
|
||||
public abstract string Id { get; }
|
||||
|
||||
public virtual string Caption => string.Empty;
|
||||
|
||||
public virtual string Description => string.Empty;
|
||||
|
||||
public abstract string Executable { get; }
|
||||
|
||||
public string ExecutablePath => Process.GetCurrentProcess().MainModule.FileName;
|
||||
|
||||
public virtual bool HideWindow => false;
|
||||
|
||||
// Installation
|
||||
public virtual bool AllowServiceAcountLogonRight => false;
|
||||
|
||||
public virtual string? ServiceAccountPassword => null;
|
||||
|
||||
public virtual string? ServiceAccountUserName => null;
|
||||
|
||||
public virtual Native.SC_ACTION[] FailureActions => new Native.SC_ACTION[0];
|
||||
|
||||
public virtual TimeSpan ResetFailureAfter => TimeSpan.FromDays(1);
|
||||
|
||||
// Executable management
|
||||
public virtual string Arguments => string.Empty;
|
||||
|
||||
public virtual string? StartArguments => null;
|
||||
|
||||
public virtual string? StopExecutable => null;
|
||||
|
||||
public virtual string? StopArguments => null;
|
||||
|
||||
public virtual string WorkingDirectory => Path.GetDirectoryName(this.FullPath)!;
|
||||
|
||||
public virtual ProcessPriorityClass Priority => ProcessPriorityClass.Normal;
|
||||
|
||||
public virtual TimeSpan StopTimeout => TimeSpan.FromSeconds(15);
|
||||
|
||||
// Service management
|
||||
public virtual ServiceStartMode StartMode => ServiceStartMode.Automatic;
|
||||
|
||||
public virtual string[] ServiceDependencies => new string[0];
|
||||
|
||||
public virtual bool Interactive => false;
|
||||
|
||||
public virtual bool DelayedAutoStart => false;
|
||||
|
||||
public virtual bool Preshutdown => false;
|
||||
|
||||
// Logging
|
||||
public virtual string LogDirectory => Path.GetDirectoryName(this.ExecutablePath)!;
|
||||
|
||||
public virtual string LogMode => "append";
|
||||
|
||||
public virtual bool OutFileDisabled => false;
|
||||
|
||||
public virtual bool ErrFileDisabled => false;
|
||||
|
||||
public virtual string OutFilePattern => ".out.log";
|
||||
|
||||
public virtual string ErrFilePattern => ".err.log";
|
||||
|
||||
// Environment
|
||||
public virtual List<Download> Downloads => new List<Download>(0);
|
||||
|
||||
public virtual Dictionary<string, string> EnvironmentVariables => new Dictionary<string, string>(0);
|
||||
|
||||
// Misc
|
||||
public virtual bool BeepOnShutdown => false;
|
||||
|
||||
// Extensions
|
||||
public virtual XmlNode? ExtensionsConfiguration => null;
|
||||
}
|
||||
}
|
|
@ -15,17 +15,15 @@ namespace WinSW
|
|||
/// <summary>
|
||||
/// In-memory representation of the configuration file.
|
||||
/// </summary>
|
||||
public class ServiceDescriptor : IWinSWConfiguration
|
||||
public class XmlServiceConfig : ServiceConfig
|
||||
{
|
||||
protected readonly XmlDocument dom = new XmlDocument();
|
||||
|
||||
private readonly Dictionary<string, string> environmentVariables;
|
||||
|
||||
internal static ServiceDescriptor? TestDescriptor;
|
||||
internal static XmlServiceConfig? TestConfig;
|
||||
|
||||
public static DefaultWinSWSettings Defaults { get; } = new DefaultWinSWSettings();
|
||||
|
||||
public string FullPath { get; }
|
||||
public override string FullPath { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Where did we find the configuration file?
|
||||
|
@ -41,10 +39,7 @@ namespace WinSW
|
|||
/// </summary>
|
||||
public string BaseName { get; set; }
|
||||
|
||||
// Currently there is no opportunity to alter the executable path
|
||||
public virtual string ExecutablePath => Defaults.ExecutablePath;
|
||||
|
||||
public ServiceDescriptor()
|
||||
public XmlServiceConfig()
|
||||
{
|
||||
string path = this.ExecutablePath;
|
||||
string baseName = Path.GetFileNameWithoutExtension(path);
|
||||
|
@ -84,7 +79,7 @@ namespace WinSW
|
|||
}
|
||||
|
||||
/// <exception cref="FileNotFoundException" />
|
||||
public ServiceDescriptor(string path)
|
||||
public XmlServiceConfig(string path)
|
||||
{
|
||||
if (!File.Exists(path))
|
||||
{
|
||||
|
@ -122,10 +117,10 @@ namespace WinSW
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Loads descriptor from existing DOM
|
||||
/// Loads config from existing DOM
|
||||
/// </summary>
|
||||
#pragma warning disable CS8618 // Non-nullable field is uninitialized. Consider declaring as nullable.
|
||||
public ServiceDescriptor(XmlDocument dom)
|
||||
public XmlServiceConfig(XmlDocument dom)
|
||||
#pragma warning restore CS8618 // Non-nullable field is uninitialized. Consider declaring as nullable.
|
||||
{
|
||||
this.dom = dom;
|
||||
|
@ -133,16 +128,16 @@ namespace WinSW
|
|||
this.environmentVariables = this.LoadEnvironmentVariables();
|
||||
}
|
||||
|
||||
internal static ServiceDescriptor Create(string? path)
|
||||
internal static XmlServiceConfig Create(string? path)
|
||||
{
|
||||
return path != null ? new ServiceDescriptor(path) : TestDescriptor ?? new ServiceDescriptor();
|
||||
return path != null ? new XmlServiceConfig(path) : TestConfig ?? new XmlServiceConfig();
|
||||
}
|
||||
|
||||
public static ServiceDescriptor FromXml(string xml)
|
||||
public static XmlServiceConfig FromXml(string xml)
|
||||
{
|
||||
var dom = new XmlDocument();
|
||||
dom.LoadXml(xml);
|
||||
return new ServiceDescriptor(dom);
|
||||
return new XmlServiceConfig(dom);
|
||||
}
|
||||
|
||||
private string SingleElement(string tagName)
|
||||
|
@ -213,31 +208,31 @@ namespace WinSW
|
|||
/// <summary>
|
||||
/// Path to the executable.
|
||||
/// </summary>
|
||||
public string Executable => this.SingleElement("executable");
|
||||
public override string Executable => this.SingleElement("executable");
|
||||
|
||||
public bool HideWindow => this.SingleBoolElement("hidewindow", Defaults.HideWindow);
|
||||
public override bool HideWindow => this.SingleBoolElement("hidewindow", base.HideWindow);
|
||||
|
||||
/// <summary>
|
||||
/// Optionally specify a different Path to an executable to shutdown the service.
|
||||
/// </summary>
|
||||
public string? StopExecutable => this.SingleElement("stopexecutable", true);
|
||||
public override string? StopExecutable => this.SingleElement("stopexecutable", true);
|
||||
|
||||
/// <summary>
|
||||
/// The <c>arguments</c> element.
|
||||
/// </summary>
|
||||
public string Arguments
|
||||
public override string Arguments
|
||||
{
|
||||
get
|
||||
{
|
||||
XmlNode? argumentsNode = this.dom.SelectSingleNode("//arguments");
|
||||
return argumentsNode is null ? Defaults.Arguments : Environment.ExpandEnvironmentVariables(argumentsNode.InnerText);
|
||||
return argumentsNode is null ? base.Arguments : Environment.ExpandEnvironmentVariables(argumentsNode.InnerText);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The <c>startarguments</c> element.
|
||||
/// </summary>
|
||||
public string? StartArguments
|
||||
public override string? StartArguments
|
||||
{
|
||||
get
|
||||
{
|
||||
|
@ -249,7 +244,7 @@ namespace WinSW
|
|||
/// <summary>
|
||||
/// The <c>stoparguments</c> element.
|
||||
/// </summary>
|
||||
public string? StopArguments
|
||||
public override string? StopArguments
|
||||
{
|
||||
get
|
||||
{
|
||||
|
@ -274,12 +269,12 @@ namespace WinSW
|
|||
|
||||
public string? PoststopArguments => this.GetArguments(Names.Poststop);
|
||||
|
||||
public string WorkingDirectory
|
||||
public override string WorkingDirectory
|
||||
{
|
||||
get
|
||||
{
|
||||
var wd = this.SingleElement("workingdirectory", true);
|
||||
return string.IsNullOrEmpty(wd) ? Defaults.WorkingDirectory : wd!;
|
||||
return string.IsNullOrEmpty(wd) ? base.WorkingDirectory : wd!;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -304,7 +299,7 @@ namespace WinSW
|
|||
}
|
||||
}
|
||||
|
||||
public XmlNode? ExtensionsConfiguration => this.dom.SelectSingleNode("//extensions");
|
||||
public override XmlNode? ExtensionsConfiguration => this.dom.SelectSingleNode("//extensions");
|
||||
|
||||
/// <summary>
|
||||
/// Combines the contents of all the elements of the given name,
|
||||
|
@ -351,19 +346,19 @@ namespace WinSW
|
|||
/// <summary>
|
||||
/// LogDirectory is the service wrapper executable directory or the optionally specified logpath element.
|
||||
/// </summary>
|
||||
public string LogDirectory
|
||||
public override string LogDirectory
|
||||
{
|
||||
get
|
||||
{
|
||||
XmlNode? loggingNode = this.dom.SelectSingleNode("//logpath");
|
||||
|
||||
return loggingNode is null
|
||||
? Defaults.LogDirectory
|
||||
? base.LogDirectory
|
||||
: Environment.ExpandEnvironmentVariables(loggingNode.InnerText);
|
||||
}
|
||||
}
|
||||
|
||||
public string LogMode
|
||||
public override string LogMode
|
||||
{
|
||||
get
|
||||
{
|
||||
|
@ -385,7 +380,7 @@ namespace WinSW
|
|||
}
|
||||
}
|
||||
|
||||
return mode ?? Defaults.LogMode;
|
||||
return mode ?? base.LogMode;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -399,27 +394,27 @@ namespace WinSW
|
|||
}
|
||||
}
|
||||
|
||||
public bool OutFileDisabled => this.SingleBoolElement("outfiledisabled", Defaults.OutFileDisabled);
|
||||
public override bool OutFileDisabled => this.SingleBoolElement("outfiledisabled", base.OutFileDisabled);
|
||||
|
||||
public bool ErrFileDisabled => this.SingleBoolElement("errfiledisabled", Defaults.ErrFileDisabled);
|
||||
public override bool ErrFileDisabled => this.SingleBoolElement("errfiledisabled", base.ErrFileDisabled);
|
||||
|
||||
public string OutFilePattern
|
||||
public override string OutFilePattern
|
||||
{
|
||||
get
|
||||
{
|
||||
XmlNode? loggingName = this.dom.SelectSingleNode("//outfilepattern");
|
||||
|
||||
return loggingName is null ? Defaults.OutFilePattern : Environment.ExpandEnvironmentVariables(loggingName.InnerText);
|
||||
return loggingName is null ? base.OutFilePattern : Environment.ExpandEnvironmentVariables(loggingName.InnerText);
|
||||
}
|
||||
}
|
||||
|
||||
public string ErrFilePattern
|
||||
public override string ErrFilePattern
|
||||
{
|
||||
get
|
||||
{
|
||||
XmlNode? loggingName = this.dom.SelectSingleNode("//errfilepattern");
|
||||
|
||||
return loggingName is null ? Defaults.ErrFilePattern : Environment.ExpandEnvironmentVariables(loggingName.InnerText);
|
||||
return loggingName is null ? base.ErrFilePattern : Environment.ExpandEnvironmentVariables(loggingName.InnerText);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -514,14 +509,14 @@ namespace WinSW
|
|||
/// <summary>
|
||||
/// Optionally specified depend services that must start before this service starts.
|
||||
/// </summary>
|
||||
public string[] ServiceDependencies
|
||||
public override string[] ServiceDependencies
|
||||
{
|
||||
get
|
||||
{
|
||||
XmlNodeList? nodeList = this.dom.SelectNodes("//depend");
|
||||
if (nodeList is null)
|
||||
{
|
||||
return Defaults.ServiceDependencies;
|
||||
return base.ServiceDependencies;
|
||||
}
|
||||
|
||||
string[] serviceDependencies = new string[nodeList.Count];
|
||||
|
@ -534,23 +529,23 @@ namespace WinSW
|
|||
}
|
||||
}
|
||||
|
||||
public string Id => this.SingleElement("id");
|
||||
public override string Id => this.SingleElement("id");
|
||||
|
||||
public string Caption => this.SingleElement("name", true) ?? Defaults.Caption;
|
||||
public override string Caption => this.SingleElement("name", true) ?? base.Caption;
|
||||
|
||||
public string Description => this.SingleElement("description", true) ?? Defaults.Description;
|
||||
public override string Description => this.SingleElement("description", true) ?? base.Description;
|
||||
|
||||
/// <summary>
|
||||
/// Start mode of the Service
|
||||
/// </summary>
|
||||
public ServiceStartMode StartMode
|
||||
public override ServiceStartMode StartMode
|
||||
{
|
||||
get
|
||||
{
|
||||
string? p = this.SingleElement("startmode", true);
|
||||
if (p is null)
|
||||
{
|
||||
return Defaults.StartMode;
|
||||
return base.StartMode;
|
||||
}
|
||||
|
||||
try
|
||||
|
@ -575,9 +570,9 @@ namespace WinSW
|
|||
/// True if the service should be installed with the DelayedAutoStart flag.
|
||||
/// This setting will be applyed only during the install command and only when the Automatic start mode is configured.
|
||||
/// </summary>
|
||||
public bool DelayedAutoStart => this.SingleBoolElement("delayedAutoStart", Defaults.DelayedAutoStart);
|
||||
public override bool DelayedAutoStart => this.SingleBoolElement("delayedAutoStart", base.DelayedAutoStart);
|
||||
|
||||
public bool Preshutdown => this.SingleBoolElement("preshutdown", Defaults.Preshutdown);
|
||||
public override bool Preshutdown => this.SingleBoolElement("preshutdown", base.Preshutdown);
|
||||
|
||||
public TimeSpan? PreshutdownTimeout
|
||||
{
|
||||
|
@ -592,30 +587,30 @@ namespace WinSW
|
|||
/// True if the service should beep when finished on shutdown.
|
||||
/// This doesn't work on some OSes. See http://msdn.microsoft.com/en-us/library/ms679277%28VS.85%29.aspx
|
||||
/// </summary>
|
||||
public bool BeepOnShutdown => this.SingleBoolElement("beeponshutdown", Defaults.DelayedAutoStart);
|
||||
public override bool BeepOnShutdown => this.SingleBoolElement("beeponshutdown", base.DelayedAutoStart);
|
||||
|
||||
/// <summary>
|
||||
/// True if the service can interact with the desktop.
|
||||
/// </summary>
|
||||
public bool Interactive => this.SingleBoolElement("interactive", Defaults.DelayedAutoStart);
|
||||
public override bool Interactive => this.SingleBoolElement("interactive", base.DelayedAutoStart);
|
||||
|
||||
/// <summary>
|
||||
/// Environment variable overrides
|
||||
/// </summary>
|
||||
public Dictionary<string, string> EnvironmentVariables => new Dictionary<string, string>(this.environmentVariables);
|
||||
public override Dictionary<string, string> EnvironmentVariables => new Dictionary<string, string>(this.environmentVariables);
|
||||
|
||||
/// <summary>
|
||||
/// List of downloads to be performed by the wrapper before starting
|
||||
/// a service.
|
||||
/// </summary>
|
||||
public List<Download> Downloads
|
||||
public override List<Download> Downloads
|
||||
{
|
||||
get
|
||||
{
|
||||
XmlNodeList? nodeList = this.dom.SelectNodes("//download");
|
||||
if (nodeList is null)
|
||||
{
|
||||
return Defaults.Downloads;
|
||||
return base.Downloads;
|
||||
}
|
||||
|
||||
List<Download> result = new List<Download>(nodeList.Count);
|
||||
|
@ -631,7 +626,7 @@ namespace WinSW
|
|||
}
|
||||
}
|
||||
|
||||
public SC_ACTION[] FailureActions
|
||||
public override SC_ACTION[] FailureActions
|
||||
{
|
||||
get
|
||||
{
|
||||
|
@ -661,7 +656,7 @@ namespace WinSW
|
|||
}
|
||||
}
|
||||
|
||||
public TimeSpan ResetFailureAfter => this.SingleTimeSpanElement(this.dom, "resetfailure", Defaults.ResetFailureAfter);
|
||||
public override TimeSpan ResetFailureAfter => this.SingleTimeSpanElement(this.dom, "resetfailure", base.ResetFailureAfter);
|
||||
|
||||
protected string? GetServiceAccountPart(string subNodeName)
|
||||
{
|
||||
|
@ -683,16 +678,16 @@ namespace WinSW
|
|||
|
||||
protected string? AllowServiceLogon => this.GetServiceAccountPart("allowservicelogon");
|
||||
|
||||
public string? ServiceAccountPassword => this.GetServiceAccountPart("password");
|
||||
public override string? ServiceAccountPassword => this.GetServiceAccountPart("password");
|
||||
|
||||
public string? ServiceAccountUserName => this.GetServiceAccountPart("username");
|
||||
public override string? ServiceAccountUserName => this.GetServiceAccountPart("username");
|
||||
|
||||
public bool HasServiceAccount()
|
||||
{
|
||||
return this.dom.SelectSingleNode("//serviceaccount") != null;
|
||||
}
|
||||
|
||||
public bool AllowServiceAcountLogonRight
|
||||
public override bool AllowServiceAcountLogonRight
|
||||
{
|
||||
get
|
||||
{
|
||||
|
@ -711,19 +706,19 @@ namespace WinSW
|
|||
/// <summary>
|
||||
/// Time to wait for the service to gracefully shutdown the executable before we forcibly kill it
|
||||
/// </summary>
|
||||
public TimeSpan StopTimeout => this.SingleTimeSpanElement(this.dom, "stoptimeout", Defaults.StopTimeout);
|
||||
public override TimeSpan StopTimeout => this.SingleTimeSpanElement(this.dom, "stoptimeout", base.StopTimeout);
|
||||
|
||||
/// <summary>
|
||||
/// Desired process priority or null if not specified.
|
||||
/// </summary>
|
||||
public ProcessPriorityClass Priority
|
||||
public override ProcessPriorityClass Priority
|
||||
{
|
||||
get
|
||||
{
|
||||
string? p = this.SingleElement("priority", true);
|
||||
if (p is null)
|
||||
{
|
||||
return Defaults.Priority;
|
||||
return base.Priority;
|
||||
}
|
||||
|
||||
return (ProcessPriorityClass)Enum.Parse(typeof(ProcessPriorityClass), p, true);
|
|
@ -10,7 +10,7 @@ namespace WinSW.Extensions
|
|||
public WinSWExtensionDescriptor Descriptor { get; set; }
|
||||
#pragma warning restore CS8618 // Non-nullable field is uninitialized. Consider declaring as nullable.
|
||||
|
||||
public virtual void Configure(ServiceDescriptor descriptor, XmlNode node)
|
||||
public virtual void Configure(XmlServiceConfig config, XmlNode node)
|
||||
{
|
||||
// Do nothing
|
||||
}
|
||||
|
|
|
@ -25,9 +25,9 @@ namespace WinSW.Extensions
|
|||
/// <summary>
|
||||
/// Init handler. Extension should load it's config during that step
|
||||
/// </summary>
|
||||
/// <param name="descriptor">Service descriptor</param>
|
||||
/// <param name="config">Service config</param>
|
||||
/// <param name="node">Configuration node</param>
|
||||
void Configure(ServiceDescriptor descriptor, XmlNode node);
|
||||
void Configure(XmlServiceConfig config, XmlNode node);
|
||||
|
||||
/// <summary>
|
||||
/// Start handler. Called during startup of the service before the child process.
|
||||
|
|
|
@ -9,13 +9,13 @@ namespace WinSW.Extensions
|
|||
{
|
||||
public Dictionary<string, IWinSWExtension> Extensions { get; }
|
||||
|
||||
public ServiceDescriptor ServiceDescriptor { get; }
|
||||
public XmlServiceConfig ServiceConfig { get; }
|
||||
|
||||
private static readonly ILog Log = LogManager.GetLogger(typeof(WinSWExtensionManager));
|
||||
|
||||
public WinSWExtensionManager(ServiceDescriptor serviceDescriptor)
|
||||
public WinSWExtensionManager(XmlServiceConfig serviceConfig)
|
||||
{
|
||||
this.ServiceDescriptor = serviceDescriptor;
|
||||
this.ServiceConfig = serviceConfig;
|
||||
this.Extensions = new Dictionary<string, IWinSWExtension>();
|
||||
}
|
||||
|
||||
|
@ -107,7 +107,7 @@ namespace WinSW.Extensions
|
|||
/// <exception cref="Exception">Loading failure</exception>
|
||||
public void LoadExtensions()
|
||||
{
|
||||
var extensionIds = this.ServiceDescriptor.ExtensionIds;
|
||||
var extensionIds = this.ServiceConfig.ExtensionIds;
|
||||
foreach (string extensionId in extensionIds)
|
||||
{
|
||||
this.LoadExtension(extensionId);
|
||||
|
@ -127,7 +127,7 @@ namespace WinSW.Extensions
|
|||
throw new ExtensionException(id, "Extension has been already loaded");
|
||||
}
|
||||
|
||||
XmlNode? extensionsConfig = this.ServiceDescriptor.ExtensionsConfiguration;
|
||||
XmlNode? extensionsConfig = this.ServiceConfig.ExtensionsConfiguration;
|
||||
XmlElement? configNode = extensionsConfig is null ? null : extensionsConfig.SelectSingleNode("extension[@id='" + id + "'][1]") as XmlElement;
|
||||
if (configNode is null)
|
||||
{
|
||||
|
@ -141,7 +141,7 @@ namespace WinSW.Extensions
|
|||
extension.Descriptor = descriptor;
|
||||
try
|
||||
{
|
||||
extension.Configure(this.ServiceDescriptor, configNode);
|
||||
extension.Configure(this.ServiceConfig, configNode);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{ // Consider any unexpected exception as fatal
|
||||
|
|
|
@ -173,13 +173,13 @@ namespace WinSW.Plugins.RunawayProcessKiller
|
|||
return parameters.Environment;
|
||||
}
|
||||
|
||||
public override void Configure(ServiceDescriptor descriptor, XmlNode node)
|
||||
public override void Configure(XmlServiceConfig config, XmlNode node)
|
||||
{
|
||||
// We expect the upper logic to process any errors
|
||||
// TODO: a better parser API for types would be useful
|
||||
this.Pidfile = XmlHelper.SingleElement(node, "pidfile", false)!;
|
||||
this.StopTimeout = TimeSpan.FromMilliseconds(int.Parse(XmlHelper.SingleElement(node, "stopTimeout", false)!));
|
||||
this.ServiceId = descriptor.Id;
|
||||
this.ServiceId = config.Id;
|
||||
|
||||
// TODO: Consider making it documented
|
||||
var checkWinSWEnvironmentVariable = XmlHelper.SingleElement(node, "checkWinSWEnvironmentVariable", true);
|
||||
|
|
|
@ -27,7 +27,7 @@ namespace WinSW.Plugins.SharedDirectoryMapper
|
|||
this.entries.Add(config);
|
||||
}
|
||||
|
||||
public override void Configure(ServiceDescriptor descriptor, XmlNode node)
|
||||
public override void Configure(XmlServiceConfig config, XmlNode node)
|
||||
{
|
||||
XmlNodeList? mapNodes = XmlHelper.SingleNode(node, "mapping", false)!.SelectNodes("map");
|
||||
if (mapNodes != null)
|
||||
|
@ -36,8 +36,7 @@ namespace WinSW.Plugins.SharedDirectoryMapper
|
|||
{
|
||||
if (mapNodes[i] is XmlElement mapElement)
|
||||
{
|
||||
var config = SharedDirectoryMapperConfig.FromXml(mapElement);
|
||||
this.entries.Add(config);
|
||||
this.entries.Add(SharedDirectoryMapperConfig.FromXml(mapElement));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,26 +15,26 @@ namespace WinSW.Tests.Configuration
|
|||
[Fact]
|
||||
public void AllOptionsConfigShouldDeclareDefaults()
|
||||
{
|
||||
ServiceDescriptor desc = Load("complete");
|
||||
XmlServiceConfig config = Load("complete");
|
||||
|
||||
Assert.Equal("myapp", desc.Id);
|
||||
Assert.Equal("%BASE%\\myExecutable.exe", desc.Executable);
|
||||
Assert.Equal("myapp", config.Id);
|
||||
Assert.Equal("%BASE%\\myExecutable.exe", config.Executable);
|
||||
|
||||
ServiceDescriptorAssert.AssertAllOptionalPropertiesAreDefault(desc);
|
||||
ServiceConfigAssert.AssertAllOptionalPropertiesAreDefault(config);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MinimalConfigShouldDeclareDefaults()
|
||||
{
|
||||
ServiceDescriptor desc = Load("minimal");
|
||||
XmlServiceConfig config = Load("minimal");
|
||||
|
||||
Assert.Equal("myapp", desc.Id);
|
||||
Assert.Equal("%BASE%\\myExecutable.exe", desc.Executable);
|
||||
Assert.Equal("myapp", config.Id);
|
||||
Assert.Equal("%BASE%\\myExecutable.exe", config.Executable);
|
||||
|
||||
ServiceDescriptorAssert.AssertAllOptionalPropertiesAreDefault(desc);
|
||||
ServiceConfigAssert.AssertAllOptionalPropertiesAreDefault(config);
|
||||
}
|
||||
|
||||
private static ServiceDescriptor Load(string exampleName)
|
||||
private static XmlServiceConfig Load(string exampleName)
|
||||
{
|
||||
string directory = Environment.CurrentDirectory;
|
||||
while (true)
|
||||
|
@ -53,7 +53,7 @@ namespace WinSW.Tests.Configuration
|
|||
|
||||
XmlDocument dom = new XmlDocument();
|
||||
dom.Load(path);
|
||||
return new ServiceDescriptor(dom);
|
||||
return new XmlServiceConfig(dom);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,10 +22,10 @@ namespace WinSW.Tests
|
|||
{
|
||||
// Roundtrip data
|
||||
Download d = new Download(From, To);
|
||||
var sd = ConfigXmlBuilder.Create(this.output)
|
||||
var config = ConfigXmlBuilder.Create(this.output)
|
||||
.WithDownload(d)
|
||||
.ToServiceDescriptor(true);
|
||||
var loaded = this.GetSingleEntry(sd);
|
||||
.ToServiceConfig(true);
|
||||
var loaded = this.GetSingleEntry(config);
|
||||
|
||||
// Check default values
|
||||
Assert.False(loaded.FailOnError);
|
||||
|
@ -40,10 +40,10 @@ namespace WinSW.Tests
|
|||
{
|
||||
// Roundtrip data
|
||||
Download d = new Download(From, To, true, Download.AuthType.Basic, "aUser", "aPassword", true);
|
||||
var sd = ConfigXmlBuilder.Create(this.output)
|
||||
var config = ConfigXmlBuilder.Create(this.output)
|
||||
.WithDownload(d)
|
||||
.ToServiceDescriptor(true);
|
||||
var loaded = this.GetSingleEntry(sd);
|
||||
.ToServiceConfig(true);
|
||||
var loaded = this.GetSingleEntry(config);
|
||||
|
||||
// Check default values
|
||||
Assert.True(loaded.FailOnError);
|
||||
|
@ -58,10 +58,10 @@ namespace WinSW.Tests
|
|||
{
|
||||
// Roundtrip data
|
||||
Download d = new Download(From, To, false, Download.AuthType.Sspi);
|
||||
var sd = ConfigXmlBuilder.Create(this.output)
|
||||
var config = ConfigXmlBuilder.Create(this.output)
|
||||
.WithDownload(d)
|
||||
.ToServiceDescriptor(true);
|
||||
var loaded = this.GetSingleEntry(sd);
|
||||
.ToServiceConfig(true);
|
||||
var loaded = this.GetSingleEntry(config);
|
||||
|
||||
// Check default values
|
||||
Assert.False(loaded.FailOnError);
|
||||
|
@ -107,11 +107,11 @@ namespace WinSW.Tests
|
|||
{
|
||||
Download d = new Download(From, To, failOnError);
|
||||
|
||||
var sd = ConfigXmlBuilder.Create(this.output)
|
||||
var config = ConfigXmlBuilder.Create(this.output)
|
||||
.WithDownload(d)
|
||||
.ToServiceDescriptor(true);
|
||||
.ToServiceConfig(true);
|
||||
|
||||
var loaded = this.GetSingleEntry(sd);
|
||||
var loaded = this.GetSingleEntry(config);
|
||||
Assert.Equal(From, loaded.From);
|
||||
Assert.Equal(To, loaded.To);
|
||||
Assert.Equal(failOnError, loaded.FailOnError);
|
||||
|
@ -123,11 +123,11 @@ namespace WinSW.Tests
|
|||
[Fact]
|
||||
public void Download_FailOnError_Undefined()
|
||||
{
|
||||
var sd = ConfigXmlBuilder.Create(this.output)
|
||||
var config = ConfigXmlBuilder.Create(this.output)
|
||||
.WithRawEntry("<download from=\"http://www.nosuchhostexists.foo.myorg/foo.xml\" to=\"%BASE%\\foo.xml\"/>")
|
||||
.ToServiceDescriptor(true);
|
||||
.ToServiceConfig(true);
|
||||
|
||||
var loaded = this.GetSingleEntry(sd);
|
||||
var loaded = this.GetSingleEntry(config);
|
||||
Assert.False(loaded.FailOnError);
|
||||
}
|
||||
|
||||
|
@ -138,10 +138,10 @@ namespace WinSW.Tests
|
|||
[InlineData("Sspi")]
|
||||
public void AuthType_Is_CaseInsensitive(string authType)
|
||||
{
|
||||
var sd = ConfigXmlBuilder.Create(this.output)
|
||||
var config = ConfigXmlBuilder.Create(this.output)
|
||||
.WithRawEntry("<download from=\"http://www.nosuchhostexists.foo.myorg/foo.xml\" to=\"%BASE%\\foo.xml\" auth=\"" + authType + "\"/>")
|
||||
.ToServiceDescriptor(true);
|
||||
var loaded = this.GetSingleEntry(sd);
|
||||
.ToServiceConfig(true);
|
||||
var loaded = this.GetSingleEntry(config);
|
||||
Assert.Equal(Download.AuthType.Sspi, loaded.Auth);
|
||||
}
|
||||
|
||||
|
@ -149,11 +149,11 @@ namespace WinSW.Tests
|
|||
public void Should_Fail_On_Unsupported_AuthType()
|
||||
{
|
||||
// TODO: will need refactoring once all fields are being parsed on startup
|
||||
var sd = ConfigXmlBuilder.Create(this.output)
|
||||
var config = ConfigXmlBuilder.Create(this.output)
|
||||
.WithRawEntry("<download from=\"http://www.nosuchhostexists.foo.myorg/foo.xml\" to=\"%BASE%\\foo.xml\" auth=\"digest\"/>")
|
||||
.ToServiceDescriptor(true);
|
||||
.ToServiceConfig(true);
|
||||
|
||||
var e = Assert.Throws<InvalidDataException>(() => this.GetSingleEntry(sd));
|
||||
var e = Assert.Throws<InvalidDataException>(() => this.GetSingleEntry(config));
|
||||
Assert.StartsWith("Cannot parse <auth> Enum value from string 'digest'", e.Message);
|
||||
}
|
||||
|
||||
|
@ -187,19 +187,19 @@ namespace WinSW.Tests
|
|||
}
|
||||
}
|
||||
|
||||
private Download GetSingleEntry(ServiceDescriptor sd)
|
||||
private Download GetSingleEntry(XmlServiceConfig config)
|
||||
{
|
||||
var downloads = sd.Downloads.ToArray();
|
||||
var downloads = config.Downloads.ToArray();
|
||||
return Assert.Single(downloads);
|
||||
}
|
||||
|
||||
private void AssertInitializationFails(Download download, string expectedMessagePart = null)
|
||||
{
|
||||
var sd = ConfigXmlBuilder.Create(this.output)
|
||||
var config = ConfigXmlBuilder.Create(this.output)
|
||||
.WithDownload(download)
|
||||
.ToServiceDescriptor(true);
|
||||
.ToServiceConfig(true);
|
||||
|
||||
var e = Assert.Throws<InvalidDataException>(() => this.GetSingleEntry(sd));
|
||||
var e = Assert.Throws<InvalidDataException>(() => this.GetSingleEntry(config));
|
||||
Assert.StartsWith(expectedMessagePart, e.Message);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,7 +12,7 @@ namespace WinSW.Tests.Extensions
|
|||
{
|
||||
public class RunawayProcessKillerExtensionTest : ExtensionTestBase
|
||||
{
|
||||
private readonly ServiceDescriptor testServiceDescriptor;
|
||||
private readonly XmlServiceConfig serviceConfig;
|
||||
|
||||
private readonly string testExtension = GetExtensionClassNameWithAssembly(typeof(RunawayProcessKillerExtension));
|
||||
|
||||
|
@ -37,13 +37,13 @@ $@"<service>
|
|||
</extension>
|
||||
</extensions>
|
||||
</service>";
|
||||
this.testServiceDescriptor = ServiceDescriptor.FromXml(seedXml);
|
||||
this.serviceConfig = XmlServiceConfig.FromXml(seedXml);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void LoadExtensions()
|
||||
{
|
||||
WinSWExtensionManager manager = new WinSWExtensionManager(this.testServiceDescriptor);
|
||||
WinSWExtensionManager manager = new WinSWExtensionManager(this.serviceConfig);
|
||||
manager.LoadExtensions();
|
||||
_ = Assert.Single(manager.Extensions);
|
||||
|
||||
|
@ -57,7 +57,7 @@ $@"<service>
|
|||
[Fact]
|
||||
public void StartStopExtension()
|
||||
{
|
||||
WinSWExtensionManager manager = new WinSWExtensionManager(this.testServiceDescriptor);
|
||||
WinSWExtensionManager manager = new WinSWExtensionManager(this.serviceConfig);
|
||||
manager.LoadExtensions();
|
||||
manager.FireOnWrapperStarted();
|
||||
manager.FireBeforeWrapperStopped();
|
||||
|
@ -84,10 +84,10 @@ $@"<service>
|
|||
{
|
||||
// Generate extension and ensure that the roundtrip is correct
|
||||
var pidfile = Path.Combine(tmpDir, "process.pid");
|
||||
var sd = ConfigXmlBuilder.Create(this.output, id: winswId)
|
||||
var config = ConfigXmlBuilder.Create(this.output, id: winswId)
|
||||
.WithRunawayProcessKiller(new RunawayProcessKillerExtension(pidfile), extensionId)
|
||||
.ToServiceDescriptor();
|
||||
WinSWExtensionManager manager = new WinSWExtensionManager(sd);
|
||||
.ToServiceConfig();
|
||||
WinSWExtensionManager manager = new WinSWExtensionManager(config);
|
||||
manager.LoadExtensions();
|
||||
var extension = manager.Extensions[extensionId] as RunawayProcessKillerExtension;
|
||||
Assert.NotNull(extension);
|
||||
|
|
|
@ -6,7 +6,7 @@ namespace WinSW.Tests.Extensions
|
|||
{
|
||||
public class SharedDirectoryMapperConfigTest : ExtensionTestBase
|
||||
{
|
||||
private readonly ServiceDescriptor testServiceDescriptor;
|
||||
private readonly XmlServiceConfig serviceConfig;
|
||||
|
||||
private readonly string testExtension = GetExtensionClassNameWithAssembly(typeof(SharedDirectoryMapper));
|
||||
|
||||
|
@ -35,13 +35,13 @@ $@"<service>
|
|||
</extension>
|
||||
</extensions>
|
||||
</service>";
|
||||
this.testServiceDescriptor = ServiceDescriptor.FromXml(seedXml);
|
||||
this.serviceConfig = XmlServiceConfig.FromXml(seedXml);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void LoadExtensions()
|
||||
{
|
||||
WinSWExtensionManager manager = new WinSWExtensionManager(this.testServiceDescriptor);
|
||||
WinSWExtensionManager manager = new WinSWExtensionManager(this.serviceConfig);
|
||||
manager.LoadExtensions();
|
||||
Assert.Equal(2, manager.Extensions.Count);
|
||||
}
|
||||
|
@ -49,7 +49,7 @@ $@"<service>
|
|||
[Fact]
|
||||
public void StartStopExtension()
|
||||
{
|
||||
WinSWExtensionManager manager = new WinSWExtensionManager(this.testServiceDescriptor);
|
||||
WinSWExtensionManager manager = new WinSWExtensionManager(this.serviceConfig);
|
||||
manager.LoadExtensions();
|
||||
manager.FireOnWrapperStarted();
|
||||
manager.FireBeforeWrapperStopped();
|
||||
|
|
|
@ -8,7 +8,7 @@ using Xunit.Abstractions;
|
|||
|
||||
namespace WinSW.Tests
|
||||
{
|
||||
public class ServiceDescriptorTests
|
||||
public class ServiceConfigTests
|
||||
{
|
||||
private const string ExpectedWorkingDirectory = @"Z:\Path\SubPath";
|
||||
private const string Username = "User";
|
||||
|
@ -18,9 +18,9 @@ namespace WinSW.Tests
|
|||
|
||||
private readonly ITestOutputHelper output;
|
||||
|
||||
private ServiceDescriptor extendedServiceDescriptor;
|
||||
private XmlServiceConfig extendedServiceConfig;
|
||||
|
||||
public ServiceDescriptorTests(ITestOutputHelper output)
|
||||
public ServiceConfigTests(ITestOutputHelper output)
|
||||
{
|
||||
this.output = output;
|
||||
|
||||
|
@ -40,13 +40,13 @@ $@"<service>
|
|||
<workingdirectory>{ExpectedWorkingDirectory}</workingdirectory>
|
||||
<logpath>C:\logs</logpath>
|
||||
</service>";
|
||||
this.extendedServiceDescriptor = ServiceDescriptor.FromXml(seedXml);
|
||||
this.extendedServiceConfig = XmlServiceConfig.FromXml(seedXml);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DefaultStartMode()
|
||||
{
|
||||
Assert.Equal(ServiceStartMode.Automatic, this.extendedServiceDescriptor.StartMode);
|
||||
Assert.Equal(ServiceStartMode.Automatic, this.extendedServiceConfig.StartMode);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -70,8 +70,8 @@ $@"<service>
|
|||
<logpath>C:\logs</logpath>
|
||||
</service>";
|
||||
|
||||
this.extendedServiceDescriptor = ServiceDescriptor.FromXml(seedXml);
|
||||
_ = Assert.Throws<InvalidDataException>(() => this.extendedServiceDescriptor.StartMode);
|
||||
this.extendedServiceConfig = XmlServiceConfig.FromXml(seedXml);
|
||||
_ = Assert.Throws<InvalidDataException>(() => this.extendedServiceConfig.StartMode);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -95,48 +95,45 @@ $@"<service>
|
|||
<logpath>C:\logs</logpath>
|
||||
</service>";
|
||||
|
||||
this.extendedServiceDescriptor = ServiceDescriptor.FromXml(seedXml);
|
||||
Assert.Equal(ServiceStartMode.Manual, this.extendedServiceDescriptor.StartMode);
|
||||
this.extendedServiceConfig = XmlServiceConfig.FromXml(seedXml);
|
||||
Assert.Equal(ServiceStartMode.Manual, this.extendedServiceConfig.StartMode);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void VerifyWorkingDirectory()
|
||||
{
|
||||
Debug.WriteLine("_extendedServiceDescriptor.WorkingDirectory :: " + this.extendedServiceDescriptor.WorkingDirectory);
|
||||
Assert.Equal(ExpectedWorkingDirectory, this.extendedServiceDescriptor.WorkingDirectory);
|
||||
Assert.Equal(ExpectedWorkingDirectory, this.extendedServiceConfig.WorkingDirectory);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void VerifyServiceLogonRight()
|
||||
{
|
||||
Assert.True(this.extendedServiceDescriptor.AllowServiceAcountLogonRight);
|
||||
Assert.True(this.extendedServiceConfig.AllowServiceAcountLogonRight);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void VerifyUsername()
|
||||
{
|
||||
Debug.WriteLine("_extendedServiceDescriptor.WorkingDirectory :: " + this.extendedServiceDescriptor.WorkingDirectory);
|
||||
Assert.Equal(Domain + "\\" + Username, this.extendedServiceDescriptor.ServiceAccountUserName);
|
||||
Assert.Equal(Domain + "\\" + Username, this.extendedServiceConfig.ServiceAccountUserName);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void VerifyPassword()
|
||||
{
|
||||
Debug.WriteLine("_extendedServiceDescriptor.WorkingDirectory :: " + this.extendedServiceDescriptor.WorkingDirectory);
|
||||
Assert.Equal(Password, this.extendedServiceDescriptor.ServiceAccountPassword);
|
||||
Assert.Equal(Password, this.extendedServiceConfig.ServiceAccountPassword);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Priority()
|
||||
{
|
||||
var sd = ServiceDescriptor.FromXml("<service><id>test</id><priority>normal</priority></service>");
|
||||
Assert.Equal(ProcessPriorityClass.Normal, sd.Priority);
|
||||
var config = XmlServiceConfig.FromXml("<service><id>test</id><priority>normal</priority></service>");
|
||||
Assert.Equal(ProcessPriorityClass.Normal, config.Priority);
|
||||
|
||||
sd = ServiceDescriptor.FromXml("<service><id>test</id><priority>idle</priority></service>");
|
||||
Assert.Equal(ProcessPriorityClass.Idle, sd.Priority);
|
||||
config = XmlServiceConfig.FromXml("<service><id>test</id><priority>idle</priority></service>");
|
||||
Assert.Equal(ProcessPriorityClass.Idle, config.Priority);
|
||||
|
||||
sd = ServiceDescriptor.FromXml("<service><id>test</id></service>");
|
||||
Assert.Equal(ProcessPriorityClass.Normal, sd.Priority);
|
||||
config = XmlServiceConfig.FromXml("<service><id>test</id></service>");
|
||||
Assert.Equal(ProcessPriorityClass.Normal, config.Priority);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -145,9 +142,9 @@ $@"<service>
|
|||
const string seedXml = "<service>"
|
||||
+ "<stoptimeout>60sec</stoptimeout>"
|
||||
+ "</service>";
|
||||
var serviceDescriptor = ServiceDescriptor.FromXml(seedXml);
|
||||
var config = XmlServiceConfig.FromXml(seedXml);
|
||||
|
||||
Assert.Equal(TimeSpan.FromSeconds(60), serviceDescriptor.StopTimeout);
|
||||
Assert.Equal(TimeSpan.FromSeconds(60), config.StopTimeout);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -156,9 +153,9 @@ $@"<service>
|
|||
const string seedXml = "<service>"
|
||||
+ "<stoptimeout>10min</stoptimeout>"
|
||||
+ "</service>";
|
||||
var serviceDescriptor = ServiceDescriptor.FromXml(seedXml);
|
||||
var config = XmlServiceConfig.FromXml(seedXml);
|
||||
|
||||
Assert.Equal(TimeSpan.FromMinutes(10), serviceDescriptor.StopTimeout);
|
||||
Assert.Equal(TimeSpan.FromMinutes(10), config.StopTimeout);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -167,9 +164,9 @@ $@"<service>
|
|||
const string seedXml = "<service>"
|
||||
+ "<logname>MyTestApp</logname>"
|
||||
+ "</service>";
|
||||
var serviceDescriptor = ServiceDescriptor.FromXml(seedXml);
|
||||
var config = XmlServiceConfig.FromXml(seedXml);
|
||||
|
||||
Assert.Equal("MyTestApp", serviceDescriptor.LogName);
|
||||
Assert.Equal("MyTestApp", config.LogName);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -178,9 +175,9 @@ $@"<service>
|
|||
const string seedXml = "<service>"
|
||||
+ "<outfiledisabled>true</outfiledisabled>"
|
||||
+ "</service>";
|
||||
var serviceDescriptor = ServiceDescriptor.FromXml(seedXml);
|
||||
var config = XmlServiceConfig.FromXml(seedXml);
|
||||
|
||||
Assert.True(serviceDescriptor.OutFileDisabled);
|
||||
Assert.True(config.OutFileDisabled);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -189,9 +186,9 @@ $@"<service>
|
|||
const string seedXml = "<service>"
|
||||
+ "<errfiledisabled>true</errfiledisabled>"
|
||||
+ "</service>";
|
||||
var serviceDescriptor = ServiceDescriptor.FromXml(seedXml);
|
||||
var config = XmlServiceConfig.FromXml(seedXml);
|
||||
|
||||
Assert.True(serviceDescriptor.ErrFileDisabled);
|
||||
Assert.True(config.ErrFileDisabled);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -200,9 +197,9 @@ $@"<service>
|
|||
const string seedXml = "<service>"
|
||||
+ "<outfilepattern>.out.test.log</outfilepattern>"
|
||||
+ "</service>";
|
||||
var serviceDescriptor = ServiceDescriptor.FromXml(seedXml);
|
||||
var config = XmlServiceConfig.FromXml(seedXml);
|
||||
|
||||
Assert.Equal(".out.test.log", serviceDescriptor.OutFilePattern);
|
||||
Assert.Equal(".out.test.log", config.OutFilePattern);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -211,9 +208,9 @@ $@"<service>
|
|||
const string seedXml = "<service>"
|
||||
+ "<errfilepattern>.err.test.log</errfilepattern>"
|
||||
+ "</service>";
|
||||
var serviceDescriptor = ServiceDescriptor.FromXml(seedXml);
|
||||
var config = XmlServiceConfig.FromXml(seedXml);
|
||||
|
||||
Assert.Equal(".err.test.log", serviceDescriptor.ErrFilePattern);
|
||||
Assert.Equal(".err.test.log", config.ErrFilePattern);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -227,10 +224,10 @@ $@"<service>
|
|||
+ "</log>"
|
||||
+ "</service>";
|
||||
|
||||
var serviceDescriptor = ServiceDescriptor.FromXml(seedXml);
|
||||
serviceDescriptor.BaseName = "service";
|
||||
var config = XmlServiceConfig.FromXml(seedXml);
|
||||
config.BaseName = "service";
|
||||
|
||||
var logHandler = serviceDescriptor.LogHandler as SizeBasedRollingLogAppender;
|
||||
var logHandler = config.LogHandler as SizeBasedRollingLogAppender;
|
||||
Assert.NotNull(logHandler);
|
||||
Assert.Equal(112 * 1024, logHandler.SizeThreshold);
|
||||
Assert.Equal(113, logHandler.FilesToKeep);
|
||||
|
@ -247,10 +244,10 @@ $@"<service>
|
|||
+ "</log>"
|
||||
+ "</service>";
|
||||
|
||||
var serviceDescriptor = ServiceDescriptor.FromXml(seedXml);
|
||||
serviceDescriptor.BaseName = "service";
|
||||
var config = XmlServiceConfig.FromXml(seedXml);
|
||||
config.BaseName = "service";
|
||||
|
||||
var logHandler = serviceDescriptor.LogHandler as TimeBasedRollingLogAppender;
|
||||
var logHandler = config.LogHandler as TimeBasedRollingLogAppender;
|
||||
Assert.NotNull(logHandler);
|
||||
Assert.Equal(7, logHandler.Period);
|
||||
Assert.Equal("log pattern", logHandler.Pattern);
|
||||
|
@ -268,10 +265,10 @@ $@"<service>
|
|||
+ "</log>"
|
||||
+ "</service>";
|
||||
|
||||
var serviceDescriptor = ServiceDescriptor.FromXml(seedXml);
|
||||
serviceDescriptor.BaseName = "service";
|
||||
var config = XmlServiceConfig.FromXml(seedXml);
|
||||
config.BaseName = "service";
|
||||
|
||||
var logHandler = serviceDescriptor.LogHandler as RollingSizeTimeLogAppender;
|
||||
var logHandler = config.LogHandler as RollingSizeTimeLogAppender;
|
||||
Assert.NotNull(logHandler);
|
||||
Assert.Equal(10240 * 1024, logHandler.SizeThreshold);
|
||||
Assert.Equal("yyyy-MM-dd", logHandler.FilePattern);
|
||||
|
@ -289,8 +286,8 @@ $@"<service>
|
|||
+ "<allowservicelogon>true1</allowservicelogon>"
|
||||
+ "</serviceaccount>"
|
||||
+ "</service>";
|
||||
var serviceDescriptor = ServiceDescriptor.FromXml(seedXml);
|
||||
Assert.False(serviceDescriptor.AllowServiceAcountLogonRight);
|
||||
var config = XmlServiceConfig.FromXml(seedXml);
|
||||
Assert.False(config.AllowServiceAcountLogonRight);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -303,22 +300,22 @@ $@"<service>
|
|||
+ "<password>" + Password + "</password>"
|
||||
+ "</serviceaccount>"
|
||||
+ "</service>";
|
||||
var serviceDescriptor = ServiceDescriptor.FromXml(seedXml);
|
||||
Assert.False(serviceDescriptor.AllowServiceAcountLogonRight);
|
||||
var config = XmlServiceConfig.FromXml(seedXml);
|
||||
Assert.False(config.AllowServiceAcountLogonRight);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void VerifyResetFailureAfter()
|
||||
{
|
||||
var sd = ConfigXmlBuilder.Create(this.output).WithTag("resetfailure", "75 sec").ToServiceDescriptor(true);
|
||||
Assert.Equal(TimeSpan.FromSeconds(75), sd.ResetFailureAfter);
|
||||
var config = ConfigXmlBuilder.Create(this.output).WithTag("resetfailure", "75 sec").ToServiceConfig(true);
|
||||
Assert.Equal(TimeSpan.FromSeconds(75), config.ResetFailureAfter);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void VerifyStopTimeout()
|
||||
{
|
||||
var sd = ConfigXmlBuilder.Create(this.output).WithTag("stoptimeout", "35 secs").ToServiceDescriptor(true);
|
||||
Assert.Equal(TimeSpan.FromSeconds(35), sd.StopTimeout);
|
||||
var config = ConfigXmlBuilder.Create(this.output).WithTag("stoptimeout", "35 secs").ToServiceConfig(true);
|
||||
Assert.Equal(TimeSpan.FromSeconds(35), config.StopTimeout);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -327,8 +324,8 @@ $@"<service>
|
|||
[Fact]
|
||||
public void Arguments_LegacyParam()
|
||||
{
|
||||
var sd = ConfigXmlBuilder.Create(this.output).WithTag("arguments", "arg").ToServiceDescriptor(true);
|
||||
Assert.Equal("arg", sd.Arguments);
|
||||
var config = ConfigXmlBuilder.Create(this.output).WithTag("arguments", "arg").ToServiceConfig(true);
|
||||
Assert.Equal("arg", config.Arguments);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
|
@ -342,8 +339,8 @@ $@"<service>
|
|||
bldr = bldr.WithDelayedAutoStart();
|
||||
}
|
||||
|
||||
var sd = bldr.ToServiceDescriptor();
|
||||
Assert.Equal(enabled, sd.DelayedAutoStart);
|
||||
var config = bldr.ToServiceConfig();
|
||||
Assert.Equal(enabled, config.DelayedAutoStart);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -378,16 +375,16 @@ $@"<service>
|
|||
</poststop>
|
||||
</service>";
|
||||
|
||||
ServiceDescriptor descriptor = ServiceDescriptor.FromXml(seedXml);
|
||||
XmlServiceConfig config = XmlServiceConfig.FromXml(seedXml);
|
||||
|
||||
Assert.Equal(prestartExecutable, descriptor.PrestartExecutable);
|
||||
Assert.Equal(prestartArguments, descriptor.PrestartArguments);
|
||||
Assert.Equal(poststartExecutable, descriptor.PoststartExecutable);
|
||||
Assert.Equal(poststartArguments, descriptor.PoststartArguments);
|
||||
Assert.Equal(prestopExecutable, descriptor.PrestopExecutable);
|
||||
Assert.Equal(prestopArguments, descriptor.PrestopArguments);
|
||||
Assert.Equal(poststopExecutable, descriptor.PoststopExecutable);
|
||||
Assert.Equal(poststopArguments, descriptor.PoststopArguments);
|
||||
Assert.Equal(prestartExecutable, config.PrestartExecutable);
|
||||
Assert.Equal(prestartArguments, config.PrestartArguments);
|
||||
Assert.Equal(poststartExecutable, config.PoststartExecutable);
|
||||
Assert.Equal(poststartArguments, config.PoststartArguments);
|
||||
Assert.Equal(prestopExecutable, config.PrestopExecutable);
|
||||
Assert.Equal(prestopArguments, config.PrestopArguments);
|
||||
Assert.Equal(poststopExecutable, config.PoststopExecutable);
|
||||
Assert.Equal(poststopArguments, config.PoststopArguments);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -24,16 +24,16 @@ $@"<service>
|
|||
<logpath>C:\winsw\logs</logpath>
|
||||
</service>";
|
||||
|
||||
public static readonly ServiceDescriptor DefaultServiceDescriptor = ServiceDescriptor.FromXml(SeedXml);
|
||||
public static readonly XmlServiceConfig DefaultServiceConfig = XmlServiceConfig.FromXml(SeedXml);
|
||||
|
||||
/// <summary>
|
||||
/// Runs a simle test, which returns the output CLI
|
||||
/// </summary>
|
||||
/// <param name="arguments">CLI arguments to be passed</param>
|
||||
/// <param name="descriptor">Optional Service descriptor (will be used for initializationpurposes)</param>
|
||||
/// <param name="config">Optional Service config (will be used for initialization purposes)</param>
|
||||
/// <returns>STDOUT if there's no exceptions</returns>
|
||||
/// <exception cref="Exception">Command failure</exception>
|
||||
public static string Test(string[] arguments, ServiceDescriptor descriptor = null)
|
||||
public static string Test(string[] arguments, XmlServiceConfig config = null)
|
||||
{
|
||||
TextWriter tmpOut = Console.Out;
|
||||
TextWriter tmpError = Console.Error;
|
||||
|
@ -43,7 +43,7 @@ $@"<service>
|
|||
|
||||
Console.SetOut(swOut);
|
||||
Console.SetError(swError);
|
||||
ServiceDescriptor.TestDescriptor = descriptor ?? DefaultServiceDescriptor;
|
||||
XmlServiceConfig.TestConfig = config ?? DefaultServiceConfig;
|
||||
try
|
||||
{
|
||||
_ = Program.Run(arguments);
|
||||
|
@ -52,7 +52,7 @@ $@"<service>
|
|||
{
|
||||
Console.SetOut(tmpOut);
|
||||
Console.SetError(tmpError);
|
||||
ServiceDescriptor.TestDescriptor = null;
|
||||
XmlServiceConfig.TestConfig = null;
|
||||
}
|
||||
|
||||
Assert.Equal(string.Empty, swError.ToString());
|
||||
|
@ -63,9 +63,9 @@ $@"<service>
|
|||
/// Runs a simle test, which returns the output CLI
|
||||
/// </summary>
|
||||
/// <param name="arguments">CLI arguments to be passed</param>
|
||||
/// <param name="descriptor">Optional Service descriptor (will be used for initializationpurposes)</param>
|
||||
/// <param name="config">Optional Service config (will be used for initialization purposes)</param>
|
||||
/// <returns>Test results</returns>
|
||||
public static CommandLineTestResult ErrorTest(string[] arguments, ServiceDescriptor descriptor = null)
|
||||
public static CommandLineTestResult ErrorTest(string[] arguments, XmlServiceConfig config = null)
|
||||
{
|
||||
Exception exception = null;
|
||||
|
||||
|
@ -77,7 +77,7 @@ $@"<service>
|
|||
|
||||
Console.SetOut(swOut);
|
||||
Console.SetError(swError);
|
||||
ServiceDescriptor.TestDescriptor = descriptor ?? DefaultServiceDescriptor;
|
||||
XmlServiceConfig.TestConfig = config ?? DefaultServiceConfig;
|
||||
Program.TestExceptionHandler = (e, _) => exception = e;
|
||||
try
|
||||
{
|
||||
|
@ -91,7 +91,7 @@ $@"<service>
|
|||
{
|
||||
Console.SetOut(tmpOut);
|
||||
Console.SetError(tmpError);
|
||||
ServiceDescriptor.TestDescriptor = null;
|
||||
XmlServiceConfig.TestConfig = null;
|
||||
Program.TestExceptionHandler = null;
|
||||
}
|
||||
|
||||
|
|
|
@ -104,9 +104,9 @@ namespace WinSW.Tests.Util
|
|||
return res;
|
||||
}
|
||||
|
||||
public ServiceDescriptor ToServiceDescriptor(bool dumpConfig = false)
|
||||
public XmlServiceConfig ToServiceConfig(bool dumpConfig = false)
|
||||
{
|
||||
return ServiceDescriptor.FromXml(this.ToXmlString(dumpConfig));
|
||||
return XmlServiceConfig.FromXml(this.ToXmlString(dumpConfig));
|
||||
}
|
||||
|
||||
public ConfigXmlBuilder WithRawEntry(string entry)
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
using System.Reflection;
|
||||
using WinSW.Configuration;
|
||||
using Xunit;
|
||||
|
||||
namespace WinSW.Tests.Util
|
||||
{
|
||||
public static class ServiceConfigAssert
|
||||
{
|
||||
public static void AssertAllOptionalPropertiesAreDefault(XmlServiceConfig config)
|
||||
{
|
||||
var testConfig = new TestServiceConfig(config);
|
||||
foreach (var property in typeof(ServiceConfig).GetProperties())
|
||||
{
|
||||
if (property.GetMethod!.IsVirtual)
|
||||
{
|
||||
Assert.Equal(property.GetValue(testConfig, null), property.GetValue(config, null));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class TestServiceConfig : ServiceConfig
|
||||
{
|
||||
private readonly XmlServiceConfig config;
|
||||
|
||||
internal TestServiceConfig(XmlServiceConfig config) => this.config = config;
|
||||
|
||||
public override string FullPath => this.config.FullPath;
|
||||
|
||||
public override string Id => this.config.Id;
|
||||
|
||||
public override string Executable => this.config.Executable;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,64 +0,0 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using WinSW.Configuration;
|
||||
using Xunit;
|
||||
|
||||
namespace WinSW.Tests.Util
|
||||
{
|
||||
public static class ServiceDescriptorAssert
|
||||
{
|
||||
// TODO: convert to Extension attributes once the .NET dependency is upgraded
|
||||
// BTW there is a way to get them working in .NET2, but KISS
|
||||
public static void AssertPropertyIsDefault(ServiceDescriptor desc, string property)
|
||||
{
|
||||
PropertyInfo actualProperty = typeof(ServiceDescriptor).GetProperty(property);
|
||||
Assert.NotNull(actualProperty);
|
||||
|
||||
PropertyInfo defaultProperty = typeof(DefaultWinSWSettings).GetProperty(property);
|
||||
Assert.NotNull(defaultProperty);
|
||||
|
||||
Assert.Equal(defaultProperty.GetValue(ServiceDescriptor.Defaults, null), actualProperty.GetValue(desc, null));
|
||||
}
|
||||
|
||||
public static void AssertPropertyIsDefault(ServiceDescriptor desc, List<string> properties)
|
||||
{
|
||||
foreach (var prop in properties)
|
||||
{
|
||||
AssertPropertyIsDefault(desc, prop);
|
||||
}
|
||||
}
|
||||
|
||||
public static void AssertAllOptionalPropertiesAreDefault(ServiceDescriptor desc)
|
||||
{
|
||||
AssertPropertyIsDefault(desc, AllOptionalProperties);
|
||||
}
|
||||
|
||||
private static List<string> AllProperties
|
||||
{
|
||||
get
|
||||
{
|
||||
var res = new List<string>();
|
||||
var properties = typeof(IWinSWConfiguration).GetProperties();
|
||||
foreach (var prop in properties)
|
||||
{
|
||||
res.Add(prop.Name);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
private static List<string> AllOptionalProperties
|
||||
{
|
||||
get
|
||||
{
|
||||
var properties = AllProperties;
|
||||
properties.Remove("FullPath");
|
||||
properties.Remove("Id");
|
||||
properties.Remove("Executable");
|
||||
properties.Remove("WorkingDirectory");
|
||||
return properties;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -71,20 +71,20 @@ namespace WinSW
|
|||
{
|
||||
Handler = CommandHandler.Create((string? pathToConfig) =>
|
||||
{
|
||||
ServiceDescriptor descriptor;
|
||||
XmlServiceConfig config;
|
||||
try
|
||||
{
|
||||
descriptor = ServiceDescriptor.Create(pathToConfig);
|
||||
config = XmlServiceConfig.Create(pathToConfig);
|
||||
}
|
||||
catch (FileNotFoundException)
|
||||
{
|
||||
throw new CommandException("The specified command or file was not found.");
|
||||
}
|
||||
|
||||
InitLoggers(descriptor, enableConsoleLogging: false);
|
||||
InitLoggers(config, enableConsoleLogging: false);
|
||||
|
||||
Log.Debug("Starting WinSW in service mode");
|
||||
ServiceBase.Run(new WrapperService(descriptor));
|
||||
ServiceBase.Run(new WrapperService(config));
|
||||
}),
|
||||
};
|
||||
|
||||
|
@ -320,8 +320,8 @@ namespace WinSW
|
|||
|
||||
void Install(string? pathToConfig, bool noElevate, string? username, string? password)
|
||||
{
|
||||
ServiceDescriptor descriptor = ServiceDescriptor.Create(pathToConfig);
|
||||
InitLoggers(descriptor, enableConsoleLogging: true);
|
||||
XmlServiceConfig config = XmlServiceConfig.Create(pathToConfig);
|
||||
InitLoggers(config, enableConsoleLogging: true);
|
||||
|
||||
if (!elevated)
|
||||
{
|
||||
|
@ -329,25 +329,25 @@ namespace WinSW
|
|||
return;
|
||||
}
|
||||
|
||||
Log.Info("Installing the service with id '" + descriptor.Id + "'");
|
||||
Log.Info("Installing the service with id '" + config.Id + "'");
|
||||
|
||||
using ServiceManager scm = ServiceManager.Open();
|
||||
|
||||
if (scm.ServiceExists(descriptor.Id))
|
||||
if (scm.ServiceExists(config.Id))
|
||||
{
|
||||
Console.WriteLine("Service with id '" + descriptor.Id + "' already exists");
|
||||
Console.WriteLine("Service with id '" + config.Id + "' already exists");
|
||||
Console.WriteLine("To install the service, delete the existing one or change service Id in the configuration file");
|
||||
throw new CommandException("Installation failure: Service with id '" + descriptor.Id + "' already exists");
|
||||
throw new CommandException("Installation failure: Service with id '" + config.Id + "' already exists");
|
||||
}
|
||||
|
||||
if (descriptor.HasServiceAccount())
|
||||
if (config.HasServiceAccount())
|
||||
{
|
||||
username = descriptor.ServiceAccountUserName ?? username;
|
||||
password = descriptor.ServiceAccountPassword ?? password;
|
||||
username = config.ServiceAccountUserName ?? username;
|
||||
password = config.ServiceAccountPassword ?? password;
|
||||
|
||||
if (username is null || password is null)
|
||||
{
|
||||
switch (descriptor.ServiceAccountPrompt)
|
||||
switch (config.ServiceAccountPrompt)
|
||||
{
|
||||
case "dialog":
|
||||
Credentials.PropmtForCredentialsDialog(
|
||||
|
@ -370,45 +370,45 @@ namespace WinSW
|
|||
}
|
||||
|
||||
using Service sc = scm.CreateService(
|
||||
descriptor.Id,
|
||||
descriptor.Caption,
|
||||
descriptor.StartMode,
|
||||
"\"" + descriptor.ExecutablePath + "\"" + (pathToConfig != null ? " \"" + Path.GetFullPath(pathToConfig) + "\"" : null),
|
||||
descriptor.ServiceDependencies,
|
||||
config.Id,
|
||||
config.Caption,
|
||||
config.StartMode,
|
||||
"\"" + config.ExecutablePath + "\"" + (pathToConfig != null ? " \"" + Path.GetFullPath(pathToConfig) + "\"" : null),
|
||||
config.ServiceDependencies,
|
||||
username,
|
||||
password);
|
||||
|
||||
string description = descriptor.Description;
|
||||
string description = config.Description;
|
||||
if (description.Length != 0)
|
||||
{
|
||||
sc.SetDescription(description);
|
||||
}
|
||||
|
||||
SC_ACTION[] actions = descriptor.FailureActions;
|
||||
SC_ACTION[] actions = config.FailureActions;
|
||||
if (actions.Length > 0)
|
||||
{
|
||||
sc.SetFailureActions(descriptor.ResetFailureAfter, actions);
|
||||
sc.SetFailureActions(config.ResetFailureAfter, actions);
|
||||
}
|
||||
|
||||
bool isDelayedAutoStart = descriptor.StartMode == ServiceStartMode.Automatic && descriptor.DelayedAutoStart;
|
||||
bool isDelayedAutoStart = config.StartMode == ServiceStartMode.Automatic && config.DelayedAutoStart;
|
||||
if (isDelayedAutoStart)
|
||||
{
|
||||
sc.SetDelayedAutoStart(true);
|
||||
}
|
||||
|
||||
if (descriptor.PreshutdownTimeout is TimeSpan preshutdownTimeout)
|
||||
if (config.PreshutdownTimeout is TimeSpan preshutdownTimeout)
|
||||
{
|
||||
sc.SetPreshutdownTimeout(preshutdownTimeout);
|
||||
}
|
||||
|
||||
string? securityDescriptor = descriptor.SecurityDescriptor;
|
||||
string? securityDescriptor = config.SecurityDescriptor;
|
||||
if (securityDescriptor != null)
|
||||
{
|
||||
// throws ArgumentException
|
||||
sc.SetSecurityDescriptor(new RawSecurityDescriptor(securityDescriptor));
|
||||
}
|
||||
|
||||
string eventLogSource = descriptor.Id;
|
||||
string eventLogSource = config.Id;
|
||||
if (!EventLog.SourceExists(eventLogSource))
|
||||
{
|
||||
EventLog.CreateEventSource(eventLogSource, "Application");
|
||||
|
@ -434,8 +434,8 @@ namespace WinSW
|
|||
|
||||
void Uninstall(string? pathToConfig, bool noElevate)
|
||||
{
|
||||
ServiceDescriptor descriptor = ServiceDescriptor.Create(pathToConfig);
|
||||
InitLoggers(descriptor, enableConsoleLogging: true);
|
||||
XmlServiceConfig config = XmlServiceConfig.Create(pathToConfig);
|
||||
InitLoggers(config, enableConsoleLogging: true);
|
||||
|
||||
if (!elevated)
|
||||
{
|
||||
|
@ -443,18 +443,18 @@ namespace WinSW
|
|||
return;
|
||||
}
|
||||
|
||||
Log.Info("Uninstalling the service with id '" + descriptor.Id + "'");
|
||||
Log.Info("Uninstalling the service with id '" + config.Id + "'");
|
||||
|
||||
using ServiceManager scm = ServiceManager.Open();
|
||||
try
|
||||
{
|
||||
using Service sc = scm.OpenService(descriptor.Id);
|
||||
using Service sc = scm.OpenService(config.Id);
|
||||
|
||||
if (sc.Status == ServiceControllerStatus.Running)
|
||||
{
|
||||
// We could fail the opeartion here, but it would be an incompatible change.
|
||||
// So it is just a warning
|
||||
Log.Warn("The service with id '" + descriptor.Id + "' is running. It may be impossible to uninstall it");
|
||||
Log.Warn("The service with id '" + config.Id + "' is running. It may be impossible to uninstall it");
|
||||
}
|
||||
|
||||
sc.Delete();
|
||||
|
@ -464,18 +464,18 @@ namespace WinSW
|
|||
switch (inner.NativeErrorCode)
|
||||
{
|
||||
case Errors.ERROR_SERVICE_DOES_NOT_EXIST:
|
||||
Log.Warn("The service with id '" + descriptor.Id + "' does not exist. Nothing to uninstall");
|
||||
Log.Warn("The service with id '" + config.Id + "' does not exist. Nothing to uninstall");
|
||||
break; // there's no such service, so consider it already uninstalled
|
||||
|
||||
case Errors.ERROR_SERVICE_MARKED_FOR_DELETE:
|
||||
Log.Error("Failed to uninstall the service with id '" + descriptor.Id + "'"
|
||||
Log.Error("Failed to uninstall the service with id '" + config.Id + "'"
|
||||
+ ". It has been marked for deletion.");
|
||||
|
||||
// TODO: change the default behavior to Error?
|
||||
break; // it's already uninstalled, so consider it a success
|
||||
|
||||
default:
|
||||
Log.Fatal("Failed to uninstall the service with id '" + descriptor.Id + "'. Error code is '" + inner.NativeErrorCode + "'");
|
||||
Log.Fatal("Failed to uninstall the service with id '" + config.Id + "'. Error code is '" + inner.NativeErrorCode + "'");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
@ -483,8 +483,8 @@ namespace WinSW
|
|||
|
||||
void Start(string? pathToConfig, bool noElevate)
|
||||
{
|
||||
ServiceDescriptor descriptor = ServiceDescriptor.Create(pathToConfig);
|
||||
InitLoggers(descriptor, enableConsoleLogging: true);
|
||||
XmlServiceConfig config = XmlServiceConfig.Create(pathToConfig);
|
||||
InitLoggers(config, enableConsoleLogging: true);
|
||||
|
||||
if (!elevated)
|
||||
{
|
||||
|
@ -492,9 +492,9 @@ namespace WinSW
|
|||
return;
|
||||
}
|
||||
|
||||
Log.Info("Starting the service with id '" + descriptor.Id + "'");
|
||||
Log.Info("Starting the service with id '" + config.Id + "'");
|
||||
|
||||
using var svc = new ServiceController(descriptor.Id);
|
||||
using var svc = new ServiceController(config.Id);
|
||||
|
||||
try
|
||||
{
|
||||
|
@ -508,14 +508,14 @@ namespace WinSW
|
|||
catch (InvalidOperationException e)
|
||||
when (e.InnerException is Win32Exception inner && inner.NativeErrorCode == Errors.ERROR_SERVICE_ALREADY_RUNNING)
|
||||
{
|
||||
Log.Info($"The service with ID '{descriptor.Id}' has already been started");
|
||||
Log.Info($"The service with ID '{config.Id}' has already been started");
|
||||
}
|
||||
}
|
||||
|
||||
void Stop(string? pathToConfig, bool noElevate, bool noWait, bool force)
|
||||
{
|
||||
ServiceDescriptor descriptor = ServiceDescriptor.Create(pathToConfig);
|
||||
InitLoggers(descriptor, enableConsoleLogging: true);
|
||||
XmlServiceConfig config = XmlServiceConfig.Create(pathToConfig);
|
||||
InitLoggers(config, enableConsoleLogging: true);
|
||||
|
||||
if (!elevated)
|
||||
{
|
||||
|
@ -523,9 +523,9 @@ namespace WinSW
|
|||
return;
|
||||
}
|
||||
|
||||
Log.Info("Stopping the service with id '" + descriptor.Id + "'");
|
||||
Log.Info("Stopping the service with id '" + config.Id + "'");
|
||||
|
||||
using var svc = new ServiceController(descriptor.Id);
|
||||
using var svc = new ServiceController(config.Id);
|
||||
|
||||
try
|
||||
{
|
||||
|
@ -567,8 +567,8 @@ namespace WinSW
|
|||
|
||||
void Restart(string? pathToConfig, bool noElevate, bool force)
|
||||
{
|
||||
ServiceDescriptor descriptor = ServiceDescriptor.Create(pathToConfig);
|
||||
InitLoggers(descriptor, enableConsoleLogging: true);
|
||||
XmlServiceConfig config = XmlServiceConfig.Create(pathToConfig);
|
||||
InitLoggers(config, enableConsoleLogging: true);
|
||||
|
||||
if (!elevated)
|
||||
{
|
||||
|
@ -576,9 +576,9 @@ namespace WinSW
|
|||
return;
|
||||
}
|
||||
|
||||
Log.Info("Restarting the service with id '" + descriptor.Id + "'");
|
||||
Log.Info("Restarting the service with id '" + config.Id + "'");
|
||||
|
||||
using var svc = new ServiceController(descriptor.Id);
|
||||
using var svc = new ServiceController(config.Id);
|
||||
|
||||
List<ServiceController>? startedDependentServices = null;
|
||||
|
||||
|
@ -632,19 +632,19 @@ namespace WinSW
|
|||
|
||||
void RestartSelf(string? pathToConfig)
|
||||
{
|
||||
ServiceDescriptor descriptor = ServiceDescriptor.Create(pathToConfig);
|
||||
InitLoggers(descriptor, enableConsoleLogging: true);
|
||||
XmlServiceConfig config = XmlServiceConfig.Create(pathToConfig);
|
||||
InitLoggers(config, enableConsoleLogging: true);
|
||||
|
||||
if (!elevated)
|
||||
{
|
||||
throw new CommandException(new Win32Exception(Errors.ERROR_ACCESS_DENIED));
|
||||
}
|
||||
|
||||
Log.Info("Restarting the service with id '" + descriptor.Id + "'");
|
||||
Log.Info("Restarting the service with id '" + config.Id + "'");
|
||||
|
||||
// run restart from another process group. see README.md for why this is useful.
|
||||
|
||||
if (!ProcessApis.CreateProcess(null, descriptor.ExecutablePath + " restart", IntPtr.Zero, IntPtr.Zero, false, ProcessApis.CREATE_NEW_PROCESS_GROUP, IntPtr.Zero, null, default, out _))
|
||||
if (!ProcessApis.CreateProcess(null, config.ExecutablePath + " restart", IntPtr.Zero, IntPtr.Zero, false, ProcessApis.CREATE_NEW_PROCESS_GROUP, IntPtr.Zero, null, default, out _))
|
||||
{
|
||||
throw new CommandException("Failed to invoke restart: " + Marshal.GetLastWin32Error());
|
||||
}
|
||||
|
@ -652,11 +652,11 @@ namespace WinSW
|
|||
|
||||
static void Status(string? pathToConfig)
|
||||
{
|
||||
ServiceDescriptor descriptor = ServiceDescriptor.Create(pathToConfig);
|
||||
InitLoggers(descriptor, enableConsoleLogging: true);
|
||||
XmlServiceConfig config = XmlServiceConfig.Create(pathToConfig);
|
||||
InitLoggers(config, enableConsoleLogging: true);
|
||||
|
||||
Log.Debug("User requested the status of the process with id '" + descriptor.Id + "'");
|
||||
using var svc = new ServiceController(descriptor.Id);
|
||||
Log.Debug("User requested the status of the process with id '" + config.Id + "'");
|
||||
using var svc = new ServiceController(config.Id);
|
||||
try
|
||||
{
|
||||
Console.WriteLine(svc.Status == ServiceControllerStatus.Running ? "Started" : "Stopped");
|
||||
|
@ -670,8 +670,8 @@ namespace WinSW
|
|||
|
||||
void Test(string? pathToConfig, bool noElevate, int? timeout, bool noBreak)
|
||||
{
|
||||
ServiceDescriptor descriptor = ServiceDescriptor.Create(pathToConfig);
|
||||
InitLoggers(descriptor, enableConsoleLogging: true);
|
||||
XmlServiceConfig config = XmlServiceConfig.Create(pathToConfig);
|
||||
InitLoggers(config, enableConsoleLogging: true);
|
||||
|
||||
if (!elevated)
|
||||
{
|
||||
|
@ -679,7 +679,7 @@ namespace WinSW
|
|||
return;
|
||||
}
|
||||
|
||||
using WrapperService wsvc = new WrapperService(descriptor);
|
||||
using WrapperService wsvc = new WrapperService(config);
|
||||
wsvc.RaiseOnStart(args);
|
||||
try
|
||||
{
|
||||
|
@ -715,8 +715,8 @@ namespace WinSW
|
|||
|
||||
void Refresh(string? pathToConfig, bool noElevate)
|
||||
{
|
||||
ServiceDescriptor descriptor = ServiceDescriptor.Create(pathToConfig);
|
||||
InitLoggers(descriptor, enableConsoleLogging: true);
|
||||
XmlServiceConfig config = XmlServiceConfig.Create(pathToConfig);
|
||||
InitLoggers(config, enableConsoleLogging: true);
|
||||
|
||||
if (!elevated)
|
||||
{
|
||||
|
@ -727,30 +727,30 @@ namespace WinSW
|
|||
using ServiceManager scm = ServiceManager.Open();
|
||||
try
|
||||
{
|
||||
using Service sc = scm.OpenService(descriptor.Id);
|
||||
using Service sc = scm.OpenService(config.Id);
|
||||
|
||||
sc.ChangeConfig(descriptor.Caption, descriptor.StartMode, descriptor.ServiceDependencies);
|
||||
sc.ChangeConfig(config.Caption, config.StartMode, config.ServiceDependencies);
|
||||
|
||||
sc.SetDescription(descriptor.Description);
|
||||
sc.SetDescription(config.Description);
|
||||
|
||||
SC_ACTION[] actions = descriptor.FailureActions;
|
||||
SC_ACTION[] actions = config.FailureActions;
|
||||
if (actions.Length > 0)
|
||||
{
|
||||
sc.SetFailureActions(descriptor.ResetFailureAfter, actions);
|
||||
sc.SetFailureActions(config.ResetFailureAfter, actions);
|
||||
}
|
||||
|
||||
bool isDelayedAutoStart = descriptor.StartMode == ServiceStartMode.Automatic && descriptor.DelayedAutoStart;
|
||||
bool isDelayedAutoStart = config.StartMode == ServiceStartMode.Automatic && config.DelayedAutoStart;
|
||||
if (isDelayedAutoStart)
|
||||
{
|
||||
sc.SetDelayedAutoStart(true);
|
||||
}
|
||||
|
||||
if (descriptor.PreshutdownTimeout is TimeSpan preshutdownTimeout)
|
||||
if (config.PreshutdownTimeout is TimeSpan preshutdownTimeout)
|
||||
{
|
||||
sc.SetPreshutdownTimeout(preshutdownTimeout);
|
||||
}
|
||||
|
||||
string? securityDescriptor = descriptor.SecurityDescriptor;
|
||||
string? securityDescriptor = config.SecurityDescriptor;
|
||||
if (securityDescriptor != null)
|
||||
{
|
||||
// throws ArgumentException
|
||||
|
@ -807,9 +807,9 @@ namespace WinSW
|
|||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
private static void ThrowNoSuchService(Win32Exception inner) => throw new CommandException(inner);
|
||||
|
||||
private static void InitLoggers(ServiceDescriptor descriptor, bool enableConsoleLogging)
|
||||
private static void InitLoggers(XmlServiceConfig config, bool enableConsoleLogging)
|
||||
{
|
||||
if (ServiceDescriptor.TestDescriptor != null)
|
||||
if (XmlServiceConfig.TestConfig != null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
@ -828,7 +828,7 @@ namespace WinSW
|
|||
List<IAppender> appenders = new List<IAppender>();
|
||||
|
||||
// .wrapper.log
|
||||
string wrapperLogPath = Path.Combine(descriptor.LogDirectory, descriptor.BaseName + ".wrapper.log");
|
||||
string wrapperLogPath = Path.Combine(config.LogDirectory, config.BaseName + ".wrapper.log");
|
||||
var wrapperLog = new FileAppender
|
||||
{
|
||||
AppendToFile = true,
|
||||
|
|
|
@ -17,7 +17,7 @@ namespace WinSW
|
|||
public sealed class WrapperService : ServiceBase, IEventLogger, IServiceEventLog
|
||||
{
|
||||
private readonly Process process = new Process();
|
||||
private readonly ServiceDescriptor descriptor;
|
||||
private readonly XmlServiceConfig config;
|
||||
private Dictionary<string, string>? envs;
|
||||
|
||||
internal WinSWExtensionManager ExtensionManager { get; }
|
||||
|
@ -45,11 +45,11 @@ namespace WinSW
|
|||
/// </remarks>
|
||||
public static Version Version => Assembly.GetExecutingAssembly().GetName().Version!;
|
||||
|
||||
public WrapperService(ServiceDescriptor descriptor)
|
||||
public WrapperService(XmlServiceConfig config)
|
||||
{
|
||||
this.descriptor = descriptor;
|
||||
this.ServiceName = this.descriptor.Id;
|
||||
this.ExtensionManager = new WinSWExtensionManager(this.descriptor);
|
||||
this.config = config;
|
||||
this.ServiceName = config.Id;
|
||||
this.ExtensionManager = new WinSWExtensionManager(config);
|
||||
this.CanShutdown = true;
|
||||
this.CanStop = true;
|
||||
this.CanPauseAndContinue = false;
|
||||
|
@ -59,12 +59,12 @@ namespace WinSW
|
|||
// Register the event log provider
|
||||
eventLogProvider.Service = this;
|
||||
|
||||
if (descriptor.Preshutdown)
|
||||
if (config.Preshutdown)
|
||||
{
|
||||
this.AcceptPreshutdown();
|
||||
}
|
||||
|
||||
Environment.CurrentDirectory = descriptor.WorkingDirectory;
|
||||
Environment.CurrentDirectory = config.WorkingDirectory;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -73,7 +73,7 @@ namespace WinSW
|
|||
/// </summary>
|
||||
private void HandleFileCopies()
|
||||
{
|
||||
var file = this.descriptor.BasePath + ".copies";
|
||||
var file = this.config.BasePath + ".copies";
|
||||
if (!File.Exists(file))
|
||||
{
|
||||
return; // nothing to handle
|
||||
|
@ -123,14 +123,14 @@ namespace WinSW
|
|||
/// <returns>Log Handler, which should be used for the spawned process</returns>
|
||||
private LogHandler CreateExecutableLogHandler()
|
||||
{
|
||||
string logDirectory = this.descriptor.LogDirectory;
|
||||
string logDirectory = this.config.LogDirectory;
|
||||
|
||||
if (!Directory.Exists(logDirectory))
|
||||
{
|
||||
Directory.CreateDirectory(logDirectory);
|
||||
}
|
||||
|
||||
LogHandler logAppender = this.descriptor.LogHandler;
|
||||
LogHandler logAppender = this.config.LogHandler;
|
||||
logAppender.EventLogger = this;
|
||||
return logAppender;
|
||||
}
|
||||
|
@ -243,12 +243,12 @@ namespace WinSW
|
|||
|
||||
private void DoStart()
|
||||
{
|
||||
this.envs = this.descriptor.EnvironmentVariables;
|
||||
this.envs = this.config.EnvironmentVariables;
|
||||
|
||||
this.HandleFileCopies();
|
||||
|
||||
// handle downloads
|
||||
List<Download> downloads = this.descriptor.Downloads;
|
||||
List<Download> downloads = this.config.Downloads;
|
||||
Task[] tasks = new Task[downloads.Count];
|
||||
for (int i = 0; i < downloads.Count; i++)
|
||||
{
|
||||
|
@ -287,10 +287,10 @@ namespace WinSW
|
|||
|
||||
try
|
||||
{
|
||||
string? prestartExecutable = this.descriptor.PrestartExecutable;
|
||||
string? prestartExecutable = this.config.PrestartExecutable;
|
||||
if (prestartExecutable != null)
|
||||
{
|
||||
using Process process = this.StartProcess(prestartExecutable, this.descriptor.PrestartArguments);
|
||||
using Process process = this.StartProcess(prestartExecutable, this.config.PrestartArguments);
|
||||
this.WaitForProcessToExit(process);
|
||||
this.LogInfo($"Pre-start process '{process.Format()}' exited with code {process.ExitCode}.");
|
||||
}
|
||||
|
@ -300,24 +300,24 @@ namespace WinSW
|
|||
Log.Error(e);
|
||||
}
|
||||
|
||||
string startArguments = this.descriptor.StartArguments ?? this.descriptor.Arguments;
|
||||
string startArguments = this.config.StartArguments ?? this.config.Arguments;
|
||||
|
||||
this.LogInfo("Starting " + this.descriptor.Executable);
|
||||
this.LogInfo("Starting " + this.config.Executable);
|
||||
|
||||
// Load and start extensions
|
||||
this.ExtensionManager.LoadExtensions();
|
||||
this.ExtensionManager.FireOnWrapperStarted();
|
||||
|
||||
LogHandler executableLogHandler = this.CreateExecutableLogHandler();
|
||||
this.StartProcess(this.process, startArguments, this.descriptor.Executable, executableLogHandler);
|
||||
this.StartProcess(this.process, startArguments, this.config.Executable, executableLogHandler);
|
||||
this.ExtensionManager.FireOnProcessStarted(this.process);
|
||||
|
||||
try
|
||||
{
|
||||
string? poststartExecutable = this.descriptor.PoststartExecutable;
|
||||
string? poststartExecutable = this.config.PoststartExecutable;
|
||||
if (poststartExecutable != null)
|
||||
{
|
||||
using Process process = this.StartProcess(poststartExecutable, this.descriptor.PoststartArguments, process =>
|
||||
using Process process = this.StartProcess(poststartExecutable, this.config.PoststartArguments, process =>
|
||||
{
|
||||
this.LogInfo($"Post-start process '{process.Format()}' exited with code {process.ExitCode}.");
|
||||
});
|
||||
|
@ -336,10 +336,10 @@ namespace WinSW
|
|||
{
|
||||
try
|
||||
{
|
||||
string? prestopExecutable = this.descriptor.PrestopExecutable;
|
||||
string? prestopExecutable = this.config.PrestopExecutable;
|
||||
if (prestopExecutable != null)
|
||||
{
|
||||
using Process process = this.StartProcess(prestopExecutable, this.descriptor.PrestopArguments);
|
||||
using Process process = this.StartProcess(prestopExecutable, this.config.PrestopArguments);
|
||||
this.WaitForProcessToExit(process);
|
||||
this.LogInfo($"Pre-stop process '{process.Format()}' exited with code {process.ExitCode}.");
|
||||
}
|
||||
|
@ -349,15 +349,15 @@ namespace WinSW
|
|||
Log.Error(e);
|
||||
}
|
||||
|
||||
string? stopArguments = this.descriptor.StopArguments;
|
||||
this.LogInfo("Stopping " + this.descriptor.Id);
|
||||
string? stopArguments = this.config.StopArguments;
|
||||
this.LogInfo("Stopping " + this.config.Id);
|
||||
this.orderlyShutdown = true;
|
||||
this.process.EnableRaisingEvents = false;
|
||||
|
||||
if (stopArguments is null)
|
||||
{
|
||||
Log.Debug("ProcessKill " + this.process.Id);
|
||||
ProcessHelper.StopProcessTree(this.process, this.descriptor.StopTimeout);
|
||||
ProcessHelper.StopProcessTree(this.process, this.config.StopTimeout);
|
||||
this.ExtensionManager.FireOnProcessTerminated(this.process);
|
||||
}
|
||||
else
|
||||
|
@ -366,7 +366,7 @@ namespace WinSW
|
|||
|
||||
Process stopProcess = new Process();
|
||||
|
||||
string stopExecutable = this.descriptor.StopExecutable ?? this.descriptor.Executable;
|
||||
string stopExecutable = this.config.StopExecutable ?? this.config.Executable;
|
||||
|
||||
// TODO: Redirect logging to Log4Net once https://github.com/kohsuke/winsw/pull/213 is integrated
|
||||
this.StartProcess(stopProcess, stopArguments, stopExecutable, null);
|
||||
|
@ -378,10 +378,10 @@ namespace WinSW
|
|||
|
||||
try
|
||||
{
|
||||
string? poststopExecutable = this.descriptor.PoststopExecutable;
|
||||
string? poststopExecutable = this.config.PoststopExecutable;
|
||||
if (poststopExecutable != null)
|
||||
{
|
||||
using Process process = this.StartProcess(poststopExecutable, this.descriptor.PoststopArguments);
|
||||
using Process process = this.StartProcess(poststopExecutable, this.config.PoststopArguments);
|
||||
this.WaitForProcessToExit(process);
|
||||
this.LogInfo($"Post-stop process '{process.Format()}' exited with code {process.ExitCode}.");
|
||||
}
|
||||
|
@ -394,12 +394,12 @@ namespace WinSW
|
|||
// Stop extensions
|
||||
this.ExtensionManager.FireBeforeWrapperStopped();
|
||||
|
||||
if (this.shuttingdown && this.descriptor.BeepOnShutdown)
|
||||
if (this.shuttingdown && this.config.BeepOnShutdown)
|
||||
{
|
||||
Console.Beep();
|
||||
}
|
||||
|
||||
Log.Info("Finished " + this.descriptor.Id);
|
||||
Log.Info("Finished " + this.config.Id);
|
||||
}
|
||||
|
||||
private void WaitForProcessToExit(Process process)
|
||||
|
@ -485,11 +485,11 @@ namespace WinSW
|
|||
executable: executable,
|
||||
arguments: arguments,
|
||||
envVars: this.envs,
|
||||
workingDirectory: this.descriptor.WorkingDirectory,
|
||||
priority: this.descriptor.Priority,
|
||||
workingDirectory: this.config.WorkingDirectory,
|
||||
priority: this.config.Priority,
|
||||
onExited: OnProcessCompleted,
|
||||
logHandler: logHandler,
|
||||
hideWindow: this.descriptor.HideWindow);
|
||||
hideWindow: this.config.HideWindow);
|
||||
}
|
||||
|
||||
private Process StartProcess(string executable, string? arguments, Action<Process>? onExited = null)
|
||||
|
@ -497,7 +497,7 @@ namespace WinSW
|
|||
var info = new ProcessStartInfo(executable, arguments)
|
||||
{
|
||||
UseShellExecute = false,
|
||||
WorkingDirectory = this.descriptor.WorkingDirectory,
|
||||
WorkingDirectory = this.config.WorkingDirectory,
|
||||
};
|
||||
|
||||
Process process = Process.Start(info);
|
||||
|
|
Loading…
Reference in New Issue