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",
"$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"
},

View File

@ -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

View File

@ -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

View File

@ -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);
}
}
}

View File

@ -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;
}

View File

@ -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<RawYamlServiceConfig>(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<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
#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<RawYamlServiceConfig>(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<T>(
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<ServiceStartMode>(this.raw.StartMode) ?? this.defaults.StartMode;
private List<Download> GetDownloads(List<YamlDownload>? downloads)
{
if (downloads is null)
{
return this.defaults.Downloads;
}
public bool DelayedAutoStart => ExpandBoolean(this.raw.DelayedAutoStart) ?? this.defaults.DelayedAutoStart;
var result = new List<Download>(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<SC_ACTION>();
#else
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 ?
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<ProcessPriorityClass>(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<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 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<YamlExtensionConfig>? YamlExtensions { get; set; }
public List<YamlExtensionConfig>? YamlExtensions => this.raw.Extensions;
public List<string> 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<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;
}
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);
}
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());
}

View File

@ -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
/// <summary>
/// No action.
/// </summary>
SC_ACTION_NONE = 0,
NONE = 0,
/// <summary>
/// Restart the service.
/// </summary>
SC_ACTION_RESTART = 1,
RESTART = 1,
/// <summary>
/// Reboot the computer.
/// </summary>
SC_ACTION_REBOOT = 2,
REBOOT = 2,
/// <summary>
/// Run a command.
/// </summary>
SC_ACTION_RUN_COMMAND = 3,
RUN_COMMAND = 3,
}
public struct SC_ACTION

View File

@ -17,7 +17,7 @@ namespace WinSW.Plugins
/// <summary>
/// Absolute path to the PID file, which stores ID of the previously launched process.
/// </summary>
public string Pidfile { get; private set; }
public string PidFile { get; private set; }
/// <summary>
/// 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
/// <param name="process"></param>
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);
}
}
}

View File

@ -49,7 +49,7 @@ namespace WinSW.Plugins
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");
}

View File

@ -32,7 +32,7 @@ namespace WinSW.Plugins
public static SharedDirectoryMapperConfig FromYaml(object yamlObject)
{
if (!(yamlObject is Dictionary<object, object> dict))
if (yamlObject is not Dictionary<object, object> 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);
}

View File

@ -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());

View File

@ -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]

View File

@ -102,28 +102,25 @@ $@"<service>
[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 @@ $@"<service>
+ "</serviceaccount>"
+ "</service>";
var serviceDescriptor = XmlServiceConfig.FromXML(seedXml);
Assert.That(serviceDescriptor.ServiceAccount.AllowServiceAcountLogonRight, Is.False);
Assert.That(serviceDescriptor.ServiceAccount.AllowServiceLogonRight, Is.False);
}
[Test]
@ -321,7 +318,7 @@ $@"<service>
+ "</serviceaccount>"
+ "</service>";
var serviceDescriptor = XmlServiceConfig.FromXML(seedXml);
Assert.That(serviceDescriptor.ServiceAccount.AllowServiceAcountLogonRight, Is.False);
Assert.That(serviceDescriptor.ServiceAccount.AllowServiceLogonRight, Is.False);
}
[Test]

View File

@ -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<InvalidOperationException>());
}

View File

@ -114,7 +114,7 @@ namespace winswTests.Util
string fullyQualifiedExtensionName = ExtensionTestBase.GetExtensionClassNameWithAssembly(typeof(RunawayProcessKillerExtension));
var str = new StringBuilder();
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(" <stopParentFirst>{0}</stopParentFirst>\n", ext.StopParentProcessFirst);
str.AppendFormat(" <checkWinSWEnvironmentVariable>{0}</checkWinSWEnvironmentVariable>\n", ext.CheckWinSWEnvironmentVariable);

View File

@ -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