Fix inconsistencies in YAML config (#678)

pull/763/head
Buddhika Chathuranga 2020-12-28 21:09:44 +05:30 committed by GitHub
parent 027a8d30e3
commit 11d09aa6ae
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 407 additions and 760 deletions

View File

@ -175,8 +175,8 @@
"description": "Optionally specify the order of service shutdown. If configed as true, parent process is shutdown first", "description": "Optionally specify the order of service shutdown. If configed as true, parent process is shutdown first",
"$ref": "#/definitions/boolPattern" "$ref": "#/definitions/boolPattern"
}, },
"resetFailureAfter": { "resetFailure": {
"$id": "#/properties/resetFailureAfter", "$id": "#/properties/resetFailure",
"description": "This optional element controls the timing in which Windows SCM resets the failure count.", "description": "This optional element controls the timing in which Windows SCM resets the failure count.",
"type": "string" "type": "string"
}, },

View File

@ -16,7 +16,7 @@ executable: java
# delay: 20 sec # delay: 20 sec
# - # -
# action: reboot # action: reboot
#resetFailureAfter: 01:00:00 #resetFailure: 01:00:00
#securityDescriptor: security descriptor string #securityDescriptor: security descriptor string
#arguments: > #arguments: >
# -classpath # -classpath

View File

@ -78,10 +78,10 @@ namespace WinSW.Configuration
public ServiceAccount ServiceAccount => new() public ServiceAccount ServiceAccount => new()
{ {
ServiceAccountName = null, User = null,
ServiceAccountDomain = null, Domain = null,
ServiceAccountPassword = null, Password = null,
AllowServiceAcountLogonRight = false AllowServiceLogonRight = false
}; };
public class LogDefaults : Log public class LogDefaults : Log

View File

@ -1,29 +1,23 @@
using YamlDotNet.Serialization; namespace WinSW.Configuration
namespace WinSW.Configuration
{ {
public class ServiceAccount public class ServiceAccount
{ {
[YamlMember(Alias = "user")] public string? User { get; set; }
public string? ServiceAccountName { get; set; }
[YamlMember(Alias = "domain")] public string? Domain { get; set; }
public string? ServiceAccountDomain { get; set; }
[YamlMember(Alias = "password")] public string? Password { get; set; }
public string? ServiceAccountPassword { get; set; }
[YamlMember(Alias = "allowservicelogon")] public bool AllowServiceLogonRight { get; set; }
public bool AllowServiceAcountLogonRight { get; set; }
public string? ServiceAccountUser public string? FullUser
{ {
get => this.ServiceAccountName is null ? null : (this.ServiceAccountDomain ?? ".") + "\\" + this.ServiceAccountName; get => this.User is null ? null : (this.Domain ?? ".") + "\\" + this.User;
} }
public bool HasServiceAccount() public bool HasServiceAccount()
{ {
return !string.IsNullOrEmpty(this.ServiceAccountName); return !string.IsNullOrEmpty(this.User);
} }
} }
} }

View File

@ -572,9 +572,9 @@ namespace WinSW
string action = node.Attributes!["action"]?.Value ?? throw new InvalidDataException("'action' is missing"); string action = node.Attributes!["action"]?.Value ?? throw new InvalidDataException("'action' is missing");
var type = action switch var type = action switch
{ {
"restart" => SC_ACTION_TYPE.SC_ACTION_RESTART, "restart" => SC_ACTION_TYPE.RESTART,
"none" => SC_ACTION_TYPE.SC_ACTION_NONE, "none" => SC_ACTION_TYPE.NONE,
"reboot" => SC_ACTION_TYPE.SC_ACTION_REBOOT, "reboot" => SC_ACTION_TYPE.REBOOT,
_ => throw new Exception("Invalid failure action: " + action) _ => throw new Exception("Invalid failure action: " + action)
}; };
var delay = node.Attributes["delay"]; var delay = node.Attributes["delay"];
@ -621,15 +621,15 @@ namespace WinSW
var serviceAccount = Defaults.ServiceAccount; var serviceAccount = Defaults.ServiceAccount;
serviceAccount.ServiceAccountDomain = this.GetServiceAccountPart(node, "domain"); serviceAccount.Domain = this.GetServiceAccountPart(node, "domain");
serviceAccount.ServiceAccountName = this.GetServiceAccountPart(node, "user"); serviceAccount.User = this.GetServiceAccountPart(node, "user");
serviceAccount.ServiceAccountPassword = this.GetServiceAccountPart(node, "password"); serviceAccount.Password = this.GetServiceAccountPart(node, "password");
string? loginRight = this.GetServiceAccountPart(node, "allowservicelogon"); string? loginRight = this.GetServiceAccountPart(node, "allowservicelogon");
serviceAccount.AllowServiceAcountLogonRight = this.ParseAllowServiceAcountLogonRight(loginRight); serviceAccount.AllowServiceLogonRight = this.ParseAllowServiceAcountLogonRight(loginRight);
return serviceAccount; return serviceAccount;
} }

View File

@ -2,629 +2,214 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
#if VNEXT
using System.Runtime.CompilerServices;
#endif
using System.ServiceProcess; using System.ServiceProcess;
using System.Xml; using System.Xml;
using WinSW.Native; using WinSW.Native;
using WinSW.Util; using WinSW.Util;
using YamlDotNet.Serialization; using YamlDotNet.Serialization;
using static System.Environment; using YamlDotNet.Serialization.NamingConventions;
using static WinSW.Download;
namespace WinSW.Configuration namespace WinSW.Configuration
{ {
public class YamlServiceConfig : IServiceConfig public class YamlServiceConfig : IServiceConfig
{ {
private readonly DefaultSettings defaults; private readonly DefaultSettings defaults;
private readonly RawYamlServiceConfig raw;
public YamlServiceConfig() public YamlServiceConfig(string baseName, string directory)
{ {
this.defaults = new DefaultSettings(); this.defaults = new DefaultSettings();
this.BaseName = this.defaults.BaseName;
this.BasePath = this.defaults.BasePath; this.BaseName = baseName;
this.BasePath = Path.Combine(directory, baseName);
using (var reader = new StreamReader(this.BasePath + ".yml"))
{
var deserializer = new DeserializerBuilder().IgnoreUnmatchedProperties().WithNamingConvention(CamelCaseNamingConvention.Instance).Build();
this.raw = deserializer.Deserialize<RawYamlServiceConfig>(reader);
} }
[YamlMember(Alias = "id")] Environment.SetEnvironmentVariable("BASE", directory);
public string? IdYaml { get; set; }
[YamlMember(Alias = "name")] // ditto for ID
public string? NameYaml { get; set; } Environment.SetEnvironmentVariable("SERVICE_ID", this.Name);
[YamlMember(Alias = "description")] // New name
public string? DescriptionYaml { get; set; } Environment.SetEnvironmentVariable(WinSWSystem.EnvVarNameExecutablePath, this.ExecutablePath);
[YamlMember(Alias = "executable")] // Also inject system environment variables
public string? ExecutableYaml { get; set; } Environment.SetEnvironmentVariable(WinSWSystem.EnvVarNameServiceId, this.Name);
[YamlMember(Alias = "executablePath")] this.LoadEnvironmentVariables();
public string? ExecutablePathYaml { get; set; }
[YamlMember(Alias = "hideWindow")]
public bool? HideWindowYaml { get; set; }
[YamlMember(Alias = "workingdirectory")]
public string? WorkingDirectoryYaml { get; set; }
[YamlMember(Alias = "serviceaccount")]
public ServiceAccount? ServiceAccountYaml { get; set; }
[YamlMember(Alias = "log")]
public YamlLog? YAMLLog { get; set; }
[YamlMember(Alias = "download")]
public List<YamlDownload>? DownloadsYaml { get; set; }
[YamlMember(Alias = "arguments")]
public string? ArgumentsYaml { get; set; }
[YamlMember(Alias = "startArguments")]
public string? StartArgumentsYaml { get; set; }
[YamlMember(Alias = "stopArguments")]
public string? StopArgumentsYaml { get; set; }
[YamlMember(Alias = "stopExecutable")]
public string? StopExecutableYaml { get; set; }
[YamlMember(Alias = "stopParentProcessFirst")]
public bool? StopParentProcessFirstYaml { get; set; }
[YamlMember(Alias = "resetFailureAfter")]
public string? ResetFailureAfterYaml { get; set; }
[YamlMember(Alias = "stopTimeout")]
public string? StopTimeoutYaml { get; set; }
[YamlMember(Alias = "startMode")]
public string? StartModeYaml { get; set; }
[YamlMember(Alias = "serviceDependencies")]
public string[]? ServiceDependenciesYaml { get; set; }
[YamlMember(Alias = "waitHint")]
public string? WaitHintYaml { get; set; }
[YamlMember(Alias = "sleepTime")]
public string? SleepTimeYaml { get; set; }
[YamlMember(Alias = "interactive")]
public bool? InteractiveYaml { get; set; }
[YamlMember(Alias = "priority")]
public string? PriorityYaml { get; set; }
[YamlMember(Alias = "beepOnShutdown")]
public bool BeepOnShutdown { get; set; }
[YamlMember(Alias = "env")]
public List<YamlEnv>? EnvironmentVariablesYaml { get; set; }
[YamlMember(Alias = "onFailure")]
public List<YamlFailureAction>? YamlFailureActions { get; set; }
[YamlMember(Alias = "delayedAutoStart")]
public bool DelayedAutoStart { get; set; }
[YamlMember(Alias = "securityDescriptor")]
public string? SecurityDescriptorYaml { get; set; }
public class YamlEnv
{
[YamlMember(Alias = "name")]
public string? Name { get; set; }
[YamlMember(Alias = "value")]
public string? Value { get; set; }
} }
public class YamlLog : Log #pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
private YamlServiceConfig(RawYamlServiceConfig raw)
#pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
{ {
private readonly YamlServiceConfig configs; this.defaults = new DefaultSettings();
this.raw = raw;
public YamlLog()
{
this.configs = new YamlServiceConfig();
} }
[YamlMember(Alias = "mode")] public static YamlServiceConfig FromYaml(string yaml)
public string? ModeYamlLog { get; set; }
[YamlMember(Alias = "name")]
public string? NameYamlLog { get; set; }
[YamlMember(Alias = "sizeThreshold")]
public int? SizeThresholdYamlLog { get; set; }
[YamlMember(Alias = "keepFiles")]
public int? KeepFilesYamlLog { get; set; }
[YamlMember(Alias = "pattern")]
public string? PatternYamlLog { get; set; }
[YamlMember(Alias = "period")]
public int? PeriodYamlLog { get; set; }
[YamlMember(Alias = "logpath")]
public string? LogPathYamlLog { get; set; }
// Filters
[YamlMember(Alias = "outFileDisabled")]
public bool? OutFileDisabledYamlLog { get; set; }
[YamlMember(Alias = "errFileDisabled")]
public bool? ErrFileDisabledYamlLog { get; set; }
[YamlMember(Alias = "outFilePattern")]
public string? OutFilePatternYamlLog { get; set; }
[YamlMember(Alias = "errFilePattern")]
public string? ErrFilePatternYamlLog { get; set; }
// Zip options
[YamlMember(Alias = "autoRollAtTime")]
public string? AutoRollAtTimeYamlLog { get; set; }
[YamlMember(Alias = "zipOlderThanNumDays")]
public string? ZipOlderThanNumDaysYamlLog { get; set; }
[YamlMember(Alias = "zipDateFormat")]
public string? ZipDateFormatYamlLog { get; set; }
public override string Mode => this.ModeYamlLog is null ?
DefaultSettings.DefaultLogSettings.Mode :
this.ModeYamlLog;
public override string Name
{ {
get var deserializer = new DeserializerBuilder().IgnoreUnmatchedProperties().WithNamingConvention(CamelCaseNamingConvention.Instance).Build();
{ var raw = deserializer.Deserialize<RawYamlServiceConfig>(yaml);
return this.NameYamlLog is null ? return new(raw);
DefaultSettings.DefaultLogSettings.Name :
ExpandEnvironmentVariables(this.NameYamlLog);
}
} }
public override string Directory private static string? Expand(string? value) => value is null ? null : Environment.ExpandEnvironmentVariables(value);
private static bool? ExpandBoolean(string? value) => value is null ? null : ConfigHelper.YamlBoolParse(Environment.ExpandEnvironmentVariables(value));
private static TimeSpan? ExpandTimeSpan(string? value) => value is null ? null : ConfigHelper.ParseTimeSpan(Environment.ExpandEnvironmentVariables(value));
private static T? ExpandEnum<T>(
string? value,
#if VNEXT
[CallerMemberName] string? name = null)
#else
string? name = null)
#endif
where T : unmanaged, Enum
{ {
get if (value is null)
{ {
return this.LogPathYamlLog is null ? return null;
DefaultSettings.DefaultLogSettings.Directory :
ExpandEnvironmentVariables(this.LogPathYamlLog);
} }
}
public override int? SizeThreshold
{
get
{
return this.SizeThresholdYamlLog is null ?
DefaultSettings.DefaultLogSettings.SizeThreshold :
this.SizeThresholdYamlLog;
}
}
public override int? KeepFiles
{
get
{
return this.KeepFilesYamlLog is null ?
DefaultSettings.DefaultLogSettings.KeepFiles :
this.KeepFilesYamlLog;
}
}
public override string Pattern
{
get
{
if (this.PatternYamlLog != null)
{
return this.PatternYamlLog;
}
return DefaultSettings.DefaultLogSettings.Pattern;
}
}
public override int? Period => this.PeriodYamlLog is null ? 1 : this.PeriodYamlLog;
public override bool OutFileDisabled
{
get
{
return this.OutFileDisabledYamlLog is null ?
DefaultSettings.DefaultLogSettings.OutFileDisabled :
(bool)this.OutFileDisabledYamlLog;
}
}
public override bool ErrFileDisabled
{
get
{
return this.ErrFileDisabledYamlLog is null ?
this.configs.defaults.ErrFileDisabled :
(bool)this.ErrFileDisabledYamlLog;
}
}
public override string OutFilePattern
{
get
{
return this.OutFilePatternYamlLog is null ?
DefaultSettings.DefaultLogSettings.OutFilePattern :
ExpandEnvironmentVariables(this.OutFilePatternYamlLog);
}
}
public override string ErrFilePattern
{
get
{
return this.ErrFilePatternYamlLog is null ?
DefaultSettings.DefaultLogSettings.ErrFilePattern :
ExpandEnvironmentVariables(this.ErrFilePatternYamlLog);
}
}
public override string? AutoRollAtTime
{
get
{
return this.AutoRollAtTimeYamlLog is null ?
DefaultSettings.DefaultLogSettings.AutoRollAtTime :
this.AutoRollAtTimeYamlLog;
}
}
public override int? ZipOlderThanNumDays
{
get
{
int? zipolderthannumdays = null;
if (!string.IsNullOrEmpty(this.ZipOlderThanNumDaysYamlLog))
{
if (!int.TryParse(this.ZipOlderThanNumDaysYamlLog, out int zipolderthannumdaysValue))
{
throw new InvalidDataException("Roll-Size-Time Based rolling policy is specified but zipOlderThanNumDays does not match the int format found in configuration XML.");
}
zipolderthannumdays = zipolderthannumdaysValue;
}
return zipolderthannumdays;
}
}
public override string? ZipDateFormat
{
get
{
return this.ZipDateFormatYamlLog is null ?
DefaultSettings.DefaultLogSettings.ZipDateFormat :
this.ZipDateFormatYamlLog;
}
}
}
public class YamlDownload
{
[YamlMember(Alias = "from")]
public string FromYamlDownload { get; set; } = string.Empty;
[YamlMember(Alias = "to")]
public string ToYamlDownload { get; set; } = string.Empty;
[YamlMember(Alias = "auth")]
public string? AuthYamlDownload { get; set; }
[YamlMember(Alias = "username")]
public string? UsernameYamlDownload { get; set; }
[YamlMember(Alias = "password")]
public string? PasswordYamlDownload { get; set; }
[YamlMember(Alias = "unsecureAuth")]
public bool UnsecureAuthYamlDownload { get; set; }
[YamlMember(Alias = "failOnError")]
public bool FailOnErrorYamlDownload { get; set; }
[YamlMember(Alias = "proxy")]
public string? ProxyYamlDownload { get; set; }
public string FromDownload => ExpandEnvironmentVariables(this.FromYamlDownload);
public string ToDownload => ExpandEnvironmentVariables(this.ToYamlDownload);
public string? UsernameDownload => this.UsernameYamlDownload is null ? null : ExpandEnvironmentVariables(this.UsernameYamlDownload);
public string? PasswordDownload => this.PasswordYamlDownload is null ? null : ExpandEnvironmentVariables(this.PasswordYamlDownload);
public string? ProxyDownload => this.ProxyYamlDownload is null ? null : ExpandEnvironmentVariables(this.ProxyYamlDownload);
public AuthType AuthDownload
{
get
{
if (this.AuthYamlDownload is null)
{
return AuthType.None;
}
string auth = ExpandEnvironmentVariables(this.AuthYamlDownload);
try try
{ {
return (AuthType)Enum.Parse(typeof(AuthType), auth, true); return (T)Enum.Parse(typeof(T), value, true);
} }
catch catch
{ {
Console.WriteLine("Auth type in YAML must be one of the following:"); Console.WriteLine($"'{name ?? typeof(T).FullName}' in YAML must be one of the followings: {string.Join(", ", Enum.GetNames(typeof(T)))}.");
foreach (string at in Enum.GetNames(typeof(AuthType)))
{
Console.WriteLine(at);
}
throw; throw;
} }
} }
}
}
public class YamlFailureAction public string Name => Expand(this.raw.Id) ?? this.defaults.Name;
{
[YamlMember(Alias = "action")]
public string? FailureAction { get; set; }
[YamlMember(Alias = "delay")] public string Description => Expand(this.raw.Description) ?? this.defaults.Description;
public string? FailureActionDelay { get; set; }
public SC_ACTION_TYPE Type public string Executable => Expand(this.raw.Executable) ?? this.defaults.Executable;
{
get
{
var actionType = this.FailureAction switch
{
"restart" => SC_ACTION_TYPE.SC_ACTION_RESTART,
"none" => SC_ACTION_TYPE.SC_ACTION_NONE,
"reboot" => SC_ACTION_TYPE.SC_ACTION_REBOOT,
_ => throw new InvalidDataException("Invalid failure action: " + this.FailureAction)
};
return actionType; public string ExecutablePath => this.defaults.ExecutablePath;
}
}
public TimeSpan Delay => this.FailureActionDelay is null ? TimeSpan.Zero : ConfigHelper.ParseTimeSpan(this.FailureActionDelay); public string DisplayName => Expand(this.raw.Name) ?? this.defaults.DisplayName;
}
private string? GetArguments(string? args, ArgType type) public bool HideWindow => ExpandBoolean(this.raw.HideWindow) ?? this.defaults.HideWindow;
{
if (args is null)
{
switch (type)
{
case ArgType.Arg:
return this.defaults.Arguments;
case ArgType.Startarg:
return this.defaults.StartArguments;
case ArgType.Stoparg:
return this.defaults.StopArguments;
default:
return string.Empty;
}
}
return ExpandEnvironmentVariables(args); public bool StopParentProcessFirst => ExpandBoolean(this.raw.StopParentProcessFirst) ?? this.defaults.StopParentProcessFirst;
}
private enum ArgType public ServiceStartMode StartMode => ExpandEnum<ServiceStartMode>(this.raw.StartMode) ?? this.defaults.StartMode;
{
Arg = 0,
Startarg = 1,
Stoparg = 2
}
private List<Download> GetDownloads(List<YamlDownload>? downloads) public bool DelayedAutoStart => ExpandBoolean(this.raw.DelayedAutoStart) ?? this.defaults.DelayedAutoStart;
{
if (downloads is null)
{
return this.defaults.Downloads;
}
var result = new List<Download>(downloads.Count); public bool BeepOnShutdown => ExpandBoolean(this.raw.BeepOnShutdown) ?? this.defaults.BeepOnShutdown;
foreach (var item in downloads) public string Arguments => Expand(this.raw.Arguments) ?? this.defaults.Arguments;
{
result.Add(new Download(
item.FromDownload,
item.ToDownload,
item.FailOnErrorYamlDownload,
item.AuthDownload,
item.UsernameDownload,
item.PasswordDownload,
item.UnsecureAuthYamlDownload,
item.ProxyDownload));
}
return result; public string? StartArguments => Expand(this.raw.StartArguments) ?? this.defaults.StartArguments;
}
public string Name => this.IdYaml is null ? this.defaults.Name : ExpandEnvironmentVariables(this.IdYaml); public string? StopArguments => Expand(this.raw.StopArguments) ?? this.defaults.StopArguments;
public string Description => this.DescriptionYaml is null ? this.defaults.Description : ExpandEnvironmentVariables(this.DescriptionYaml); public string? StopExecutable => Expand(this.raw.StopExecutable) ?? this.defaults.StopExecutable;
public string Executable => this.ExecutableYaml is null ? this.defaults.Executable : ExpandEnvironmentVariables(this.ExecutableYaml);
public string ExecutablePath => this.ExecutablePathYaml is null ?
this.defaults.ExecutablePath :
ExpandEnvironmentVariables(this.ExecutablePathYaml);
public string DisplayName => this.NameYaml is null ? this.defaults.DisplayName : ExpandEnvironmentVariables(this.NameYaml);
public bool HideWindow => this.HideWindowYaml is null ? this.defaults.HideWindow : (bool)this.HideWindowYaml;
public bool StopParentProcessFirst
{
get
{
return this.StopParentProcessFirstYaml is null ?
this.defaults.StopParentProcessFirst :
(bool)this.StopParentProcessFirstYaml;
}
}
public ServiceStartMode StartMode
{
get
{
if (this.StartModeYaml is null)
{
return this.defaults.StartMode;
}
string p = ExpandEnvironmentVariables(this.StartModeYaml);
try
{
return (ServiceStartMode)Enum.Parse(typeof(ServiceStartMode), p, true);
}
catch
{
Console.WriteLine("Start mode in YAML must be one of the following:");
foreach (string sm in Enum.GetNames(typeof(ServiceStartMode)))
{
Console.WriteLine(sm);
}
throw;
}
}
}
public string Arguments
{
get
{
string? args = this.GetArguments(this.ArgumentsYaml, ArgType.Arg);
return args is null ? this.defaults.Arguments : args;
}
}
public string? StartArguments => this.GetArguments(this.StartArgumentsYaml, ArgType.Startarg);
public string? StopArguments => this.GetArguments(this.StopArgumentsYaml, ArgType.Stoparg);
public string? StopExecutable
{
get
{
return this.StopExecutableYaml is null ?
this.defaults.StopExecutable :
ExpandEnvironmentVariables(this.StopExecutableYaml);
}
}
public SC_ACTION[] FailureActions public SC_ACTION[] FailureActions
{ {
get get
{ {
if (this.YamlFailureActions is null) if (this.raw.OnFailure is null)
{ {
#if VNEXT
return Array.Empty<SC_ACTION>();
#else
return new SC_ACTION[0]; return new SC_ACTION[0];
#endif
} }
var arr = new List<SC_ACTION>(); var result = new SC_ACTION[this.raw.OnFailure.Count];
foreach (var item in this.YamlFailureActions) for (int i = 0; i < result.Length; i++)
{ {
arr.Add(new SC_ACTION(item.Type, item.Delay)); var item = new YamlFailureAction(this.raw.OnFailure[i]);
result[i] = new(item.Type, item.Delay);
} }
return arr.ToArray(); return result;
} }
} }
public TimeSpan ResetFailureAfter => this.ResetFailureAfterYaml is null ? public TimeSpan ResetFailureAfter => ExpandTimeSpan(this.raw.ResetFailure) ?? this.defaults.ResetFailureAfter;
this.defaults.ResetFailureAfter :
ConfigHelper.ParseTimeSpan(this.ResetFailureAfterYaml);
public string WorkingDirectory => this.WorkingDirectoryYaml is null ? public string WorkingDirectory => Expand(this.raw.WorkingDirectory) ?? this.defaults.WorkingDirectory;
this.defaults.WorkingDirectory :
ExpandEnvironmentVariables(this.WorkingDirectoryYaml);
public ProcessPriorityClass Priority public ProcessPriorityClass Priority => ExpandEnum<ProcessPriorityClass>(this.raw.Priority) ?? this.defaults.Priority;
{
get
{
if (this.PriorityYaml is null)
{
return this.defaults.Priority;
}
string p = ExpandEnvironmentVariables(this.PriorityYaml); public TimeSpan StopTimeout => ExpandTimeSpan(this.raw.StopTimeout) ?? this.defaults.StopTimeout;
try
{
return (ProcessPriorityClass)Enum.Parse(typeof(ProcessPriorityClass), p, true);
}
catch
{
Console.WriteLine("Priority in YAML must be one of the following:");
foreach (string pr in Enum.GetNames(typeof(ProcessPriorityClass)))
{
Console.WriteLine(pr);
}
throw;
}
}
}
public TimeSpan StopTimeout => this.StopTimeoutYaml is null ? this.defaults.StopTimeout : ConfigHelper.ParseTimeSpan(this.StopTimeoutYaml);
public string[] ServiceDependencies public string[] ServiceDependencies
{ {
get get
{ {
if (this.ServiceDependenciesYaml is null) if (this.raw.Depend is null)
{ {
return this.defaults.ServiceDependencies; return this.defaults.ServiceDependencies;
} }
var result = new List<string>(0); string[] result = new string[this.raw.Depend.Length];
foreach (string item in this.ServiceDependenciesYaml) for (int i = 0; i < result.Length; i++)
{ {
result.Add(ExpandEnvironmentVariables(item)); result[i] = Environment.ExpandEnvironmentVariables(this.raw.Depend[i]);
} }
return result.ToArray(); return result;
} }
} }
public TimeSpan WaitHint => this.WaitHintYaml is null ? this.defaults.WaitHint : ConfigHelper.ParseTimeSpan(this.WaitHintYaml); public TimeSpan WaitHint => this.defaults.WaitHint;
public TimeSpan SleepTime => this.SleepTimeYaml is null ? this.defaults.SleepTime : ConfigHelper.ParseTimeSpan(this.SleepTimeYaml); public TimeSpan SleepTime => this.defaults.SleepTime;
public bool Interactive => this.InteractiveYaml is null ? this.defaults.Interactive : (bool)this.InteractiveYaml; public bool Interactive => ExpandBoolean(this.raw.Interactive) ?? this.defaults.Interactive;
public List<Download> Downloads => this.GetDownloads(this.DownloadsYaml); public List<Download> Downloads
{
get
{
if (this.raw.Download is null)
{
return this.defaults.Downloads;
}
var result = new List<Download>(this.raw.Download.Count);
foreach (var item in this.raw.Download)
{
result.Add(new YamlDownload(item).Download);
}
return result;
}
}
public Dictionary<string, string> EnvironmentVariables { get; set; } = new Dictionary<string, string>(); public Dictionary<string, string> EnvironmentVariables { get; set; } = new Dictionary<string, string>();
public void LoadEnvironmentVariables() public void LoadEnvironmentVariables()
{ {
if (this.EnvironmentVariablesYaml is null) if (this.raw.Env is null)
{ {
this.EnvironmentVariables = this.defaults.EnvironmentVariables; this.EnvironmentVariables = this.defaults.EnvironmentVariables;
return;
} }
else
{ foreach (var item in this.raw.Env)
foreach (var item in this.EnvironmentVariablesYaml)
{ {
if (item.Name is null || item.Value is null) if (item.Name is null || item.Value is null)
{ {
@ -632,17 +217,34 @@ namespace WinSW.Configuration
} }
string key = item.Name; string key = item.Name;
string value = ExpandEnvironmentVariables(item.Value); string value = Environment.ExpandEnvironmentVariables(item.Value);
this.EnvironmentVariables[key] = value; this.EnvironmentVariables[key] = value;
SetEnvironmentVariable(key, value); Environment.SetEnvironmentVariable(key, value);
}
} }
} }
public ServiceAccount ServiceAccount => this.ServiceAccountYaml is null ? this.defaults.ServiceAccount : this.ServiceAccountYaml; public ServiceAccount ServiceAccount
{
get
{
var rawServiceAccount = this.raw.ServiceAccount;
if (rawServiceAccount is null)
{
return this.defaults.ServiceAccount;
}
public Log Log => this.YAMLLog is null ? this.defaults.Log : this.YAMLLog; return new()
{
User = Expand(rawServiceAccount.User),
Domain = Expand(rawServiceAccount.Domain),
Password = Expand(rawServiceAccount.Password),
AllowServiceLogonRight = ExpandBoolean(rawServiceAccount.AllowServiceLogon) ?? false,
};
}
}
public Log Log => this.raw.Log is null ? this.defaults.Log : new YamlLog(this.raw.Log);
public string LogDirectory => this.Log.Directory; public string LogDirectory => this.Log.Directory;
@ -650,9 +252,7 @@ namespace WinSW.Configuration
public XmlNode? XmlExtensions => null; public XmlNode? XmlExtensions => null;
// YAML Extension public List<YamlExtensionConfig>? YamlExtensions => this.raw.Extensions;
[YamlMember(Alias = "extensions")]
public List<YamlExtensionConfig>? YamlExtensions { get; set; }
public List<string> ExtensionIds public List<string> ExtensionIds
{ {
@ -689,17 +289,158 @@ namespace WinSW.Configuration
public string BasePath { get; set; } public string BasePath { get; set; }
public string? SecurityDescriptor public string? SecurityDescriptor => Expand(this.raw.SecurityDescriptor) ?? this.defaults.SecurityDescriptor;
internal sealed class RawYamlServiceConfig
{ {
get public string? Id;
{ public string? Name;
if (this.SecurityDescriptorYaml is null) public string? Description;
{ public string? Executable;
return this.defaults.SecurityDescriptor; public string? HideWindow;
public string? WorkingDirectory;
public RawServiceAccount? ServiceAccount;
public RawYamlLog? Log;
public List<RawYamlDownload>? Download;
public string? Arguments;
public string? StartArguments;
public string? StopArguments;
public string? StopExecutable;
public string? StopParentProcessFirst;
public string? ResetFailure;
public string? StopTimeout;
public string? StartMode;
public string[]? Depend;
public string? Interactive;
public string? Priority;
public string? BeepOnShutdown;
public List<RawYamlEnv>? Env;
public List<RawYamlFailureAction>? OnFailure;
public string? DelayedAutoStart;
public string? SecurityDescriptor;
public List<YamlExtensionConfig>? Extensions;
} }
return ExpandEnvironmentVariables(this.SecurityDescriptorYaml); internal sealed class RawServiceAccount
} {
public string? User;
public string? Domain;
public string? Password;
public string? AllowServiceLogon;
}
internal sealed class YamlLog : Log
{
private readonly DefaultSettings.LogDefaults defaults = new();
private readonly RawYamlLog raw;
internal YamlLog(RawYamlLog raw) => this.raw = raw;
public override string Mode => Expand(this.raw.Mode) ?? this.defaults.Mode;
public override string Name => Expand(this.raw.Name) ?? this.defaults.Name;
public override string Directory => Expand(this.raw.LogPath) ?? this.defaults.Directory;
public override int? SizeThreshold => this.raw.SizeThreshold ?? this.defaults.SizeThreshold;
public override int? KeepFiles => this.raw.KeepFiles ?? this.defaults.KeepFiles;
public override string Pattern => Expand(this.raw.Pattern) ?? this.defaults.Pattern;
public override int? Period => this.raw.Period ?? this.defaults.Period;
public override bool OutFileDisabled => ExpandBoolean(this.raw.OutFileDisabled) ?? this.defaults.OutFileDisabled;
public override bool ErrFileDisabled => ExpandBoolean(this.raw.ErrFileDisabled) ?? this.defaults.ErrFileDisabled;
public override string OutFilePattern => Expand(this.raw.OutFilePattern) ?? this.defaults.OutFilePattern;
public override string ErrFilePattern => Expand(this.raw.ErrFilePattern) ?? this.defaults.ErrFilePattern;
public override string? AutoRollAtTime => Expand(this.raw.AutoRollAtTime) ?? this.defaults.AutoRollAtTime;
public override int? ZipOlderThanNumDays => this.raw.ZipOlderThanNumDays;
public override string? ZipDateFormat => Expand(this.raw.ZipDateFormat) ?? this.defaults.ZipDateFormat;
}
internal sealed class RawYamlLog
{
public string? Mode;
public string? Name;
public int? SizeThreshold;
public int? KeepFiles;
public string? Pattern;
public int? Period;
public string? LogPath;
public string? OutFileDisabled;
public string? ErrFileDisabled;
public string? OutFilePattern;
public string? ErrFilePattern;
public string? AutoRollAtTime;
public int? ZipOlderThanNumDays;
public string? ZipDateFormat;
}
internal sealed class YamlDownload
{
private readonly RawYamlDownload raw;
internal YamlDownload(RawYamlDownload raw) => this.raw = raw;
public Download Download => new(this.From, this.To, this.FailOnError, this.Auth, this.Username, this.Password, this.UnsecureAuth, this.Proxy);
public string From => Expand(this.raw.From)!;
public string To => Expand(this.raw.To)!;
public string? Username => Expand(this.raw.Username);
public string? Password => Expand(this.raw.Password);
public bool UnsecureAuth => ExpandBoolean(this.raw.UnsecureAuth) ?? false;
public bool FailOnError => ExpandBoolean(this.raw.FailOnError) ?? false;
public string? Proxy => Expand(this.raw.Proxy);
public Download.AuthType Auth => ExpandEnum<Download.AuthType>(this.raw.Auth) ?? Download.AuthType.None;
}
internal sealed class RawYamlDownload
{
public string From = string.Empty;
public string To = string.Empty;
public string? Auth;
public string? Username;
public string? Password;
public string? UnsecureAuth;
public string? FailOnError;
public string? Proxy;
}
internal sealed class RawYamlEnv
{
public string? Name;
public string? Value;
}
internal sealed class YamlFailureAction
{
private readonly RawYamlFailureAction raw;
internal YamlFailureAction(RawYamlFailureAction raw) => this.raw = raw;
public SC_ACTION_TYPE Type => ExpandEnum<SC_ACTION_TYPE>(this.raw.Action) ?? SC_ACTION_TYPE.NONE;
public TimeSpan Delay => ExpandTimeSpan(this.raw.Delay) ?? TimeSpan.Zero;
}
internal sealed class RawYamlFailureAction
{
public string? Action;
public string? Delay;
} }
} }
} }

View File

@ -1,51 +0,0 @@
using System;
using System.IO;
using WinSW.Configuration;
using YamlDotNet.Serialization;
namespace WinSW
{
public class YamlServiceConfigLoader
{
public readonly YamlServiceConfig Config;
public YamlServiceConfigLoader(string baseName, string directory)
{
string basepath = Path.Combine(directory, baseName);
using (var reader = new StreamReader(basepath + ".yml"))
{
string file = reader.ReadToEnd();
var deserializer = new DeserializerBuilder().IgnoreUnmatchedProperties().Build();
this.Config = deserializer.Deserialize<YamlServiceConfig>(file);
}
Environment.SetEnvironmentVariable("BASE", directory);
// ditto for ID
Environment.SetEnvironmentVariable("SERVICE_ID", this.Config.Name);
// New name
Environment.SetEnvironmentVariable(WinSWSystem.EnvVarNameExecutablePath, new DefaultSettings().ExecutablePath);
// Also inject system environment variables
Environment.SetEnvironmentVariable(WinSWSystem.EnvVarNameServiceId, this.Config.Name);
this.Config.LoadEnvironmentVariables();
}
public YamlServiceConfigLoader(YamlServiceConfig configs)
{
this.Config = configs;
this.Config.LoadEnvironmentVariables();
}
public static YamlServiceConfigLoader FromYaml(string yaml)
{
var deserializer = new DeserializerBuilder().IgnoreUnmatchedProperties().Build();
var configs = deserializer.Deserialize<YamlServiceConfig>(yaml);
return new YamlServiceConfigLoader(configs);
}
}
}

View File

@ -249,7 +249,7 @@ namespace WinSW.Extensions
throw new ExtensionException(id, "Cannot load the class by name: " + className, ex); throw new ExtensionException(id, "Cannot load the class by name: " + className, ex);
} }
if (!(created is IWinSWExtension extension)) if (created is not IWinSWExtension extension)
{ {
throw new ExtensionException(id, "The loaded class is not a WinSW extension: " + className + ". Type is " + created.GetType()); throw new ExtensionException(id, "The loaded class is not a WinSW extension: " + className + ". Type is " + created.GetType());
} }

View File

@ -1,5 +1,4 @@
using System; using System;
using System.Runtime.InteropServices;
using System.Security.AccessControl; using System.Security.AccessControl;
using System.ServiceProcess; using System.ServiceProcess;
using System.Text; using System.Text;
@ -12,22 +11,22 @@ namespace WinSW.Native
/// <summary> /// <summary>
/// No action. /// No action.
/// </summary> /// </summary>
SC_ACTION_NONE = 0, NONE = 0,
/// <summary> /// <summary>
/// Restart the service. /// Restart the service.
/// </summary> /// </summary>
SC_ACTION_RESTART = 1, RESTART = 1,
/// <summary> /// <summary>
/// Reboot the computer. /// Reboot the computer.
/// </summary> /// </summary>
SC_ACTION_REBOOT = 2, REBOOT = 2,
/// <summary> /// <summary>
/// Run a command. /// Run a command.
/// </summary> /// </summary>
SC_ACTION_RUN_COMMAND = 3, RUN_COMMAND = 3,
} }
public struct SC_ACTION public struct SC_ACTION

View File

@ -17,7 +17,7 @@ namespace WinSW.Plugins
/// <summary> /// <summary>
/// Absolute path to the PID file, which stores ID of the previously launched process. /// Absolute path to the PID file, which stores ID of the previously launched process.
/// </summary> /// </summary>
public string Pidfile { get; private set; } public string PidFile { get; private set; }
/// <summary> /// <summary>
/// Defines the process termination timeout in milliseconds. /// Defines the process termination timeout in milliseconds.
@ -53,7 +53,7 @@ namespace WinSW.Plugins
public RunawayProcessKillerExtension(string pidfile, int stopTimeoutMs = 5000, bool stopParentFirst = true, bool checkWinSWEnvironmentVariable = true) public RunawayProcessKillerExtension(string pidfile, int stopTimeoutMs = 5000, bool stopParentFirst = true, bool checkWinSWEnvironmentVariable = true)
#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.Pidfile = pidfile; this.PidFile = pidfile;
this.StopTimeout = TimeSpan.FromMilliseconds(stopTimeoutMs); this.StopTimeout = TimeSpan.FromMilliseconds(stopTimeoutMs);
this.StopParentProcessFirst = stopParentFirst; this.StopParentProcessFirst = stopParentFirst;
this.CheckWinSWEnvironmentVariable = checkWinSWEnvironmentVariable; this.CheckWinSWEnvironmentVariable = checkWinSWEnvironmentVariable;
@ -184,7 +184,7 @@ namespace WinSW.Plugins
{ {
// 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.StopParentProcessFirst = bool.Parse(XmlHelper.SingleElement(node, "stopParentFirst", false)!); this.StopParentProcessFirst = bool.Parse(XmlHelper.SingleElement(node, "stopParentFirst", false)!);
this.ServiceId = descriptor.Name; this.ServiceId = descriptor.Name;
@ -197,12 +197,12 @@ namespace WinSW.Plugins
{ {
var dict = config.GetSettings(); var dict = config.GetSettings();
this.Pidfile = ExpandEnvironmentVariables((string)dict["pidfile"]); this.PidFile = ExpandEnvironmentVariables((string)dict["pidFile"]);
string stopTimeOutConfig = ExpandEnvironmentVariables((string)dict["stopTimeOut"]); string stopTimeOutConfig = ExpandEnvironmentVariables((string)dict["stopTimeout"]);
this.StopTimeout = TimeSpan.FromMilliseconds(int.Parse(stopTimeOutConfig)); this.StopTimeout = TimeSpan.FromMilliseconds(int.Parse(stopTimeOutConfig));
string StopParentProcessFirstConfig = ExpandEnvironmentVariables((string)dict["StopParentFirst"]); string StopParentProcessFirstConfig = ExpandEnvironmentVariables((string)dict["stopParentFirst"]);
this.StopParentProcessFirst = bool.Parse(StopParentProcessFirstConfig); this.StopParentProcessFirst = bool.Parse(StopParentProcessFirstConfig);
try try
@ -224,16 +224,16 @@ namespace WinSW.Plugins
{ {
// Read PID file from the disk // Read PID file from the disk
int pid; int pid;
if (File.Exists(this.Pidfile)) if (File.Exists(this.PidFile))
{ {
string pidstring; string pidstring;
try try
{ {
pidstring = File.ReadAllText(this.Pidfile); pidstring = File.ReadAllText(this.PidFile);
} }
catch (Exception ex) catch (Exception ex)
{ {
Logger.Error("Cannot read PID file from " + this.Pidfile, ex); Logger.Error("Cannot read PID file from " + this.PidFile, ex);
return; return;
} }
@ -243,13 +243,13 @@ namespace WinSW.Plugins
} }
catch (FormatException e) catch (FormatException e)
{ {
Logger.Error("Invalid PID file number in '" + this.Pidfile + "'. The runaway process won't be checked", e); Logger.Error("Invalid PID file number in '" + this.PidFile + "'. The runaway process won't be checked", e);
return; return;
} }
} }
else else
{ {
Logger.Warn("The requested PID file '" + this.Pidfile + "' does not exist. The runaway process won't be checked"); Logger.Warn("The requested PID file '" + this.PidFile + "' does not exist. The runaway process won't be checked");
return; return;
} }
@ -309,14 +309,14 @@ namespace WinSW.Plugins
/// <param name="process"></param> /// <param name="process"></param>
public override void OnProcessStarted(Process process) public override void OnProcessStarted(Process process)
{ {
Logger.Info("Recording PID of the started process:" + process.Id + ". PID file destination is " + this.Pidfile); Logger.Info("Recording PID of the started process:" + process.Id + ". PID file destination is " + this.PidFile);
try try
{ {
File.WriteAllText(this.Pidfile, process.Id.ToString()); File.WriteAllText(this.PidFile, process.Id.ToString());
} }
catch (Exception ex) catch (Exception ex)
{ {
Logger.Error("Cannot update the PID file " + this.Pidfile, ex); Logger.Error("Cannot update the PID file " + this.PidFile, ex);
} }
} }
} }

View File

@ -49,7 +49,7 @@ namespace WinSW.Plugins
object mappingNode = dict["mapping"]; object mappingNode = dict["mapping"];
if (!(mappingNode is List<object> mappings)) if (mappingNode is not List<object> mappings)
{ {
throw new InvalidDataException("SharedDirectoryMapper mapping should be a list"); throw new InvalidDataException("SharedDirectoryMapper mapping should be a list");
} }

View File

@ -32,7 +32,7 @@ namespace WinSW.Plugins
public static SharedDirectoryMapperConfig FromYaml(object yamlObject) public static SharedDirectoryMapperConfig FromYaml(object yamlObject)
{ {
if (!(yamlObject is Dictionary<object, object> dict)) if (yamlObject is not Dictionary<object, object> dict)
{ {
// TODO : throw ExtensionExeption // TODO : throw ExtensionExeption
throw new InvalidDataException("SharedDirectoryMapperConfig config error"); throw new InvalidDataException("SharedDirectoryMapperConfig config error");
@ -42,7 +42,7 @@ namespace WinSW.Plugins
bool enableMapping = ConfigHelper.YamlBoolParse(enableMappingConfig); bool enableMapping = ConfigHelper.YamlBoolParse(enableMappingConfig);
string label = ExpandEnvironmentVariables((string)dict["label"]); string label = ExpandEnvironmentVariables((string)dict["label"]);
string uncPath = ExpandEnvironmentVariables((string)dict["uncpath"]); string uncPath = ExpandEnvironmentVariables((string)dict["uncPath"]);
return new SharedDirectoryMapperConfig(enableMapping, label, uncPath); return new SharedDirectoryMapperConfig(enableMapping, label, uncPath);
} }

View File

@ -57,11 +57,11 @@ extensions:
enabled: yes enabled: yes
className: ""{this.testExtension}"" className: ""{this.testExtension}""
settings: settings:
pidfile: 'foo/bar/pid.txt' pidFile: 'foo/bar/pid.txt'
stopTimeOut: 5000 stopTimeout: 5000
StopParentFirst: true"; stopParentFirst: true";
this._testServiceDescriptorYaml = YamlServiceConfigLoader.FromYaml(seedYaml).Config; this._testServiceDescriptorYaml = YamlServiceConfig.FromYaml(seedYaml);
} }
[Test] [Test]
@ -74,7 +74,7 @@ extensions:
// Check the file is correct // Check the file is correct
var extension = manager.Extensions["killRunawayProcess"] as RunawayProcessKillerExtension; var extension = manager.Extensions["killRunawayProcess"] as RunawayProcessKillerExtension;
Assert.IsNotNull(extension, "RunawayProcessKillerExtension should be loaded"); Assert.IsNotNull(extension, "RunawayProcessKillerExtension should be loaded");
Assert.AreEqual("foo/bar/pid.txt", extension.Pidfile, "Loaded PID file path is not equal to the expected one"); Assert.AreEqual("foo/bar/pid.txt", extension.PidFile, "Loaded PID file path is not equal to the expected one");
Assert.AreEqual(5000, extension.StopTimeout.TotalMilliseconds, "Loaded Stop Timeout is not equal to the expected one"); Assert.AreEqual(5000, extension.StopTimeout.TotalMilliseconds, "Loaded Stop Timeout is not equal to the expected one");
Assert.AreEqual(true, extension.StopParentProcessFirst, "Loaded StopParentFirst is not equal to the expected one"); Assert.AreEqual(true, extension.StopParentProcessFirst, "Loaded StopParentFirst is not equal to the expected one");
} }
@ -89,7 +89,7 @@ extensions:
// Check the file is correct // Check the file is correct
var extension = manager.Extensions["killRunawayProcess"] as RunawayProcessKillerExtension; var extension = manager.Extensions["killRunawayProcess"] as RunawayProcessKillerExtension;
Assert.IsNotNull(extension, "RunawayProcessKillerExtension should be loaded"); Assert.IsNotNull(extension, "RunawayProcessKillerExtension should be loaded");
Assert.AreEqual("foo/bar/pid.txt", extension.Pidfile, "Loaded PID file path is not equal to the expected one"); Assert.AreEqual("foo/bar/pid.txt", extension.PidFile, "Loaded PID file path is not equal to the expected one");
Assert.AreEqual(5000, extension.StopTimeout.TotalMilliseconds, "Loaded Stop Timeout is not equal to the expected one"); Assert.AreEqual(5000, extension.StopTimeout.TotalMilliseconds, "Loaded Stop Timeout is not equal to the expected one");
Assert.AreEqual(true, extension.StopParentProcessFirst, "Loaded StopParentFirst is not equal to the expected one"); Assert.AreEqual(true, extension.StopParentProcessFirst, "Loaded StopParentFirst is not equal to the expected one");
} }
@ -133,7 +133,7 @@ extensions:
manager.LoadExtensions(); manager.LoadExtensions();
var extension = manager.Extensions[extensionId] as RunawayProcessKillerExtension; var extension = manager.Extensions[extensionId] as RunawayProcessKillerExtension;
Assert.IsNotNull(extension, "RunawayProcessKillerExtension should be loaded"); Assert.IsNotNull(extension, "RunawayProcessKillerExtension should be loaded");
Assert.AreEqual(pidfile, extension.Pidfile, "PidFile should have been retained during the config roundtrip"); Assert.AreEqual(pidfile, extension.PidFile, "PidFile should have been retained during the config roundtrip");
// Inject PID // Inject PID
File.WriteAllText(pidfile, proc.Id.ToString()); File.WriteAllText(pidfile, proc.Id.ToString());

View File

@ -62,10 +62,10 @@ extensions:
mapping: mapping:
- enabled: false - enabled: false
label: N label: N
uncpath: \\UNC uncPath: \\UNC
- enabled: false - enabled: false
label: M label: M
uncpath: \\UNC2 uncPath: \\UNC2
- id: mapNetworDirs2 - id: mapNetworDirs2
className: ""{this.testExtension}"" className: ""{this.testExtension}""
enabled: true enabled: true
@ -73,12 +73,12 @@ extensions:
mapping: mapping:
- enabled: false - enabled: false
label: X label: X
uncpath: \\UNC uncPath: \\UNC
- enabled: false - enabled: false
label: Y label: Y
uncpath: \\UNC2"; uncPath: \\UNC2";
this._testServiceDescriptorYaml = YamlServiceConfigLoader.FromYaml(seedYaml).Config; this._testServiceDescriptorYaml = YamlServiceConfig.FromYaml(seedYaml);
} }
[Test] [Test]

View File

@ -102,28 +102,25 @@ $@"<service>
[Test] [Test]
public void VerifyWorkingDirectory() public void VerifyWorkingDirectory()
{ {
Debug.WriteLine("_extendedServiceDescriptor.WorkingDirectory :: " + this._extendedServiceDescriptor.WorkingDirectory);
Assert.That(this._extendedServiceDescriptor.WorkingDirectory, Is.EqualTo(ExpectedWorkingDirectory)); Assert.That(this._extendedServiceDescriptor.WorkingDirectory, Is.EqualTo(ExpectedWorkingDirectory));
} }
[Test] [Test]
public void VerifyServiceLogonRight() public void VerifyServiceLogonRight()
{ {
Assert.That(_extendedServiceDescriptor.ServiceAccount.AllowServiceAcountLogonRight, Is.True); Assert.That(_extendedServiceDescriptor.ServiceAccount.AllowServiceLogonRight, Is.True);
} }
[Test] [Test]
public void VerifyUsername() public void VerifyUsername()
{ {
Debug.WriteLine("_extendedServiceDescriptor.WorkingDirectory :: " + _extendedServiceDescriptor.WorkingDirectory); Assert.That(_extendedServiceDescriptor.ServiceAccount.FullUser, Is.EqualTo(Domain + "\\" + Username));
Assert.That(_extendedServiceDescriptor.ServiceAccount.ServiceAccountUser, Is.EqualTo(Domain + "\\" + Username));
} }
[Test] [Test]
public void VerifyPassword() public void VerifyPassword()
{ {
Debug.WriteLine("_extendedServiceDescriptor.WorkingDirectory :: " + _extendedServiceDescriptor.WorkingDirectory); Assert.That(_extendedServiceDescriptor.ServiceAccount.Password, Is.EqualTo(Password));
Assert.That(_extendedServiceDescriptor.ServiceAccount.ServiceAccountPassword, Is.EqualTo(Password));
} }
[Test] [Test]
@ -307,7 +304,7 @@ $@"<service>
+ "</serviceaccount>" + "</serviceaccount>"
+ "</service>"; + "</service>";
var serviceDescriptor = XmlServiceConfig.FromXML(seedXml); var serviceDescriptor = XmlServiceConfig.FromXML(seedXml);
Assert.That(serviceDescriptor.ServiceAccount.AllowServiceAcountLogonRight, Is.False); Assert.That(serviceDescriptor.ServiceAccount.AllowServiceLogonRight, Is.False);
} }
[Test] [Test]
@ -321,7 +318,7 @@ $@"<service>
+ "</serviceaccount>" + "</serviceaccount>"
+ "</service>"; + "</service>";
var serviceDescriptor = XmlServiceConfig.FromXML(seedXml); var serviceDescriptor = XmlServiceConfig.FromXML(seedXml);
Assert.That(serviceDescriptor.ServiceAccount.AllowServiceAcountLogonRight, Is.False); Assert.That(serviceDescriptor.ServiceAccount.AllowServiceLogonRight, Is.False);
} }
[Test] [Test]

View File

@ -30,14 +30,14 @@ arguments: My Arguments
log: log:
mode: roll mode: roll
logpath: c:\logs logpath: c:\logs
serviceaccount: serviceAccount:
domain: {Domain} domain: {Domain}
user: {Username} user: {Username}
password: {Password} password: {Password}
allowservicelogon: {AllowServiceAccountLogonRight} allowServiceLogon: {AllowServiceAccountLogonRight}
workingdirectory: {ExpectedWorkingDirectory}"; workingDirectory: {ExpectedWorkingDirectory}";
this._extendedServiceDescriptor = YamlServiceConfigLoader.FromYaml(yaml).Config; this._extendedServiceDescriptor = YamlServiceConfig.FromYaml(yaml);
} }
[Test] [Test]
@ -57,7 +57,7 @@ executable: node.exe
arguments: My Arguments arguments: My Arguments
startMode: roll"; startMode: roll";
this._extendedServiceDescriptor = YamlServiceConfigLoader.FromYaml(yaml).Config; this._extendedServiceDescriptor = YamlServiceConfig.FromYaml(yaml);
Assert.That(() => this._extendedServiceDescriptor.StartMode, Throws.ArgumentException); Assert.That(() => this._extendedServiceDescriptor.StartMode, Throws.ArgumentException);
} }
@ -72,61 +72,58 @@ executable: node.exe
arguments: My Arguments arguments: My Arguments
startMode: manual"; startMode: manual";
this._extendedServiceDescriptor = YamlServiceConfigLoader.FromYaml(yaml).Config; this._extendedServiceDescriptor = YamlServiceConfig.FromYaml(yaml);
Assert.That(this._extendedServiceDescriptor.StartMode, Is.EqualTo(ServiceStartMode.Manual)); Assert.That(this._extendedServiceDescriptor.StartMode, Is.EqualTo(ServiceStartMode.Manual));
} }
[Test] [Test]
public void VerifyWorkingDirectory() public void VerifyWorkingDirectory()
{ {
Debug.WriteLine("_extendedServiceDescriptor.WorkingDirectory :: " + this._extendedServiceDescriptor.WorkingDirectory);
Assert.That(this._extendedServiceDescriptor.WorkingDirectory, Is.EqualTo(ExpectedWorkingDirectory)); Assert.That(this._extendedServiceDescriptor.WorkingDirectory, Is.EqualTo(ExpectedWorkingDirectory));
} }
[Test] [Test]
public void VerifyServiceLogonRight() public void VerifyServiceLogonRight()
{ {
Assert.That(_extendedServiceDescriptor.ServiceAccount.AllowServiceAcountLogonRight, Is.True); Assert.That(_extendedServiceDescriptor.ServiceAccount.AllowServiceLogonRight, Is.True);
} }
[Test] [Test]
public void VerifyUsername() public void VerifyUsername()
{ {
Debug.WriteLine("_extendedServiceDescriptor.WorkingDirectory :: " + _extendedServiceDescriptor.WorkingDirectory); Assert.That(_extendedServiceDescriptor.ServiceAccount.FullUser, Is.EqualTo(Domain + "\\" + Username));
Assert.That(_extendedServiceDescriptor.ServiceAccount.ServiceAccountUser, Is.EqualTo(Domain + "\\" + Username));
} }
[Test] [Test]
public void VerifyPassword() public void VerifyPassword()
{ {
Debug.WriteLine("_extendedServiceDescriptor.WorkingDirectory :: " + _extendedServiceDescriptor.WorkingDirectory); Assert.That(_extendedServiceDescriptor.ServiceAccount.Password, Is.EqualTo(Password));
Assert.That(_extendedServiceDescriptor.ServiceAccount.ServiceAccountPassword, Is.EqualTo(Password));
} }
[Test] [Test]
public void Priority() public void Priority()
{ {
var sd = YamlServiceConfigLoader.FromYaml(@" var sd = YamlServiceConfig.FromYaml(@"
id: service.exe id: service.exe
name: Service name: Service
description: The Service. description: The Service.
executable: node.exe executable: node.exe
priority: normal").Config; priority: normal");
Assert.That(sd.Priority, Is.EqualTo(ProcessPriorityClass.Normal)); Assert.That(sd.Priority, Is.EqualTo(ProcessPriorityClass.Normal));
sd = YamlServiceConfigLoader.FromYaml(@" sd = YamlServiceConfig.FromYaml(@"
id: service.exe id: service.exe
name: Service name: Service
description: The Service. description: The Service.
executable: node.exe executable: node.exe
priority: idle").Config; priority: idle");
Assert.That(sd.Priority, Is.EqualTo(ProcessPriorityClass.Idle)); Assert.That(sd.Priority, Is.EqualTo(ProcessPriorityClass.Idle));
sd = YamlServiceConfigLoader.FromYaml(@" sd = YamlServiceConfig.FromYaml(@"
id: service.exe id: service.exe
name: Service name: Service
description: The Service. description: The Service.
executable: node.exe").Config; executable: node.exe");
Assert.That(sd.Priority, Is.EqualTo(ProcessPriorityClass.Normal)); Assert.That(sd.Priority, Is.EqualTo(ProcessPriorityClass.Normal));
} }
@ -145,7 +142,7 @@ name: Service
description: The Service. description: The Service.
executable: node.exe executable: node.exe
stopParentProcessFirst: false"; stopParentProcessFirst: false";
var serviceDescriptor = YamlServiceConfigLoader.FromYaml(yaml).Config; var serviceDescriptor = YamlServiceConfig.FromYaml(yaml);
Assert.That(serviceDescriptor.StopParentProcessFirst, Is.False); Assert.That(serviceDescriptor.StopParentProcessFirst, Is.False);
} }
@ -159,7 +156,7 @@ description: The Service.
executable: node.exe executable: node.exe
stopTimeout: 60sec"; stopTimeout: 60sec";
var serviceDescriptor = YamlServiceConfigLoader.FromYaml(yaml).Config; var serviceDescriptor = YamlServiceConfig.FromYaml(yaml);
Assert.That(serviceDescriptor.StopTimeout, Is.EqualTo(TimeSpan.FromSeconds(60))); Assert.That(serviceDescriptor.StopTimeout, Is.EqualTo(TimeSpan.FromSeconds(60)));
} }
@ -174,7 +171,7 @@ description: The Service.
executable: node.exe executable: node.exe
stopTimeout: 10min"; stopTimeout: 10min";
var serviceDescriptor = YamlServiceConfigLoader.FromYaml(yaml).Config; var serviceDescriptor = YamlServiceConfig.FromYaml(yaml);
Assert.That(serviceDescriptor.StopTimeout, Is.EqualTo(TimeSpan.FromMinutes(10))); Assert.That(serviceDescriptor.StopTimeout, Is.EqualTo(TimeSpan.FromMinutes(10)));
} }
@ -190,7 +187,7 @@ executable: node.exe
log: log:
name: MyTestApp"; name: MyTestApp";
var serviceDescriptor = YamlServiceConfigLoader.FromYaml(yaml).Config; var serviceDescriptor = YamlServiceConfig.FromYaml(yaml);
Assert.That(serviceDescriptor.Log.Name, Is.EqualTo("MyTestApp")); Assert.That(serviceDescriptor.Log.Name, Is.EqualTo("MyTestApp"));
} }
@ -206,7 +203,7 @@ executable: node.exe
log: log:
outFileDisabled: true"; outFileDisabled: true";
var serviceDescriptor = YamlServiceConfigLoader.FromYaml(yaml).Config; var serviceDescriptor = YamlServiceConfig.FromYaml(yaml);
Assert.That(serviceDescriptor.Log.OutFileDisabled, Is.True); Assert.That(serviceDescriptor.Log.OutFileDisabled, Is.True);
} }
@ -222,7 +219,7 @@ executable: node.exe
log: log:
errFileDisabled: true"; errFileDisabled: true";
var serviceDescriptor = YamlServiceConfigLoader.FromYaml(yaml).Config; var serviceDescriptor = YamlServiceConfig.FromYaml(yaml);
Assert.That(serviceDescriptor.Log.ErrFileDisabled, Is.True); Assert.That(serviceDescriptor.Log.ErrFileDisabled, Is.True);
} }
@ -238,7 +235,7 @@ executable: node.exe
log: log:
outFilePattern: .out.test.log"; outFilePattern: .out.test.log";
var serviceDescriptor = YamlServiceConfigLoader.FromYaml(yaml).Config; var serviceDescriptor = YamlServiceConfig.FromYaml(yaml);
Assert.That(serviceDescriptor.Log.OutFilePattern, Is.EqualTo(".out.test.log")); Assert.That(serviceDescriptor.Log.OutFilePattern, Is.EqualTo(".out.test.log"));
} }
@ -254,7 +251,7 @@ executable: node.exe
log: log:
errFilePattern: .err.test.log"; errFilePattern: .err.test.log";
var serviceDescriptor = YamlServiceConfigLoader.FromYaml(yaml).Config; var serviceDescriptor = YamlServiceConfig.FromYaml(yaml);
Assert.That(serviceDescriptor.Log.ErrFilePattern, Is.EqualTo(".err.test.log")); Assert.That(serviceDescriptor.Log.ErrFilePattern, Is.EqualTo(".err.test.log"));
} }
@ -273,7 +270,7 @@ log:
sizeThreshold: 112 sizeThreshold: 112
keepFiles: 113"; keepFiles: 113";
var serviceDescriptor = YamlServiceConfigLoader.FromYaml(yaml).Config; var serviceDescriptor = YamlServiceConfig.FromYaml(yaml);
serviceDescriptor.BaseName = "service"; serviceDescriptor.BaseName = "service";
@ -297,7 +294,7 @@ log:
period: 7 period: 7
pattern: log pattern"; pattern: log pattern";
var serviceDescriptor = YamlServiceConfigLoader.FromYaml(yaml).Config; var serviceDescriptor = YamlServiceConfig.FromYaml(yaml);
serviceDescriptor.BaseName = "service"; serviceDescriptor.BaseName = "service";
@ -322,7 +319,7 @@ log:
pattern: yyyy-MM-dd pattern: yyyy-MM-dd
autoRollAtTime: 00:00:00"; autoRollAtTime: 00:00:00";
var serviceDescriptor = YamlServiceConfigLoader.FromYaml(yaml).Config; var serviceDescriptor = YamlServiceConfig.FromYaml(yaml);
serviceDescriptor.BaseName = "service"; serviceDescriptor.BaseName = "service";
@ -341,15 +338,15 @@ id: service.exe
name: Service name: Service
description: The Service. description: The Service.
executable: node.exe executable: node.exe
serviceaccount: serviceAccount:
domain: {Domain} domain: {Domain}
user: {Username} user: {Username}
password: {Password} password: {Password}
allowservicelogon: false"; allowServiceLogon: false";
var serviceDescriptor = YamlServiceConfigLoader.FromYaml(yaml).Config; var serviceDescriptor = YamlServiceConfig.FromYaml(yaml);
Assert.That(serviceDescriptor.ServiceAccount.AllowServiceAcountLogonRight, Is.False); Assert.That(serviceDescriptor.ServiceAccount.AllowServiceLogonRight, Is.False);
} }
[Test] [Test]
@ -360,44 +357,14 @@ id: service.exe
name: Service name: Service
description: The Service. description: The Service.
executable: node.exe executable: node.exe
serviceaccount: serviceAccount:
domain: {Domain} domain: {Domain}
user: {Username} user: {Username}
password: {Password}"; password: {Password}";
var serviceDescriptor = YamlServiceConfigLoader.FromYaml(yaml).Config; var serviceDescriptor = YamlServiceConfig.FromYaml(yaml);
Assert.That(serviceDescriptor.ServiceAccount.AllowServiceAcountLogonRight, Is.False); Assert.That(serviceDescriptor.ServiceAccount.AllowServiceLogonRight, Is.False);
}
[Test]
public void VerifyWaitHint()
{
string yaml = $@"
id: service.exe
name: Service
description: The Service.
executable: node.exe
waitHint: 20 min";
var serviceDescriptor = YamlServiceConfigLoader.FromYaml(yaml).Config;
Assert.That(serviceDescriptor.WaitHint, Is.EqualTo(TimeSpan.FromMinutes(20)));
}
[Test]
public void VerifySleepTime()
{
string yaml = $@"
id: service.exe
name: Service
description: The Service.
executable: node.exe
sleepTime: 3 hrs";
var serviceDescriptor = YamlServiceConfigLoader.FromYaml(yaml).Config;
Assert.That(serviceDescriptor.SleepTime, Is.EqualTo(TimeSpan.FromHours(3)));
} }
[Test] [Test]
@ -408,9 +375,9 @@ id: service.exe
name: Service name: Service
description: The Service. description: The Service.
executable: node.exe executable: node.exe
resetFailureAfter: 75 sec"; resetFailure: 75 sec";
var serviceDescriptor = YamlServiceConfigLoader.FromYaml(yaml).Config; var serviceDescriptor = YamlServiceConfig.FromYaml(yaml);
Assert.That(serviceDescriptor.ResetFailureAfter, Is.EqualTo(TimeSpan.FromSeconds(75))); Assert.That(serviceDescriptor.ResetFailureAfter, Is.EqualTo(TimeSpan.FromSeconds(75)));
} }
@ -425,7 +392,7 @@ description: The Service.
executable: node.exe executable: node.exe
stopTimeout: 35 sec"; stopTimeout: 35 sec";
var serviceDescriptor = YamlServiceConfigLoader.FromYaml(yaml).Config; var serviceDescriptor = YamlServiceConfig.FromYaml(yaml);
Assert.That(serviceDescriptor.StopTimeout, Is.EqualTo(TimeSpan.FromSeconds(35))); Assert.That(serviceDescriptor.StopTimeout, Is.EqualTo(TimeSpan.FromSeconds(35)));
} }
@ -440,7 +407,7 @@ description: The Service.
executable: node.exe executable: node.exe
arguments: arg"; arguments: arg";
var serviceDescriptor = YamlServiceConfigLoader.FromYaml(yaml).Config; var serviceDescriptor = YamlServiceConfig.FromYaml(yaml);
Assert.That(serviceDescriptor.Arguments, Is.EqualTo("arg")); Assert.That(serviceDescriptor.Arguments, Is.EqualTo("arg"));
} }
@ -454,7 +421,7 @@ description: The Service.
executable: node.exe executable: node.exe
delayedAutoStart: true"; delayedAutoStart: true";
var serviceDescriptor = YamlServiceConfigLoader.FromYaml(yaml).Config; var serviceDescriptor = YamlServiceConfig.FromYaml(yaml);
Assert.That(serviceDescriptor.DelayedAutoStart, Is.EqualTo(true)); Assert.That(serviceDescriptor.DelayedAutoStart, Is.EqualTo(true));
} }
@ -470,7 +437,7 @@ description: This is test winsw";
Assert.That(() => Assert.That(() =>
{ {
_ = YamlServiceConfigLoader.FromYaml(yml).Config.Name; _ = YamlServiceConfig.FromYaml(yml).Name;
}, Throws.TypeOf<InvalidOperationException>()); }, Throws.TypeOf<InvalidOperationException>());
} }

View File

@ -114,7 +114,7 @@ namespace winswTests.Util
string fullyQualifiedExtensionName = ExtensionTestBase.GetExtensionClassNameWithAssembly(typeof(RunawayProcessKillerExtension)); string fullyQualifiedExtensionName = ExtensionTestBase.GetExtensionClassNameWithAssembly(typeof(RunawayProcessKillerExtension));
var str = new StringBuilder(); var str = new StringBuilder();
str.AppendFormat(" <extension enabled=\"{0}\" className=\"{1}\" id=\"{2}\">\n", new object[] { enabled, fullyQualifiedExtensionName, extensionId }); str.AppendFormat(" <extension enabled=\"{0}\" className=\"{1}\" id=\"{2}\">\n", new object[] { enabled, fullyQualifiedExtensionName, extensionId });
str.AppendFormat(" <pidfile>{0}</pidfile>\n", ext.Pidfile); str.AppendFormat(" <pidfile>{0}</pidfile>\n", ext.PidFile);
str.AppendFormat(" <stopTimeout>{0}</stopTimeout>\n", ext.StopTimeout.TotalMilliseconds); str.AppendFormat(" <stopTimeout>{0}</stopTimeout>\n", ext.StopTimeout.TotalMilliseconds);
str.AppendFormat(" <stopParentFirst>{0}</stopParentFirst>\n", ext.StopParentProcessFirst); str.AppendFormat(" <stopParentFirst>{0}</stopParentFirst>\n", ext.StopParentProcessFirst);
str.AppendFormat(" <checkWinSWEnvironmentVariable>{0}</checkWinSWEnvironmentVariable>\n", ext.CheckWinSWEnvironmentVariable); str.AppendFormat(" <checkWinSWEnvironmentVariable>{0}</checkWinSWEnvironmentVariable>\n", ext.CheckWinSWEnvironmentVariable);

View File

@ -272,15 +272,15 @@ namespace WinSW
{ {
if (config.ServiceAccount.HasServiceAccount()) if (config.ServiceAccount.HasServiceAccount())
{ {
username = config.ServiceAccount.ServiceAccountUser; username = config.ServiceAccount.FullUser;
password = config.ServiceAccount.ServiceAccountPassword; password = config.ServiceAccount.Password;
allowServiceLogonRight = config.ServiceAccount.AllowServiceAcountLogonRight; allowServiceLogonRight = config.ServiceAccount.AllowServiceLogonRight;
} }
} }
if (allowServiceLogonRight) if (allowServiceLogonRight)
{ {
Security.AddServiceLogonRight(config.ServiceAccount.ServiceAccountDomain!, config.ServiceAccount.ServiceAccountName!); Security.AddServiceLogonRight(config.ServiceAccount.Domain!, config.ServiceAccount.User!);
} }
using var sc = scm.CreateService( using var sc = scm.CreateService(
@ -704,7 +704,7 @@ namespace WinSW
IServiceConfig config = IServiceConfig config =
File.Exists(Path.Combine(directory, baseName + ".xml")) ? new XmlServiceConfig(baseName, directory) : File.Exists(Path.Combine(directory, baseName + ".xml")) ? new XmlServiceConfig(baseName, directory) :
File.Exists(Path.Combine(directory, baseName + ".yml")) ? new YamlServiceConfigLoader(baseName, directory).Config : File.Exists(Path.Combine(directory, baseName + ".yml")) ? new YamlServiceConfig(baseName, directory) :
throw new FileNotFoundException($"Unable to locate {baseName}.[xml|yml] file within executable directory"); throw new FileNotFoundException($"Unable to locate {baseName}.[xml|yml] file within executable directory");
// .wrapper.log // .wrapper.log