diff --git a/doc/yamlConfigurationSchema.json b/doc/yamlConfigurationSchema.json index 1ff9aee..53e5090 100644 --- a/doc/yamlConfigurationSchema.json +++ b/doc/yamlConfigurationSchema.json @@ -175,8 +175,8 @@ "description": "Optionally specify the order of service shutdown. If configed as true, parent process is shutdown first", "$ref": "#/definitions/boolPattern" }, - "resetFailureAfter": { - "$id": "#/properties/resetFailureAfter", + "resetFailure": { + "$id": "#/properties/resetFailure", "description": "This optional element controls the timing in which Windows SCM resets the failure count.", "type": "string" }, diff --git a/examples/sample-allOption.yml b/examples/sample-allOption.yml index d63d9fe..177f0b1 100644 --- a/examples/sample-allOption.yml +++ b/examples/sample-allOption.yml @@ -16,7 +16,7 @@ executable: java # delay: 20 sec # - # action: reboot -#resetFailureAfter: 01:00:00 +#resetFailure: 01:00:00 #securityDescriptor: security descriptor string #arguments: > # -classpath diff --git a/src/WinSW.Core/Configuration/DefaultSettings.cs b/src/WinSW.Core/Configuration/DefaultSettings.cs index 45e1131..acabcc7 100644 --- a/src/WinSW.Core/Configuration/DefaultSettings.cs +++ b/src/WinSW.Core/Configuration/DefaultSettings.cs @@ -78,10 +78,10 @@ namespace WinSW.Configuration public ServiceAccount ServiceAccount => new() { - ServiceAccountName = null, - ServiceAccountDomain = null, - ServiceAccountPassword = null, - AllowServiceAcountLogonRight = false + User = null, + Domain = null, + Password = null, + AllowServiceLogonRight = false }; public class LogDefaults : Log diff --git a/src/WinSW.Core/Configuration/ServiceAccount.cs b/src/WinSW.Core/Configuration/ServiceAccount.cs index 1791f7a..27a3bae 100644 --- a/src/WinSW.Core/Configuration/ServiceAccount.cs +++ b/src/WinSW.Core/Configuration/ServiceAccount.cs @@ -1,29 +1,23 @@ -using YamlDotNet.Serialization; - -namespace WinSW.Configuration +namespace WinSW.Configuration { public class ServiceAccount { - [YamlMember(Alias = "user")] - public string? ServiceAccountName { get; set; } + public string? User { get; set; } - [YamlMember(Alias = "domain")] - public string? ServiceAccountDomain { get; set; } + public string? Domain { get; set; } - [YamlMember(Alias = "password")] - public string? ServiceAccountPassword { get; set; } + public string? Password { get; set; } - [YamlMember(Alias = "allowservicelogon")] - public bool AllowServiceAcountLogonRight { get; set; } + public bool AllowServiceLogonRight { 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() { - return !string.IsNullOrEmpty(this.ServiceAccountName); + return !string.IsNullOrEmpty(this.User); } } } diff --git a/src/WinSW.Core/Configuration/XmlServiceConfig.cs b/src/WinSW.Core/Configuration/XmlServiceConfig.cs index e6e3755..b1bbbda 100644 --- a/src/WinSW.Core/Configuration/XmlServiceConfig.cs +++ b/src/WinSW.Core/Configuration/XmlServiceConfig.cs @@ -572,9 +572,9 @@ namespace WinSW string action = node.Attributes!["action"]?.Value ?? throw new InvalidDataException("'action' is missing"); var type = action switch { - "restart" => SC_ACTION_TYPE.SC_ACTION_RESTART, - "none" => SC_ACTION_TYPE.SC_ACTION_NONE, - "reboot" => SC_ACTION_TYPE.SC_ACTION_REBOOT, + "restart" => SC_ACTION_TYPE.RESTART, + "none" => SC_ACTION_TYPE.NONE, + "reboot" => SC_ACTION_TYPE.REBOOT, _ => throw new Exception("Invalid failure action: " + action) }; var delay = node.Attributes["delay"]; @@ -621,15 +621,15 @@ namespace WinSW 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"); - serviceAccount.AllowServiceAcountLogonRight = this.ParseAllowServiceAcountLogonRight(loginRight); + serviceAccount.AllowServiceLogonRight = this.ParseAllowServiceAcountLogonRight(loginRight); return serviceAccount; } diff --git a/src/WinSW.Core/Configuration/YamlServiceConfig.cs b/src/WinSW.Core/Configuration/YamlServiceConfig.cs index 339e5c3..b6613d3 100644 --- a/src/WinSW.Core/Configuration/YamlServiceConfig.cs +++ b/src/WinSW.Core/Configuration/YamlServiceConfig.cs @@ -2,647 +2,249 @@ using System.Collections.Generic; using System.Diagnostics; using System.IO; +#if VNEXT +using System.Runtime.CompilerServices; +#endif using System.ServiceProcess; using System.Xml; using WinSW.Native; using WinSW.Util; using YamlDotNet.Serialization; -using static System.Environment; -using static WinSW.Download; +using YamlDotNet.Serialization.NamingConventions; namespace WinSW.Configuration { public class YamlServiceConfig : IServiceConfig { private readonly DefaultSettings defaults; + private readonly RawYamlServiceConfig raw; - public YamlServiceConfig() + public YamlServiceConfig(string baseName, string directory) { 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(reader); + } + + Environment.SetEnvironmentVariable("BASE", directory); + + // ditto for ID + Environment.SetEnvironmentVariable("SERVICE_ID", this.Name); + + // New name + Environment.SetEnvironmentVariable(WinSWSystem.EnvVarNameExecutablePath, this.ExecutablePath); + + // Also inject system environment variables + Environment.SetEnvironmentVariable(WinSWSystem.EnvVarNameServiceId, this.Name); + + this.LoadEnvironmentVariables(); } - [YamlMember(Alias = "id")] - public string? IdYaml { get; set; } - - [YamlMember(Alias = "name")] - public string? NameYaml { get; set; } - - [YamlMember(Alias = "description")] - public string? DescriptionYaml { get; set; } - - [YamlMember(Alias = "executable")] - public string? ExecutableYaml { get; set; } - - [YamlMember(Alias = "executablePath")] - 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? 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? EnvironmentVariablesYaml { get; set; } - - [YamlMember(Alias = "onFailure")] - public List? YamlFailureActions { get; set; } - - [YamlMember(Alias = "delayedAutoStart")] - public bool DelayedAutoStart { get; set; } - - [YamlMember(Alias = "securityDescriptor")] - public string? SecurityDescriptorYaml { get; set; } - - public class YamlEnv +#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. { - [YamlMember(Alias = "name")] - public string? Name { get; set; } - - [YamlMember(Alias = "value")] - public string? Value { get; set; } + this.defaults = new DefaultSettings(); + this.raw = raw; } - public class YamlLog : Log + public static YamlServiceConfig FromYaml(string yaml) { - private readonly YamlServiceConfig configs; - - public YamlLog() - { - this.configs = new YamlServiceConfig(); - } - - [YamlMember(Alias = "mode")] - 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 - { - return this.NameYamlLog is null ? - DefaultSettings.DefaultLogSettings.Name : - ExpandEnvironmentVariables(this.NameYamlLog); - } - } - - public override string Directory - { - get - { - return this.LogPathYamlLog is 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; - } - } + var deserializer = new DeserializerBuilder().IgnoreUnmatchedProperties().WithNamingConvention(CamelCaseNamingConvention.Instance).Build(); + var raw = deserializer.Deserialize(yaml); + return new(raw); } - public class YamlDownload + 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( + string? value, +#if VNEXT + [CallerMemberName] string? name = null) +#else + string? name = null) +#endif + where T : unmanaged, Enum { - [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 + if (value is null) { - get - { - if (this.AuthYamlDownload is null) - { - return AuthType.None; - } + return null; + } - string auth = ExpandEnvironmentVariables(this.AuthYamlDownload); - - try - { - return (AuthType)Enum.Parse(typeof(AuthType), auth, true); - } - catch - { - Console.WriteLine("Auth type in YAML must be one of the following:"); - foreach (string at in Enum.GetNames(typeof(AuthType))) - { - Console.WriteLine(at); - } - - throw; - } - } + try + { + return (T)Enum.Parse(typeof(T), value, true); + } + catch + { + Console.WriteLine($"'{name ?? typeof(T).FullName}' in YAML must be one of the followings: {string.Join(", ", Enum.GetNames(typeof(T)))}."); + throw; } } - public class YamlFailureAction - { - [YamlMember(Alias = "action")] - public string? FailureAction { get; set; } + public string Name => Expand(this.raw.Id) ?? this.defaults.Name; - [YamlMember(Alias = "delay")] - public string? FailureActionDelay { get; set; } + public string Description => Expand(this.raw.Description) ?? this.defaults.Description; - public SC_ACTION_TYPE Type - { - 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) - }; + public string Executable => Expand(this.raw.Executable) ?? this.defaults.Executable; - 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) - { - 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; - } - } + public bool HideWindow => ExpandBoolean(this.raw.HideWindow) ?? this.defaults.HideWindow; - return ExpandEnvironmentVariables(args); - } + public bool StopParentProcessFirst => ExpandBoolean(this.raw.StopParentProcessFirst) ?? this.defaults.StopParentProcessFirst; - private enum ArgType - { - Arg = 0, - Startarg = 1, - Stoparg = 2 - } + public ServiceStartMode StartMode => ExpandEnum(this.raw.StartMode) ?? this.defaults.StartMode; - private List GetDownloads(List? downloads) - { - if (downloads is null) - { - return this.defaults.Downloads; - } + public bool DelayedAutoStart => ExpandBoolean(this.raw.DelayedAutoStart) ?? this.defaults.DelayedAutoStart; - var result = new List(downloads.Count); + public bool BeepOnShutdown => ExpandBoolean(this.raw.BeepOnShutdown) ?? this.defaults.BeepOnShutdown; - foreach (var item in downloads) - { - result.Add(new Download( - item.FromDownload, - item.ToDownload, - item.FailOnErrorYamlDownload, - item.AuthDownload, - item.UsernameDownload, - item.PasswordDownload, - item.UnsecureAuthYamlDownload, - item.ProxyDownload)); - } + public string Arguments => Expand(this.raw.Arguments) ?? this.defaults.Arguments; - 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 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 string? StopExecutable => Expand(this.raw.StopExecutable) ?? this.defaults.StopExecutable; public SC_ACTION[] FailureActions { get { - if (this.YamlFailureActions is null) + if (this.raw.OnFailure is null) { +#if VNEXT + return Array.Empty(); +#else return new SC_ACTION[0]; +#endif } - var arr = new List(); + 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 ? - this.defaults.ResetFailureAfter : - ConfigHelper.ParseTimeSpan(this.ResetFailureAfterYaml); + public TimeSpan ResetFailureAfter => ExpandTimeSpan(this.raw.ResetFailure) ?? this.defaults.ResetFailureAfter; - public string WorkingDirectory => this.WorkingDirectoryYaml is null ? - this.defaults.WorkingDirectory : - ExpandEnvironmentVariables(this.WorkingDirectoryYaml); + public string WorkingDirectory => Expand(this.raw.WorkingDirectory) ?? this.defaults.WorkingDirectory; - public ProcessPriorityClass Priority - { - get - { - if (this.PriorityYaml is null) - { - return this.defaults.Priority; - } + public ProcessPriorityClass Priority => ExpandEnum(this.raw.Priority) ?? this.defaults.Priority; - string p = ExpandEnvironmentVariables(this.PriorityYaml); - - 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 TimeSpan StopTimeout => ExpandTimeSpan(this.raw.StopTimeout) ?? this.defaults.StopTimeout; public string[] ServiceDependencies { get { - if (this.ServiceDependenciesYaml is null) + if (this.raw.Depend is null) { return this.defaults.ServiceDependencies; } - var result = new List(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 Downloads => this.GetDownloads(this.DownloadsYaml); + public List Downloads + { + get + { + if (this.raw.Download is null) + { + return this.defaults.Downloads; + } + + var result = new List(this.raw.Download.Count); + + foreach (var item in this.raw.Download) + { + result.Add(new YamlDownload(item).Download); + } + + return result; + } + } public Dictionary EnvironmentVariables { get; set; } = new Dictionary(); public void LoadEnvironmentVariables() { - if (this.EnvironmentVariablesYaml is null) + if (this.raw.Env is null) { 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) - { - continue; - } - - string key = item.Name; - string value = ExpandEnvironmentVariables(item.Value); - - this.EnvironmentVariables[key] = value; - SetEnvironmentVariable(key, value); + continue; } + + string key = item.Name; + string value = Environment.ExpandEnvironmentVariables(item.Value); + + this.EnvironmentVariables[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; @@ -650,9 +252,7 @@ namespace WinSW.Configuration public XmlNode? XmlExtensions => null; - // YAML Extension - [YamlMember(Alias = "extensions")] - public List? YamlExtensions { get; set; } + public List? YamlExtensions => this.raw.Extensions; public List ExtensionIds { @@ -689,17 +289,158 @@ namespace WinSW.Configuration public string BasePath { get; set; } - public string? SecurityDescriptor - { - get - { - if (this.SecurityDescriptorYaml is null) - { - return this.defaults.SecurityDescriptor; - } + public string? SecurityDescriptor => Expand(this.raw.SecurityDescriptor) ?? this.defaults.SecurityDescriptor; - return ExpandEnvironmentVariables(this.SecurityDescriptorYaml); - } + internal sealed class RawYamlServiceConfig + { + public string? Id; + public string? Name; + public string? Description; + public string? Executable; + public string? HideWindow; + public string? WorkingDirectory; + public RawServiceAccount? ServiceAccount; + public RawYamlLog? Log; + public List? 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? Env; + public List? OnFailure; + public string? DelayedAutoStart; + public string? SecurityDescriptor; + public List? Extensions; + } + + 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(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(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; } } } diff --git a/src/WinSW.Core/Configuration/YamlServiceConfigLoader.cs b/src/WinSW.Core/Configuration/YamlServiceConfigLoader.cs deleted file mode 100644 index 2d3d438..0000000 --- a/src/WinSW.Core/Configuration/YamlServiceConfigLoader.cs +++ /dev/null @@ -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(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(yaml); - return new YamlServiceConfigLoader(configs); - } - } -} diff --git a/src/WinSW.Core/Extensions/WinSWExtensionManager.cs b/src/WinSW.Core/Extensions/WinSWExtensionManager.cs index 8dc6b6f..c34a3da 100644 --- a/src/WinSW.Core/Extensions/WinSWExtensionManager.cs +++ b/src/WinSW.Core/Extensions/WinSWExtensionManager.cs @@ -249,7 +249,7 @@ namespace WinSW.Extensions 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()); } diff --git a/src/WinSW.Core/Native/Service.cs b/src/WinSW.Core/Native/Service.cs index d941498..cf7deed 100644 --- a/src/WinSW.Core/Native/Service.cs +++ b/src/WinSW.Core/Native/Service.cs @@ -1,5 +1,4 @@ using System; -using System.Runtime.InteropServices; using System.Security.AccessControl; using System.ServiceProcess; using System.Text; @@ -12,22 +11,22 @@ namespace WinSW.Native /// /// No action. /// - SC_ACTION_NONE = 0, + NONE = 0, /// /// Restart the service. /// - SC_ACTION_RESTART = 1, + RESTART = 1, /// /// Reboot the computer. /// - SC_ACTION_REBOOT = 2, + REBOOT = 2, /// /// Run a command. /// - SC_ACTION_RUN_COMMAND = 3, + RUN_COMMAND = 3, } public struct SC_ACTION diff --git a/src/WinSW.Plugins/RunawayProcessKillerExtension.cs b/src/WinSW.Plugins/RunawayProcessKillerExtension.cs index e1a673b..93cc2e0 100644 --- a/src/WinSW.Plugins/RunawayProcessKillerExtension.cs +++ b/src/WinSW.Plugins/RunawayProcessKillerExtension.cs @@ -17,7 +17,7 @@ namespace WinSW.Plugins /// /// Absolute path to the PID file, which stores ID of the previously launched process. /// - public string Pidfile { get; private set; } + public string PidFile { get; private set; } /// /// 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) #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.StopParentProcessFirst = stopParentFirst; this.CheckWinSWEnvironmentVariable = checkWinSWEnvironmentVariable; @@ -184,7 +184,7 @@ namespace WinSW.Plugins { // We expect the upper logic to process any errors // TODO: a better parser API for types would be useful - this.Pidfile = XmlHelper.SingleElement(node, "pidfile", false)!; + this.PidFile = XmlHelper.SingleElement(node, "pidfile", false)!; this.StopTimeout = TimeSpan.FromMilliseconds(int.Parse(XmlHelper.SingleElement(node, "stopTimeout", false)!)); this.StopParentProcessFirst = bool.Parse(XmlHelper.SingleElement(node, "stopParentFirst", false)!); this.ServiceId = descriptor.Name; @@ -197,12 +197,12 @@ namespace WinSW.Plugins { 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)); - string StopParentProcessFirstConfig = ExpandEnvironmentVariables((string)dict["StopParentFirst"]); + string StopParentProcessFirstConfig = ExpandEnvironmentVariables((string)dict["stopParentFirst"]); this.StopParentProcessFirst = bool.Parse(StopParentProcessFirstConfig); try @@ -224,16 +224,16 @@ namespace WinSW.Plugins { // Read PID file from the disk int pid; - if (File.Exists(this.Pidfile)) + if (File.Exists(this.PidFile)) { string pidstring; try { - pidstring = File.ReadAllText(this.Pidfile); + pidstring = File.ReadAllText(this.PidFile); } catch (Exception ex) { - Logger.Error("Cannot read PID file from " + this.Pidfile, ex); + Logger.Error("Cannot read PID file from " + this.PidFile, ex); return; } @@ -243,13 +243,13 @@ namespace WinSW.Plugins } 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; } } 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; } @@ -309,14 +309,14 @@ namespace WinSW.Plugins /// 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 { - File.WriteAllText(this.Pidfile, process.Id.ToString()); + File.WriteAllText(this.PidFile, process.Id.ToString()); } catch (Exception ex) { - Logger.Error("Cannot update the PID file " + this.Pidfile, ex); + Logger.Error("Cannot update the PID file " + this.PidFile, ex); } } } diff --git a/src/WinSW.Plugins/SharedDirectoryMapper.cs b/src/WinSW.Plugins/SharedDirectoryMapper.cs index 6647f1b..045f6ae 100644 --- a/src/WinSW.Plugins/SharedDirectoryMapper.cs +++ b/src/WinSW.Plugins/SharedDirectoryMapper.cs @@ -49,7 +49,7 @@ namespace WinSW.Plugins object mappingNode = dict["mapping"]; - if (!(mappingNode is List mappings)) + if (mappingNode is not List mappings) { throw new InvalidDataException("SharedDirectoryMapper mapping should be a list"); } diff --git a/src/WinSW.Plugins/SharedDirectoryMapperConfig.cs b/src/WinSW.Plugins/SharedDirectoryMapperConfig.cs index 69bc7c0..089a4e1 100644 --- a/src/WinSW.Plugins/SharedDirectoryMapperConfig.cs +++ b/src/WinSW.Plugins/SharedDirectoryMapperConfig.cs @@ -32,7 +32,7 @@ namespace WinSW.Plugins public static SharedDirectoryMapperConfig FromYaml(object yamlObject) { - if (!(yamlObject is Dictionary dict)) + if (yamlObject is not Dictionary dict) { // TODO : throw ExtensionExeption throw new InvalidDataException("SharedDirectoryMapperConfig config error"); @@ -42,7 +42,7 @@ namespace WinSW.Plugins bool enableMapping = ConfigHelper.YamlBoolParse(enableMappingConfig); string label = ExpandEnvironmentVariables((string)dict["label"]); - string uncPath = ExpandEnvironmentVariables((string)dict["uncpath"]); + string uncPath = ExpandEnvironmentVariables((string)dict["uncPath"]); return new SharedDirectoryMapperConfig(enableMapping, label, uncPath); } diff --git a/src/WinSW.Tests/Extensions/RunawayProcessKillerTest.cs b/src/WinSW.Tests/Extensions/RunawayProcessKillerTest.cs index 98808ea..876bedd 100644 --- a/src/WinSW.Tests/Extensions/RunawayProcessKillerTest.cs +++ b/src/WinSW.Tests/Extensions/RunawayProcessKillerTest.cs @@ -57,11 +57,11 @@ extensions: enabled: yes className: ""{this.testExtension}"" settings: - pidfile: 'foo/bar/pid.txt' - stopTimeOut: 5000 - StopParentFirst: true"; + pidFile: 'foo/bar/pid.txt' + stopTimeout: 5000 + stopParentFirst: true"; - this._testServiceDescriptorYaml = YamlServiceConfigLoader.FromYaml(seedYaml).Config; + this._testServiceDescriptorYaml = YamlServiceConfig.FromYaml(seedYaml); } [Test] @@ -74,7 +74,7 @@ extensions: // Check the file is correct var extension = manager.Extensions["killRunawayProcess"] as RunawayProcessKillerExtension; 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(true, extension.StopParentProcessFirst, "Loaded StopParentFirst is not equal to the expected one"); } @@ -89,7 +89,7 @@ extensions: // Check the file is correct var extension = manager.Extensions["killRunawayProcess"] as RunawayProcessKillerExtension; 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(true, extension.StopParentProcessFirst, "Loaded StopParentFirst is not equal to the expected one"); } @@ -133,7 +133,7 @@ extensions: manager.LoadExtensions(); var extension = manager.Extensions[extensionId] as RunawayProcessKillerExtension; 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 File.WriteAllText(pidfile, proc.Id.ToString()); diff --git a/src/WinSW.Tests/Extensions/SharedDirectoryMapperTest.cs b/src/WinSW.Tests/Extensions/SharedDirectoryMapperTest.cs index 6119abe..18c79cd 100644 --- a/src/WinSW.Tests/Extensions/SharedDirectoryMapperTest.cs +++ b/src/WinSW.Tests/Extensions/SharedDirectoryMapperTest.cs @@ -62,10 +62,10 @@ extensions: mapping: - enabled: false label: N - uncpath: \\UNC + uncPath: \\UNC - enabled: false label: M - uncpath: \\UNC2 + uncPath: \\UNC2 - id: mapNetworDirs2 className: ""{this.testExtension}"" enabled: true @@ -73,12 +73,12 @@ extensions: mapping: - enabled: false label: X - uncpath: \\UNC + uncPath: \\UNC - enabled: false label: Y - uncpath: \\UNC2"; + uncPath: \\UNC2"; - this._testServiceDescriptorYaml = YamlServiceConfigLoader.FromYaml(seedYaml).Config; + this._testServiceDescriptorYaml = YamlServiceConfig.FromYaml(seedYaml); } [Test] diff --git a/src/WinSW.Tests/ServiceDescriptorTests.cs b/src/WinSW.Tests/ServiceDescriptorTests.cs index 2aa290a..385798e 100644 --- a/src/WinSW.Tests/ServiceDescriptorTests.cs +++ b/src/WinSW.Tests/ServiceDescriptorTests.cs @@ -102,28 +102,25 @@ $@" [Test] public void VerifyWorkingDirectory() { - Debug.WriteLine("_extendedServiceDescriptor.WorkingDirectory :: " + this._extendedServiceDescriptor.WorkingDirectory); Assert.That(this._extendedServiceDescriptor.WorkingDirectory, Is.EqualTo(ExpectedWorkingDirectory)); } [Test] public void VerifyServiceLogonRight() { - Assert.That(_extendedServiceDescriptor.ServiceAccount.AllowServiceAcountLogonRight, Is.True); + Assert.That(_extendedServiceDescriptor.ServiceAccount.AllowServiceLogonRight, Is.True); } [Test] public void VerifyUsername() { - Debug.WriteLine("_extendedServiceDescriptor.WorkingDirectory :: " + _extendedServiceDescriptor.WorkingDirectory); - Assert.That(_extendedServiceDescriptor.ServiceAccount.ServiceAccountUser, Is.EqualTo(Domain + "\\" + Username)); + Assert.That(_extendedServiceDescriptor.ServiceAccount.FullUser, Is.EqualTo(Domain + "\\" + Username)); } [Test] public void VerifyPassword() { - Debug.WriteLine("_extendedServiceDescriptor.WorkingDirectory :: " + _extendedServiceDescriptor.WorkingDirectory); - Assert.That(_extendedServiceDescriptor.ServiceAccount.ServiceAccountPassword, Is.EqualTo(Password)); + Assert.That(_extendedServiceDescriptor.ServiceAccount.Password, Is.EqualTo(Password)); } [Test] @@ -307,7 +304,7 @@ $@" + "" + ""; var serviceDescriptor = XmlServiceConfig.FromXML(seedXml); - Assert.That(serviceDescriptor.ServiceAccount.AllowServiceAcountLogonRight, Is.False); + Assert.That(serviceDescriptor.ServiceAccount.AllowServiceLogonRight, Is.False); } [Test] @@ -321,7 +318,7 @@ $@" + "" + ""; var serviceDescriptor = XmlServiceConfig.FromXML(seedXml); - Assert.That(serviceDescriptor.ServiceAccount.AllowServiceAcountLogonRight, Is.False); + Assert.That(serviceDescriptor.ServiceAccount.AllowServiceLogonRight, Is.False); } [Test] diff --git a/src/WinSW.Tests/ServiceDescriptorYamlTest.cs b/src/WinSW.Tests/ServiceDescriptorYamlTest.cs index 8128905..5bbbdca 100644 --- a/src/WinSW.Tests/ServiceDescriptorYamlTest.cs +++ b/src/WinSW.Tests/ServiceDescriptorYamlTest.cs @@ -30,14 +30,14 @@ arguments: My Arguments log: mode: roll logpath: c:\logs -serviceaccount: +serviceAccount: domain: {Domain} user: {Username} password: {Password} - allowservicelogon: {AllowServiceAccountLogonRight} -workingdirectory: {ExpectedWorkingDirectory}"; + allowServiceLogon: {AllowServiceAccountLogonRight} +workingDirectory: {ExpectedWorkingDirectory}"; - this._extendedServiceDescriptor = YamlServiceConfigLoader.FromYaml(yaml).Config; + this._extendedServiceDescriptor = YamlServiceConfig.FromYaml(yaml); } [Test] @@ -57,7 +57,7 @@ executable: node.exe arguments: My Arguments startMode: roll"; - this._extendedServiceDescriptor = YamlServiceConfigLoader.FromYaml(yaml).Config; + this._extendedServiceDescriptor = YamlServiceConfig.FromYaml(yaml); Assert.That(() => this._extendedServiceDescriptor.StartMode, Throws.ArgumentException); } @@ -72,61 +72,58 @@ executable: node.exe arguments: My Arguments startMode: manual"; - this._extendedServiceDescriptor = YamlServiceConfigLoader.FromYaml(yaml).Config; + this._extendedServiceDescriptor = YamlServiceConfig.FromYaml(yaml); Assert.That(this._extendedServiceDescriptor.StartMode, Is.EqualTo(ServiceStartMode.Manual)); } [Test] public void VerifyWorkingDirectory() { - Debug.WriteLine("_extendedServiceDescriptor.WorkingDirectory :: " + this._extendedServiceDescriptor.WorkingDirectory); Assert.That(this._extendedServiceDescriptor.WorkingDirectory, Is.EqualTo(ExpectedWorkingDirectory)); } [Test] public void VerifyServiceLogonRight() { - Assert.That(_extendedServiceDescriptor.ServiceAccount.AllowServiceAcountLogonRight, Is.True); + Assert.That(_extendedServiceDescriptor.ServiceAccount.AllowServiceLogonRight, Is.True); } [Test] public void VerifyUsername() { - Debug.WriteLine("_extendedServiceDescriptor.WorkingDirectory :: " + _extendedServiceDescriptor.WorkingDirectory); - Assert.That(_extendedServiceDescriptor.ServiceAccount.ServiceAccountUser, Is.EqualTo(Domain + "\\" + Username)); + Assert.That(_extendedServiceDescriptor.ServiceAccount.FullUser, Is.EqualTo(Domain + "\\" + Username)); } [Test] public void VerifyPassword() { - Debug.WriteLine("_extendedServiceDescriptor.WorkingDirectory :: " + _extendedServiceDescriptor.WorkingDirectory); - Assert.That(_extendedServiceDescriptor.ServiceAccount.ServiceAccountPassword, Is.EqualTo(Password)); + Assert.That(_extendedServiceDescriptor.ServiceAccount.Password, Is.EqualTo(Password)); } [Test] public void Priority() { - var sd = YamlServiceConfigLoader.FromYaml(@" + var sd = YamlServiceConfig.FromYaml(@" id: service.exe name: Service description: The Service. executable: node.exe -priority: normal").Config; +priority: normal"); Assert.That(sd.Priority, Is.EqualTo(ProcessPriorityClass.Normal)); - sd = YamlServiceConfigLoader.FromYaml(@" + sd = YamlServiceConfig.FromYaml(@" id: service.exe name: Service description: The Service. executable: node.exe -priority: idle").Config; +priority: idle"); Assert.That(sd.Priority, Is.EqualTo(ProcessPriorityClass.Idle)); - sd = YamlServiceConfigLoader.FromYaml(@" + sd = YamlServiceConfig.FromYaml(@" id: service.exe name: Service description: The Service. -executable: node.exe").Config; +executable: node.exe"); Assert.That(sd.Priority, Is.EqualTo(ProcessPriorityClass.Normal)); } @@ -145,7 +142,7 @@ name: Service description: The Service. executable: node.exe stopParentProcessFirst: false"; - var serviceDescriptor = YamlServiceConfigLoader.FromYaml(yaml).Config; + var serviceDescriptor = YamlServiceConfig.FromYaml(yaml); Assert.That(serviceDescriptor.StopParentProcessFirst, Is.False); } @@ -159,7 +156,7 @@ description: The Service. executable: node.exe stopTimeout: 60sec"; - var serviceDescriptor = YamlServiceConfigLoader.FromYaml(yaml).Config; + var serviceDescriptor = YamlServiceConfig.FromYaml(yaml); Assert.That(serviceDescriptor.StopTimeout, Is.EqualTo(TimeSpan.FromSeconds(60))); } @@ -174,7 +171,7 @@ description: The Service. executable: node.exe stopTimeout: 10min"; - var serviceDescriptor = YamlServiceConfigLoader.FromYaml(yaml).Config; + var serviceDescriptor = YamlServiceConfig.FromYaml(yaml); Assert.That(serviceDescriptor.StopTimeout, Is.EqualTo(TimeSpan.FromMinutes(10))); } @@ -190,7 +187,7 @@ executable: node.exe log: name: MyTestApp"; - var serviceDescriptor = YamlServiceConfigLoader.FromYaml(yaml).Config; + var serviceDescriptor = YamlServiceConfig.FromYaml(yaml); Assert.That(serviceDescriptor.Log.Name, Is.EqualTo("MyTestApp")); } @@ -206,7 +203,7 @@ executable: node.exe log: outFileDisabled: true"; - var serviceDescriptor = YamlServiceConfigLoader.FromYaml(yaml).Config; + var serviceDescriptor = YamlServiceConfig.FromYaml(yaml); Assert.That(serviceDescriptor.Log.OutFileDisabled, Is.True); } @@ -222,7 +219,7 @@ executable: node.exe log: errFileDisabled: true"; - var serviceDescriptor = YamlServiceConfigLoader.FromYaml(yaml).Config; + var serviceDescriptor = YamlServiceConfig.FromYaml(yaml); Assert.That(serviceDescriptor.Log.ErrFileDisabled, Is.True); } @@ -238,7 +235,7 @@ executable: node.exe 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")); } @@ -254,7 +251,7 @@ executable: node.exe 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")); } @@ -273,7 +270,7 @@ log: sizeThreshold: 112 keepFiles: 113"; - var serviceDescriptor = YamlServiceConfigLoader.FromYaml(yaml).Config; + var serviceDescriptor = YamlServiceConfig.FromYaml(yaml); serviceDescriptor.BaseName = "service"; @@ -297,7 +294,7 @@ log: period: 7 pattern: log pattern"; - var serviceDescriptor = YamlServiceConfigLoader.FromYaml(yaml).Config; + var serviceDescriptor = YamlServiceConfig.FromYaml(yaml); serviceDescriptor.BaseName = "service"; @@ -322,7 +319,7 @@ log: pattern: yyyy-MM-dd autoRollAtTime: 00:00:00"; - var serviceDescriptor = YamlServiceConfigLoader.FromYaml(yaml).Config; + var serviceDescriptor = YamlServiceConfig.FromYaml(yaml); serviceDescriptor.BaseName = "service"; @@ -341,15 +338,15 @@ id: service.exe name: Service description: The Service. executable: node.exe -serviceaccount: +serviceAccount: domain: {Domain} user: {Username} 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] @@ -360,44 +357,14 @@ id: service.exe name: Service description: The Service. executable: node.exe -serviceaccount: +serviceAccount: domain: {Domain} user: {Username} password: {Password}"; - var serviceDescriptor = YamlServiceConfigLoader.FromYaml(yaml).Config; + var serviceDescriptor = YamlServiceConfig.FromYaml(yaml); - Assert.That(serviceDescriptor.ServiceAccount.AllowServiceAcountLogonRight, 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))); + Assert.That(serviceDescriptor.ServiceAccount.AllowServiceLogonRight, Is.False); } [Test] @@ -408,9 +375,9 @@ id: service.exe name: Service description: The Service. 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))); } @@ -425,7 +392,7 @@ description: The Service. executable: node.exe stopTimeout: 35 sec"; - var serviceDescriptor = YamlServiceConfigLoader.FromYaml(yaml).Config; + var serviceDescriptor = YamlServiceConfig.FromYaml(yaml); Assert.That(serviceDescriptor.StopTimeout, Is.EqualTo(TimeSpan.FromSeconds(35))); } @@ -440,7 +407,7 @@ description: The Service. executable: node.exe arguments: arg"; - var serviceDescriptor = YamlServiceConfigLoader.FromYaml(yaml).Config; + var serviceDescriptor = YamlServiceConfig.FromYaml(yaml); Assert.That(serviceDescriptor.Arguments, Is.EqualTo("arg")); } @@ -454,7 +421,7 @@ description: The Service. executable: node.exe delayedAutoStart: true"; - var serviceDescriptor = YamlServiceConfigLoader.FromYaml(yaml).Config; + var serviceDescriptor = YamlServiceConfig.FromYaml(yaml); Assert.That(serviceDescriptor.DelayedAutoStart, Is.EqualTo(true)); } @@ -470,7 +437,7 @@ description: This is test winsw"; Assert.That(() => { - _ = YamlServiceConfigLoader.FromYaml(yml).Config.Name; + _ = YamlServiceConfig.FromYaml(yml).Name; }, Throws.TypeOf()); } diff --git a/src/WinSW.Tests/Util/ConfigXmlBuilder.cs b/src/WinSW.Tests/Util/ConfigXmlBuilder.cs index b9f3b2d..20edd9b 100644 --- a/src/WinSW.Tests/Util/ConfigXmlBuilder.cs +++ b/src/WinSW.Tests/Util/ConfigXmlBuilder.cs @@ -114,7 +114,7 @@ namespace winswTests.Util string fullyQualifiedExtensionName = ExtensionTestBase.GetExtensionClassNameWithAssembly(typeof(RunawayProcessKillerExtension)); var str = new StringBuilder(); str.AppendFormat(" \n", new object[] { enabled, fullyQualifiedExtensionName, extensionId }); - str.AppendFormat(" {0}\n", ext.Pidfile); + str.AppendFormat(" {0}\n", ext.PidFile); str.AppendFormat(" {0}\n", ext.StopTimeout.TotalMilliseconds); str.AppendFormat(" {0}\n", ext.StopParentProcessFirst); str.AppendFormat(" {0}\n", ext.CheckWinSWEnvironmentVariable); diff --git a/src/WinSW/Program.cs b/src/WinSW/Program.cs index 23b6705..bbab450 100644 --- a/src/WinSW/Program.cs +++ b/src/WinSW/Program.cs @@ -272,15 +272,15 @@ namespace WinSW { if (config.ServiceAccount.HasServiceAccount()) { - username = config.ServiceAccount.ServiceAccountUser; - password = config.ServiceAccount.ServiceAccountPassword; - allowServiceLogonRight = config.ServiceAccount.AllowServiceAcountLogonRight; + username = config.ServiceAccount.FullUser; + password = config.ServiceAccount.Password; + allowServiceLogonRight = config.ServiceAccount.AllowServiceLogonRight; } } if (allowServiceLogonRight) { - Security.AddServiceLogonRight(config.ServiceAccount.ServiceAccountDomain!, config.ServiceAccount.ServiceAccountName!); + Security.AddServiceLogonRight(config.ServiceAccount.Domain!, config.ServiceAccount.User!); } using var sc = scm.CreateService( @@ -704,7 +704,7 @@ namespace WinSW IServiceConfig config = 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"); // .wrapper.log