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