mirror of https://github.com/winsw/winsw
Standardize coding styles
parent
885beb89a2
commit
35af3bf78d
|
|
@ -1,8 +1,13 @@
|
||||||
<Project>
|
<Project>
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
|
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||||
<DebugType>full</DebugType>
|
<DebugType>full</DebugType>
|
||||||
<ArtifactsDir>$(MSBuildThisFileDirectory)artifacts\</ArtifactsDir>
|
<ArtifactsDir>$(MSBuildThisFileDirectory)artifacts\</ArtifactsDir>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<AdditionalFiles Include="$(MSBuildThisFileDirectory)src\stylecop.json" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,70 @@
|
||||||
|
[*.cs]
|
||||||
|
|
||||||
|
# SA0001: XML comment analysis is disabled due to project configuration
|
||||||
|
dotnet_diagnostic.SA0001.severity = none
|
||||||
|
|
||||||
|
# SA1114: Parameter list should follow declaration
|
||||||
|
dotnet_diagnostic.SA1114.severity = none
|
||||||
|
|
||||||
|
# SA1124: Do not use regions
|
||||||
|
dotnet_diagnostic.SA1124.severity = none
|
||||||
|
|
||||||
|
# SA1201: Elements should appear in the correct order
|
||||||
|
dotnet_diagnostic.SA1201.severity = none
|
||||||
|
|
||||||
|
# SA1202: Elements should be ordered by access
|
||||||
|
dotnet_diagnostic.SA1202.severity = none
|
||||||
|
|
||||||
|
# SA1401: Fields should be private
|
||||||
|
dotnet_diagnostic.SA1401.severity = none
|
||||||
|
|
||||||
|
# SA1402: File may only contain a single type
|
||||||
|
dotnet_diagnostic.SA1402.severity = none
|
||||||
|
|
||||||
|
# SA1405: Debug.Assert should provide message text
|
||||||
|
dotnet_diagnostic.SA1405.severity = none
|
||||||
|
|
||||||
|
# SA1413: Use trailing comma in multi-line initializers
|
||||||
|
dotnet_diagnostic.SA1413.severity = none
|
||||||
|
|
||||||
|
# SA1600: Elements should be documented
|
||||||
|
dotnet_diagnostic.SA1600.severity = none
|
||||||
|
|
||||||
|
# SA1601: Partial elements should be documented
|
||||||
|
dotnet_diagnostic.SA1601.severity = none
|
||||||
|
|
||||||
|
# SA1602: Enumeration items should be documented
|
||||||
|
dotnet_diagnostic.SA1602.severity = none
|
||||||
|
|
||||||
|
# SA1604: Element documentation should have summary
|
||||||
|
dotnet_diagnostic.SA1604.severity = none
|
||||||
|
|
||||||
|
# SA1606: Element documentation should have summary text
|
||||||
|
dotnet_diagnostic.SA1606.severity = none
|
||||||
|
|
||||||
|
# SA1611: Element parameters should be documented
|
||||||
|
dotnet_diagnostic.SA1611.severity = none
|
||||||
|
|
||||||
|
# SA1615: Element return value should be documented
|
||||||
|
dotnet_diagnostic.SA1615.severity = none
|
||||||
|
|
||||||
|
# SA1618: Generic type parameters should be documented
|
||||||
|
dotnet_diagnostic.SA1618.severity = none
|
||||||
|
|
||||||
|
# SA1623: Property summary documentation should match accessors
|
||||||
|
dotnet_diagnostic.SA1623.severity = none
|
||||||
|
|
||||||
|
# SA1627: Documentation text should not be empty
|
||||||
|
dotnet_diagnostic.SA1627.severity = none
|
||||||
|
|
||||||
|
# SA1629: Documentation text should end with a period
|
||||||
|
dotnet_diagnostic.SA1629.severity = none
|
||||||
|
|
||||||
|
# SA1633: File should have header
|
||||||
|
dotnet_diagnostic.SA1633.severity = none
|
||||||
|
|
||||||
|
# SA1642: Constructor summary documentation should begin with standard text
|
||||||
|
dotnet_diagnostic.SA1642.severity = none
|
||||||
|
|
||||||
|
# SA1649: File name should match first type name
|
||||||
|
dotnet_diagnostic.SA1649.severity = none
|
||||||
|
|
@ -1,20 +1,20 @@
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
|
|
||||||
namespace winsw.Logging
|
namespace WinSW.Logging
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Implements caching of the WindowsService reference in WinSW.
|
/// Implements caching of the WindowsService reference in WinSW.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class WrapperServiceEventLogProvider : IServiceEventLogProvider
|
public class WrapperServiceEventLogProvider : IServiceEventLogProvider
|
||||||
{
|
{
|
||||||
public WrapperService? service { get; set; }
|
public WrapperService? Service { get; set; }
|
||||||
|
|
||||||
public EventLog? locate()
|
public EventLog? Locate()
|
||||||
{
|
{
|
||||||
WrapperService? _service = service;
|
WrapperService? service = this.Service;
|
||||||
if (_service != null && !_service.IsShuttingDown)
|
if (service != null && !service.IsShuttingDown)
|
||||||
{
|
{
|
||||||
return _service.EventLog;
|
return service.EventLog;
|
||||||
}
|
}
|
||||||
|
|
||||||
// By default return null
|
// By default return null
|
||||||
|
|
|
||||||
|
|
@ -16,10 +16,10 @@ using log4net.Appender;
|
||||||
using log4net.Config;
|
using log4net.Config;
|
||||||
using log4net.Core;
|
using log4net.Core;
|
||||||
using log4net.Layout;
|
using log4net.Layout;
|
||||||
using winsw.Logging;
|
using WinSW.Logging;
|
||||||
using winsw.Native;
|
using WinSW.Native;
|
||||||
|
|
||||||
namespace winsw
|
namespace WinSW
|
||||||
{
|
{
|
||||||
public static class Program
|
public static class Program
|
||||||
{
|
{
|
||||||
|
|
@ -456,7 +456,6 @@ namespace winsw
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw;
|
throw;
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -530,7 +529,6 @@ namespace winsw
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw;
|
throw;
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -723,7 +721,7 @@ namespace winsw
|
||||||
{
|
{
|
||||||
Name = "Wrapper event log",
|
Name = "Wrapper event log",
|
||||||
Threshold = eventLogLevel,
|
Threshold = eventLogLevel,
|
||||||
provider = WrapperService.eventLogProvider,
|
Provider = WrapperService.eventLogProvider,
|
||||||
};
|
};
|
||||||
systemEventLogger.ActivateOptions();
|
systemEventLogger.ActivateOptions();
|
||||||
appenders.Add(systemEventLogger);
|
appenders.Add(systemEventLogger);
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
using System.ServiceProcess;
|
using System.ServiceProcess;
|
||||||
using TimeoutException = System.ServiceProcess.TimeoutException;
|
using TimeoutException = System.ServiceProcess.TimeoutException;
|
||||||
|
|
||||||
namespace winsw
|
namespace WinSW
|
||||||
{
|
{
|
||||||
internal static class ServiceControllerExtension
|
internal static class ServiceControllerExtension
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace winsw
|
namespace WinSW
|
||||||
{
|
{
|
||||||
internal sealed class UserException : Exception
|
internal sealed class UserException : Exception
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -8,36 +8,37 @@ using System.Text;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using log4net;
|
using log4net;
|
||||||
using winsw.Extensions;
|
using WinSW.Extensions;
|
||||||
using winsw.Logging;
|
using WinSW.Logging;
|
||||||
using winsw.Native;
|
using WinSW.Native;
|
||||||
using winsw.Util;
|
using WinSW.Util;
|
||||||
|
|
||||||
namespace winsw
|
namespace WinSW
|
||||||
{
|
{
|
||||||
public class WrapperService : ServiceBase, EventLogger
|
public class WrapperService : ServiceBase, IEventLogger
|
||||||
{
|
{
|
||||||
private ServiceApis.SERVICE_STATUS _wrapperServiceStatus;
|
private ServiceApis.SERVICE_STATUS wrapperServiceStatus;
|
||||||
|
|
||||||
private readonly Process _process = new Process();
|
private readonly Process process = new Process();
|
||||||
private readonly ServiceDescriptor _descriptor;
|
private readonly ServiceDescriptor descriptor;
|
||||||
private Dictionary<string, string>? _envs;
|
private Dictionary<string, string>? envs;
|
||||||
|
|
||||||
internal WinSWExtensionManager ExtensionManager { get; private set; }
|
internal WinSWExtensionManager ExtensionManager { get; }
|
||||||
|
|
||||||
private static readonly ILog Log = LogManager.GetLogger(
|
private static readonly ILog Log = LogManager.GetLogger(
|
||||||
#if NETCOREAPP
|
#if NETCOREAPP
|
||||||
Assembly.GetExecutingAssembly(),
|
Assembly.GetExecutingAssembly(),
|
||||||
#endif
|
#endif
|
||||||
"WinSW");
|
"WinSW");
|
||||||
|
|
||||||
internal static readonly WrapperServiceEventLogProvider eventLogProvider = new WrapperServiceEventLogProvider();
|
internal static readonly WrapperServiceEventLogProvider eventLogProvider = new WrapperServiceEventLogProvider();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Indicates to the watch dog thread that we are going to terminate the process,
|
/// Indicates to the watch dog thread that we are going to terminate the process,
|
||||||
/// so don't try to kill us when the child exits.
|
/// so don't try to kill us when the child exits.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private bool _orderlyShutdown;
|
private bool orderlyShutdown;
|
||||||
private bool _systemShuttingdown;
|
private bool systemShuttingdown;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Version of Windows service wrapper
|
/// Version of Windows service wrapper
|
||||||
|
|
@ -50,24 +51,25 @@ namespace winsw
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Indicates that the system is shutting down.
|
/// Indicates that the system is shutting down.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool IsShuttingDown => _systemShuttingdown;
|
public bool IsShuttingDown => this.systemShuttingdown;
|
||||||
|
|
||||||
public WrapperService(ServiceDescriptor descriptor)
|
public WrapperService(ServiceDescriptor descriptor)
|
||||||
{
|
{
|
||||||
_descriptor = descriptor;
|
this.descriptor = descriptor;
|
||||||
ServiceName = _descriptor.Id;
|
this.ServiceName = this.descriptor.Id;
|
||||||
ExtensionManager = new WinSWExtensionManager(_descriptor);
|
this.ExtensionManager = new WinSWExtensionManager(this.descriptor);
|
||||||
CanShutdown = true;
|
this.CanShutdown = true;
|
||||||
CanStop = true;
|
this.CanStop = true;
|
||||||
CanPauseAndContinue = false;
|
this.CanPauseAndContinue = false;
|
||||||
AutoLog = true;
|
this.AutoLog = true;
|
||||||
_systemShuttingdown = false;
|
this.systemShuttingdown = false;
|
||||||
|
|
||||||
// Register the event log provider
|
// Register the event log provider
|
||||||
eventLogProvider.service = this;
|
eventLogProvider.Service = this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public WrapperService() : this(new ServiceDescriptor())
|
public WrapperService()
|
||||||
|
: this(new ServiceDescriptor())
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -77,9 +79,11 @@ namespace winsw
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private void HandleFileCopies()
|
private void HandleFileCopies()
|
||||||
{
|
{
|
||||||
var file = _descriptor.BasePath + ".copies";
|
var file = this.descriptor.BasePath + ".copies";
|
||||||
if (!File.Exists(file))
|
if (!File.Exists(file))
|
||||||
|
{
|
||||||
return; // nothing to handle
|
return; // nothing to handle
|
||||||
|
}
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
|
@ -87,15 +91,15 @@ namespace winsw
|
||||||
string? line;
|
string? line;
|
||||||
while ((line = tr.ReadLine()) != null)
|
while ((line = tr.ReadLine()) != null)
|
||||||
{
|
{
|
||||||
LogEvent("Handling copy: " + line);
|
this.LogEvent("Handling copy: " + line);
|
||||||
string[] tokens = line.Split('>');
|
string[] tokens = line.Split('>');
|
||||||
if (tokens.Length > 2)
|
if (tokens.Length > 2)
|
||||||
{
|
{
|
||||||
LogEvent("Too many delimiters in " + line);
|
this.LogEvent("Too many delimiters in " + line);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
MoveFile(tokens[0], tokens[1]);
|
this.MoveFile(tokens[0], tokens[1]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
|
|
@ -115,7 +119,7 @@ namespace winsw
|
||||||
}
|
}
|
||||||
catch (IOException e)
|
catch (IOException e)
|
||||||
{
|
{
|
||||||
LogEvent("Failed to move :" + sourceFileName + " to " + destFileName + " because " + e.Message);
|
this.LogEvent("Failed to move :" + sourceFileName + " to " + destFileName + " because " + e.Message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -125,21 +129,21 @@ namespace winsw
|
||||||
/// <returns>Log Handler, which should be used for the spawned process</returns>
|
/// <returns>Log Handler, which should be used for the spawned process</returns>
|
||||||
private LogHandler CreateExecutableLogHandler()
|
private LogHandler CreateExecutableLogHandler()
|
||||||
{
|
{
|
||||||
string logDirectory = _descriptor.LogDirectory;
|
string logDirectory = this.descriptor.LogDirectory;
|
||||||
|
|
||||||
if (!Directory.Exists(logDirectory))
|
if (!Directory.Exists(logDirectory))
|
||||||
{
|
{
|
||||||
Directory.CreateDirectory(logDirectory);
|
Directory.CreateDirectory(logDirectory);
|
||||||
}
|
}
|
||||||
|
|
||||||
LogHandler logAppender = _descriptor.LogHandler;
|
LogHandler logAppender = this.descriptor.LogHandler;
|
||||||
logAppender.EventLogger = this;
|
logAppender.EventLogger = this;
|
||||||
return logAppender;
|
return logAppender;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void LogEvent(string message)
|
public void LogEvent(string message)
|
||||||
{
|
{
|
||||||
if (_systemShuttingdown)
|
if (this.systemShuttingdown)
|
||||||
{
|
{
|
||||||
/* NOP - cannot call EventLog because of shutdown. */
|
/* NOP - cannot call EventLog because of shutdown. */
|
||||||
}
|
}
|
||||||
|
|
@ -147,7 +151,7 @@ namespace winsw
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
EventLog.WriteEntry(message);
|
this.EventLog.WriteEntry(message);
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
|
|
@ -158,7 +162,7 @@ namespace winsw
|
||||||
|
|
||||||
public void LogEvent(string message, EventLogEntryType type)
|
public void LogEvent(string message, EventLogEntryType type)
|
||||||
{
|
{
|
||||||
if (_systemShuttingdown)
|
if (this.systemShuttingdown)
|
||||||
{
|
{
|
||||||
/* NOP - cannot call EventLog because of shutdown. */
|
/* NOP - cannot call EventLog because of shutdown. */
|
||||||
}
|
}
|
||||||
|
|
@ -166,7 +170,7 @@ namespace winsw
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
EventLog.WriteEntry(message, type);
|
this.EventLog.WriteEntry(message, type);
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
|
|
@ -177,24 +181,18 @@ namespace winsw
|
||||||
|
|
||||||
protected override void OnStart(string[] args)
|
protected override void OnStart(string[] args)
|
||||||
{
|
{
|
||||||
_envs = _descriptor.EnvironmentVariables;
|
this.envs = this.descriptor.EnvironmentVariables;
|
||||||
// TODO: Disabled according to security concerns in https://github.com/kohsuke/winsw/issues/54
|
|
||||||
// Could be restored, but unlikely it's required in event logs at all
|
this.HandleFileCopies();
|
||||||
/**
|
|
||||||
foreach (string key in _envs.Keys)
|
|
||||||
{
|
|
||||||
LogEvent("envar " + key + '=' + _envs[key]);
|
|
||||||
}*/
|
|
||||||
HandleFileCopies();
|
|
||||||
|
|
||||||
// handle downloads
|
// handle downloads
|
||||||
List<Download> downloads = _descriptor.Downloads;
|
List<Download> downloads = this.descriptor.Downloads;
|
||||||
Task[] tasks = new Task[downloads.Count];
|
Task[] tasks = new Task[downloads.Count];
|
||||||
for (int i = 0; i < downloads.Count; i++)
|
for (int i = 0; i < downloads.Count; i++)
|
||||||
{
|
{
|
||||||
Download download = downloads[i];
|
Download download = downloads[i];
|
||||||
string downloadMessage = $"Downloading: {download.From} to {download.To}. failOnError={download.FailOnError.ToString()}";
|
string downloadMessage = $"Downloading: {download.From} to {download.To}. failOnError={download.FailOnError.ToString()}";
|
||||||
LogEvent(downloadMessage);
|
this.LogEvent(downloadMessage);
|
||||||
Log.Info(downloadMessage);
|
Log.Info(downloadMessage);
|
||||||
tasks[i] = download.PerformAsync();
|
tasks[i] = download.PerformAsync();
|
||||||
}
|
}
|
||||||
|
|
@ -213,7 +211,7 @@ namespace winsw
|
||||||
Download download = downloads[i];
|
Download download = downloads[i];
|
||||||
string errorMessage = $"Failed to download {download.From} to {download.To}";
|
string errorMessage = $"Failed to download {download.From} to {download.To}";
|
||||||
AggregateException exception = tasks[i].Exception!;
|
AggregateException exception = tasks[i].Exception!;
|
||||||
LogEvent($"{errorMessage}. {exception.Message}");
|
this.LogEvent($"{errorMessage}. {exception.Message}");
|
||||||
Log.Error(errorMessage, exception);
|
Log.Error(errorMessage, exception);
|
||||||
|
|
||||||
// TODO: move this code into the download logic
|
// TODO: move this code into the download logic
|
||||||
|
|
@ -227,34 +225,34 @@ namespace winsw
|
||||||
throw new AggregateException(exceptions);
|
throw new AggregateException(exceptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
string? startArguments = _descriptor.StartArguments;
|
string? startArguments = this.descriptor.StartArguments;
|
||||||
|
|
||||||
if (startArguments is null)
|
if (startArguments is null)
|
||||||
{
|
{
|
||||||
startArguments = _descriptor.Arguments;
|
startArguments = this.descriptor.Arguments;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
startArguments += " " + _descriptor.Arguments;
|
startArguments += " " + this.descriptor.Arguments;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Converting newlines, line returns, tabs into a single
|
// Converting newlines, line returns, tabs into a single
|
||||||
// space. This allows users to provide multi-line arguments
|
// space. This allows users to provide multi-line arguments
|
||||||
// in the xml for readability.
|
// in the xml for readability.
|
||||||
startArguments = Regex.Replace(startArguments, @"\s*[\n\r]+\s*", " ");
|
startArguments = Regex.Replace(startArguments, @"\s*[\n\r]+\s*", " ");
|
||||||
|
|
||||||
LogEvent("Starting " + _descriptor.Executable + ' ' + startArguments);
|
this.LogEvent("Starting " + this.descriptor.Executable + ' ' + startArguments);
|
||||||
Log.Info("Starting " + _descriptor.Executable + ' ' + startArguments);
|
Log.Info("Starting " + this.descriptor.Executable + ' ' + startArguments);
|
||||||
|
|
||||||
// Load and start extensions
|
// Load and start extensions
|
||||||
ExtensionManager.LoadExtensions();
|
this.ExtensionManager.LoadExtensions();
|
||||||
ExtensionManager.FireOnWrapperStarted();
|
this.ExtensionManager.FireOnWrapperStarted();
|
||||||
|
|
||||||
LogHandler executableLogHandler = CreateExecutableLogHandler();
|
LogHandler executableLogHandler = this.CreateExecutableLogHandler();
|
||||||
StartProcess(_process, startArguments, _descriptor.Executable, executableLogHandler, true);
|
this.StartProcess(this.process, startArguments, this.descriptor.Executable, executableLogHandler, true);
|
||||||
ExtensionManager.FireOnProcessStarted(_process);
|
this.ExtensionManager.FireOnProcessStarted(this.process);
|
||||||
|
|
||||||
_process.StandardInput.Close(); // nothing for you to read!
|
this.process.StandardInput.Close(); // nothing for you to read!
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnShutdown()
|
protected override void OnShutdown()
|
||||||
|
|
@ -263,8 +261,8 @@ namespace winsw
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
_systemShuttingdown = true;
|
this.systemShuttingdown = true;
|
||||||
StopIt();
|
this.StopIt();
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
|
@ -278,7 +276,7 @@ namespace winsw
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
StopIt();
|
this.StopIt();
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
|
@ -295,18 +293,18 @@ namespace winsw
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private void StopIt()
|
private void StopIt()
|
||||||
{
|
{
|
||||||
string? stopArguments = _descriptor.StopArguments;
|
string? stopArguments = this.descriptor.StopArguments;
|
||||||
LogEvent("Stopping " + _descriptor.Id);
|
this.LogEvent("Stopping " + this.descriptor.Id);
|
||||||
Log.Info("Stopping " + _descriptor.Id);
|
Log.Info("Stopping " + this.descriptor.Id);
|
||||||
_orderlyShutdown = true;
|
this.orderlyShutdown = true;
|
||||||
|
|
||||||
if (stopArguments is null)
|
if (stopArguments is null)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Log.Debug("ProcessKill " + _process.Id);
|
Log.Debug("ProcessKill " + this.process.Id);
|
||||||
ProcessHelper.StopProcessTree(_process, _descriptor.StopTimeout);
|
ProcessHelper.StopProcessTree(this.process, this.descriptor.StopTimeout);
|
||||||
ExtensionManager.FireOnProcessTerminated(_process);
|
this.ExtensionManager.FireOnProcessTerminated(this.process);
|
||||||
}
|
}
|
||||||
catch (InvalidOperationException)
|
catch (InvalidOperationException)
|
||||||
{
|
{
|
||||||
|
|
@ -315,48 +313,48 @@ namespace winsw
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
SignalShutdownPending();
|
this.SignalShutdownPending();
|
||||||
|
|
||||||
stopArguments += " " + _descriptor.Arguments;
|
stopArguments += " " + this.descriptor.Arguments;
|
||||||
|
|
||||||
Process stopProcess = new Process();
|
Process stopProcess = new Process();
|
||||||
string? executable = _descriptor.StopExecutable;
|
string? executable = this.descriptor.StopExecutable;
|
||||||
|
|
||||||
executable ??= _descriptor.Executable;
|
executable ??= this.descriptor.Executable;
|
||||||
|
|
||||||
// TODO: Redirect logging to Log4Net once https://github.com/kohsuke/winsw/pull/213 is integrated
|
// TODO: Redirect logging to Log4Net once https://github.com/kohsuke/winsw/pull/213 is integrated
|
||||||
StartProcess(stopProcess, stopArguments, executable, null, false);
|
this.StartProcess(stopProcess, stopArguments, executable, null, false);
|
||||||
|
|
||||||
Log.Debug("WaitForProcessToExit " + _process.Id + "+" + stopProcess.Id);
|
Log.Debug("WaitForProcessToExit " + this.process.Id + "+" + stopProcess.Id);
|
||||||
WaitForProcessToExit(_process);
|
this.WaitForProcessToExit(this.process);
|
||||||
WaitForProcessToExit(stopProcess);
|
this.WaitForProcessToExit(stopProcess);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stop extensions
|
// Stop extensions
|
||||||
ExtensionManager.FireBeforeWrapperStopped();
|
this.ExtensionManager.FireBeforeWrapperStopped();
|
||||||
|
|
||||||
if (_systemShuttingdown && _descriptor.BeepOnShutdown)
|
if (this.systemShuttingdown && this.descriptor.BeepOnShutdown)
|
||||||
{
|
{
|
||||||
Console.Beep();
|
Console.Beep();
|
||||||
}
|
}
|
||||||
|
|
||||||
Log.Info("Finished " + _descriptor.Id);
|
Log.Info("Finished " + this.descriptor.Id);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void WaitForProcessToExit(Process processoWait)
|
private void WaitForProcessToExit(Process processoWait)
|
||||||
{
|
{
|
||||||
SignalShutdownPending();
|
this.SignalShutdownPending();
|
||||||
|
|
||||||
int effectiveProcessWaitSleepTime;
|
int effectiveProcessWaitSleepTime;
|
||||||
if (_descriptor.SleepTime.TotalMilliseconds > int.MaxValue)
|
if (this.descriptor.SleepTime.TotalMilliseconds > int.MaxValue)
|
||||||
{
|
{
|
||||||
Log.Warn("The requested sleep time " + _descriptor.SleepTime.TotalMilliseconds + "is greater that the max value " +
|
Log.Warn("The requested sleep time " + this.descriptor.SleepTime.TotalMilliseconds + "is greater that the max value " +
|
||||||
int.MaxValue + ". The value will be truncated");
|
int.MaxValue + ". The value will be truncated");
|
||||||
effectiveProcessWaitSleepTime = int.MaxValue;
|
effectiveProcessWaitSleepTime = int.MaxValue;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
effectiveProcessWaitSleepTime = (int)_descriptor.SleepTime.TotalMilliseconds;
|
effectiveProcessWaitSleepTime = (int)this.descriptor.SleepTime.TotalMilliseconds;
|
||||||
}
|
}
|
||||||
|
|
||||||
try
|
try
|
||||||
|
|
@ -365,7 +363,7 @@ namespace winsw
|
||||||
|
|
||||||
while (!processoWait.WaitForExit(effectiveProcessWaitSleepTime))
|
while (!processoWait.WaitForExit(effectiveProcessWaitSleepTime))
|
||||||
{
|
{
|
||||||
SignalShutdownPending();
|
this.SignalShutdownPending();
|
||||||
// WriteEvent("WaitForProcessToExit [repeat]");
|
// WriteEvent("WaitForProcessToExit [repeat]");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -380,27 +378,27 @@ namespace winsw
|
||||||
private void SignalShutdownPending()
|
private void SignalShutdownPending()
|
||||||
{
|
{
|
||||||
int effectiveWaitHint;
|
int effectiveWaitHint;
|
||||||
if (_descriptor.WaitHint.TotalMilliseconds > int.MaxValue)
|
if (this.descriptor.WaitHint.TotalMilliseconds > int.MaxValue)
|
||||||
{
|
{
|
||||||
Log.Warn("The requested WaitHint value (" + _descriptor.WaitHint.TotalMilliseconds + " ms) is greater that the max value " +
|
Log.Warn("The requested WaitHint value (" + this.descriptor.WaitHint.TotalMilliseconds + " ms) is greater that the max value " +
|
||||||
int.MaxValue + ". The value will be truncated");
|
int.MaxValue + ". The value will be truncated");
|
||||||
effectiveWaitHint = int.MaxValue;
|
effectiveWaitHint = int.MaxValue;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
effectiveWaitHint = (int)_descriptor.WaitHint.TotalMilliseconds;
|
effectiveWaitHint = (int)this.descriptor.WaitHint.TotalMilliseconds;
|
||||||
}
|
}
|
||||||
|
|
||||||
RequestAdditionalTime(effectiveWaitHint);
|
this.RequestAdditionalTime(effectiveWaitHint);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SignalShutdownComplete()
|
private void SignalShutdownComplete()
|
||||||
{
|
{
|
||||||
IntPtr handle = ServiceHandle;
|
IntPtr handle = this.ServiceHandle;
|
||||||
_wrapperServiceStatus.CheckPoint++;
|
this.wrapperServiceStatus.CheckPoint++;
|
||||||
// WriteEvent("SignalShutdownComplete " + wrapperServiceStatus.checkPoint + ":" + wrapperServiceStatus.waitHint);
|
// WriteEvent("SignalShutdownComplete " + wrapperServiceStatus.checkPoint + ":" + wrapperServiceStatus.waitHint);
|
||||||
_wrapperServiceStatus.CurrentState = ServiceControllerStatus.Stopped;
|
this.wrapperServiceStatus.CurrentState = ServiceControllerStatus.Stopped;
|
||||||
ServiceApis.SetServiceStatus(handle, _wrapperServiceStatus);
|
ServiceApis.SetServiceStatus(handle, this.wrapperServiceStatus);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void StartProcess(Process processToStart, string arguments, string executable, LogHandler? logHandler, bool redirectStdin)
|
private void StartProcess(Process processToStart, string arguments, string executable, LogHandler? logHandler, bool redirectStdin)
|
||||||
|
|
@ -411,25 +409,28 @@ namespace winsw
|
||||||
string msg = processToStart.Id + " - " + processToStart.StartInfo.FileName + " " + processToStart.StartInfo.Arguments;
|
string msg = processToStart.Id + " - " + processToStart.StartInfo.FileName + " " + processToStart.StartInfo.Arguments;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (_orderlyShutdown)
|
if (this.orderlyShutdown)
|
||||||
{
|
{
|
||||||
LogEvent("Child process [" + msg + "] terminated with " + proc.ExitCode, EventLogEntryType.Information);
|
this.LogEvent("Child process [" + msg + "] terminated with " + proc.ExitCode, EventLogEntryType.Information);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
LogEvent("Child process [" + msg + "] finished with " + proc.ExitCode, EventLogEntryType.Warning);
|
this.LogEvent("Child process [" + msg + "] finished with " + proc.ExitCode, EventLogEntryType.Warning);
|
||||||
|
|
||||||
// if we finished orderly, report that to SCM.
|
// if we finished orderly, report that to SCM.
|
||||||
// by not reporting unclean shutdown, we let Windows SCM to decide if it wants to
|
// by not reporting unclean shutdown, we let Windows SCM to decide if it wants to
|
||||||
// restart the service automatically
|
// restart the service automatically
|
||||||
if (proc.ExitCode == 0)
|
if (proc.ExitCode == 0)
|
||||||
SignalShutdownComplete();
|
{
|
||||||
|
this.SignalShutdownComplete();
|
||||||
|
}
|
||||||
|
|
||||||
Environment.Exit(proc.ExitCode);
|
Environment.Exit(proc.ExitCode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (InvalidOperationException ioe)
|
catch (InvalidOperationException ioe)
|
||||||
{
|
{
|
||||||
LogEvent("WaitForExit " + ioe.Message);
|
this.LogEvent("WaitForExit " + ioe.Message);
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
|
|
@ -442,13 +443,13 @@ namespace winsw
|
||||||
processToStart: processToStart,
|
processToStart: processToStart,
|
||||||
executable: executable,
|
executable: executable,
|
||||||
arguments: arguments,
|
arguments: arguments,
|
||||||
envVars: _envs,
|
envVars: this.envs,
|
||||||
workingDirectory: _descriptor.WorkingDirectory,
|
workingDirectory: this.descriptor.WorkingDirectory,
|
||||||
priority: _descriptor.Priority,
|
priority: this.descriptor.Priority,
|
||||||
callback: OnProcessCompleted,
|
callback: OnProcessCompleted,
|
||||||
logHandler: logHandler,
|
logHandler: logHandler,
|
||||||
redirectStdin: redirectStdin,
|
redirectStdin: redirectStdin,
|
||||||
hideWindow: _descriptor.HideWindow);
|
hideWindow: this.descriptor.HideWindow);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,56 +5,80 @@ using System.IO;
|
||||||
using System.ServiceProcess;
|
using System.ServiceProcess;
|
||||||
using System.Xml;
|
using System.Xml;
|
||||||
|
|
||||||
namespace winsw.Configuration
|
namespace WinSW.Configuration
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Default WinSW settings
|
/// Default WinSW settings
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public sealed class DefaultWinSWSettings : IWinSWConfiguration
|
public sealed class DefaultWinSWSettings : IWinSWConfiguration
|
||||||
{
|
{
|
||||||
public string Id => throw new InvalidOperationException(nameof(Id) + " must be specified.");
|
public string Id => throw new InvalidOperationException(nameof(this.Id) + " must be specified.");
|
||||||
public string Caption => throw new InvalidOperationException(nameof(Caption) + " must be specified.");
|
|
||||||
public string Description => throw new InvalidOperationException(nameof(Description) + " must be specified.");
|
public string Caption => throw new InvalidOperationException(nameof(this.Caption) + " must be specified.");
|
||||||
public string Executable => throw new InvalidOperationException(nameof(Executable) + " must be specified.");
|
|
||||||
|
public string Description => throw new InvalidOperationException(nameof(this.Description) + " must be specified.");
|
||||||
|
|
||||||
|
public string Executable => throw new InvalidOperationException(nameof(this.Executable) + " must be specified.");
|
||||||
|
|
||||||
public bool HideWindow => false;
|
public bool HideWindow => false;
|
||||||
|
|
||||||
public string ExecutablePath => Process.GetCurrentProcess().MainModule.FileName;
|
public string ExecutablePath => Process.GetCurrentProcess().MainModule.FileName;
|
||||||
|
|
||||||
// Installation
|
// Installation
|
||||||
public bool AllowServiceAcountLogonRight => false;
|
public bool AllowServiceAcountLogonRight => false;
|
||||||
|
|
||||||
public string? ServiceAccountPassword => null;
|
public string? ServiceAccountPassword => null;
|
||||||
|
|
||||||
public string? ServiceAccountUserName => null;
|
public string? ServiceAccountUserName => null;
|
||||||
|
|
||||||
public Native.SC_ACTION[] FailureActions => new Native.SC_ACTION[0];
|
public Native.SC_ACTION[] FailureActions => new Native.SC_ACTION[0];
|
||||||
|
|
||||||
public TimeSpan ResetFailureAfter => TimeSpan.FromDays(1);
|
public TimeSpan ResetFailureAfter => TimeSpan.FromDays(1);
|
||||||
|
|
||||||
// Executable management
|
// Executable management
|
||||||
public string Arguments => string.Empty;
|
public string Arguments => string.Empty;
|
||||||
|
|
||||||
public string? StartArguments => null;
|
public string? StartArguments => null;
|
||||||
|
|
||||||
public string? StopExecutable => null;
|
public string? StopExecutable => null;
|
||||||
|
|
||||||
public string? StopArguments => null;
|
public string? StopArguments => null;
|
||||||
public string WorkingDirectory => Path.GetDirectoryName(ExecutablePath)!;
|
|
||||||
|
public string WorkingDirectory => Path.GetDirectoryName(this.ExecutablePath)!;
|
||||||
|
|
||||||
public ProcessPriorityClass Priority => ProcessPriorityClass.Normal;
|
public ProcessPriorityClass Priority => ProcessPriorityClass.Normal;
|
||||||
|
|
||||||
public TimeSpan StopTimeout => TimeSpan.FromSeconds(15);
|
public TimeSpan StopTimeout => TimeSpan.FromSeconds(15);
|
||||||
|
|
||||||
// Service management
|
// Service management
|
||||||
public ServiceStartMode StartMode => ServiceStartMode.Automatic;
|
public ServiceStartMode StartMode => ServiceStartMode.Automatic;
|
||||||
|
|
||||||
public bool DelayedAutoStart => false;
|
public bool DelayedAutoStart => false;
|
||||||
|
|
||||||
public string[] ServiceDependencies => new string[0];
|
public string[] ServiceDependencies => new string[0];
|
||||||
|
|
||||||
public TimeSpan WaitHint => TimeSpan.FromSeconds(15);
|
public TimeSpan WaitHint => TimeSpan.FromSeconds(15);
|
||||||
|
|
||||||
public TimeSpan SleepTime => TimeSpan.FromSeconds(1);
|
public TimeSpan SleepTime => TimeSpan.FromSeconds(1);
|
||||||
|
|
||||||
public bool Interactive => false;
|
public bool Interactive => false;
|
||||||
|
|
||||||
// Logging
|
// Logging
|
||||||
public string LogDirectory => Path.GetDirectoryName(ExecutablePath)!;
|
public string LogDirectory => Path.GetDirectoryName(this.ExecutablePath)!;
|
||||||
|
|
||||||
public string LogMode => "append";
|
public string LogMode => "append";
|
||||||
|
|
||||||
public bool OutFileDisabled => false;
|
public bool OutFileDisabled => false;
|
||||||
|
|
||||||
public bool ErrFileDisabled => false;
|
public bool ErrFileDisabled => false;
|
||||||
|
|
||||||
public string OutFilePattern => ".out.log";
|
public string OutFilePattern => ".out.log";
|
||||||
|
|
||||||
public string ErrFilePattern => ".err.log";
|
public string ErrFilePattern => ".err.log";
|
||||||
|
|
||||||
// Environment
|
// Environment
|
||||||
public List<Download> Downloads => new List<Download>(0);
|
public List<Download> Downloads => new List<Download>(0);
|
||||||
|
|
||||||
public Dictionary<string, string> EnvironmentVariables => new Dictionary<string, string>(0);
|
public Dictionary<string, string> EnvironmentVariables => new Dictionary<string, string>(0);
|
||||||
|
|
||||||
// Misc
|
// Misc
|
||||||
|
|
|
||||||
|
|
@ -4,49 +4,69 @@ using System.Diagnostics;
|
||||||
using System.ServiceProcess;
|
using System.ServiceProcess;
|
||||||
using System.Xml;
|
using System.Xml;
|
||||||
|
|
||||||
namespace winsw.Configuration
|
namespace WinSW.Configuration
|
||||||
{
|
{
|
||||||
|
// TODO: Document the parameters && refactor
|
||||||
public interface IWinSWConfiguration
|
public interface IWinSWConfiguration
|
||||||
{
|
{
|
||||||
// TODO: Document the parameters && refactor
|
|
||||||
|
|
||||||
string Id { get; }
|
string Id { get; }
|
||||||
|
|
||||||
string Caption { get; }
|
string Caption { get; }
|
||||||
|
|
||||||
string Description { get; }
|
string Description { get; }
|
||||||
|
|
||||||
string Executable { get; }
|
string Executable { get; }
|
||||||
|
|
||||||
string ExecutablePath { get; }
|
string ExecutablePath { get; }
|
||||||
|
|
||||||
bool HideWindow { get; }
|
bool HideWindow { get; }
|
||||||
|
|
||||||
// Installation
|
// Installation
|
||||||
bool AllowServiceAcountLogonRight { get; }
|
bool AllowServiceAcountLogonRight { get; }
|
||||||
|
|
||||||
string? ServiceAccountPassword { get; }
|
string? ServiceAccountPassword { get; }
|
||||||
|
|
||||||
string? ServiceAccountUserName { get; }
|
string? ServiceAccountUserName { get; }
|
||||||
|
|
||||||
Native.SC_ACTION[] FailureActions { get; }
|
Native.SC_ACTION[] FailureActions { get; }
|
||||||
|
|
||||||
TimeSpan ResetFailureAfter { get; }
|
TimeSpan ResetFailureAfter { get; }
|
||||||
|
|
||||||
// Executable management
|
// Executable management
|
||||||
string Arguments { get; }
|
string Arguments { get; }
|
||||||
|
|
||||||
string? StartArguments { get; }
|
string? StartArguments { get; }
|
||||||
|
|
||||||
string? StopExecutable { get; }
|
string? StopExecutable { get; }
|
||||||
|
|
||||||
string? StopArguments { get; }
|
string? StopArguments { get; }
|
||||||
|
|
||||||
string WorkingDirectory { get; }
|
string WorkingDirectory { get; }
|
||||||
|
|
||||||
ProcessPriorityClass Priority { get; }
|
ProcessPriorityClass Priority { get; }
|
||||||
|
|
||||||
TimeSpan StopTimeout { get; }
|
TimeSpan StopTimeout { get; }
|
||||||
|
|
||||||
// Service management
|
// Service management
|
||||||
ServiceStartMode StartMode { get; }
|
ServiceStartMode StartMode { get; }
|
||||||
|
|
||||||
string[] ServiceDependencies { get; }
|
string[] ServiceDependencies { get; }
|
||||||
|
|
||||||
TimeSpan WaitHint { get; }
|
TimeSpan WaitHint { get; }
|
||||||
|
|
||||||
TimeSpan SleepTime { get; }
|
TimeSpan SleepTime { get; }
|
||||||
|
|
||||||
bool Interactive { get; }
|
bool Interactive { get; }
|
||||||
|
|
||||||
// Logging
|
// Logging
|
||||||
string LogDirectory { get; }
|
string LogDirectory { get; }
|
||||||
|
|
||||||
// TODO: replace by enum
|
// TODO: replace by enum
|
||||||
string LogMode { get; }
|
string LogMode { get; }
|
||||||
|
|
||||||
// Environment
|
// Environment
|
||||||
List<Download> Downloads { get; }
|
List<Download> Downloads { get; }
|
||||||
|
|
||||||
Dictionary<string, string> EnvironmentVariables { get; }
|
Dictionary<string, string> EnvironmentVariables { get; }
|
||||||
|
|
||||||
// Misc
|
// Misc
|
||||||
|
|
|
||||||
|
|
@ -5,9 +5,9 @@ using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Xml;
|
using System.Xml;
|
||||||
using log4net;
|
using log4net;
|
||||||
using winsw.Util;
|
using WinSW.Util;
|
||||||
|
|
||||||
namespace winsw
|
namespace WinSW
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Specify the download activities prior to the launch.
|
/// Specify the download activities prior to the launch.
|
||||||
|
|
@ -17,9 +17,9 @@ namespace winsw
|
||||||
{
|
{
|
||||||
public enum AuthType
|
public enum AuthType
|
||||||
{
|
{
|
||||||
none = 0,
|
None = 0,
|
||||||
sspi,
|
Sspi,
|
||||||
basic
|
Basic
|
||||||
}
|
}
|
||||||
|
|
||||||
private static readonly ILog Logger = LogManager.GetLogger(typeof(Download));
|
private static readonly ILog Logger = LogManager.GetLogger(typeof(Download));
|
||||||
|
|
@ -33,7 +33,7 @@ namespace winsw
|
||||||
public readonly bool FailOnError;
|
public readonly bool FailOnError;
|
||||||
public readonly string? Proxy;
|
public readonly string? Proxy;
|
||||||
|
|
||||||
public string ShortId => $"(download from {From})";
|
public string ShortId => $"(download from {this.From})";
|
||||||
|
|
||||||
#if NET461
|
#if NET461
|
||||||
static Download()
|
static Download()
|
||||||
|
|
@ -48,20 +48,20 @@ namespace winsw
|
||||||
string from,
|
string from,
|
||||||
string to,
|
string to,
|
||||||
bool failOnError = false,
|
bool failOnError = false,
|
||||||
AuthType auth = AuthType.none,
|
AuthType auth = AuthType.None,
|
||||||
string? username = null,
|
string? username = null,
|
||||||
string? password = null,
|
string? password = null,
|
||||||
bool unsecureAuth = false,
|
bool unsecureAuth = false,
|
||||||
string? proxy = null)
|
string? proxy = null)
|
||||||
{
|
{
|
||||||
From = from;
|
this.From = from;
|
||||||
To = to;
|
this.To = to;
|
||||||
FailOnError = failOnError;
|
this.FailOnError = failOnError;
|
||||||
Proxy = proxy;
|
this.Proxy = proxy;
|
||||||
Auth = auth;
|
this.Auth = auth;
|
||||||
Username = username;
|
this.Username = username;
|
||||||
Password = password;
|
this.Password = password;
|
||||||
UnsecureAuth = unsecureAuth;
|
this.UnsecureAuth = unsecureAuth;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -71,36 +71,36 @@ namespace winsw
|
||||||
/// <exception cref="InvalidDataException">The required attribute is missing or the configuration is invalid</exception>
|
/// <exception cref="InvalidDataException">The required attribute is missing or the configuration is invalid</exception>
|
||||||
internal Download(XmlElement n)
|
internal Download(XmlElement n)
|
||||||
{
|
{
|
||||||
From = XmlHelper.SingleAttribute<string>(n, "from");
|
this.From = XmlHelper.SingleAttribute<string>(n, "from");
|
||||||
To = XmlHelper.SingleAttribute<string>(n, "to");
|
this.To = XmlHelper.SingleAttribute<string>(n, "to");
|
||||||
|
|
||||||
// All arguments below are optional
|
// All arguments below are optional
|
||||||
FailOnError = XmlHelper.SingleAttribute(n, "failOnError", false);
|
this.FailOnError = XmlHelper.SingleAttribute(n, "failOnError", false);
|
||||||
Proxy = XmlHelper.SingleAttribute<string>(n, "proxy", null);
|
this.Proxy = XmlHelper.SingleAttribute<string>(n, "proxy", null);
|
||||||
|
|
||||||
Auth = XmlHelper.EnumAttribute(n, "auth", AuthType.none);
|
this.Auth = XmlHelper.EnumAttribute(n, "auth", AuthType.None);
|
||||||
Username = XmlHelper.SingleAttribute<string>(n, "user", null);
|
this.Username = XmlHelper.SingleAttribute<string>(n, "user", null);
|
||||||
Password = XmlHelper.SingleAttribute<string>(n, "password", null);
|
this.Password = XmlHelper.SingleAttribute<string>(n, "password", null);
|
||||||
UnsecureAuth = XmlHelper.SingleAttribute(n, "unsecureAuth", false);
|
this.UnsecureAuth = XmlHelper.SingleAttribute(n, "unsecureAuth", false);
|
||||||
|
|
||||||
if (Auth == AuthType.basic)
|
if (this.Auth == AuthType.Basic)
|
||||||
{
|
{
|
||||||
// Allow it only for HTTPS or for UnsecureAuth
|
// Allow it only for HTTPS or for UnsecureAuth
|
||||||
if (!From.StartsWith("https:") && !UnsecureAuth)
|
if (!this.From.StartsWith("https:") && !this.UnsecureAuth)
|
||||||
{
|
{
|
||||||
throw new InvalidDataException("Warning: you're sending your credentials in clear text to the server " + ShortId +
|
throw new InvalidDataException("Warning: you're sending your credentials in clear text to the server " + this.ShortId +
|
||||||
"If you really want this you must enable 'unsecureAuth' in the configuration");
|
"If you really want this you must enable 'unsecureAuth' in the configuration");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Also fail if there is no user/password
|
// Also fail if there is no user/password
|
||||||
if (Username is null)
|
if (this.Username is null)
|
||||||
{
|
{
|
||||||
throw new InvalidDataException("Basic Auth is enabled, but username is not specified " + ShortId);
|
throw new InvalidDataException("Basic Auth is enabled, but username is not specified " + this.ShortId);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Password is null)
|
if (this.Password is null)
|
||||||
{
|
{
|
||||||
throw new InvalidDataException("Basic Auth is enabled, but password is not specified " + ShortId);
|
throw new InvalidDataException("Basic Auth is enabled, but password is not specified " + this.ShortId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -121,10 +121,10 @@ namespace winsw
|
||||||
/// </exception>
|
/// </exception>
|
||||||
public async Task PerformAsync()
|
public async Task PerformAsync()
|
||||||
{
|
{
|
||||||
WebRequest request = WebRequest.Create(From);
|
WebRequest request = WebRequest.Create(this.From);
|
||||||
if (!string.IsNullOrEmpty(Proxy))
|
if (!string.IsNullOrEmpty(this.Proxy))
|
||||||
{
|
{
|
||||||
CustomProxyInformation proxyInformation = new CustomProxyInformation(Proxy);
|
CustomProxyInformation proxyInformation = new CustomProxyInformation(this.Proxy!);
|
||||||
if (proxyInformation.Credentials != null)
|
if (proxyInformation.Credentials != null)
|
||||||
{
|
{
|
||||||
request.Proxy = new WebProxy(proxyInformation.ServerAddress, false, null, proxyInformation.Credentials);
|
request.Proxy = new WebProxy(proxyInformation.ServerAddress, false, null, proxyInformation.Credentials);
|
||||||
|
|
@ -135,35 +135,35 @@ namespace winsw
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (Auth)
|
switch (this.Auth)
|
||||||
{
|
{
|
||||||
case AuthType.none:
|
case AuthType.None:
|
||||||
// Do nothing
|
// Do nothing
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case AuthType.sspi:
|
case AuthType.Sspi:
|
||||||
request.UseDefaultCredentials = true;
|
request.UseDefaultCredentials = true;
|
||||||
request.PreAuthenticate = true;
|
request.PreAuthenticate = true;
|
||||||
request.Credentials = CredentialCache.DefaultCredentials;
|
request.Credentials = CredentialCache.DefaultCredentials;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case AuthType.basic:
|
case AuthType.Basic:
|
||||||
SetBasicAuthHeader(request, Username!, Password!);
|
this.SetBasicAuthHeader(request, this.Username!, this.Password!);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw new WebException("Code defect. Unsupported authentication type: " + Auth);
|
throw new WebException("Code defect. Unsupported authentication type: " + this.Auth);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool supportsIfModifiedSince = false;
|
bool supportsIfModifiedSince = false;
|
||||||
if (request is HttpWebRequest httpRequest && File.Exists(To))
|
if (request is HttpWebRequest httpRequest && File.Exists(this.To))
|
||||||
{
|
{
|
||||||
supportsIfModifiedSince = true;
|
supportsIfModifiedSince = true;
|
||||||
httpRequest.IfModifiedSince = File.GetLastWriteTime(To);
|
httpRequest.IfModifiedSince = File.GetLastWriteTime(this.To);
|
||||||
}
|
}
|
||||||
|
|
||||||
DateTime lastModified = default;
|
DateTime lastModified = default;
|
||||||
string tmpFilePath = To + ".tmp";
|
string tmpFilePath = this.To + ".tmp";
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
using (WebResponse response = await request.GetResponseAsync())
|
using (WebResponse response = await request.GetResponseAsync())
|
||||||
|
|
@ -178,18 +178,18 @@ namespace winsw
|
||||||
await responseStream.CopyToAsync(tmpStream);
|
await responseStream.CopyToAsync(tmpStream);
|
||||||
}
|
}
|
||||||
|
|
||||||
FileHelper.MoveOrReplaceFile(To + ".tmp", To);
|
FileHelper.MoveOrReplaceFile(this.To + ".tmp", this.To);
|
||||||
|
|
||||||
if (supportsIfModifiedSince)
|
if (supportsIfModifiedSince)
|
||||||
{
|
{
|
||||||
File.SetLastWriteTime(To, lastModified);
|
File.SetLastWriteTime(this.To, lastModified);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (WebException e)
|
catch (WebException e)
|
||||||
{
|
{
|
||||||
if (supportsIfModifiedSince && ((HttpWebResponse)e.Response).StatusCode == HttpStatusCode.NotModified)
|
if (supportsIfModifiedSince && ((HttpWebResponse)e.Response).StatusCode == HttpStatusCode.NotModified)
|
||||||
{
|
{
|
||||||
Logger.Info($"Skipped downloading unmodified resource '{From}'");
|
Logger.Info($"Skipped downloading unmodified resource '{this.From}'");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
@ -201,8 +201,9 @@ namespace winsw
|
||||||
|
|
||||||
public class CustomProxyInformation
|
public class CustomProxyInformation
|
||||||
{
|
{
|
||||||
public string ServerAddress { get; set; }
|
public string ServerAddress { get; }
|
||||||
public NetworkCredential? Credentials { get; set; }
|
|
||||||
|
public NetworkCredential? Credentials { get; }
|
||||||
|
|
||||||
public CustomProxyInformation(string proxy)
|
public CustomProxyInformation(string proxy)
|
||||||
{
|
{
|
||||||
|
|
@ -216,12 +217,12 @@ namespace winsw
|
||||||
|
|
||||||
string username = completeCredsStr.Substring(0, credsSeparator);
|
string username = completeCredsStr.Substring(0, credsSeparator);
|
||||||
string password = completeCredsStr.Substring(credsSeparator + 1);
|
string password = completeCredsStr.Substring(credsSeparator + 1);
|
||||||
Credentials = new NetworkCredential(username, password);
|
this.Credentials = new NetworkCredential(username, password);
|
||||||
ServerAddress = proxy.Replace(completeCredsStr + "@", "");
|
this.ServerAddress = proxy.Replace(completeCredsStr + "@", null);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ServerAddress = proxy;
|
this.ServerAddress = proxy;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
using System.Xml;
|
using System.Xml;
|
||||||
|
|
||||||
namespace winsw.Extensions
|
namespace WinSW.Extensions
|
||||||
{
|
{
|
||||||
public abstract class AbstractWinSWExtension : IWinSWExtension
|
public abstract class AbstractWinSWExtension : IWinSWExtension
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -1,23 +1,23 @@
|
||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace winsw.Extensions
|
namespace WinSW.Extensions
|
||||||
{
|
{
|
||||||
public class ExtensionException : WinSWException
|
public class ExtensionException : WinSWException
|
||||||
{
|
{
|
||||||
public string ExtensionId { get; private set; }
|
public string ExtensionId { get; }
|
||||||
|
|
||||||
public ExtensionException(string extensionName, string message)
|
public ExtensionException(string extensionName, string message)
|
||||||
: base(message)
|
: base(message)
|
||||||
{
|
{
|
||||||
ExtensionId = extensionName;
|
this.ExtensionId = extensionName;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ExtensionException(string extensionName, string message, Exception innerException)
|
public ExtensionException(string extensionName, string message, Exception innerException)
|
||||||
: base(message, innerException)
|
: base(message, innerException)
|
||||||
{
|
{
|
||||||
ExtensionId = extensionName;
|
this.ExtensionId = extensionName;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override string Message => ExtensionId + ": " + base.Message;
|
public override string Message => this.ExtensionId + ": " + base.Message;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,12 +0,0 @@
|
||||||
namespace winsw.Extensions
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// This attribute is used to identify extension points within the code
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// Each extension point implements its own entry type.
|
|
||||||
/// </remarks>
|
|
||||||
class ExtensionPointAttribute
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
using System.Xml;
|
using System.Xml;
|
||||||
|
|
||||||
namespace winsw.Extensions
|
namespace WinSW.Extensions
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Interface for Win Service Wrapper Extension
|
/// Interface for Win Service Wrapper Extension
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
using System.Xml;
|
using System.Xml;
|
||||||
using winsw.Util;
|
using WinSW.Util;
|
||||||
|
|
||||||
namespace winsw.Extensions
|
namespace WinSW.Extensions
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Describes WinSW extensions in <see cref="IWinSWExtension"/>
|
/// Describes WinSW extensions in <see cref="IWinSWExtension"/>
|
||||||
|
|
@ -14,23 +14,23 @@ namespace winsw.Extensions
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Unique extension ID
|
/// Unique extension ID
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string Id { get; private set; }
|
public string Id { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Exception is enabled
|
/// Exception is enabled
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool Enabled { get; private set; }
|
public bool Enabled { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Extension classname
|
/// Extension classname
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string ClassName { get; private set; }
|
public string ClassName { get; }
|
||||||
|
|
||||||
private WinSWExtensionDescriptor(string id, string className, bool enabled)
|
private WinSWExtensionDescriptor(string id, string className, bool enabled)
|
||||||
{
|
{
|
||||||
Id = id;
|
this.Id = id;
|
||||||
Enabled = enabled;
|
this.Enabled = enabled;
|
||||||
ClassName = className;
|
this.ClassName = className;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static WinSWExtensionDescriptor FromXml(XmlElement node)
|
public static WinSWExtensionDescriptor FromXml(XmlElement node)
|
||||||
|
|
|
||||||
|
|
@ -3,20 +3,20 @@ using System.Collections.Generic;
|
||||||
using System.Xml;
|
using System.Xml;
|
||||||
using log4net;
|
using log4net;
|
||||||
|
|
||||||
namespace winsw.Extensions
|
namespace WinSW.Extensions
|
||||||
{
|
{
|
||||||
public class WinSWExtensionManager
|
public class WinSWExtensionManager
|
||||||
{
|
{
|
||||||
public Dictionary<string, IWinSWExtension> Extensions { get; private set; }
|
public Dictionary<string, IWinSWExtension> Extensions { get; }
|
||||||
|
|
||||||
public ServiceDescriptor ServiceDescriptor { get; private set; }
|
public ServiceDescriptor ServiceDescriptor { get; }
|
||||||
|
|
||||||
private static readonly ILog Log = LogManager.GetLogger(typeof(WinSWExtensionManager));
|
private static readonly ILog Log = LogManager.GetLogger(typeof(WinSWExtensionManager));
|
||||||
|
|
||||||
public WinSWExtensionManager(ServiceDescriptor serviceDescriptor)
|
public WinSWExtensionManager(ServiceDescriptor serviceDescriptor)
|
||||||
{
|
{
|
||||||
ServiceDescriptor = serviceDescriptor;
|
this.ServiceDescriptor = serviceDescriptor;
|
||||||
Extensions = new Dictionary<string, IWinSWExtension>();
|
this.Extensions = new Dictionary<string, IWinSWExtension>();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -27,7 +27,7 @@ namespace winsw.Extensions
|
||||||
/// <exception cref="Exception">Start failure</exception>
|
/// <exception cref="Exception">Start failure</exception>
|
||||||
public void FireOnWrapperStarted()
|
public void FireOnWrapperStarted()
|
||||||
{
|
{
|
||||||
foreach (var ext in Extensions)
|
foreach (var ext in this.Extensions)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
|
@ -47,7 +47,7 @@ namespace winsw.Extensions
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void FireBeforeWrapperStopped()
|
public void FireBeforeWrapperStopped()
|
||||||
{
|
{
|
||||||
foreach (var ext in Extensions)
|
foreach (var ext in this.Extensions)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
|
@ -66,7 +66,7 @@ namespace winsw.Extensions
|
||||||
/// <param name="process">Process</param>
|
/// <param name="process">Process</param>
|
||||||
public void FireOnProcessStarted(System.Diagnostics.Process process)
|
public void FireOnProcessStarted(System.Diagnostics.Process process)
|
||||||
{
|
{
|
||||||
foreach (var ext in Extensions)
|
foreach (var ext in this.Extensions)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
|
@ -85,7 +85,7 @@ namespace winsw.Extensions
|
||||||
/// <param name="process">Process</param>
|
/// <param name="process">Process</param>
|
||||||
public void FireOnProcessTerminated(System.Diagnostics.Process process)
|
public void FireOnProcessTerminated(System.Diagnostics.Process process)
|
||||||
{
|
{
|
||||||
foreach (var ext in Extensions)
|
foreach (var ext in this.Extensions)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
|
@ -107,10 +107,10 @@ namespace winsw.Extensions
|
||||||
/// <exception cref="Exception">Loading failure</exception>
|
/// <exception cref="Exception">Loading failure</exception>
|
||||||
public void LoadExtensions()
|
public void LoadExtensions()
|
||||||
{
|
{
|
||||||
var extensionIds = ServiceDescriptor.ExtensionIds;
|
var extensionIds = this.ServiceDescriptor.ExtensionIds;
|
||||||
foreach (string extensionId in extensionIds)
|
foreach (string extensionId in extensionIds)
|
||||||
{
|
{
|
||||||
LoadExtension(extensionId);
|
this.LoadExtension(extensionId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -122,12 +122,12 @@ namespace winsw.Extensions
|
||||||
/// <exception cref="Exception">Loading failure</exception>
|
/// <exception cref="Exception">Loading failure</exception>
|
||||||
private void LoadExtension(string id)
|
private void LoadExtension(string id)
|
||||||
{
|
{
|
||||||
if (Extensions.ContainsKey(id))
|
if (this.Extensions.ContainsKey(id))
|
||||||
{
|
{
|
||||||
throw new ExtensionException(id, "Extension has been already loaded");
|
throw new ExtensionException(id, "Extension has been already loaded");
|
||||||
}
|
}
|
||||||
|
|
||||||
XmlNode? extensionsConfig = ServiceDescriptor.ExtensionsConfiguration;
|
XmlNode? extensionsConfig = this.ServiceDescriptor.ExtensionsConfiguration;
|
||||||
XmlElement? configNode = extensionsConfig is null ? null : extensionsConfig.SelectSingleNode("extension[@id='" + id + "'][1]") as XmlElement;
|
XmlElement? configNode = extensionsConfig is null ? null : extensionsConfig.SelectSingleNode("extension[@id='" + id + "'][1]") as XmlElement;
|
||||||
if (configNode is null)
|
if (configNode is null)
|
||||||
{
|
{
|
||||||
|
|
@ -137,11 +137,11 @@ namespace winsw.Extensions
|
||||||
var descriptor = WinSWExtensionDescriptor.FromXml(configNode);
|
var descriptor = WinSWExtensionDescriptor.FromXml(configNode);
|
||||||
if (descriptor.Enabled)
|
if (descriptor.Enabled)
|
||||||
{
|
{
|
||||||
IWinSWExtension extension = CreateExtensionInstance(descriptor.Id, descriptor.ClassName);
|
IWinSWExtension extension = this.CreateExtensionInstance(descriptor.Id, descriptor.ClassName);
|
||||||
extension.Descriptor = descriptor;
|
extension.Descriptor = descriptor;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
extension.Configure(ServiceDescriptor, configNode);
|
extension.Configure(this.ServiceDescriptor, configNode);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{ // Consider any unexpected exception as fatal
|
{ // Consider any unexpected exception as fatal
|
||||||
|
|
@ -149,7 +149,7 @@ namespace winsw.Extensions
|
||||||
throw ex;
|
throw ex;
|
||||||
}
|
}
|
||||||
|
|
||||||
Extensions.Add(id, extension);
|
this.Extensions.Add(id, extension);
|
||||||
Log.Info("Extension loaded: " + id);
|
Log.Info("Extension loaded: " + id);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,13 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.IO.Compression;
|
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.IO.Compression;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using winsw.Util;
|
using WinSW.Util;
|
||||||
|
|
||||||
namespace winsw
|
namespace WinSW
|
||||||
{
|
{
|
||||||
// ReSharper disable once InconsistentNaming
|
public interface IEventLogger
|
||||||
public interface EventLogger
|
|
||||||
{
|
{
|
||||||
void LogEvent(string message);
|
void LogEvent(string message);
|
||||||
|
|
||||||
|
|
@ -20,14 +19,13 @@ namespace winsw
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public abstract class LogHandler
|
public abstract class LogHandler
|
||||||
{
|
{
|
||||||
// ReSharper disable once InconsistentNaming
|
public abstract void Log(StreamReader outputReader, StreamReader errorReader);
|
||||||
public abstract void log(StreamReader outputReader, StreamReader errorReader);
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Error and information about logging should be reported here.
|
/// Error and information about logging should be reported here.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
#pragma warning disable CS8618 // Non-nullable field is uninitialized. Consider declaring as nullable.
|
#pragma warning disable CS8618 // Non-nullable field is uninitialized. Consider declaring as nullable.
|
||||||
public EventLogger EventLogger { get; set; }
|
public IEventLogger EventLogger { get; set; }
|
||||||
#pragma warning restore CS8618 // Non-nullable field is uninitialized. Consider declaring as nullable.
|
#pragma warning restore CS8618 // Non-nullable field is uninitialized. Consider declaring as nullable.
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -56,7 +54,7 @@ namespace winsw
|
||||||
}
|
}
|
||||||
catch (IOException e)
|
catch (IOException e)
|
||||||
{
|
{
|
||||||
EventLogger.LogEvent("Failed to move :" + sourceFileName + " to " + destFileName + " because " + e.Message);
|
this.EventLogger.LogEvent("Failed to move :" + sourceFileName + " to " + destFileName + " because " + e.Message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -66,22 +64,26 @@ namespace winsw
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public abstract class AbstractFileLogAppender : LogHandler
|
public abstract class AbstractFileLogAppender : LogHandler
|
||||||
{
|
{
|
||||||
protected string BaseLogFileName { get; private set; }
|
protected string BaseLogFileName { get; }
|
||||||
protected bool OutFileDisabled { get; private set; }
|
|
||||||
protected bool ErrFileDisabled { get; private set; }
|
protected bool OutFileDisabled { get; }
|
||||||
protected string OutFilePattern { get; private set; }
|
|
||||||
protected string ErrFilePattern { get; private set; }
|
protected bool ErrFileDisabled { get; }
|
||||||
|
|
||||||
|
protected string OutFilePattern { get; }
|
||||||
|
|
||||||
|
protected string ErrFilePattern { get; }
|
||||||
|
|
||||||
protected AbstractFileLogAppender(string logDirectory, string baseName, bool outFileDisabled, bool errFileDisabled, string outFilePattern, string errFilePattern)
|
protected AbstractFileLogAppender(string logDirectory, string baseName, bool outFileDisabled, bool errFileDisabled, string outFilePattern, string errFilePattern)
|
||||||
{
|
{
|
||||||
BaseLogFileName = Path.Combine(logDirectory, baseName);
|
this.BaseLogFileName = Path.Combine(logDirectory, baseName);
|
||||||
OutFileDisabled = outFileDisabled;
|
this.OutFileDisabled = outFileDisabled;
|
||||||
OutFilePattern = outFilePattern;
|
this.OutFilePattern = outFilePattern;
|
||||||
ErrFileDisabled = errFileDisabled;
|
this.ErrFileDisabled = errFileDisabled;
|
||||||
ErrFilePattern = errFilePattern;
|
this.ErrFilePattern = errFilePattern;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void log(StreamReader outputReader, StreamReader errorReader)
|
public override void Log(StreamReader outputReader, StreamReader errorReader)
|
||||||
{
|
{
|
||||||
if (this.OutFileDisabled)
|
if (this.OutFileDisabled)
|
||||||
{
|
{
|
||||||
|
|
@ -111,26 +113,28 @@ namespace winsw
|
||||||
|
|
||||||
public abstract class SimpleLogAppender : AbstractFileLogAppender
|
public abstract class SimpleLogAppender : AbstractFileLogAppender
|
||||||
{
|
{
|
||||||
public FileMode FileMode { get; private set; }
|
public FileMode FileMode { get; }
|
||||||
public string OutputLogFileName { get; private set; }
|
|
||||||
public string ErrorLogFileName { get; private set; }
|
public string OutputLogFileName { get; }
|
||||||
|
|
||||||
|
public string ErrorLogFileName { get; }
|
||||||
|
|
||||||
protected SimpleLogAppender(string logDirectory, string baseName, FileMode fileMode, bool outFileDisabled, bool errFileDisabled, string outFilePattern, string errFilePattern)
|
protected SimpleLogAppender(string logDirectory, string baseName, FileMode fileMode, bool outFileDisabled, bool errFileDisabled, string outFilePattern, string errFilePattern)
|
||||||
: base(logDirectory, baseName, outFileDisabled, errFileDisabled, outFilePattern, errFilePattern)
|
: base(logDirectory, baseName, outFileDisabled, errFileDisabled, outFilePattern, errFilePattern)
|
||||||
{
|
{
|
||||||
FileMode = fileMode;
|
this.FileMode = fileMode;
|
||||||
OutputLogFileName = BaseLogFileName + ".out.log";
|
this.OutputLogFileName = this.BaseLogFileName + ".out.log";
|
||||||
ErrorLogFileName = BaseLogFileName + ".err.log";
|
this.ErrorLogFileName = this.BaseLogFileName + ".err.log";
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void LogOutput(StreamReader outputReader)
|
protected override void LogOutput(StreamReader outputReader)
|
||||||
{
|
{
|
||||||
new Thread(() => CopyStream(outputReader, CreateWriter(new FileStream(OutputLogFileName, FileMode)))).Start();
|
new Thread(() => this.CopyStream(outputReader, this.CreateWriter(new FileStream(this.OutputLogFileName, this.FileMode)))).Start();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void LogError(StreamReader errorReader)
|
protected override void LogError(StreamReader errorReader)
|
||||||
{
|
{
|
||||||
new Thread(() => CopyStream(errorReader, CreateWriter(new FileStream(ErrorLogFileName, FileMode)))).Start();
|
new Thread(() => this.CopyStream(errorReader, this.CreateWriter(new FileStream(this.ErrorLogFileName, this.FileMode)))).Start();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -155,7 +159,7 @@ namespace winsw
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class IgnoreLogAppender : LogHandler
|
public class IgnoreLogAppender : LogHandler
|
||||||
{
|
{
|
||||||
public override void log(StreamReader outputReader, StreamReader errorReader)
|
public override void Log(StreamReader outputReader, StreamReader errorReader)
|
||||||
{
|
{
|
||||||
outputReader.Dispose();
|
outputReader.Dispose();
|
||||||
errorReader.Dispose();
|
errorReader.Dispose();
|
||||||
|
|
@ -164,24 +168,25 @@ namespace winsw
|
||||||
|
|
||||||
public class TimeBasedRollingLogAppender : AbstractFileLogAppender
|
public class TimeBasedRollingLogAppender : AbstractFileLogAppender
|
||||||
{
|
{
|
||||||
public string Pattern { get; private set; }
|
public string Pattern { get; }
|
||||||
public int Period { get; private set; }
|
|
||||||
|
public int Period { get; }
|
||||||
|
|
||||||
public TimeBasedRollingLogAppender(string logDirectory, string baseName, bool outFileDisabled, bool errFileDisabled, string outFilePattern, string errFilePattern, string pattern, int period)
|
public TimeBasedRollingLogAppender(string logDirectory, string baseName, bool outFileDisabled, bool errFileDisabled, string outFilePattern, string errFilePattern, string pattern, int period)
|
||||||
: base(logDirectory, baseName, outFileDisabled, errFileDisabled, outFilePattern, errFilePattern)
|
: base(logDirectory, baseName, outFileDisabled, errFileDisabled, outFilePattern, errFilePattern)
|
||||||
{
|
{
|
||||||
Pattern = pattern;
|
this.Pattern = pattern;
|
||||||
Period = period;
|
this.Period = period;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void LogOutput(StreamReader outputReader)
|
protected override void LogOutput(StreamReader outputReader)
|
||||||
{
|
{
|
||||||
new Thread(() => CopyStreamWithDateRotation(outputReader, OutFilePattern)).Start();
|
new Thread(() => this.CopyStreamWithDateRotation(outputReader, this.OutFilePattern)).Start();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void LogError(StreamReader errorReader)
|
protected override void LogError(StreamReader errorReader)
|
||||||
{
|
{
|
||||||
new Thread(() => CopyStreamWithDateRotation(errorReader, ErrFilePattern)).Start();
|
new Thread(() => this.CopyStreamWithDateRotation(errorReader, this.ErrFilePattern)).Start();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -189,17 +194,17 @@ namespace winsw
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private void CopyStreamWithDateRotation(StreamReader reader, string ext)
|
private void CopyStreamWithDateRotation(StreamReader reader, string ext)
|
||||||
{
|
{
|
||||||
PeriodicRollingCalendar periodicRollingCalendar = new PeriodicRollingCalendar(Pattern, Period);
|
PeriodicRollingCalendar periodicRollingCalendar = new PeriodicRollingCalendar(this.Pattern, this.Period);
|
||||||
periodicRollingCalendar.init();
|
periodicRollingCalendar.Init();
|
||||||
|
|
||||||
StreamWriter writer = CreateWriter(new FileStream(BaseLogFileName + "_" + periodicRollingCalendar.format + ext, FileMode.Append));
|
StreamWriter writer = this.CreateWriter(new FileStream(this.BaseLogFileName + "_" + periodicRollingCalendar.Format + ext, FileMode.Append));
|
||||||
string? line;
|
string? line;
|
||||||
while ((line = reader.ReadLine()) != null)
|
while ((line = reader.ReadLine()) != null)
|
||||||
{
|
{
|
||||||
if (periodicRollingCalendar.shouldRoll)
|
if (periodicRollingCalendar.ShouldRoll)
|
||||||
{
|
{
|
||||||
writer.Dispose();
|
writer.Dispose();
|
||||||
writer = CreateWriter(new FileStream(BaseLogFileName + "_" + periodicRollingCalendar.format + ext, FileMode.Create));
|
writer = this.CreateWriter(new FileStream(this.BaseLogFileName + "_" + periodicRollingCalendar.Format + ext, FileMode.Create));
|
||||||
}
|
}
|
||||||
|
|
||||||
writer.WriteLine(line);
|
writer.WriteLine(line);
|
||||||
|
|
@ -212,37 +217,35 @@ namespace winsw
|
||||||
|
|
||||||
public class SizeBasedRollingLogAppender : AbstractFileLogAppender
|
public class SizeBasedRollingLogAppender : AbstractFileLogAppender
|
||||||
{
|
{
|
||||||
// ReSharper disable once InconsistentNaming
|
public const int BytesPerKB = 1024;
|
||||||
public static int BYTES_PER_KB = 1024;
|
public const int BytesPerMB = 1024 * BytesPerKB;
|
||||||
// ReSharper disable once InconsistentNaming
|
public const int DefaultSizeThreshold = 10 * BytesPerMB; // roll every 10MB.
|
||||||
public static int BYTES_PER_MB = 1024 * BYTES_PER_KB;
|
public const int DefaultFilesToKeep = 8;
|
||||||
// ReSharper disable once InconsistentNaming
|
|
||||||
public static int DEFAULT_SIZE_THRESHOLD = 10 * BYTES_PER_MB; // roll every 10MB.
|
|
||||||
// ReSharper disable once InconsistentNaming
|
|
||||||
public static int DEFAULT_FILES_TO_KEEP = 8;
|
|
||||||
|
|
||||||
public int SizeTheshold { get; private set; }
|
public int SizeThreshold { get; }
|
||||||
|
|
||||||
public int FilesToKeep { get; private set; }
|
public int FilesToKeep { get; }
|
||||||
|
|
||||||
public SizeBasedRollingLogAppender(string logDirectory, string baseName, bool outFileDisabled, bool errFileDisabled, string outFilePattern, string errFilePattern, int sizeThreshold, int filesToKeep)
|
public SizeBasedRollingLogAppender(string logDirectory, string baseName, bool outFileDisabled, bool errFileDisabled, string outFilePattern, string errFilePattern, int sizeThreshold, int filesToKeep)
|
||||||
: base(logDirectory, baseName, outFileDisabled, errFileDisabled, outFilePattern, errFilePattern)
|
: base(logDirectory, baseName, outFileDisabled, errFileDisabled, outFilePattern, errFilePattern)
|
||||||
{
|
{
|
||||||
SizeTheshold = sizeThreshold;
|
this.SizeThreshold = sizeThreshold;
|
||||||
FilesToKeep = filesToKeep;
|
this.FilesToKeep = filesToKeep;
|
||||||
}
|
}
|
||||||
|
|
||||||
public SizeBasedRollingLogAppender(string logDirectory, string baseName, bool outFileDisabled, bool errFileDisabled, string outFilePattern, string errFilePattern)
|
public SizeBasedRollingLogAppender(string logDirectory, string baseName, bool outFileDisabled, bool errFileDisabled, string outFilePattern, string errFilePattern)
|
||||||
: this(logDirectory, baseName, outFileDisabled, errFileDisabled, outFilePattern, errFilePattern, DEFAULT_SIZE_THRESHOLD, DEFAULT_FILES_TO_KEEP) { }
|
: this(logDirectory, baseName, outFileDisabled, errFileDisabled, outFilePattern, errFilePattern, DefaultSizeThreshold, DefaultFilesToKeep)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
protected override void LogOutput(StreamReader outputReader)
|
protected override void LogOutput(StreamReader outputReader)
|
||||||
{
|
{
|
||||||
new Thread(() => CopyStreamWithRotation(outputReader, OutFilePattern)).Start();
|
new Thread(() => this.CopyStreamWithRotation(outputReader, this.OutFilePattern)).Start();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void LogError(StreamReader errorReader)
|
protected override void LogError(StreamReader errorReader)
|
||||||
{
|
{
|
||||||
new Thread(() => CopyStreamWithRotation(errorReader, ErrFilePattern)).Start();
|
new Thread(() => this.CopyStreamWithRotation(errorReader, this.ErrFilePattern)).Start();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -250,41 +253,45 @@ namespace winsw
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private void CopyStreamWithRotation(StreamReader reader, string ext)
|
private void CopyStreamWithRotation(StreamReader reader, string ext)
|
||||||
{
|
{
|
||||||
StreamWriter writer = CreateWriter(new FileStream(BaseLogFileName + ext, FileMode.Append));
|
StreamWriter writer = this.CreateWriter(new FileStream(this.BaseLogFileName + ext, FileMode.Append));
|
||||||
long fileLength = new FileInfo(BaseLogFileName + ext).Length;
|
long fileLength = new FileInfo(this.BaseLogFileName + ext).Length;
|
||||||
|
|
||||||
string? line;
|
string? line;
|
||||||
while ((line = reader.ReadLine()) != null)
|
while ((line = reader.ReadLine()) != null)
|
||||||
{
|
{
|
||||||
int lengthToWrite = (line.Length + Environment.NewLine.Length) * sizeof(char);
|
int lengthToWrite = (line.Length + Environment.NewLine.Length) * sizeof(char);
|
||||||
if (fileLength + lengthToWrite > SizeTheshold)
|
if (fileLength + lengthToWrite > this.SizeThreshold)
|
||||||
{
|
{
|
||||||
writer.Dispose();
|
writer.Dispose();
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
for (int j = FilesToKeep; j >= 1; j--)
|
for (int j = this.FilesToKeep; j >= 1; j--)
|
||||||
{
|
{
|
||||||
string dst = BaseLogFileName + "." + (j - 1) + ext;
|
string dst = this.BaseLogFileName + "." + (j - 1) + ext;
|
||||||
string src = BaseLogFileName + "." + (j - 2) + ext;
|
string src = this.BaseLogFileName + "." + (j - 2) + ext;
|
||||||
if (File.Exists(dst))
|
if (File.Exists(dst))
|
||||||
|
{
|
||||||
File.Delete(dst);
|
File.Delete(dst);
|
||||||
|
}
|
||||||
|
|
||||||
if (File.Exists(src))
|
if (File.Exists(src))
|
||||||
|
{
|
||||||
File.Move(src, dst);
|
File.Move(src, dst);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
File.Move(BaseLogFileName + ext, BaseLogFileName + ".0" + ext);
|
File.Move(this.BaseLogFileName + ext, this.BaseLogFileName + ".0" + ext);
|
||||||
}
|
}
|
||||||
catch (IOException e)
|
catch (IOException e)
|
||||||
{
|
{
|
||||||
EventLogger.LogEvent("Failed to roll log: " + e.Message);
|
this.EventLogger.LogEvent("Failed to roll log: " + e.Message);
|
||||||
}
|
}
|
||||||
|
|
||||||
// even if the log rotation fails, create a new one, or else
|
// even if the log rotation fails, create a new one, or else
|
||||||
// we'll infinitely try to roll.
|
// we'll infinitely try to roll.
|
||||||
writer = CreateWriter(new FileStream(BaseLogFileName + ext, FileMode.Create));
|
writer = this.CreateWriter(new FileStream(this.BaseLogFileName + ext, FileMode.Create));
|
||||||
fileLength = new FileInfo(BaseLogFileName + ext).Length;
|
fileLength = new FileInfo(this.BaseLogFileName + ext).Length;
|
||||||
}
|
}
|
||||||
|
|
||||||
writer.WriteLine(line);
|
writer.WriteLine(line);
|
||||||
|
|
@ -306,26 +313,35 @@ namespace winsw
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void log(StreamReader outputReader, StreamReader errorReader)
|
public override void Log(StreamReader outputReader, StreamReader errorReader)
|
||||||
{
|
{
|
||||||
if (!OutFileDisabled)
|
if (!this.OutFileDisabled)
|
||||||
MoveFile(OutputLogFileName, OutputLogFileName + ".old");
|
{
|
||||||
|
this.MoveFile(this.OutputLogFileName, this.OutputLogFileName + ".old");
|
||||||
|
}
|
||||||
|
|
||||||
if (!ErrFileDisabled)
|
if (!this.ErrFileDisabled)
|
||||||
MoveFile(ErrorLogFileName, ErrorLogFileName + ".old");
|
{
|
||||||
|
this.MoveFile(this.ErrorLogFileName, this.ErrorLogFileName + ".old");
|
||||||
|
}
|
||||||
|
|
||||||
base.log(outputReader, errorReader);
|
base.Log(outputReader, errorReader);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class RollingSizeTimeLogAppender : AbstractFileLogAppender
|
public class RollingSizeTimeLogAppender : AbstractFileLogAppender
|
||||||
{
|
{
|
||||||
public static int BYTES_PER_KB = 1024;
|
public const int BytesPerKB = 1024;
|
||||||
public int SizeTheshold { get; private set; }
|
|
||||||
public string FilePattern { get; private set; }
|
public int SizeThreshold { get; }
|
||||||
public TimeSpan? AutoRollAtTime { get; private set; }
|
|
||||||
public int? ZipOlderThanNumDays { get; private set; }
|
public string FilePattern { get; }
|
||||||
public string ZipDateFormat { get; private set; }
|
|
||||||
|
public TimeSpan? AutoRollAtTime { get; }
|
||||||
|
|
||||||
|
public int? ZipOlderThanNumDays { get; }
|
||||||
|
|
||||||
|
public string ZipDateFormat { get; }
|
||||||
|
|
||||||
public RollingSizeTimeLogAppender(
|
public RollingSizeTimeLogAppender(
|
||||||
string logDirectory,
|
string logDirectory,
|
||||||
|
|
@ -341,21 +357,21 @@ namespace winsw
|
||||||
string zipdateformat)
|
string zipdateformat)
|
||||||
: base(logDirectory, baseName, outFileDisabled, errFileDisabled, outFilePattern, errFilePattern)
|
: base(logDirectory, baseName, outFileDisabled, errFileDisabled, outFilePattern, errFilePattern)
|
||||||
{
|
{
|
||||||
SizeTheshold = sizeThreshold;
|
this.SizeThreshold = sizeThreshold;
|
||||||
FilePattern = filePattern;
|
this.FilePattern = filePattern;
|
||||||
AutoRollAtTime = autoRollAtTime;
|
this.AutoRollAtTime = autoRollAtTime;
|
||||||
ZipOlderThanNumDays = zipolderthannumdays;
|
this.ZipOlderThanNumDays = zipolderthannumdays;
|
||||||
ZipDateFormat = zipdateformat;
|
this.ZipDateFormat = zipdateformat;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void LogOutput(StreamReader outputReader)
|
protected override void LogOutput(StreamReader outputReader)
|
||||||
{
|
{
|
||||||
new Thread(() => CopyStreamWithRotation(outputReader, OutFilePattern)).Start();
|
new Thread(() => this.CopyStreamWithRotation(outputReader, this.OutFilePattern)).Start();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void LogError(StreamReader errorReader)
|
protected override void LogError(StreamReader errorReader)
|
||||||
{
|
{
|
||||||
new Thread(() => CopyStreamWithRotation(errorReader, ErrFilePattern)).Start();
|
new Thread(() => this.CopyStreamWithRotation(errorReader, this.ErrFilePattern)).Start();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void CopyStreamWithRotation(StreamReader reader, string extension)
|
private void CopyStreamWithRotation(StreamReader reader, string extension)
|
||||||
|
|
@ -363,18 +379,18 @@ namespace winsw
|
||||||
// lock required as the timer thread and the thread that will write to the stream could try and access the file stream at the same time
|
// lock required as the timer thread and the thread that will write to the stream could try and access the file stream at the same time
|
||||||
var fileLock = new object();
|
var fileLock = new object();
|
||||||
|
|
||||||
var baseDirectory = Path.GetDirectoryName(BaseLogFileName)!;
|
var baseDirectory = Path.GetDirectoryName(this.BaseLogFileName)!;
|
||||||
var baseFileName = Path.GetFileName(BaseLogFileName);
|
var baseFileName = Path.GetFileName(this.BaseLogFileName);
|
||||||
var logFile = BaseLogFileName + extension;
|
var logFile = this.BaseLogFileName + extension;
|
||||||
|
|
||||||
var writer = CreateWriter(new FileStream(logFile, FileMode.Append));
|
var writer = this.CreateWriter(new FileStream(logFile, FileMode.Append));
|
||||||
var fileLength = new FileInfo(logFile).Length;
|
var fileLength = new FileInfo(logFile).Length;
|
||||||
|
|
||||||
// We auto roll at time is configured then we need to create a timer and wait until time is elasped and roll the file over
|
// We auto roll at time is configured then we need to create a timer and wait until time is elasped and roll the file over
|
||||||
if (AutoRollAtTime is TimeSpan autoRollAtTime)
|
if (this.AutoRollAtTime is TimeSpan autoRollAtTime)
|
||||||
{
|
{
|
||||||
// Run at start
|
// Run at start
|
||||||
var tickTime = SetupRollTimer(autoRollAtTime);
|
var tickTime = this.SetupRollTimer(autoRollAtTime);
|
||||||
var timer = new System.Timers.Timer(tickTime);
|
var timer = new System.Timers.Timer(tickTime);
|
||||||
timer.Elapsed += (s, e) =>
|
timer.Elapsed += (s, e) =>
|
||||||
{
|
{
|
||||||
|
|
@ -386,25 +402,25 @@ namespace winsw
|
||||||
writer.Dispose();
|
writer.Dispose();
|
||||||
|
|
||||||
var now = DateTime.Now.AddDays(-1);
|
var now = DateTime.Now.AddDays(-1);
|
||||||
var nextFileNumber = GetNextFileNumber(extension, baseDirectory, baseFileName, now);
|
var nextFileNumber = this.GetNextFileNumber(extension, baseDirectory, baseFileName, now);
|
||||||
var nextFileName = Path.Combine(baseDirectory, string.Format("{0}.{1}.#{2:D4}{3}", baseFileName, now.ToString(FilePattern), nextFileNumber, extension));
|
var nextFileName = Path.Combine(baseDirectory, string.Format("{0}.{1}.#{2:D4}{3}", baseFileName, now.ToString(this.FilePattern), nextFileNumber, extension));
|
||||||
File.Move(logFile, nextFileName);
|
File.Move(logFile, nextFileName);
|
||||||
|
|
||||||
writer = CreateWriter(new FileStream(logFile, FileMode.Create));
|
writer = this.CreateWriter(new FileStream(logFile, FileMode.Create));
|
||||||
fileLength = new FileInfo(logFile).Length;
|
fileLength = new FileInfo(logFile).Length;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Next day so check if file can be zipped
|
// Next day so check if file can be zipped
|
||||||
ZipFiles(baseDirectory, extension, baseFileName);
|
this.ZipFiles(baseDirectory, extension, baseFileName);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
EventLogger.LogEvent($"Failed to to trigger auto roll at time event due to: {ex.Message}");
|
this.EventLogger.LogEvent($"Failed to to trigger auto roll at time event due to: {ex.Message}");
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
// Recalculate the next interval
|
// Recalculate the next interval
|
||||||
timer.Interval = SetupRollTimer(autoRollAtTime);
|
timer.Interval = this.SetupRollTimer(autoRollAtTime);
|
||||||
timer.Start();
|
timer.Start();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
@ -417,26 +433,26 @@ namespace winsw
|
||||||
lock (fileLock)
|
lock (fileLock)
|
||||||
{
|
{
|
||||||
int lengthToWrite = (line.Length + Environment.NewLine.Length) * sizeof(char);
|
int lengthToWrite = (line.Length + Environment.NewLine.Length) * sizeof(char);
|
||||||
if (fileLength + lengthToWrite > SizeTheshold)
|
if (fileLength + lengthToWrite > this.SizeThreshold)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// roll file
|
// roll file
|
||||||
var now = DateTime.Now;
|
var now = DateTime.Now;
|
||||||
var nextFileNumber = GetNextFileNumber(extension, baseDirectory, baseFileName, now);
|
var nextFileNumber = this.GetNextFileNumber(extension, baseDirectory, baseFileName, now);
|
||||||
var nextFileName =
|
var nextFileName = Path.Combine(
|
||||||
Path.Combine(baseDirectory,
|
baseDirectory,
|
||||||
string.Format("{0}.{1}.#{2:D4}{3}", baseFileName, now.ToString(FilePattern), nextFileNumber, extension));
|
string.Format("{0}.{1}.#{2:D4}{3}", baseFileName, now.ToString(this.FilePattern), nextFileNumber, extension));
|
||||||
File.Move(logFile, nextFileName);
|
File.Move(logFile, nextFileName);
|
||||||
|
|
||||||
// even if the log rotation fails, create a new one, or else
|
// even if the log rotation fails, create a new one, or else
|
||||||
// we'll infinitely try to roll.
|
// we'll infinitely try to roll.
|
||||||
writer = CreateWriter(new FileStream(logFile, FileMode.Create));
|
writer = this.CreateWriter(new FileStream(logFile, FileMode.Create));
|
||||||
fileLength = new FileInfo(logFile).Length;
|
fileLength = new FileInfo(logFile).Length;
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
EventLogger.LogEvent($"Failed to roll size time log: {e.Message}");
|
this.EventLogger.LogEvent($"Failed to roll size time log: {e.Message}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -451,28 +467,32 @@ namespace winsw
|
||||||
|
|
||||||
private void ZipFiles(string directory, string fileExtension, string zipFileBaseName)
|
private void ZipFiles(string directory, string fileExtension, string zipFileBaseName)
|
||||||
{
|
{
|
||||||
if (ZipOlderThanNumDays is null || ZipOlderThanNumDays <= 0)
|
if (this.ZipOlderThanNumDays is null || this.ZipOlderThanNumDays <= 0)
|
||||||
|
{
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
foreach (string path in Directory.GetFiles(directory, "*" + fileExtension))
|
foreach (string path in Directory.GetFiles(directory, "*" + fileExtension))
|
||||||
{
|
{
|
||||||
var fileInfo = new FileInfo(path);
|
var fileInfo = new FileInfo(path);
|
||||||
if (fileInfo.LastWriteTimeUtc >= DateTime.UtcNow.AddDays(-ZipOlderThanNumDays.Value))
|
if (fileInfo.LastWriteTimeUtc >= DateTime.UtcNow.AddDays(-this.ZipOlderThanNumDays.Value))
|
||||||
|
{
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
string sourceFileName = Path.GetFileName(path);
|
string sourceFileName = Path.GetFileName(path);
|
||||||
string zipFilePattern = fileInfo.LastAccessTimeUtc.ToString(ZipDateFormat);
|
string zipFilePattern = fileInfo.LastAccessTimeUtc.ToString(this.ZipDateFormat);
|
||||||
string zipFilePath = Path.Combine(directory, $"{zipFileBaseName}.{zipFilePattern}.zip");
|
string zipFilePath = Path.Combine(directory, $"{zipFileBaseName}.{zipFilePattern}.zip");
|
||||||
ZipOneFile(path, sourceFileName, zipFilePath);
|
this.ZipOneFile(path, sourceFileName, zipFilePath);
|
||||||
|
|
||||||
File.Delete(path);
|
File.Delete(path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
EventLogger.LogEvent($"Failed to Zip files. Error {e.Message}");
|
this.EventLogger.LogEvent($"Failed to Zip files. Error {e.Message}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -490,7 +510,7 @@ namespace winsw
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
EventLogger.LogEvent($"Failed to Zip the File {sourceFilePath}. Error {e.Message}");
|
this.EventLogger.LogEvent($"Failed to Zip the File {sourceFilePath}. Error {e.Message}");
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
|
|
@ -510,7 +530,9 @@ namespace winsw
|
||||||
autoRollAtTime.Seconds,
|
autoRollAtTime.Seconds,
|
||||||
0);
|
0);
|
||||||
if (nowTime > scheduledTime)
|
if (nowTime > scheduledTime)
|
||||||
|
{
|
||||||
scheduledTime = scheduledTime.AddDays(1);
|
scheduledTime = scheduledTime.AddDays(1);
|
||||||
|
}
|
||||||
|
|
||||||
double tickTime = (scheduledTime - DateTime.Now).TotalMilliseconds;
|
double tickTime = (scheduledTime - DateTime.Now).TotalMilliseconds;
|
||||||
return tickTime;
|
return tickTime;
|
||||||
|
|
@ -519,7 +541,7 @@ namespace winsw
|
||||||
private int GetNextFileNumber(string ext, string baseDirectory, string baseFileName, DateTime now)
|
private int GetNextFileNumber(string ext, string baseDirectory, string baseFileName, DateTime now)
|
||||||
{
|
{
|
||||||
var nextFileNumber = 0;
|
var nextFileNumber = 0;
|
||||||
var files = Directory.GetFiles(baseDirectory, string.Format("{0}.{1}.#*{2}", baseFileName, now.ToString(FilePattern), ext));
|
var files = Directory.GetFiles(baseDirectory, string.Format("{0}.{1}.#*{2}", baseFileName, now.ToString(this.FilePattern), ext));
|
||||||
if (files.Length == 0)
|
if (files.Length == 0)
|
||||||
{
|
{
|
||||||
nextFileNumber = 1;
|
nextFileNumber = 1;
|
||||||
|
|
@ -533,11 +555,12 @@ namespace winsw
|
||||||
var filenameOnly = Path.GetFileNameWithoutExtension(f);
|
var filenameOnly = Path.GetFileNameWithoutExtension(f);
|
||||||
var hashIndex = filenameOnly.IndexOf('#');
|
var hashIndex = filenameOnly.IndexOf('#');
|
||||||
var lastNumberAsString = filenameOnly.Substring(hashIndex + 1, 4);
|
var lastNumberAsString = filenameOnly.Substring(hashIndex + 1, 4);
|
||||||
// var lastNumberAsString = filenameOnly.Substring(filenameOnly.Length - 4, 4);
|
|
||||||
if (int.TryParse(lastNumberAsString, out int lastNumber))
|
if (int.TryParse(lastNumberAsString, out int lastNumber))
|
||||||
{
|
{
|
||||||
if (lastNumber > nextFileNumber)
|
if (lastNumber > nextFileNumber)
|
||||||
|
{
|
||||||
nextFileNumber = lastNumber;
|
nextFileNumber = lastNumber;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
@ -551,7 +574,9 @@ namespace winsw
|
||||||
}
|
}
|
||||||
|
|
||||||
if (nextFileNumber == 0)
|
if (nextFileNumber == 0)
|
||||||
|
{
|
||||||
throw new IOException("Cannot roll the file because matching pattern not found");
|
throw new IOException("Cannot roll the file because matching pattern not found");
|
||||||
|
}
|
||||||
|
|
||||||
nextFileNumber++;
|
nextFileNumber++;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
|
|
||||||
namespace winsw.Logging
|
namespace WinSW.Logging
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Indicates that the class may reference the event log
|
/// Indicates that the class may reference the event log
|
||||||
|
|
@ -11,6 +11,6 @@ namespace winsw.Logging
|
||||||
/// Locates Event Log for the service.
|
/// Locates Event Log for the service.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>Event Log or null if it is not avilable</returns>
|
/// <returns>Event Log or null if it is not avilable</returns>
|
||||||
EventLog? locate();
|
EventLog? Locate();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
using log4net.Appender;
|
using log4net.Appender;
|
||||||
using log4net.Core;
|
using log4net.Core;
|
||||||
|
|
||||||
namespace winsw.Logging
|
namespace WinSW.Logging
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Implementes service Event log appender for log4j.
|
/// Implementes service Event log appender for log4j.
|
||||||
|
|
@ -11,18 +11,18 @@ namespace winsw.Logging
|
||||||
public class ServiceEventLogAppender : AppenderSkeleton
|
public class ServiceEventLogAppender : AppenderSkeleton
|
||||||
{
|
{
|
||||||
#pragma warning disable CS8618 // Non-nullable field is uninitialized. Consider declaring as nullable.
|
#pragma warning disable CS8618 // Non-nullable field is uninitialized. Consider declaring as nullable.
|
||||||
public IServiceEventLogProvider provider { get; set; }
|
public IServiceEventLogProvider Provider { get; set; }
|
||||||
#pragma warning restore CS8618 // Non-nullable field is uninitialized. Consider declaring as nullable.
|
#pragma warning restore CS8618 // Non-nullable field is uninitialized. Consider declaring as nullable.
|
||||||
|
|
||||||
override protected void Append(LoggingEvent loggingEvent)
|
protected override void Append(LoggingEvent loggingEvent)
|
||||||
{
|
{
|
||||||
EventLog? eventLog = provider.locate();
|
EventLog? eventLog = this.Provider.Locate();
|
||||||
|
|
||||||
// We write the event iff the provider is ready
|
// We write the event iff the provider is ready
|
||||||
eventLog?.WriteEntry(loggingEvent.RenderedMessage, toEventLogEntryType(loggingEvent.Level));
|
eventLog?.WriteEntry(loggingEvent.RenderedMessage, ToEventLogEntryType(loggingEvent.Level));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static EventLogEntryType toEventLogEntryType(Level level)
|
private static EventLogEntryType ToEventLogEntryType(Level level)
|
||||||
{
|
{
|
||||||
if (level.Value >= Level.Error.Value)
|
if (level.Value >= Level.Error.Value)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
using System.Runtime.InteropServices;
|
#pragma warning disable SA1310 // Field names should not contain underscore
|
||||||
|
|
||||||
namespace winsw.Native
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace WinSW.Native
|
||||||
{
|
{
|
||||||
internal static class ConsoleApis
|
internal static class ConsoleApis
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,9 @@
|
||||||
using System;
|
#pragma warning disable SA1310 // Field names should not contain underscore
|
||||||
|
|
||||||
|
using System;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
namespace winsw.Native
|
namespace WinSW.Native
|
||||||
{
|
{
|
||||||
internal static class CredentialApis
|
internal static class CredentialApis
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,6 @@
|
||||||
namespace winsw.Native
|
#pragma warning disable SA1310 // Field names should not contain underscore
|
||||||
|
|
||||||
|
namespace WinSW.Native
|
||||||
{
|
{
|
||||||
internal static class Errors
|
internal static class Errors
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
namespace winsw.Native
|
namespace WinSW.Native
|
||||||
{
|
{
|
||||||
internal static class HandleApis
|
internal static class HandleApis
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
namespace winsw.Native
|
namespace WinSW.Native
|
||||||
{
|
{
|
||||||
internal static class Libraries
|
internal static class Libraries
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,10 @@
|
||||||
using System;
|
#pragma warning disable SA1310 // Field names should not contain underscore
|
||||||
|
|
||||||
|
using System;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.Security.Principal;
|
using System.Security.Principal;
|
||||||
|
|
||||||
namespace winsw.Native
|
namespace WinSW.Native
|
||||||
{
|
{
|
||||||
internal static class ProcessApis
|
internal static class ProcessApis
|
||||||
{
|
{
|
||||||
|
|
@ -46,12 +48,14 @@ namespace winsw.Native
|
||||||
[StructLayout(LayoutKind.Sequential)]
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
internal unsafe struct PROCESS_BASIC_INFORMATION
|
internal unsafe struct PROCESS_BASIC_INFORMATION
|
||||||
{
|
{
|
||||||
|
#pragma warning disable SA1306 // Field names should begin with lower-case letter
|
||||||
private readonly IntPtr Reserved1;
|
private readonly IntPtr Reserved1;
|
||||||
private readonly IntPtr PebBaseAddress;
|
private readonly IntPtr PebBaseAddress;
|
||||||
private readonly IntPtr Reserved2_1;
|
private readonly IntPtr Reserved2_1;
|
||||||
private readonly IntPtr Reserved2_2;
|
private readonly IntPtr Reserved2_2;
|
||||||
internal readonly IntPtr UniqueProcessId;
|
internal readonly IntPtr UniqueProcessId;
|
||||||
internal readonly IntPtr InheritedFromUniqueProcessId;
|
internal readonly IntPtr InheritedFromUniqueProcessId;
|
||||||
|
#pragma warning restore SA1306 // Field names should begin with lower-case letter
|
||||||
}
|
}
|
||||||
|
|
||||||
internal struct PROCESS_INFORMATION
|
internal struct PROCESS_INFORMATION
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
using System;
|
using System;
|
||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using static winsw.Native.SecurityApis;
|
using static WinSW.Native.SecurityApis;
|
||||||
|
|
||||||
namespace winsw.Native
|
namespace WinSW.Native
|
||||||
{
|
{
|
||||||
internal static class Security
|
internal static class Security
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,9 @@
|
||||||
using System;
|
#pragma warning disable SA1310 // Field names should not contain underscore
|
||||||
|
|
||||||
|
using System;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
namespace winsw.Native
|
namespace WinSW.Native
|
||||||
{
|
{
|
||||||
internal static class SecurityApis
|
internal static class SecurityApis
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -3,9 +3,9 @@ using System.ComponentModel;
|
||||||
using System.Security.AccessControl;
|
using System.Security.AccessControl;
|
||||||
using System.ServiceProcess;
|
using System.ServiceProcess;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using static winsw.Native.ServiceApis;
|
using static WinSW.Native.ServiceApis;
|
||||||
|
|
||||||
namespace winsw.Native
|
namespace WinSW.Native
|
||||||
{
|
{
|
||||||
public enum SC_ACTION_TYPE
|
public enum SC_ACTION_TYPE
|
||||||
{
|
{
|
||||||
|
|
@ -198,7 +198,8 @@ namespace winsw.Native
|
||||||
{
|
{
|
||||||
fixed (SC_ACTION* actionsPtr = actions)
|
fixed (SC_ACTION* actionsPtr = actions)
|
||||||
{
|
{
|
||||||
if (!ChangeServiceConfig2(this.handle,
|
if (!ChangeServiceConfig2(
|
||||||
|
this.handle,
|
||||||
ServiceConfigInfoLevels.FAILURE_ACTIONS,
|
ServiceConfigInfoLevels.FAILURE_ACTIONS,
|
||||||
new SERVICE_FAILURE_ACTIONS
|
new SERVICE_FAILURE_ACTIONS
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ using System.Security.AccessControl;
|
||||||
using System.ServiceProcess;
|
using System.ServiceProcess;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
|
||||||
namespace winsw.Native
|
namespace WinSW.Native
|
||||||
{
|
{
|
||||||
internal static class ServiceApis
|
internal static class ServiceApis
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
namespace winsw.Native
|
namespace WinSW.Native
|
||||||
{
|
{
|
||||||
internal static class Throw
|
internal static class Throw
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -3,10 +3,14 @@ namespace System.Diagnostics.CodeAnalysis
|
||||||
{
|
{
|
||||||
/// <summary>Specifies that null is allowed as an input even if the corresponding type disallows it.</summary>
|
/// <summary>Specifies that null is allowed as an input even if the corresponding type disallows it.</summary>
|
||||||
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property, Inherited = false)]
|
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property, Inherited = false)]
|
||||||
internal sealed class AllowNullAttribute : Attribute { }
|
internal sealed class AllowNullAttribute : Attribute
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>Specifies that an output may be null even if the corresponding type disallows it.</summary>
|
/// <summary>Specifies that an output may be null even if the corresponding type disallows it.</summary>
|
||||||
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.ReturnValue, Inherited = false)]
|
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.ReturnValue, Inherited = false)]
|
||||||
internal sealed class MaybeNullAttribute : Attribute { }
|
internal sealed class MaybeNullAttribute : Attribute
|
||||||
|
{
|
||||||
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -1,30 +1,26 @@
|
||||||
using System;
|
using System;
|
||||||
|
|
||||||
// ReSharper disable InconsistentNaming
|
namespace WinSW
|
||||||
|
|
||||||
namespace winsw
|
|
||||||
{
|
{
|
||||||
/**
|
// This is largely borrowed from the logback Rolling Calendar.
|
||||||
* This is largely borrowed from the logback Rolling Calendar.
|
|
||||||
**/
|
|
||||||
public class PeriodicRollingCalendar
|
public class PeriodicRollingCalendar
|
||||||
{
|
{
|
||||||
private readonly string _format;
|
private readonly string format;
|
||||||
private readonly long _period;
|
private readonly long period;
|
||||||
private DateTime _currentRoll;
|
private DateTime currentRoll;
|
||||||
private DateTime _nextRoll;
|
private DateTime nextRoll;
|
||||||
|
|
||||||
public PeriodicRollingCalendar(string format, long period)
|
public PeriodicRollingCalendar(string format, long period)
|
||||||
{
|
{
|
||||||
_format = format;
|
this.format = format;
|
||||||
_period = period;
|
this.period = period;
|
||||||
_currentRoll = DateTime.Now;
|
this.currentRoll = DateTime.Now;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void init()
|
public void Init()
|
||||||
{
|
{
|
||||||
periodicityType = determinePeriodicityType();
|
this.Periodicity = this.DeterminePeriodicityType();
|
||||||
_nextRoll = nextTriggeringTime(_currentRoll, _period);
|
this.nextRoll = this.NextTriggeringTime(this.currentRoll, this.period);
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum PeriodicityType
|
public enum PeriodicityType
|
||||||
|
|
@ -37,23 +33,23 @@ namespace winsw
|
||||||
TOP_OF_DAY
|
TOP_OF_DAY
|
||||||
}
|
}
|
||||||
|
|
||||||
private static readonly PeriodicityType[] VALID_ORDERED_LIST =
|
private static readonly PeriodicityType[] ValidOrderedList =
|
||||||
{
|
{
|
||||||
PeriodicityType.TOP_OF_MILLISECOND, PeriodicityType.TOP_OF_SECOND, PeriodicityType.TOP_OF_MINUTE, PeriodicityType.TOP_OF_HOUR, PeriodicityType.TOP_OF_DAY
|
PeriodicityType.TOP_OF_MILLISECOND, PeriodicityType.TOP_OF_SECOND, PeriodicityType.TOP_OF_MINUTE, PeriodicityType.TOP_OF_HOUR, PeriodicityType.TOP_OF_DAY
|
||||||
};
|
};
|
||||||
|
|
||||||
private PeriodicityType determinePeriodicityType()
|
private PeriodicityType DeterminePeriodicityType()
|
||||||
{
|
{
|
||||||
PeriodicRollingCalendar periodicRollingCalendar = new PeriodicRollingCalendar(_format, _period);
|
PeriodicRollingCalendar periodicRollingCalendar = new PeriodicRollingCalendar(this.format, this.period);
|
||||||
DateTime epoch = new DateTime(1970, 1, 1);
|
DateTime epoch = new DateTime(1970, 1, 1);
|
||||||
|
|
||||||
foreach (PeriodicityType i in VALID_ORDERED_LIST)
|
foreach (PeriodicityType i in ValidOrderedList)
|
||||||
{
|
{
|
||||||
string r0 = epoch.ToString(_format);
|
string r0 = epoch.ToString(this.format);
|
||||||
periodicRollingCalendar.periodicityType = i;
|
periodicRollingCalendar.Periodicity = i;
|
||||||
|
|
||||||
DateTime next = periodicRollingCalendar.nextTriggeringTime(epoch, 1);
|
DateTime next = periodicRollingCalendar.NextTriggeringTime(epoch, 1);
|
||||||
string r1 = next.ToString(_format);
|
string r1 = next.ToString(this.format);
|
||||||
|
|
||||||
if (r0 != r1)
|
if (r0 != r1)
|
||||||
{
|
{
|
||||||
|
|
@ -64,7 +60,7 @@ namespace winsw
|
||||||
return PeriodicityType.ERRONEOUS;
|
return PeriodicityType.ERRONEOUS;
|
||||||
}
|
}
|
||||||
|
|
||||||
private DateTime nextTriggeringTime(DateTime input, long increment) => periodicityType switch
|
private DateTime NextTriggeringTime(DateTime input, long increment) => this.Periodicity switch
|
||||||
{
|
{
|
||||||
PeriodicityType.TOP_OF_MILLISECOND =>
|
PeriodicityType.TOP_OF_MILLISECOND =>
|
||||||
new DateTime(input.Year, input.Month, input.Day, input.Hour, input.Minute, input.Second, input.Millisecond)
|
new DateTime(input.Year, input.Month, input.Day, input.Hour, input.Minute, input.Second, input.Millisecond)
|
||||||
|
|
@ -86,20 +82,20 @@ namespace winsw
|
||||||
new DateTime(input.Year, input.Month, input.Day)
|
new DateTime(input.Year, input.Month, input.Day)
|
||||||
.AddDays(increment),
|
.AddDays(increment),
|
||||||
|
|
||||||
_ => throw new Exception("invalid periodicity type: " + periodicityType),
|
_ => throw new Exception("invalid periodicity type: " + this.Periodicity),
|
||||||
};
|
};
|
||||||
|
|
||||||
public PeriodicityType periodicityType { get; set; }
|
public PeriodicityType Periodicity { get; set; }
|
||||||
|
|
||||||
public bool shouldRoll
|
public bool ShouldRoll
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
DateTime now = DateTime.Now;
|
DateTime now = DateTime.Now;
|
||||||
if (now > _nextRoll)
|
if (now > this.nextRoll)
|
||||||
{
|
{
|
||||||
_currentRoll = now;
|
this.currentRoll = now;
|
||||||
_nextRoll = nextTriggeringTime(now, _period);
|
this.nextRoll = this.NextTriggeringTime(now, this.period);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -107,6 +103,6 @@ namespace winsw
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public string format => _currentRoll.ToString(_format);
|
public string Format => this.currentRoll.ToString(this.format);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,18 +5,17 @@ using System.IO;
|
||||||
using System.ServiceProcess;
|
using System.ServiceProcess;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Xml;
|
using System.Xml;
|
||||||
using winsw.Configuration;
|
using WinSW.Configuration;
|
||||||
using winsw.Native;
|
using WinSW.Native;
|
||||||
using winsw.Util;
|
using WinSW.Util;
|
||||||
|
|
||||||
namespace winsw
|
namespace WinSW
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// In-memory representation of the configuration file.
|
/// In-memory representation of the configuration file.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class ServiceDescriptor : IWinSWConfiguration
|
public class ServiceDescriptor : IWinSWConfiguration
|
||||||
{
|
{
|
||||||
// ReSharper disable once InconsistentNaming
|
|
||||||
protected readonly XmlDocument dom = new XmlDocument();
|
protected readonly XmlDocument dom = new XmlDocument();
|
||||||
|
|
||||||
private readonly Dictionary<string, string> environmentVariables;
|
private readonly Dictionary<string, string> environmentVariables;
|
||||||
|
|
@ -45,29 +44,35 @@ namespace winsw
|
||||||
// find co-located configuration xml. We search up to the ancestor directories to simplify debugging,
|
// find co-located configuration xml. We search up to the ancestor directories to simplify debugging,
|
||||||
// as well as trimming off ".vshost" suffix (which is used during debugging)
|
// as well as trimming off ".vshost" suffix (which is used during debugging)
|
||||||
// Get the first parent to go into the recursive loop
|
// Get the first parent to go into the recursive loop
|
||||||
string p = ExecutablePath;
|
string p = this.ExecutablePath;
|
||||||
string baseName = Path.GetFileNameWithoutExtension(p);
|
string baseName = Path.GetFileNameWithoutExtension(p);
|
||||||
if (baseName.EndsWith(".vshost"))
|
if (baseName.EndsWith(".vshost"))
|
||||||
|
{
|
||||||
baseName = baseName.Substring(0, baseName.Length - 7);
|
baseName = baseName.Substring(0, baseName.Length - 7);
|
||||||
|
}
|
||||||
|
|
||||||
DirectoryInfo d = new DirectoryInfo(Path.GetDirectoryName(p));
|
DirectoryInfo d = new DirectoryInfo(Path.GetDirectoryName(p));
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
if (File.Exists(Path.Combine(d.FullName, baseName + ".xml")))
|
if (File.Exists(Path.Combine(d.FullName, baseName + ".xml")))
|
||||||
|
{
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
if (d.Parent is null)
|
if (d.Parent is null)
|
||||||
|
{
|
||||||
throw new FileNotFoundException("Unable to locate " + baseName + ".xml file within executable directory or any parents");
|
throw new FileNotFoundException("Unable to locate " + baseName + ".xml file within executable directory or any parents");
|
||||||
|
}
|
||||||
|
|
||||||
d = d.Parent;
|
d = d.Parent;
|
||||||
}
|
}
|
||||||
|
|
||||||
BaseName = baseName;
|
this.BaseName = baseName;
|
||||||
BasePath = Path.Combine(d.FullName, BaseName);
|
this.BasePath = Path.Combine(d.FullName, this.BaseName);
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
dom.Load(BasePath + ".xml");
|
this.dom.Load(this.BasePath + ".xml");
|
||||||
}
|
}
|
||||||
catch (XmlException e)
|
catch (XmlException e)
|
||||||
{
|
{
|
||||||
|
|
@ -78,13 +83,13 @@ namespace winsw
|
||||||
Environment.SetEnvironmentVariable("BASE", d.FullName);
|
Environment.SetEnvironmentVariable("BASE", d.FullName);
|
||||||
|
|
||||||
// ditto for ID
|
// ditto for ID
|
||||||
Environment.SetEnvironmentVariable("SERVICE_ID", Id);
|
Environment.SetEnvironmentVariable("SERVICE_ID", this.Id);
|
||||||
|
|
||||||
// New name
|
// New name
|
||||||
Environment.SetEnvironmentVariable(WinSWSystem.ENVVAR_NAME_EXECUTABLE_PATH, ExecutablePath);
|
Environment.SetEnvironmentVariable(WinSWSystem.EnvVarNameExecutablePath, this.ExecutablePath);
|
||||||
|
|
||||||
// Also inject system environment variables
|
// Also inject system environment variables
|
||||||
Environment.SetEnvironmentVariable(WinSWSystem.ENVVAR_NAME_SERVICE_ID, Id);
|
Environment.SetEnvironmentVariable(WinSWSystem.EnvVarNameServiceId, this.Id);
|
||||||
|
|
||||||
this.environmentVariables = this.LoadEnvironmentVariables();
|
this.environmentVariables = this.LoadEnvironmentVariables();
|
||||||
}
|
}
|
||||||
|
|
@ -101,8 +106,7 @@ namespace winsw
|
||||||
this.environmentVariables = this.LoadEnvironmentVariables();
|
this.environmentVariables = this.LoadEnvironmentVariables();
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReSharper disable once InconsistentNaming
|
public static ServiceDescriptor FromXml(string xml)
|
||||||
public static ServiceDescriptor FromXML(string xml)
|
|
||||||
{
|
{
|
||||||
var dom = new XmlDocument();
|
var dom = new XmlDocument();
|
||||||
dom.LoadXml(xml);
|
dom.LoadXml(xml);
|
||||||
|
|
@ -111,21 +115,23 @@ namespace winsw
|
||||||
|
|
||||||
private string SingleElement(string tagName)
|
private string SingleElement(string tagName)
|
||||||
{
|
{
|
||||||
return SingleElement(tagName, false)!;
|
return this.SingleElement(tagName, false)!;
|
||||||
}
|
}
|
||||||
|
|
||||||
private string? SingleElement(string tagName, bool optional)
|
private string? SingleElement(string tagName, bool optional)
|
||||||
{
|
{
|
||||||
XmlNode? n = dom.SelectSingleNode("//" + tagName);
|
XmlNode? n = this.dom.SelectSingleNode("//" + tagName);
|
||||||
if (n is null && !optional)
|
if (n is null && !optional)
|
||||||
|
{
|
||||||
throw new InvalidDataException("<" + tagName + "> is missing in configuration XML");
|
throw new InvalidDataException("<" + tagName + "> is missing in configuration XML");
|
||||||
|
}
|
||||||
|
|
||||||
return n is null ? null : Environment.ExpandEnvironmentVariables(n.InnerText);
|
return n is null ? null : Environment.ExpandEnvironmentVariables(n.InnerText);
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool SingleBoolElement(string tagName, bool defaultValue)
|
private bool SingleBoolElement(string tagName, bool defaultValue)
|
||||||
{
|
{
|
||||||
XmlNode? e = dom.SelectSingleNode("//" + tagName);
|
XmlNode? e = this.dom.SelectSingleNode("//" + tagName);
|
||||||
|
|
||||||
return e is null ? defaultValue : bool.Parse(e.InnerText);
|
return e is null ? defaultValue : bool.Parse(e.InnerText);
|
||||||
}
|
}
|
||||||
|
|
@ -139,8 +145,8 @@ namespace winsw
|
||||||
|
|
||||||
private TimeSpan SingleTimeSpanElement(XmlNode parent, string tagName, TimeSpan defaultValue)
|
private TimeSpan SingleTimeSpanElement(XmlNode parent, string tagName, TimeSpan defaultValue)
|
||||||
{
|
{
|
||||||
string? value = SingleElement(tagName, true);
|
string? value = this.SingleElement(tagName, true);
|
||||||
return value is null ? defaultValue : ParseTimeSpan(value);
|
return value is null ? defaultValue : this.ParseTimeSpan(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
private TimeSpan ParseTimeSpan(string v)
|
private TimeSpan ParseTimeSpan(string v)
|
||||||
|
|
@ -175,14 +181,14 @@ namespace winsw
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Path to the executable.
|
/// Path to the executable.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string Executable => SingleElement("executable");
|
public string Executable => this.SingleElement("executable");
|
||||||
|
|
||||||
public bool HideWindow => SingleBoolElement("hidewindow", Defaults.HideWindow);
|
public bool HideWindow => this.SingleBoolElement("hidewindow", Defaults.HideWindow);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Optionally specify a different Path to an executable to shutdown the service.
|
/// Optionally specify a different Path to an executable to shutdown the service.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string? StopExecutable => SingleElement("stopexecutable", true);
|
public string? StopExecutable => this.SingleElement("stopexecutable", true);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// <c>arguments</c> or multiple optional <c>argument</c> elements which overrule the arguments element.
|
/// <c>arguments</c> or multiple optional <c>argument</c> elements which overrule the arguments element.
|
||||||
|
|
@ -191,14 +197,14 @@ namespace winsw
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
string? arguments = AppendTags("argument", null);
|
string? arguments = this.AppendTags("argument", null);
|
||||||
|
|
||||||
if (!(arguments is null))
|
if (!(arguments is null))
|
||||||
{
|
{
|
||||||
return arguments;
|
return arguments;
|
||||||
}
|
}
|
||||||
|
|
||||||
XmlNode? argumentsNode = dom.SelectSingleNode("//arguments");
|
XmlNode? argumentsNode = this.dom.SelectSingleNode("//arguments");
|
||||||
|
|
||||||
return argumentsNode is null ? Defaults.Arguments : Environment.ExpandEnvironmentVariables(argumentsNode.InnerText);
|
return argumentsNode is null ? Defaults.Arguments : Environment.ExpandEnvironmentVariables(argumentsNode.InnerText);
|
||||||
}
|
}
|
||||||
|
|
@ -211,14 +217,14 @@ namespace winsw
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
string? startArguments = AppendTags("startargument", null);
|
string? startArguments = this.AppendTags("startargument", null);
|
||||||
|
|
||||||
if (!(startArguments is null))
|
if (!(startArguments is null))
|
||||||
{
|
{
|
||||||
return startArguments;
|
return startArguments;
|
||||||
}
|
}
|
||||||
|
|
||||||
XmlNode? startArgumentsNode = dom.SelectSingleNode("//startarguments");
|
XmlNode? startArgumentsNode = this.dom.SelectSingleNode("//startarguments");
|
||||||
|
|
||||||
return startArgumentsNode is null ? null : Environment.ExpandEnvironmentVariables(startArgumentsNode.InnerText);
|
return startArgumentsNode is null ? null : Environment.ExpandEnvironmentVariables(startArgumentsNode.InnerText);
|
||||||
}
|
}
|
||||||
|
|
@ -231,14 +237,14 @@ namespace winsw
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
string? stopArguments = AppendTags("stopargument", null);
|
string? stopArguments = this.AppendTags("stopargument", null);
|
||||||
|
|
||||||
if (!(stopArguments is null))
|
if (!(stopArguments is null))
|
||||||
{
|
{
|
||||||
return stopArguments;
|
return stopArguments;
|
||||||
}
|
}
|
||||||
|
|
||||||
XmlNode? stopArgumentsNode = dom.SelectSingleNode("//stoparguments");
|
XmlNode? stopArgumentsNode = this.dom.SelectSingleNode("//stoparguments");
|
||||||
|
|
||||||
return stopArgumentsNode is null ? null : Environment.ExpandEnvironmentVariables(stopArgumentsNode.InnerText);
|
return stopArgumentsNode is null ? null : Environment.ExpandEnvironmentVariables(stopArgumentsNode.InnerText);
|
||||||
}
|
}
|
||||||
|
|
@ -248,7 +254,7 @@ namespace winsw
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
var wd = SingleElement("workingdirectory", true);
|
var wd = this.SingleElement("workingdirectory", true);
|
||||||
return string.IsNullOrEmpty(wd) ? Defaults.WorkingDirectory : wd!;
|
return string.IsNullOrEmpty(wd) ? Defaults.WorkingDirectory : wd!;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -257,7 +263,7 @@ namespace winsw
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
XmlNode? argumentNode = ExtensionsConfiguration;
|
XmlNode? argumentNode = this.ExtensionsConfiguration;
|
||||||
XmlNodeList? extensions = argumentNode?.SelectNodes("extension");
|
XmlNodeList? extensions = argumentNode?.SelectNodes("extension");
|
||||||
if (extensions is null)
|
if (extensions is null)
|
||||||
{
|
{
|
||||||
|
|
@ -274,7 +280,7 @@ namespace winsw
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public XmlNode? ExtensionsConfiguration => dom.SelectSingleNode("//extensions");
|
public XmlNode? ExtensionsConfiguration => this.dom.SelectSingleNode("//extensions");
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Combines the contents of all the elements of the given name,
|
/// Combines the contents of all the elements of the given name,
|
||||||
|
|
@ -282,7 +288,7 @@ namespace winsw
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private string? AppendTags(string tagName, string? defaultValue = null)
|
private string? AppendTags(string tagName, string? defaultValue = null)
|
||||||
{
|
{
|
||||||
XmlNode? argumentNode = dom.SelectSingleNode("//" + tagName);
|
XmlNode? argumentNode = this.dom.SelectSingleNode("//" + tagName);
|
||||||
if (argumentNode is null)
|
if (argumentNode is null)
|
||||||
{
|
{
|
||||||
return defaultValue;
|
return defaultValue;
|
||||||
|
|
@ -290,7 +296,7 @@ namespace winsw
|
||||||
|
|
||||||
StringBuilder arguments = new StringBuilder();
|
StringBuilder arguments = new StringBuilder();
|
||||||
|
|
||||||
XmlNodeList argumentNodeList = dom.SelectNodes("//" + tagName);
|
XmlNodeList argumentNodeList = this.dom.SelectNodes("//" + tagName);
|
||||||
for (int i = 0; i < argumentNodeList.Count; i++)
|
for (int i = 0; i < argumentNodeList.Count; i++)
|
||||||
{
|
{
|
||||||
arguments.Append(' ');
|
arguments.Append(' ');
|
||||||
|
|
@ -325,7 +331,7 @@ namespace winsw
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
XmlNode? loggingNode = dom.SelectSingleNode("//logpath");
|
XmlNode? loggingNode = this.dom.SelectSingleNode("//logpath");
|
||||||
|
|
||||||
return loggingNode is null
|
return loggingNode is null
|
||||||
? Defaults.LogDirectory
|
? Defaults.LogDirectory
|
||||||
|
|
@ -340,7 +346,7 @@ namespace winsw
|
||||||
string? mode = null;
|
string? mode = null;
|
||||||
|
|
||||||
// first, backward compatibility with older configuration
|
// first, backward compatibility with older configuration
|
||||||
XmlElement? e = (XmlElement?)dom.SelectSingleNode("//logmode");
|
XmlElement? e = (XmlElement?)this.dom.SelectSingleNode("//logmode");
|
||||||
if (e != null)
|
if (e != null)
|
||||||
{
|
{
|
||||||
mode = e.InnerText;
|
mode = e.InnerText;
|
||||||
|
|
@ -348,9 +354,11 @@ namespace winsw
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// this is more modern way, to support nested elements as configuration
|
// this is more modern way, to support nested elements as configuration
|
||||||
e = (XmlElement?)dom.SelectSingleNode("//log");
|
e = (XmlElement?)this.dom.SelectSingleNode("//log");
|
||||||
if (e != null)
|
if (e != null)
|
||||||
|
{
|
||||||
mode = e.GetAttribute("mode");
|
mode = e.GetAttribute("mode");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return mode ?? Defaults.LogMode;
|
return mode ?? Defaults.LogMode;
|
||||||
|
|
@ -361,21 +369,21 @@ namespace winsw
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
XmlNode? loggingName = dom.SelectSingleNode("//logname");
|
XmlNode? loggingName = this.dom.SelectSingleNode("//logname");
|
||||||
|
|
||||||
return loggingName is null ? BaseName : Environment.ExpandEnvironmentVariables(loggingName.InnerText);
|
return loggingName is null ? this.BaseName : Environment.ExpandEnvironmentVariables(loggingName.InnerText);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool OutFileDisabled => SingleBoolElement("outfiledisabled", Defaults.OutFileDisabled);
|
public bool OutFileDisabled => this.SingleBoolElement("outfiledisabled", Defaults.OutFileDisabled);
|
||||||
|
|
||||||
public bool ErrFileDisabled => SingleBoolElement("errfiledisabled", Defaults.ErrFileDisabled);
|
public bool ErrFileDisabled => this.SingleBoolElement("errfiledisabled", Defaults.ErrFileDisabled);
|
||||||
|
|
||||||
public string OutFilePattern
|
public string OutFilePattern
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
XmlNode? loggingName = dom.SelectSingleNode("//outfilepattern");
|
XmlNode? loggingName = this.dom.SelectSingleNode("//outfilepattern");
|
||||||
|
|
||||||
return loggingName is null ? Defaults.OutFilePattern : Environment.ExpandEnvironmentVariables(loggingName.InnerText);
|
return loggingName is null ? Defaults.OutFilePattern : Environment.ExpandEnvironmentVariables(loggingName.InnerText);
|
||||||
}
|
}
|
||||||
|
|
@ -385,7 +393,7 @@ namespace winsw
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
XmlNode? loggingName = dom.SelectSingleNode("//errfilepattern");
|
XmlNode? loggingName = this.dom.SelectSingleNode("//errfilepattern");
|
||||||
|
|
||||||
return loggingName is null ? Defaults.ErrFilePattern : Environment.ExpandEnvironmentVariables(loggingName.InnerText);
|
return loggingName is null ? Defaults.ErrFilePattern : Environment.ExpandEnvironmentVariables(loggingName.InnerText);
|
||||||
}
|
}
|
||||||
|
|
@ -395,25 +403,25 @@ namespace winsw
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
XmlElement? e = (XmlElement?)dom.SelectSingleNode("//logmode");
|
XmlElement? e = (XmlElement?)this.dom.SelectSingleNode("//logmode");
|
||||||
|
|
||||||
// this is more modern way, to support nested elements as configuration
|
// this is more modern way, to support nested elements as configuration
|
||||||
e ??= (XmlElement?)dom.SelectSingleNode("//log")!; // WARNING: NRE
|
e ??= (XmlElement?)this.dom.SelectSingleNode("//log")!; // WARNING: NRE
|
||||||
|
|
||||||
int sizeThreshold;
|
int sizeThreshold;
|
||||||
switch (LogMode)
|
switch (this.LogMode)
|
||||||
{
|
{
|
||||||
case "rotate":
|
case "rotate":
|
||||||
return new SizeBasedRollingLogAppender(LogDirectory, LogName, OutFileDisabled, ErrFileDisabled, OutFilePattern, ErrFilePattern);
|
return new SizeBasedRollingLogAppender(this.LogDirectory, this.LogName, this.OutFileDisabled, this.ErrFileDisabled, this.OutFilePattern, this.ErrFilePattern);
|
||||||
|
|
||||||
case "none":
|
case "none":
|
||||||
return new IgnoreLogAppender();
|
return new IgnoreLogAppender();
|
||||||
|
|
||||||
case "reset":
|
case "reset":
|
||||||
return new ResetLogAppender(LogDirectory, LogName, OutFileDisabled, ErrFileDisabled, OutFilePattern, ErrFilePattern);
|
return new ResetLogAppender(this.LogDirectory, this.LogName, this.OutFileDisabled, this.ErrFileDisabled, this.OutFilePattern, this.ErrFilePattern);
|
||||||
|
|
||||||
case "roll":
|
case "roll":
|
||||||
return new RollingLogAppender(LogDirectory, LogName, OutFileDisabled, ErrFileDisabled, OutFilePattern, ErrFilePattern);
|
return new RollingLogAppender(this.LogDirectory, this.LogName, this.OutFileDisabled, this.ErrFileDisabled, this.OutFilePattern, this.ErrFilePattern);
|
||||||
|
|
||||||
case "roll-by-time":
|
case "roll-by-time":
|
||||||
XmlNode? patternNode = e.SelectSingleNode("pattern");
|
XmlNode? patternNode = e.SelectSingleNode("pattern");
|
||||||
|
|
@ -423,19 +431,19 @@ namespace winsw
|
||||||
}
|
}
|
||||||
|
|
||||||
var pattern = patternNode.InnerText;
|
var pattern = patternNode.InnerText;
|
||||||
int period = SingleIntElement(e, "period", 1);
|
int period = this.SingleIntElement(e, "period", 1);
|
||||||
return new TimeBasedRollingLogAppender(LogDirectory, LogName, OutFileDisabled, ErrFileDisabled, OutFilePattern, ErrFilePattern, pattern, period);
|
return new TimeBasedRollingLogAppender(this.LogDirectory, this.LogName, this.OutFileDisabled, this.ErrFileDisabled, this.OutFilePattern, this.ErrFilePattern, pattern, period);
|
||||||
|
|
||||||
case "roll-by-size":
|
case "roll-by-size":
|
||||||
sizeThreshold = SingleIntElement(e, "sizeThreshold", 10 * 1024) * SizeBasedRollingLogAppender.BYTES_PER_KB;
|
sizeThreshold = this.SingleIntElement(e, "sizeThreshold", 10 * 1024) * SizeBasedRollingLogAppender.BytesPerKB;
|
||||||
int keepFiles = SingleIntElement(e, "keepFiles", SizeBasedRollingLogAppender.DEFAULT_FILES_TO_KEEP);
|
int keepFiles = this.SingleIntElement(e, "keepFiles", SizeBasedRollingLogAppender.DefaultFilesToKeep);
|
||||||
return new SizeBasedRollingLogAppender(LogDirectory, LogName, OutFileDisabled, ErrFileDisabled, OutFilePattern, ErrFilePattern, sizeThreshold, keepFiles);
|
return new SizeBasedRollingLogAppender(this.LogDirectory, this.LogName, this.OutFileDisabled, this.ErrFileDisabled, this.OutFilePattern, this.ErrFilePattern, sizeThreshold, keepFiles);
|
||||||
|
|
||||||
case "append":
|
case "append":
|
||||||
return new DefaultLogAppender(LogDirectory, LogName, OutFileDisabled, ErrFileDisabled, OutFilePattern, ErrFilePattern);
|
return new DefaultLogAppender(this.LogDirectory, this.LogName, this.OutFileDisabled, this.ErrFileDisabled, this.OutFilePattern, this.ErrFilePattern);
|
||||||
|
|
||||||
case "roll-by-size-time":
|
case "roll-by-size-time":
|
||||||
sizeThreshold = SingleIntElement(e, "sizeThreshold", 10 * 1024) * RollingSizeTimeLogAppender.BYTES_PER_KB;
|
sizeThreshold = this.SingleIntElement(e, "sizeThreshold", 10 * 1024) * RollingSizeTimeLogAppender.BytesPerKB;
|
||||||
XmlNode? filePatternNode = e.SelectSingleNode("pattern");
|
XmlNode? filePatternNode = e.SelectSingleNode("pattern");
|
||||||
if (filePatternNode is null)
|
if (filePatternNode is null)
|
||||||
{
|
{
|
||||||
|
|
@ -448,7 +456,9 @@ namespace winsw
|
||||||
{
|
{
|
||||||
// validate it
|
// validate it
|
||||||
if (!TimeSpan.TryParse(autoRollAtTimeNode.InnerText, out TimeSpan autoRollAtTimeValue))
|
if (!TimeSpan.TryParse(autoRollAtTimeNode.InnerText, out TimeSpan autoRollAtTimeValue))
|
||||||
|
{
|
||||||
throw new InvalidDataException("Roll-Size-Time Based rolling policy is specified but autoRollAtTime does not match the TimeSpan format HH:mm:ss found in configuration XML.");
|
throw new InvalidDataException("Roll-Size-Time Based rolling policy is specified but autoRollAtTime does not match the TimeSpan format HH:mm:ss found in configuration XML.");
|
||||||
|
}
|
||||||
|
|
||||||
autoRollAtTime = autoRollAtTimeValue;
|
autoRollAtTime = autoRollAtTimeValue;
|
||||||
}
|
}
|
||||||
|
|
@ -459,7 +469,9 @@ namespace winsw
|
||||||
{
|
{
|
||||||
// validate it
|
// validate it
|
||||||
if (!int.TryParse(zipolderthannumdaysNode.InnerText, out int zipolderthannumdaysValue))
|
if (!int.TryParse(zipolderthannumdaysNode.InnerText, 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.");
|
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;
|
zipolderthannumdays = zipolderthannumdaysValue;
|
||||||
}
|
}
|
||||||
|
|
@ -467,10 +479,10 @@ namespace winsw
|
||||||
XmlNode? zipdateformatNode = e.SelectSingleNode("zipDateFormat");
|
XmlNode? zipdateformatNode = e.SelectSingleNode("zipDateFormat");
|
||||||
string zipdateformat = zipdateformatNode is null ? "yyyyMM" : zipdateformatNode.InnerText;
|
string zipdateformat = zipdateformatNode is null ? "yyyyMM" : zipdateformatNode.InnerText;
|
||||||
|
|
||||||
return new RollingSizeTimeLogAppender(LogDirectory, LogName, OutFileDisabled, ErrFileDisabled, OutFilePattern, ErrFilePattern, sizeThreshold, filePatternNode.InnerText, autoRollAtTime, zipolderthannumdays, zipdateformat);
|
return new RollingSizeTimeLogAppender(this.LogDirectory, this.LogName, this.OutFileDisabled, this.ErrFileDisabled, this.OutFilePattern, this.ErrFilePattern, sizeThreshold, filePatternNode.InnerText, autoRollAtTime, zipolderthannumdays, zipdateformat);
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw new InvalidDataException("Undefined logging mode: " + LogMode);
|
throw new InvalidDataException("Undefined logging mode: " + this.LogMode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -482,7 +494,7 @@ namespace winsw
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
XmlNodeList? nodeList = dom.SelectNodes("//depend");
|
XmlNodeList? nodeList = this.dom.SelectNodes("//depend");
|
||||||
if (nodeList is null)
|
if (nodeList is null)
|
||||||
{
|
{
|
||||||
return Defaults.ServiceDependencies;
|
return Defaults.ServiceDependencies;
|
||||||
|
|
@ -498,11 +510,11 @@ namespace winsw
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public string Id => SingleElement("id");
|
public string Id => this.SingleElement("id");
|
||||||
|
|
||||||
public string Caption => SingleElement("name");
|
public string Caption => this.SingleElement("name");
|
||||||
|
|
||||||
public string Description => SingleElement("description");
|
public string Description => this.SingleElement("description");
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Start mode of the Service
|
/// Start mode of the Service
|
||||||
|
|
@ -511,9 +523,11 @@ namespace winsw
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
string? p = SingleElement("startmode", true);
|
string? p = this.SingleElement("startmode", true);
|
||||||
if (p is null)
|
if (p is null)
|
||||||
|
{
|
||||||
return Defaults.StartMode;
|
return Defaults.StartMode;
|
||||||
|
}
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
|
@ -536,32 +550,32 @@ namespace winsw
|
||||||
/// True if the service should be installed with the DelayedAutoStart flag.
|
/// True if the service should be installed with the DelayedAutoStart flag.
|
||||||
/// This setting will be applyed only during the install command and only when the Automatic start mode is configured.
|
/// This setting will be applyed only during the install command and only when the Automatic start mode is configured.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool DelayedAutoStart => dom.SelectSingleNode("//delayedAutoStart") != null;
|
public bool DelayedAutoStart => this.dom.SelectSingleNode("//delayedAutoStart") != null;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// True if the service should beep when finished on shutdown.
|
/// True if the service should beep when finished on shutdown.
|
||||||
/// This doesn't work on some OSes. See http://msdn.microsoft.com/en-us/library/ms679277%28VS.85%29.aspx
|
/// This doesn't work on some OSes. See http://msdn.microsoft.com/en-us/library/ms679277%28VS.85%29.aspx
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool BeepOnShutdown => dom.SelectSingleNode("//beeponshutdown") != null;
|
public bool BeepOnShutdown => this.dom.SelectSingleNode("//beeponshutdown") != null;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The estimated time required for a pending stop operation (default 15 secs).
|
/// The estimated time required for a pending stop operation (default 15 secs).
|
||||||
/// Before the specified amount of time has elapsed, the service should make its next call to the SetServiceStatus function
|
/// Before the specified amount of time has elapsed, the service should make its next call to the SetServiceStatus function
|
||||||
/// with either an incremented checkPoint value or a change in currentState. (see http://msdn.microsoft.com/en-us/library/ms685996.aspx)
|
/// with either an incremented checkPoint value or a change in currentState. (see http://msdn.microsoft.com/en-us/library/ms685996.aspx)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public TimeSpan WaitHint => SingleTimeSpanElement(dom, "waithint", Defaults.WaitHint);
|
public TimeSpan WaitHint => this.SingleTimeSpanElement(this.dom, "waithint", Defaults.WaitHint);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The time before the service should make its next call to the SetServiceStatus function
|
/// The time before the service should make its next call to the SetServiceStatus function
|
||||||
/// with an incremented checkPoint value (default 1 sec).
|
/// with an incremented checkPoint value (default 1 sec).
|
||||||
/// Do not wait longer than the wait hint. A good interval is one-tenth of the wait hint but not less than 1 second and not more than 10 seconds.
|
/// Do not wait longer than the wait hint. A good interval is one-tenth of the wait hint but not less than 1 second and not more than 10 seconds.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public TimeSpan SleepTime => SingleTimeSpanElement(dom, "sleeptime", Defaults.SleepTime);
|
public TimeSpan SleepTime => this.SingleTimeSpanElement(this.dom, "sleeptime", Defaults.SleepTime);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// True if the service can interact with the desktop.
|
/// True if the service can interact with the desktop.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool Interactive => dom.SelectSingleNode("//interactive") != null;
|
public bool Interactive => this.dom.SelectSingleNode("//interactive") != null;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Environment variable overrides
|
/// Environment variable overrides
|
||||||
|
|
@ -576,7 +590,7 @@ namespace winsw
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
XmlNodeList? nodeList = dom.SelectNodes("//download");
|
XmlNodeList? nodeList = this.dom.SelectNodes("//download");
|
||||||
if (nodeList is null)
|
if (nodeList is null)
|
||||||
{
|
{
|
||||||
return Defaults.Downloads;
|
return Defaults.Downloads;
|
||||||
|
|
@ -599,7 +613,7 @@ namespace winsw
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
XmlNodeList? childNodes = dom.SelectNodes("//onfailure");
|
XmlNodeList? childNodes = this.dom.SelectNodes("//onfailure");
|
||||||
if (childNodes is null)
|
if (childNodes is null)
|
||||||
{
|
{
|
||||||
return new SC_ACTION[0];
|
return new SC_ACTION[0];
|
||||||
|
|
@ -618,18 +632,18 @@ namespace winsw
|
||||||
_ => throw new Exception("Invalid failure action: " + action)
|
_ => throw new Exception("Invalid failure action: " + action)
|
||||||
};
|
};
|
||||||
XmlAttribute? delay = node.Attributes["delay"];
|
XmlAttribute? delay = node.Attributes["delay"];
|
||||||
result[i] = new SC_ACTION(type, delay != null ? ParseTimeSpan(delay.Value) : TimeSpan.Zero);
|
result[i] = new SC_ACTION(type, delay != null ? this.ParseTimeSpan(delay.Value) : TimeSpan.Zero);
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public TimeSpan ResetFailureAfter => SingleTimeSpanElement(dom, "resetfailure", Defaults.ResetFailureAfter);
|
public TimeSpan ResetFailureAfter => this.SingleTimeSpanElement(this.dom, "resetfailure", Defaults.ResetFailureAfter);
|
||||||
|
|
||||||
protected string? GetServiceAccountPart(string subNodeName)
|
protected string? GetServiceAccountPart(string subNodeName)
|
||||||
{
|
{
|
||||||
XmlNode? node = dom.SelectSingleNode("//serviceaccount");
|
XmlNode? node = this.dom.SelectSingleNode("//serviceaccount");
|
||||||
|
|
||||||
if (node != null)
|
if (node != null)
|
||||||
{
|
{
|
||||||
|
|
@ -643,13 +657,13 @@ namespace winsw
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public string? ServiceAccountPrompt => GetServiceAccountPart("prompt")?.ToLowerInvariant();
|
public string? ServiceAccountPrompt => this.GetServiceAccountPart("prompt")?.ToLowerInvariant();
|
||||||
|
|
||||||
protected string? AllowServiceLogon => GetServiceAccountPart("allowservicelogon");
|
protected string? AllowServiceLogon => this.GetServiceAccountPart("allowservicelogon");
|
||||||
|
|
||||||
public string? ServiceAccountPassword => GetServiceAccountPart("password");
|
public string? ServiceAccountPassword => this.GetServiceAccountPart("password");
|
||||||
|
|
||||||
public string? ServiceAccountUserName => GetServiceAccountPart("username");
|
public string? ServiceAccountUserName => this.GetServiceAccountPart("username");
|
||||||
|
|
||||||
public bool HasServiceAccount()
|
public bool HasServiceAccount()
|
||||||
{
|
{
|
||||||
|
|
@ -660,9 +674,9 @@ namespace winsw
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
if (AllowServiceLogon != null)
|
if (this.AllowServiceLogon != null)
|
||||||
{
|
{
|
||||||
if (bool.TryParse(AllowServiceLogon, out bool parsedvalue))
|
if (bool.TryParse(this.AllowServiceLogon, out bool parsedvalue))
|
||||||
{
|
{
|
||||||
return parsedvalue;
|
return parsedvalue;
|
||||||
}
|
}
|
||||||
|
|
@ -675,7 +689,7 @@ namespace winsw
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Time to wait for the service to gracefully shutdown the executable before we forcibly kill it
|
/// Time to wait for the service to gracefully shutdown the executable before we forcibly kill it
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public TimeSpan StopTimeout => SingleTimeSpanElement(dom, "stoptimeout", Defaults.StopTimeout);
|
public TimeSpan StopTimeout => this.SingleTimeSpanElement(this.dom, "stoptimeout", Defaults.StopTimeout);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Desired process priority or null if not specified.
|
/// Desired process priority or null if not specified.
|
||||||
|
|
@ -684,19 +698,21 @@ namespace winsw
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
string? p = SingleElement("priority", true);
|
string? p = this.SingleElement("priority", true);
|
||||||
if (p is null)
|
if (p is null)
|
||||||
|
{
|
||||||
return Defaults.Priority;
|
return Defaults.Priority;
|
||||||
|
}
|
||||||
|
|
||||||
return (ProcessPriorityClass)Enum.Parse(typeof(ProcessPriorityClass), p, true);
|
return (ProcessPriorityClass)Enum.Parse(typeof(ProcessPriorityClass), p, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public string? SecurityDescriptor => SingleElement("securityDescriptor", true);
|
public string? SecurityDescriptor => this.SingleElement("securityDescriptor", true);
|
||||||
|
|
||||||
private Dictionary<string, string> LoadEnvironmentVariables()
|
private Dictionary<string, string> LoadEnvironmentVariables()
|
||||||
{
|
{
|
||||||
XmlNodeList nodeList = dom.SelectNodes("//env");
|
XmlNodeList nodeList = this.dom.SelectNodes("//env");
|
||||||
Dictionary<string, string> environment = new Dictionary<string, string>(nodeList.Count);
|
Dictionary<string, string> environment = new Dictionary<string, string>(nodeList.Count);
|
||||||
for (int i = 0; i < nodeList.Count; i++)
|
for (int i = 0; i < nodeList.Count; i++)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ using System.IO;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
namespace winsw.Util
|
namespace WinSW.Util
|
||||||
{
|
{
|
||||||
public static class FileHelper
|
public static class FileHelper
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -4,9 +4,9 @@ using System.ComponentModel;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using log4net;
|
using log4net;
|
||||||
using static winsw.Native.ProcessApis;
|
using static WinSW.Native.ProcessApis;
|
||||||
|
|
||||||
namespace winsw.Util
|
namespace WinSW.Util
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Provides helper classes for Process Management
|
/// Provides helper classes for Process Management
|
||||||
|
|
@ -73,8 +73,6 @@ namespace winsw.Util
|
||||||
/// Stops the process.
|
/// Stops the process.
|
||||||
/// If the process cannot be stopped within the stop timeout, it gets killed
|
/// If the process cannot be stopped within the stop timeout, it gets killed
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="pid">PID of the process</param>
|
|
||||||
/// <param name="stopTimeout">Stop timeout</param>
|
|
||||||
public static void StopProcess(Process process, TimeSpan stopTimeout)
|
public static void StopProcess(Process process, TimeSpan stopTimeout)
|
||||||
{
|
{
|
||||||
Logger.Info("Stopping process " + process.Id);
|
Logger.Info("Stopping process " + process.Id);
|
||||||
|
|
@ -97,6 +95,7 @@ namespace winsw.Util
|
||||||
{
|
{
|
||||||
Logger.Warn("Process " + process.Id + " did not respond to Ctrl+C signal - Killing as fallback");
|
Logger.Warn("Process " + process.Id + " did not respond to Ctrl+C signal - Killing as fallback");
|
||||||
}
|
}
|
||||||
|
|
||||||
process.Kill();
|
process.Kill();
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
|
|
@ -118,8 +117,6 @@ namespace winsw.Util
|
||||||
/// Terminate process and its children.
|
/// Terminate process and its children.
|
||||||
/// By default the child processes get terminated first.
|
/// By default the child processes get terminated first.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="pid">Process PID</param>
|
|
||||||
/// <param name="stopTimeout">Stop timeout (for each process)</param>
|
|
||||||
public static void StopProcessTree(Process process, TimeSpan stopTimeout)
|
public static void StopProcessTree(Process process, TimeSpan stopTimeout)
|
||||||
{
|
{
|
||||||
StopProcess(process, stopTimeout);
|
StopProcess(process, stopTimeout);
|
||||||
|
|
@ -135,14 +132,14 @@ namespace winsw.Util
|
||||||
/// Once the process exits, the callback will be invoked.
|
/// Once the process exits, the callback will be invoked.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="processToStart">Process object to be used</param>
|
/// <param name="processToStart">Process object to be used</param>
|
||||||
/// <param name="arguments">Arguments to be passed</param>
|
|
||||||
/// <param name="executable">Executable, which should be invoked</param>
|
/// <param name="executable">Executable, which should be invoked</param>
|
||||||
|
/// <param name="arguments">Arguments to be passed</param>
|
||||||
/// <param name="envVars">Additional environment variables</param>
|
/// <param name="envVars">Additional environment variables</param>
|
||||||
/// <param name="workingDirectory">Working directory</param>
|
/// <param name="workingDirectory">Working directory</param>
|
||||||
/// <param name="priority">Priority</param>
|
/// <param name="priority">Priority</param>
|
||||||
/// <param name="callback">Completion callback. If null, the completion won't be monitored</param>
|
/// <param name="callback">Completion callback. If null, the completion won't be monitored</param>
|
||||||
/// <param name="logHandler">Log handler. If enabled, logs will be redirected to the process and then reported</param>
|
|
||||||
/// <param name="redirectStdin">Redirect standard input</param>
|
/// <param name="redirectStdin">Redirect standard input</param>
|
||||||
|
/// <param name="logHandler">Log handler. If enabled, logs will be redirected to the process and then reported</param>
|
||||||
public static void StartProcessAndCallbackForExit(
|
public static void StartProcessAndCallbackForExit(
|
||||||
Process processToStart,
|
Process processToStart,
|
||||||
string? executable = null,
|
string? executable = null,
|
||||||
|
|
@ -191,7 +188,7 @@ namespace winsw.Util
|
||||||
if (logHandler != null)
|
if (logHandler != null)
|
||||||
{
|
{
|
||||||
Logger.Debug("Forwarding logs of the process " + processToStart + " to " + logHandler);
|
Logger.Debug("Forwarding logs of the process " + processToStart + " to " + logHandler);
|
||||||
logHandler.log(processToStart.StandardOutput, processToStart.StandardError);
|
logHandler.Log(processToStart.StandardOutput, processToStart.StandardError);
|
||||||
}
|
}
|
||||||
|
|
||||||
// monitor the completion of the process
|
// monitor the completion of the process
|
||||||
|
|
|
||||||
|
|
@ -4,9 +4,9 @@ using System.ComponentModel;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using log4net;
|
using log4net;
|
||||||
using winsw.Native;
|
using WinSW.Native;
|
||||||
|
|
||||||
namespace winsw.Util
|
namespace WinSW.Util
|
||||||
{
|
{
|
||||||
internal static class SignalHelper
|
internal static class SignalHelper
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ using System.Diagnostics.CodeAnalysis;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Xml;
|
using System.Xml;
|
||||||
|
|
||||||
namespace winsw.Util
|
namespace WinSW.Util
|
||||||
{
|
{
|
||||||
public class XmlHelper
|
public class XmlHelper
|
||||||
{
|
{
|
||||||
|
|
@ -19,7 +19,9 @@ namespace winsw.Util
|
||||||
{
|
{
|
||||||
XmlNode? n = node.SelectSingleNode(tagName);
|
XmlNode? n = node.SelectSingleNode(tagName);
|
||||||
if (n is null && !optional)
|
if (n is null && !optional)
|
||||||
|
{
|
||||||
throw new InvalidDataException("<" + tagName + "> is missing in configuration XML");
|
throw new InvalidDataException("<" + tagName + "> is missing in configuration XML");
|
||||||
|
}
|
||||||
|
|
||||||
return n is null ? null : Environment.ExpandEnvironmentVariables(n.InnerText);
|
return n is null ? null : Environment.ExpandEnvironmentVariables(n.InnerText);
|
||||||
}
|
}
|
||||||
|
|
@ -36,7 +38,9 @@ namespace winsw.Util
|
||||||
{
|
{
|
||||||
XmlNode? n = node.SelectSingleNode(tagName);
|
XmlNode? n = node.SelectSingleNode(tagName);
|
||||||
if (n is null && !optional)
|
if (n is null && !optional)
|
||||||
|
{
|
||||||
throw new InvalidDataException("<" + tagName + "> is missing in configuration XML");
|
throw new InvalidDataException("<" + tagName + "> is missing in configuration XML");
|
||||||
|
}
|
||||||
|
|
||||||
return n;
|
return n;
|
||||||
}
|
}
|
||||||
|
|
@ -69,7 +73,9 @@ namespace winsw.Util
|
||||||
public static TAttributeType SingleAttribute<TAttributeType>(XmlElement node, string attributeName, [AllowNull] TAttributeType defaultValue)
|
public static TAttributeType SingleAttribute<TAttributeType>(XmlElement node, string attributeName, [AllowNull] TAttributeType defaultValue)
|
||||||
{
|
{
|
||||||
if (!node.HasAttribute(attributeName))
|
if (!node.HasAttribute(attributeName))
|
||||||
|
{
|
||||||
return defaultValue;
|
return defaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
string rawValue = node.GetAttribute(attributeName);
|
string rawValue = node.GetAttribute(attributeName);
|
||||||
string substitutedValue = Environment.ExpandEnvironmentVariables(rawValue);
|
string substitutedValue = Environment.ExpandEnvironmentVariables(rawValue);
|
||||||
|
|
@ -90,7 +96,9 @@ namespace winsw.Util
|
||||||
where TAttributeType : struct
|
where TAttributeType : struct
|
||||||
{
|
{
|
||||||
if (!node.HasAttribute(attributeName))
|
if (!node.HasAttribute(attributeName))
|
||||||
|
{
|
||||||
return defaultValue;
|
return defaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
string rawValue = node.GetAttribute(attributeName);
|
string rawValue = node.GetAttribute(attributeName);
|
||||||
string substitutedValue = Environment.ExpandEnvironmentVariables(rawValue);
|
string substitutedValue = Environment.ExpandEnvironmentVariables(rawValue);
|
||||||
|
|
@ -100,7 +108,8 @@ namespace winsw.Util
|
||||||
}
|
}
|
||||||
catch (ArgumentException ex)
|
catch (ArgumentException ex)
|
||||||
{
|
{
|
||||||
throw new InvalidDataException("Cannot parse <" + attributeName + "> Enum value from string '" + substitutedValue +
|
throw new InvalidDataException(
|
||||||
|
"Cannot parse <" + attributeName + "> Enum value from string '" + substitutedValue +
|
||||||
"'. Enum type: " + typeof(TAttributeType), ex);
|
"'. Enum type: " + typeof(TAttributeType), ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,10 @@
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="log4net" Version="2.0.8" />
|
<PackageReference Include="log4net" Version="2.0.8" />
|
||||||
|
<PackageReference Include="stylecop.analyzers" Version="1.2.0-beta.*">
|
||||||
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
|
<PrivateAssets>all</PrivateAssets>
|
||||||
|
</PackageReference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup Condition="'$(TargetFramework)' == 'netcoreapp3.1'">
|
<ItemGroup Condition="'$(TargetFramework)' == 'netcoreapp3.1'">
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,17 @@
|
||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace winsw
|
namespace WinSW
|
||||||
{
|
{
|
||||||
public class WinSWException : Exception
|
public class WinSWException : Exception
|
||||||
{
|
{
|
||||||
public WinSWException(string message)
|
public WinSWException(string message)
|
||||||
: base(message)
|
: base(message)
|
||||||
{ }
|
{
|
||||||
|
}
|
||||||
|
|
||||||
public WinSWException(string message, Exception innerException)
|
public WinSWException(string message, Exception innerException)
|
||||||
: base(message, innerException)
|
: base(message, innerException)
|
||||||
{ }
|
{
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
namespace winsw
|
namespace WinSW
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Class, which contains generic information about WinSW runtime.
|
/// Class, which contains generic information about WinSW runtime.
|
||||||
|
|
@ -9,17 +9,17 @@
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Prefix for all environment variables being injected for WinSW
|
/// Prefix for all environment variables being injected for WinSW
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static readonly string SYSTEM_EVNVVAR_PREFIX = "WINSW_";
|
public static readonly string SystemEnvVarPrefix = "WINSW_";
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Variable, which points to the service ID.
|
/// Variable, which points to the service ID.
|
||||||
/// It may be used to determine runaway processes.
|
/// It may be used to determine runaway processes.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static string ENVVAR_NAME_SERVICE_ID => SYSTEM_EVNVVAR_PREFIX + "SERVICE_ID";
|
public static string EnvVarNameServiceId => SystemEnvVarPrefix + "SERVICE_ID";
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Variable, which specifies path to the executable being launched by WinSW.
|
/// Variable, which specifies path to the executable being launched by WinSW.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static string ENVVAR_NAME_EXECUTABLE_PATH => SYSTEM_EVNVVAR_PREFIX + "EXECUTABLE";
|
public static string EnvVarNameExecutablePath => SystemEnvVarPrefix + "EXECUTABLE";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
namespace winsw.Plugins.RunawayProcessKiller
|
namespace WinSW.Plugins.RunawayProcessKiller
|
||||||
{
|
{
|
||||||
public partial class RunawayProcessKillerExtension
|
public partial class RunawayProcessKillerExtension
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,14 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Specialized;
|
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Xml;
|
using System.Xml;
|
||||||
using log4net;
|
using log4net;
|
||||||
using winsw.Extensions;
|
using WinSW.Extensions;
|
||||||
using winsw.Util;
|
using WinSW.Util;
|
||||||
using static winsw.Plugins.RunawayProcessKiller.RunawayProcessKillerExtension.NativeMethods;
|
using static WinSW.Plugins.RunawayProcessKiller.RunawayProcessKillerExtension.NativeMethods;
|
||||||
|
|
||||||
namespace winsw.Plugins.RunawayProcessKiller
|
namespace WinSW.Plugins.RunawayProcessKiller
|
||||||
{
|
{
|
||||||
public partial class RunawayProcessKillerExtension : AbstractWinSWExtension
|
public partial class RunawayProcessKillerExtension : AbstractWinSWExtension
|
||||||
{
|
{
|
||||||
|
|
@ -177,12 +176,13 @@ namespace winsw.Plugins.RunawayProcessKiller
|
||||||
{
|
{
|
||||||
// We expect the upper logic to process any errors
|
// We expect the upper logic to process any errors
|
||||||
// TODO: a better parser API for types would be useful
|
// TODO: a better parser API for types would be useful
|
||||||
Pidfile = XmlHelper.SingleElement(node, "pidfile", false)!;
|
this.Pidfile = XmlHelper.SingleElement(node, "pidfile", false)!;
|
||||||
StopTimeout = TimeSpan.FromMilliseconds(int.Parse(XmlHelper.SingleElement(node, "stopTimeout", false)!));
|
this.StopTimeout = TimeSpan.FromMilliseconds(int.Parse(XmlHelper.SingleElement(node, "stopTimeout", false)!));
|
||||||
ServiceId = descriptor.Id;
|
this.ServiceId = descriptor.Id;
|
||||||
|
|
||||||
// TODO: Consider making it documented
|
// TODO: Consider making it documented
|
||||||
var checkWinSWEnvironmentVariable = XmlHelper.SingleElement(node, "checkWinSWEnvironmentVariable", true);
|
var checkWinSWEnvironmentVariable = XmlHelper.SingleElement(node, "checkWinSWEnvironmentVariable", true);
|
||||||
CheckWinSWEnvironmentVariable = checkWinSWEnvironmentVariable is null ? true : bool.Parse(checkWinSWEnvironmentVariable);
|
this.CheckWinSWEnvironmentVariable = checkWinSWEnvironmentVariable is null ? true : bool.Parse(checkWinSWEnvironmentVariable);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -193,16 +193,16 @@ namespace winsw.Plugins.RunawayProcessKiller
|
||||||
{
|
{
|
||||||
// Read PID file from the disk
|
// Read PID file from the disk
|
||||||
int pid;
|
int pid;
|
||||||
if (File.Exists(Pidfile))
|
if (File.Exists(this.Pidfile))
|
||||||
{
|
{
|
||||||
string pidstring;
|
string pidstring;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
pidstring = File.ReadAllText(Pidfile);
|
pidstring = File.ReadAllText(this.Pidfile);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Logger.Error("Cannot read PID file from " + Pidfile, ex);
|
Logger.Error("Cannot read PID file from " + this.Pidfile, ex);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -212,13 +212,13 @@ namespace winsw.Plugins.RunawayProcessKiller
|
||||||
}
|
}
|
||||||
catch (FormatException e)
|
catch (FormatException e)
|
||||||
{
|
{
|
||||||
Logger.Error("Invalid PID file number in '" + Pidfile + "'. The runaway process won't be checked", e);
|
Logger.Error("Invalid PID file number in '" + this.Pidfile + "'. The runaway process won't be checked", e);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Logger.Warn("The requested PID file '" + Pidfile + "' does not exist. The runaway process won't be checked");
|
Logger.Warn("The requested PID file '" + this.Pidfile + "' does not exist. The runaway process won't be checked");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -236,9 +236,9 @@ namespace winsw.Plugins.RunawayProcessKiller
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure the process references the service
|
// Ensure the process references the service
|
||||||
string expectedEnvVarName = WinSWSystem.ENVVAR_NAME_SERVICE_ID;
|
string expectedEnvVarName = WinSWSystem.EnvVarNameServiceId;
|
||||||
string? affiliatedServiceId = ReadEnvironmentVariable(proc.Handle, expectedEnvVarName);
|
string? affiliatedServiceId = ReadEnvironmentVariable(proc.Handle, expectedEnvVarName);
|
||||||
if (affiliatedServiceId is null && CheckWinSWEnvironmentVariable)
|
if (affiliatedServiceId is null && this.CheckWinSWEnvironmentVariable)
|
||||||
{
|
{
|
||||||
Logger.Warn("The process " + pid + " has no " + expectedEnvVarName + " environment variable defined. "
|
Logger.Warn("The process " + pid + " has no " + expectedEnvVarName + " environment variable defined. "
|
||||||
+ "The process has not been started by WinSW, hence it won't be terminated.");
|
+ "The process has not been started by WinSW, hence it won't be terminated.");
|
||||||
|
|
@ -247,10 +247,10 @@ namespace winsw.Plugins.RunawayProcessKiller
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check the service ID value
|
// Check the service ID value
|
||||||
if (CheckWinSWEnvironmentVariable && !ServiceId.Equals(affiliatedServiceId))
|
if (this.CheckWinSWEnvironmentVariable && !this.ServiceId.Equals(affiliatedServiceId))
|
||||||
{
|
{
|
||||||
Logger.Warn("The process " + pid + " has been started by Windows service with ID='" + affiliatedServiceId + "'. "
|
Logger.Warn("The process " + pid + " has been started by Windows service with ID='" + affiliatedServiceId + "'. "
|
||||||
+ "It is another service (current service id is '" + ServiceId + "'), hence the process won't be terminated.");
|
+ "It is another service (current service id is '" + this.ServiceId + "'), hence the process won't be terminated.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -258,7 +258,7 @@ namespace winsw.Plugins.RunawayProcessKiller
|
||||||
StringBuilder bldr = new StringBuilder("Stopping the runaway process (pid=");
|
StringBuilder bldr = new StringBuilder("Stopping the runaway process (pid=");
|
||||||
bldr.Append(pid);
|
bldr.Append(pid);
|
||||||
bldr.Append(") and its children. Environment was ");
|
bldr.Append(") and its children. Environment was ");
|
||||||
if (!CheckWinSWEnvironmentVariable)
|
if (!this.CheckWinSWEnvironmentVariable)
|
||||||
{
|
{
|
||||||
bldr.Append("not ");
|
bldr.Append("not ");
|
||||||
}
|
}
|
||||||
|
|
@ -275,17 +275,16 @@ namespace winsw.Plugins.RunawayProcessKiller
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Records the started process PID for the future use in OnStart() after the restart.
|
/// Records the started process PID for the future use in OnStart() after the restart.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="process"></param>
|
|
||||||
public override void OnProcessStarted(Process process)
|
public override void OnProcessStarted(Process process)
|
||||||
{
|
{
|
||||||
Logger.Info("Recording PID of the started process:" + process.Id + ". PID file destination is " + Pidfile);
|
Logger.Info("Recording PID of the started process:" + process.Id + ". PID file destination is " + this.Pidfile);
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
File.WriteAllText(Pidfile, process.Id.ToString());
|
File.WriteAllText(this.Pidfile, process.Id.ToString());
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Logger.Error("Cannot update the PID file " + Pidfile, ex);
|
Logger.Error("Cannot update the PID file " + this.Pidfile, ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
namespace winsw.Plugins.SharedDirectoryMapper
|
namespace WinSW.Plugins.SharedDirectoryMapper
|
||||||
{
|
{
|
||||||
internal static class NativeMethods
|
internal static class NativeMethods
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -2,15 +2,15 @@
|
||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
using System.Xml;
|
using System.Xml;
|
||||||
using log4net;
|
using log4net;
|
||||||
using winsw.Extensions;
|
using WinSW.Extensions;
|
||||||
using winsw.Util;
|
using WinSW.Util;
|
||||||
using static winsw.Plugins.SharedDirectoryMapper.NativeMethods;
|
using static WinSW.Plugins.SharedDirectoryMapper.NativeMethods;
|
||||||
|
|
||||||
namespace winsw.Plugins.SharedDirectoryMapper
|
namespace WinSW.Plugins.SharedDirectoryMapper
|
||||||
{
|
{
|
||||||
public class SharedDirectoryMapper : AbstractWinSWExtension
|
public class SharedDirectoryMapper : AbstractWinSWExtension
|
||||||
{
|
{
|
||||||
private readonly List<SharedDirectoryMapperConfig> _entries = new List<SharedDirectoryMapperConfig>();
|
private readonly List<SharedDirectoryMapperConfig> entries = new List<SharedDirectoryMapperConfig>();
|
||||||
|
|
||||||
public override string DisplayName => "Shared Directory Mapper";
|
public override string DisplayName => "Shared Directory Mapper";
|
||||||
|
|
||||||
|
|
@ -23,7 +23,7 @@ namespace winsw.Plugins.SharedDirectoryMapper
|
||||||
public SharedDirectoryMapper(bool enableMapping, string directoryUNC, string driveLabel)
|
public SharedDirectoryMapper(bool enableMapping, string directoryUNC, string driveLabel)
|
||||||
{
|
{
|
||||||
SharedDirectoryMapperConfig config = new SharedDirectoryMapperConfig(enableMapping, driveLabel, directoryUNC);
|
SharedDirectoryMapperConfig config = new SharedDirectoryMapperConfig(enableMapping, driveLabel, directoryUNC);
|
||||||
this._entries.Add(config);
|
this.entries.Add(config);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Configure(ServiceDescriptor descriptor, XmlNode node)
|
public override void Configure(ServiceDescriptor descriptor, XmlNode node)
|
||||||
|
|
@ -36,7 +36,7 @@ namespace winsw.Plugins.SharedDirectoryMapper
|
||||||
if (mapNodes[i] is XmlElement mapElement)
|
if (mapNodes[i] is XmlElement mapElement)
|
||||||
{
|
{
|
||||||
var config = SharedDirectoryMapperConfig.FromXml(mapElement);
|
var config = SharedDirectoryMapperConfig.FromXml(mapElement);
|
||||||
this._entries.Add(config);
|
this.entries.Add(config);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -44,7 +44,7 @@ namespace winsw.Plugins.SharedDirectoryMapper
|
||||||
|
|
||||||
public override void OnWrapperStarted()
|
public override void OnWrapperStarted()
|
||||||
{
|
{
|
||||||
foreach (SharedDirectoryMapperConfig config in this._entries)
|
foreach (SharedDirectoryMapperConfig config in this.entries)
|
||||||
{
|
{
|
||||||
string label = config.Label;
|
string label = config.Label;
|
||||||
string uncPath = config.UNCPath;
|
string uncPath = config.UNCPath;
|
||||||
|
|
@ -72,7 +72,7 @@ namespace winsw.Plugins.SharedDirectoryMapper
|
||||||
|
|
||||||
public override void BeforeWrapperStopped()
|
public override void BeforeWrapperStopped()
|
||||||
{
|
{
|
||||||
foreach (SharedDirectoryMapperConfig config in this._entries)
|
foreach (SharedDirectoryMapperConfig config in this.entries)
|
||||||
{
|
{
|
||||||
string label = config.Label;
|
string label = config.Label;
|
||||||
if (config.EnableMapping)
|
if (config.EnableMapping)
|
||||||
|
|
|
||||||
|
|
@ -1,22 +1,24 @@
|
||||||
using System.Xml;
|
using System.Xml;
|
||||||
using winsw.Util;
|
using WinSW.Util;
|
||||||
|
|
||||||
namespace winsw.Plugins.SharedDirectoryMapper
|
namespace WinSW.Plugins.SharedDirectoryMapper
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Stores configuration entries for SharedDirectoryMapper extension.
|
/// Stores configuration entries for SharedDirectoryMapper extension.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class SharedDirectoryMapperConfig
|
public class SharedDirectoryMapperConfig
|
||||||
{
|
{
|
||||||
public bool EnableMapping { get; set; }
|
public bool EnableMapping { get; }
|
||||||
public string Label { get; set; }
|
|
||||||
public string UNCPath { get; set; }
|
public string Label { get; }
|
||||||
|
|
||||||
|
public string UNCPath { get; }
|
||||||
|
|
||||||
public SharedDirectoryMapperConfig(bool enableMapping, string label, string uncPath)
|
public SharedDirectoryMapperConfig(bool enableMapping, string label, string uncPath)
|
||||||
{
|
{
|
||||||
EnableMapping = enableMapping;
|
this.EnableMapping = enableMapping;
|
||||||
Label = label;
|
this.Label = label;
|
||||||
UNCPath = uncPath;
|
this.UNCPath = uncPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static SharedDirectoryMapperConfig FromXml(XmlElement node)
|
public static SharedDirectoryMapperConfig FromXml(XmlElement node)
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,7 @@
|
||||||
using System;
|
using System;
|
||||||
using winsw;
|
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace winswTests
|
namespace WinSW.Tests
|
||||||
{
|
{
|
||||||
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
|
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
|
||||||
internal sealed class ElevatedFactAttribute : FactAttribute
|
internal sealed class ElevatedFactAttribute : FactAttribute
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,10 @@
|
||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Xml;
|
using System.Xml;
|
||||||
using winsw;
|
using WinSW.Tests.Util;
|
||||||
using winswTests.Util;
|
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace winswTests.Configuration
|
namespace WinSW.Tests.Configuration
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Tests example configuration files.
|
/// Tests example configuration files.
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,8 @@
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using winsw;
|
using WinSW.Tests.Util;
|
||||||
using winswTests.Util;
|
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace winswTests
|
namespace WinSW.Tests
|
||||||
{
|
{
|
||||||
public class DownloadConfigTests
|
public class DownloadConfigTests
|
||||||
{
|
{
|
||||||
|
|
@ -15,14 +14,14 @@ namespace winswTests
|
||||||
{
|
{
|
||||||
// Roundtrip data
|
// Roundtrip data
|
||||||
Download d = new Download(From, To);
|
Download d = new Download(From, To);
|
||||||
var sd = ConfigXmlBuilder.create()
|
var sd = ConfigXmlBuilder.Create()
|
||||||
.WithDownload(d)
|
.WithDownload(d)
|
||||||
.ToServiceDescriptor(true);
|
.ToServiceDescriptor(true);
|
||||||
var loaded = GetSingleEntry(sd);
|
var loaded = this.GetSingleEntry(sd);
|
||||||
|
|
||||||
// Check default values
|
// Check default values
|
||||||
Assert.False(loaded.FailOnError);
|
Assert.False(loaded.FailOnError);
|
||||||
Assert.Equal(Download.AuthType.none, loaded.Auth);
|
Assert.Equal(Download.AuthType.None, loaded.Auth);
|
||||||
Assert.Null(loaded.Username);
|
Assert.Null(loaded.Username);
|
||||||
Assert.Null(loaded.Password);
|
Assert.Null(loaded.Password);
|
||||||
Assert.False(loaded.UnsecureAuth);
|
Assert.False(loaded.UnsecureAuth);
|
||||||
|
|
@ -32,15 +31,15 @@ namespace winswTests
|
||||||
public void Roundtrip_BasicAuth()
|
public void Roundtrip_BasicAuth()
|
||||||
{
|
{
|
||||||
// Roundtrip data
|
// Roundtrip data
|
||||||
Download d = new Download(From, To, true, Download.AuthType.basic, "aUser", "aPassword", true);
|
Download d = new Download(From, To, true, Download.AuthType.Basic, "aUser", "aPassword", true);
|
||||||
var sd = ConfigXmlBuilder.create()
|
var sd = ConfigXmlBuilder.Create()
|
||||||
.WithDownload(d)
|
.WithDownload(d)
|
||||||
.ToServiceDescriptor(true);
|
.ToServiceDescriptor(true);
|
||||||
var loaded = GetSingleEntry(sd);
|
var loaded = this.GetSingleEntry(sd);
|
||||||
|
|
||||||
// Check default values
|
// Check default values
|
||||||
Assert.True(loaded.FailOnError);
|
Assert.True(loaded.FailOnError);
|
||||||
Assert.Equal(Download.AuthType.basic, loaded.Auth);
|
Assert.Equal(Download.AuthType.Basic, loaded.Auth);
|
||||||
Assert.Equal("aUser", loaded.Username);
|
Assert.Equal("aUser", loaded.Username);
|
||||||
Assert.Equal("aPassword", loaded.Password);
|
Assert.Equal("aPassword", loaded.Password);
|
||||||
Assert.True(loaded.UnsecureAuth);
|
Assert.True(loaded.UnsecureAuth);
|
||||||
|
|
@ -50,15 +49,15 @@ namespace winswTests
|
||||||
public void Roundtrip_SSPI()
|
public void Roundtrip_SSPI()
|
||||||
{
|
{
|
||||||
// Roundtrip data
|
// Roundtrip data
|
||||||
Download d = new Download(From, To, false, Download.AuthType.sspi);
|
Download d = new Download(From, To, false, Download.AuthType.Sspi);
|
||||||
var sd = ConfigXmlBuilder.create()
|
var sd = ConfigXmlBuilder.Create()
|
||||||
.WithDownload(d)
|
.WithDownload(d)
|
||||||
.ToServiceDescriptor(true);
|
.ToServiceDescriptor(true);
|
||||||
var loaded = GetSingleEntry(sd);
|
var loaded = this.GetSingleEntry(sd);
|
||||||
|
|
||||||
// Check default values
|
// Check default values
|
||||||
Assert.False(loaded.FailOnError);
|
Assert.False(loaded.FailOnError);
|
||||||
Assert.Equal(Download.AuthType.sspi, loaded.Auth);
|
Assert.Equal(Download.AuthType.Sspi, loaded.Auth);
|
||||||
Assert.Null(loaded.Username);
|
Assert.Null(loaded.Username);
|
||||||
Assert.Null(loaded.Password);
|
Assert.Null(loaded.Password);
|
||||||
Assert.False(loaded.UnsecureAuth);
|
Assert.False(loaded.UnsecureAuth);
|
||||||
|
|
@ -72,22 +71,22 @@ namespace winswTests
|
||||||
public void RejectBasicAuth_With_UnsecureProtocol(string protocolPrefix)
|
public void RejectBasicAuth_With_UnsecureProtocol(string protocolPrefix)
|
||||||
{
|
{
|
||||||
string unsecureFrom = protocolPrefix + "myServer.com:8080/file.txt";
|
string unsecureFrom = protocolPrefix + "myServer.com:8080/file.txt";
|
||||||
var d = new Download(unsecureFrom, To, auth: Download.AuthType.basic, username: "aUser", password: "aPassword");
|
var d = new Download(unsecureFrom, To, auth: Download.AuthType.Basic, username: "aUser", password: "aPassword");
|
||||||
AssertInitializationFails(d, "Warning: you're sending your credentials in clear text to the server");
|
this.AssertInitializationFails(d, "Warning: you're sending your credentials in clear text to the server");
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void RejectBasicAuth_Without_Username()
|
public void RejectBasicAuth_Without_Username()
|
||||||
{
|
{
|
||||||
var d = new Download(From, To, auth: Download.AuthType.basic, username: null, password: "aPassword");
|
var d = new Download(From, To, auth: Download.AuthType.Basic, username: null, password: "aPassword");
|
||||||
AssertInitializationFails(d, "Basic Auth is enabled, but username is not specified");
|
this.AssertInitializationFails(d, "Basic Auth is enabled, but username is not specified");
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void RejectBasicAuth_Without_Password()
|
public void RejectBasicAuth_Without_Password()
|
||||||
{
|
{
|
||||||
var d = new Download(From, To, auth: Download.AuthType.basic, username: "aUser", password: null);
|
var d = new Download(From, To, auth: Download.AuthType.Basic, username: "aUser", password: null);
|
||||||
AssertInitializationFails(d, "Basic Auth is enabled, but password is not specified");
|
this.AssertInitializationFails(d, "Basic Auth is enabled, but password is not specified");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -100,11 +99,11 @@ namespace winswTests
|
||||||
{
|
{
|
||||||
Download d = new Download(From, To, failOnError);
|
Download d = new Download(From, To, failOnError);
|
||||||
|
|
||||||
var sd = ConfigXmlBuilder.create()
|
var sd = ConfigXmlBuilder.Create()
|
||||||
.WithDownload(d)
|
.WithDownload(d)
|
||||||
.ToServiceDescriptor(true);
|
.ToServiceDescriptor(true);
|
||||||
|
|
||||||
var loaded = GetSingleEntry(sd);
|
var loaded = this.GetSingleEntry(sd);
|
||||||
Assert.Equal(From, loaded.From);
|
Assert.Equal(From, loaded.From);
|
||||||
Assert.Equal(To, loaded.To);
|
Assert.Equal(To, loaded.To);
|
||||||
Assert.Equal(failOnError, loaded.FailOnError);
|
Assert.Equal(failOnError, loaded.FailOnError);
|
||||||
|
|
@ -116,11 +115,11 @@ namespace winswTests
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Download_FailOnError_Undefined()
|
public void Download_FailOnError_Undefined()
|
||||||
{
|
{
|
||||||
var sd = ConfigXmlBuilder.create()
|
var sd = ConfigXmlBuilder.Create()
|
||||||
.WithRawEntry("<download from=\"http://www.nosuchhostexists.foo.myorg/foo.xml\" to=\"%BASE%\\foo.xml\"/>")
|
.WithRawEntry("<download from=\"http://www.nosuchhostexists.foo.myorg/foo.xml\" to=\"%BASE%\\foo.xml\"/>")
|
||||||
.ToServiceDescriptor(true);
|
.ToServiceDescriptor(true);
|
||||||
|
|
||||||
var loaded = GetSingleEntry(sd);
|
var loaded = this.GetSingleEntry(sd);
|
||||||
Assert.False(loaded.FailOnError);
|
Assert.False(loaded.FailOnError);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -131,22 +130,22 @@ namespace winswTests
|
||||||
[InlineData("Sspi")]
|
[InlineData("Sspi")]
|
||||||
public void AuthType_Is_CaseInsensitive(string authType)
|
public void AuthType_Is_CaseInsensitive(string authType)
|
||||||
{
|
{
|
||||||
var sd = ConfigXmlBuilder.create()
|
var sd = ConfigXmlBuilder.Create()
|
||||||
.WithRawEntry("<download from=\"http://www.nosuchhostexists.foo.myorg/foo.xml\" to=\"%BASE%\\foo.xml\" auth=\"" + authType + "\"/>")
|
.WithRawEntry("<download from=\"http://www.nosuchhostexists.foo.myorg/foo.xml\" to=\"%BASE%\\foo.xml\" auth=\"" + authType + "\"/>")
|
||||||
.ToServiceDescriptor(true);
|
.ToServiceDescriptor(true);
|
||||||
var loaded = GetSingleEntry(sd);
|
var loaded = this.GetSingleEntry(sd);
|
||||||
Assert.Equal(Download.AuthType.sspi, loaded.Auth);
|
Assert.Equal(Download.AuthType.Sspi, loaded.Auth);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Should_Fail_On_Unsupported_AuthType()
|
public void Should_Fail_On_Unsupported_AuthType()
|
||||||
{
|
{
|
||||||
// TODO: will need refactoring once all fields are being parsed on startup
|
// TODO: will need refactoring once all fields are being parsed on startup
|
||||||
var sd = ConfigXmlBuilder.create()
|
var sd = ConfigXmlBuilder.Create()
|
||||||
.WithRawEntry("<download from=\"http://www.nosuchhostexists.foo.myorg/foo.xml\" to=\"%BASE%\\foo.xml\" auth=\"digest\"/>")
|
.WithRawEntry("<download from=\"http://www.nosuchhostexists.foo.myorg/foo.xml\" to=\"%BASE%\\foo.xml\" auth=\"digest\"/>")
|
||||||
.ToServiceDescriptor(true);
|
.ToServiceDescriptor(true);
|
||||||
|
|
||||||
var e = Assert.Throws<InvalidDataException>(() => GetSingleEntry(sd));
|
var e = Assert.Throws<InvalidDataException>(() => this.GetSingleEntry(sd));
|
||||||
Assert.StartsWith("Cannot parse <auth> Enum value from string 'digest'", e.Message);
|
Assert.StartsWith("Cannot parse <auth> Enum value from string 'digest'", e.Message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -188,11 +187,11 @@ namespace winswTests
|
||||||
|
|
||||||
private void AssertInitializationFails(Download download, string expectedMessagePart = null)
|
private void AssertInitializationFails(Download download, string expectedMessagePart = null)
|
||||||
{
|
{
|
||||||
var sd = ConfigXmlBuilder.create()
|
var sd = ConfigXmlBuilder.Create()
|
||||||
.WithDownload(download)
|
.WithDownload(download)
|
||||||
.ToServiceDescriptor(true);
|
.ToServiceDescriptor(true);
|
||||||
|
|
||||||
var e = Assert.Throws<InvalidDataException>(() => GetSingleEntry(sd));
|
var e = Assert.Throws<InvalidDataException>(() => this.GetSingleEntry(sd));
|
||||||
Assert.StartsWith(expectedMessagePart, e.Message);
|
Assert.StartsWith(expectedMessagePart, e.Message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,11 +4,10 @@ using System.Net;
|
||||||
using System.Net.Sockets;
|
using System.Net.Sockets;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using winsw;
|
using WinSW.Tests.Util;
|
||||||
using winswTests.Util;
|
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace winswTests
|
namespace WinSW.Tests
|
||||||
{
|
{
|
||||||
public class DownloadTests : IDisposable
|
public class DownloadTests : IDisposable
|
||||||
{
|
{
|
||||||
|
|
@ -107,7 +106,7 @@ namespace winswTests
|
||||||
await this.TestClientServerAsync(
|
await this.TestClientServerAsync(
|
||||||
async (source, dest) =>
|
async (source, dest) =>
|
||||||
{
|
{
|
||||||
await new Download(source, dest, false, Download.AuthType.none).PerformAsync();
|
await new Download(source, dest, false, Download.AuthType.None).PerformAsync();
|
||||||
Assert.Equal(this.contents, File.ReadAllBytes(dest));
|
Assert.Equal(this.contents, File.ReadAllBytes(dest));
|
||||||
},
|
},
|
||||||
context =>
|
context =>
|
||||||
|
|
@ -132,7 +131,7 @@ namespace winswTests
|
||||||
await this.TestClientServerAsync(
|
await this.TestClientServerAsync(
|
||||||
async (source, dest) =>
|
async (source, dest) =>
|
||||||
{
|
{
|
||||||
await new Download(source, dest, false, Download.AuthType.basic, username, password, true).PerformAsync();
|
await new Download(source, dest, false, Download.AuthType.Basic, username, password, true).PerformAsync();
|
||||||
Assert.Equal(this.contents, File.ReadAllBytes(dest));
|
Assert.Equal(this.contents, File.ReadAllBytes(dest));
|
||||||
},
|
},
|
||||||
context =>
|
context =>
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace winswTests.Extensions
|
namespace WinSW.Tests.Extensions
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Base class for testing of WinSW Extensions.
|
/// Base class for testing of WinSW Extensions.
|
||||||
|
|
|
||||||
|
|
@ -1,20 +1,19 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using winsw;
|
using WinSW.Extensions;
|
||||||
using winsw.Extensions;
|
using WinSW.Plugins.RunawayProcessKiller;
|
||||||
using winsw.Plugins.RunawayProcessKiller;
|
using WinSW.Tests.Util;
|
||||||
using winsw.Util;
|
using WinSW.Util;
|
||||||
using winswTests.Util;
|
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace winswTests.Extensions
|
namespace WinSW.Tests.Extensions
|
||||||
{
|
{
|
||||||
public class RunawayProcessKillerExtensionTest : ExtensionTestBase
|
public class RunawayProcessKillerExtensionTest : ExtensionTestBase
|
||||||
{
|
{
|
||||||
readonly ServiceDescriptor _testServiceDescriptor;
|
private readonly ServiceDescriptor testServiceDescriptor;
|
||||||
|
|
||||||
readonly string testExtension = GetExtensionClassNameWithAssembly(typeof(RunawayProcessKillerExtension));
|
private readonly string testExtension = GetExtensionClassNameWithAssembly(typeof(RunawayProcessKillerExtension));
|
||||||
|
|
||||||
public RunawayProcessKillerExtensionTest()
|
public RunawayProcessKillerExtensionTest()
|
||||||
{
|
{
|
||||||
|
|
@ -27,19 +26,19 @@ $@"<service>
|
||||||
<arguments>-Xrs -jar \""%BASE%\slave.jar\"" -jnlpUrl ...</arguments>
|
<arguments>-Xrs -jar \""%BASE%\slave.jar\"" -jnlpUrl ...</arguments>
|
||||||
<log mode=""roll""></log>
|
<log mode=""roll""></log>
|
||||||
<extensions>
|
<extensions>
|
||||||
<extension enabled=""true"" className=""{testExtension}"" id=""killRunawayProcess"">
|
<extension enabled=""true"" className=""{this.testExtension}"" id=""killRunawayProcess"">
|
||||||
<pidfile>foo/bar/pid.txt</pidfile>
|
<pidfile>foo/bar/pid.txt</pidfile>
|
||||||
<stopTimeout>5000</stopTimeout>
|
<stopTimeout>5000</stopTimeout>
|
||||||
</extension>
|
</extension>
|
||||||
</extensions>
|
</extensions>
|
||||||
</service>";
|
</service>";
|
||||||
_testServiceDescriptor = ServiceDescriptor.FromXML(seedXml);
|
this.testServiceDescriptor = ServiceDescriptor.FromXml(seedXml);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void LoadExtensions()
|
public void LoadExtensions()
|
||||||
{
|
{
|
||||||
WinSWExtensionManager manager = new WinSWExtensionManager(_testServiceDescriptor);
|
WinSWExtensionManager manager = new WinSWExtensionManager(this.testServiceDescriptor);
|
||||||
manager.LoadExtensions();
|
manager.LoadExtensions();
|
||||||
_ = Assert.Single(manager.Extensions);
|
_ = Assert.Single(manager.Extensions);
|
||||||
|
|
||||||
|
|
@ -53,7 +52,7 @@ $@"<service>
|
||||||
[Fact]
|
[Fact]
|
||||||
public void StartStopExtension()
|
public void StartStopExtension()
|
||||||
{
|
{
|
||||||
WinSWExtensionManager manager = new WinSWExtensionManager(_testServiceDescriptor);
|
WinSWExtensionManager manager = new WinSWExtensionManager(this.testServiceDescriptor);
|
||||||
manager.LoadExtensions();
|
manager.LoadExtensions();
|
||||||
manager.FireOnWrapperStarted();
|
manager.FireOnWrapperStarted();
|
||||||
manager.FireBeforeWrapperStopped();
|
manager.FireBeforeWrapperStopped();
|
||||||
|
|
@ -72,14 +71,14 @@ $@"<service>
|
||||||
ps.Arguments = "/c pause";
|
ps.Arguments = "/c pause";
|
||||||
ps.UseShellExecute = false;
|
ps.UseShellExecute = false;
|
||||||
ps.RedirectStandardOutput = true;
|
ps.RedirectStandardOutput = true;
|
||||||
ps.EnvironmentVariables[WinSWSystem.ENVVAR_NAME_SERVICE_ID] = winswId;
|
ps.EnvironmentVariables[WinSWSystem.EnvVarNameServiceId] = winswId;
|
||||||
proc.Start();
|
proc.Start();
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// Generate extension and ensure that the roundtrip is correct
|
// Generate extension and ensure that the roundtrip is correct
|
||||||
var pidfile = Path.Combine(tmpDir, "process.pid");
|
var pidfile = Path.Combine(tmpDir, "process.pid");
|
||||||
var sd = ConfigXmlBuilder.create(id: winswId)
|
var sd = ConfigXmlBuilder.Create(id: winswId)
|
||||||
.WithRunawayProcessKiller(new RunawayProcessKillerExtension(pidfile), extensionId)
|
.WithRunawayProcessKiller(new RunawayProcessKillerExtension(pidfile), extensionId)
|
||||||
.ToServiceDescriptor();
|
.ToServiceDescriptor();
|
||||||
WinSWExtensionManager manager = new WinSWExtensionManager(sd);
|
WinSWExtensionManager manager = new WinSWExtensionManager(sd);
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,14 @@
|
||||||
using winsw;
|
using WinSW.Extensions;
|
||||||
using winsw.Extensions;
|
using WinSW.Plugins.SharedDirectoryMapper;
|
||||||
using winsw.Plugins.SharedDirectoryMapper;
|
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace winswTests.Extensions
|
namespace WinSW.Tests.Extensions
|
||||||
{
|
{
|
||||||
public class SharedDirectoryMapperConfigTest : ExtensionTestBase
|
public class SharedDirectoryMapperConfigTest : ExtensionTestBase
|
||||||
{
|
{
|
||||||
readonly ServiceDescriptor _testServiceDescriptor;
|
private readonly ServiceDescriptor testServiceDescriptor;
|
||||||
|
|
||||||
readonly string testExtension = GetExtensionClassNameWithAssembly(typeof(SharedDirectoryMapper));
|
private readonly string testExtension = GetExtensionClassNameWithAssembly(typeof(SharedDirectoryMapper));
|
||||||
|
|
||||||
public SharedDirectoryMapperConfigTest()
|
public SharedDirectoryMapperConfigTest()
|
||||||
{
|
{
|
||||||
|
|
@ -22,13 +21,13 @@ $@"<service>
|
||||||
<arguments>-Xrs -jar \""%BASE%\slave.jar\"" -jnlpUrl ...</arguments>
|
<arguments>-Xrs -jar \""%BASE%\slave.jar\"" -jnlpUrl ...</arguments>
|
||||||
<log mode=""roll""></log>
|
<log mode=""roll""></log>
|
||||||
<extensions>
|
<extensions>
|
||||||
<extension enabled=""true"" className=""{testExtension}"" id=""mapNetworDirs"">
|
<extension enabled=""true"" className=""{this.testExtension}"" id=""mapNetworDirs"">
|
||||||
<mapping>
|
<mapping>
|
||||||
<map enabled=""false"" label=""N:"" uncpath=""\\UNC""/>
|
<map enabled=""false"" label=""N:"" uncpath=""\\UNC""/>
|
||||||
<map enabled=""false"" label=""M:"" uncpath=""\\UNC2""/>
|
<map enabled=""false"" label=""M:"" uncpath=""\\UNC2""/>
|
||||||
</mapping>
|
</mapping>
|
||||||
</extension>
|
</extension>
|
||||||
<extension enabled=""true"" className=""{testExtension}"" id=""mapNetworDirs2"">
|
<extension enabled=""true"" className=""{this.testExtension}"" id=""mapNetworDirs2"">
|
||||||
<mapping>
|
<mapping>
|
||||||
<map enabled=""false"" label=""X:"" uncpath=""\\UNC""/>
|
<map enabled=""false"" label=""X:"" uncpath=""\\UNC""/>
|
||||||
<map enabled=""false"" label=""Y:"" uncpath=""\\UNC2""/>
|
<map enabled=""false"" label=""Y:"" uncpath=""\\UNC2""/>
|
||||||
|
|
@ -36,13 +35,13 @@ $@"<service>
|
||||||
</extension>
|
</extension>
|
||||||
</extensions>
|
</extensions>
|
||||||
</service>";
|
</service>";
|
||||||
_testServiceDescriptor = ServiceDescriptor.FromXML(seedXml);
|
this.testServiceDescriptor = ServiceDescriptor.FromXml(seedXml);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void LoadExtensions()
|
public void LoadExtensions()
|
||||||
{
|
{
|
||||||
WinSWExtensionManager manager = new WinSWExtensionManager(_testServiceDescriptor);
|
WinSWExtensionManager manager = new WinSWExtensionManager(this.testServiceDescriptor);
|
||||||
manager.LoadExtensions();
|
manager.LoadExtensions();
|
||||||
Assert.Equal(2, manager.Extensions.Count);
|
Assert.Equal(2, manager.Extensions.Count);
|
||||||
}
|
}
|
||||||
|
|
@ -50,7 +49,7 @@ $@"<service>
|
||||||
[Fact]
|
[Fact]
|
||||||
public void StartStopExtension()
|
public void StartStopExtension()
|
||||||
{
|
{
|
||||||
WinSWExtensionManager manager = new WinSWExtensionManager(_testServiceDescriptor);
|
WinSWExtensionManager manager = new WinSWExtensionManager(this.testServiceDescriptor);
|
||||||
manager.LoadExtensions();
|
manager.LoadExtensions();
|
||||||
manager.FireOnWrapperStarted();
|
manager.FireOnWrapperStarted();
|
||||||
manager.FireBeforeWrapperStopped();
|
manager.FireBeforeWrapperStopped();
|
||||||
|
|
|
||||||
|
|
@ -3,10 +3,10 @@ using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using winsw.Plugins.SharedDirectoryMapper;
|
using WinSW.Plugins.SharedDirectoryMapper;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace winswTests.Extensions
|
namespace WinSW.Tests.Extensions
|
||||||
{
|
{
|
||||||
// TODO: Assert.Throws<ExtensionException>
|
// TODO: Assert.Throws<ExtensionException>
|
||||||
public class SharedDirectoryMapperTests
|
public class SharedDirectoryMapperTests
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,9 @@
|
||||||
using System;
|
using System;
|
||||||
using System.ServiceProcess;
|
using System.ServiceProcess;
|
||||||
using winsw;
|
using WinSW.Tests.Util;
|
||||||
using winswTests.Util;
|
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace winswTests
|
namespace WinSW.Tests
|
||||||
{
|
{
|
||||||
public class MainTest
|
public class MainTest
|
||||||
{
|
{
|
||||||
|
|
@ -47,6 +46,7 @@ namespace winswTests
|
||||||
Assert.Contains("start", cliOut);
|
Assert.Contains("start", cliOut);
|
||||||
Assert.Contains("help", cliOut);
|
Assert.Contains("help", cliOut);
|
||||||
Assert.Contains("version", cliOut);
|
Assert.Contains("version", cliOut);
|
||||||
|
|
||||||
// TODO: check all commands after the migration of ccommands to enum
|
// TODO: check all commands after the migration of ccommands to enum
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,22 +1,21 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.ServiceProcess;
|
using System.ServiceProcess;
|
||||||
using winsw;
|
using WinSW.Tests.Util;
|
||||||
using winswTests.Util;
|
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace winswTests
|
namespace WinSW.Tests
|
||||||
{
|
{
|
||||||
public class ServiceDescriptorTests
|
public class ServiceDescriptorTests
|
||||||
{
|
{
|
||||||
private ServiceDescriptor _extendedServiceDescriptor;
|
|
||||||
|
|
||||||
private const string ExpectedWorkingDirectory = @"Z:\Path\SubPath";
|
private const string ExpectedWorkingDirectory = @"Z:\Path\SubPath";
|
||||||
private const string Username = "User";
|
private const string Username = "User";
|
||||||
private const string Password = "Password";
|
private const string Password = "Password";
|
||||||
private const string Domain = "Domain";
|
private const string Domain = "Domain";
|
||||||
private const string AllowServiceAccountLogonRight = "true";
|
private const string AllowServiceAccountLogonRight = "true";
|
||||||
|
|
||||||
|
private ServiceDescriptor extendedServiceDescriptor;
|
||||||
|
|
||||||
public ServiceDescriptorTests()
|
public ServiceDescriptorTests()
|
||||||
{
|
{
|
||||||
string seedXml =
|
string seedXml =
|
||||||
|
|
@ -35,13 +34,13 @@ $@"<service>
|
||||||
<workingdirectory>{ExpectedWorkingDirectory}</workingdirectory>
|
<workingdirectory>{ExpectedWorkingDirectory}</workingdirectory>
|
||||||
<logpath>C:\logs</logpath>
|
<logpath>C:\logs</logpath>
|
||||||
</service>";
|
</service>";
|
||||||
_extendedServiceDescriptor = ServiceDescriptor.FromXML(seedXml);
|
this.extendedServiceDescriptor = ServiceDescriptor.FromXml(seedXml);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void DefaultStartMode()
|
public void DefaultStartMode()
|
||||||
{
|
{
|
||||||
Assert.Equal(ServiceStartMode.Automatic, _extendedServiceDescriptor.StartMode);
|
Assert.Equal(ServiceStartMode.Automatic, this.extendedServiceDescriptor.StartMode);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|
@ -65,8 +64,8 @@ $@"<service>
|
||||||
<logpath>C:\logs</logpath>
|
<logpath>C:\logs</logpath>
|
||||||
</service>";
|
</service>";
|
||||||
|
|
||||||
_extendedServiceDescriptor = ServiceDescriptor.FromXML(seedXml);
|
this.extendedServiceDescriptor = ServiceDescriptor.FromXml(seedXml);
|
||||||
Assert.Throws<ArgumentException>(() => _extendedServiceDescriptor.StartMode);
|
Assert.Throws<ArgumentException>(() => this.extendedServiceDescriptor.StartMode);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|
@ -90,47 +89,47 @@ $@"<service>
|
||||||
<logpath>C:\logs</logpath>
|
<logpath>C:\logs</logpath>
|
||||||
</service>";
|
</service>";
|
||||||
|
|
||||||
_extendedServiceDescriptor = ServiceDescriptor.FromXML(seedXml);
|
this.extendedServiceDescriptor = ServiceDescriptor.FromXml(seedXml);
|
||||||
Assert.Equal(ServiceStartMode.Manual, _extendedServiceDescriptor.StartMode);
|
Assert.Equal(ServiceStartMode.Manual, this.extendedServiceDescriptor.StartMode);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void VerifyWorkingDirectory()
|
public void VerifyWorkingDirectory()
|
||||||
{
|
{
|
||||||
Debug.WriteLine("_extendedServiceDescriptor.WorkingDirectory :: " + _extendedServiceDescriptor.WorkingDirectory);
|
Debug.WriteLine("_extendedServiceDescriptor.WorkingDirectory :: " + this.extendedServiceDescriptor.WorkingDirectory);
|
||||||
Assert.Equal(ExpectedWorkingDirectory, _extendedServiceDescriptor.WorkingDirectory);
|
Assert.Equal(ExpectedWorkingDirectory, this.extendedServiceDescriptor.WorkingDirectory);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void VerifyServiceLogonRight()
|
public void VerifyServiceLogonRight()
|
||||||
{
|
{
|
||||||
Assert.True(_extendedServiceDescriptor.AllowServiceAcountLogonRight);
|
Assert.True(this.extendedServiceDescriptor.AllowServiceAcountLogonRight);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void VerifyUsername()
|
public void VerifyUsername()
|
||||||
{
|
{
|
||||||
Debug.WriteLine("_extendedServiceDescriptor.WorkingDirectory :: " + _extendedServiceDescriptor.WorkingDirectory);
|
Debug.WriteLine("_extendedServiceDescriptor.WorkingDirectory :: " + this.extendedServiceDescriptor.WorkingDirectory);
|
||||||
Assert.Equal(Domain + "\\" + Username, _extendedServiceDescriptor.ServiceAccountUserName);
|
Assert.Equal(Domain + "\\" + Username, this.extendedServiceDescriptor.ServiceAccountUserName);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void VerifyPassword()
|
public void VerifyPassword()
|
||||||
{
|
{
|
||||||
Debug.WriteLine("_extendedServiceDescriptor.WorkingDirectory :: " + _extendedServiceDescriptor.WorkingDirectory);
|
Debug.WriteLine("_extendedServiceDescriptor.WorkingDirectory :: " + this.extendedServiceDescriptor.WorkingDirectory);
|
||||||
Assert.Equal(Password, _extendedServiceDescriptor.ServiceAccountPassword);
|
Assert.Equal(Password, this.extendedServiceDescriptor.ServiceAccountPassword);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Priority()
|
public void Priority()
|
||||||
{
|
{
|
||||||
var sd = ServiceDescriptor.FromXML("<service><id>test</id><priority>normal</priority></service>");
|
var sd = ServiceDescriptor.FromXml("<service><id>test</id><priority>normal</priority></service>");
|
||||||
Assert.Equal(ProcessPriorityClass.Normal, sd.Priority);
|
Assert.Equal(ProcessPriorityClass.Normal, sd.Priority);
|
||||||
|
|
||||||
sd = ServiceDescriptor.FromXML("<service><id>test</id><priority>idle</priority></service>");
|
sd = ServiceDescriptor.FromXml("<service><id>test</id><priority>idle</priority></service>");
|
||||||
Assert.Equal(ProcessPriorityClass.Idle, sd.Priority);
|
Assert.Equal(ProcessPriorityClass.Idle, sd.Priority);
|
||||||
|
|
||||||
sd = ServiceDescriptor.FromXML("<service><id>test</id></service>");
|
sd = ServiceDescriptor.FromXml("<service><id>test</id></service>");
|
||||||
Assert.Equal(ProcessPriorityClass.Normal, sd.Priority);
|
Assert.Equal(ProcessPriorityClass.Normal, sd.Priority);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -140,7 +139,7 @@ $@"<service>
|
||||||
const string seedXml = "<service>"
|
const string seedXml = "<service>"
|
||||||
+ "<stoptimeout>60sec</stoptimeout>"
|
+ "<stoptimeout>60sec</stoptimeout>"
|
||||||
+ "</service>";
|
+ "</service>";
|
||||||
var serviceDescriptor = ServiceDescriptor.FromXML(seedXml);
|
var serviceDescriptor = ServiceDescriptor.FromXml(seedXml);
|
||||||
|
|
||||||
Assert.Equal(TimeSpan.FromSeconds(60), serviceDescriptor.StopTimeout);
|
Assert.Equal(TimeSpan.FromSeconds(60), serviceDescriptor.StopTimeout);
|
||||||
}
|
}
|
||||||
|
|
@ -151,7 +150,7 @@ $@"<service>
|
||||||
const string seedXml = "<service>"
|
const string seedXml = "<service>"
|
||||||
+ "<stoptimeout>10min</stoptimeout>"
|
+ "<stoptimeout>10min</stoptimeout>"
|
||||||
+ "</service>";
|
+ "</service>";
|
||||||
var serviceDescriptor = ServiceDescriptor.FromXML(seedXml);
|
var serviceDescriptor = ServiceDescriptor.FromXml(seedXml);
|
||||||
|
|
||||||
Assert.Equal(TimeSpan.FromMinutes(10), serviceDescriptor.StopTimeout);
|
Assert.Equal(TimeSpan.FromMinutes(10), serviceDescriptor.StopTimeout);
|
||||||
}
|
}
|
||||||
|
|
@ -162,7 +161,7 @@ $@"<service>
|
||||||
const string seedXml = "<service>"
|
const string seedXml = "<service>"
|
||||||
+ "<logname>MyTestApp</logname>"
|
+ "<logname>MyTestApp</logname>"
|
||||||
+ "</service>";
|
+ "</service>";
|
||||||
var serviceDescriptor = ServiceDescriptor.FromXML(seedXml);
|
var serviceDescriptor = ServiceDescriptor.FromXml(seedXml);
|
||||||
|
|
||||||
Assert.Equal("MyTestApp", serviceDescriptor.LogName);
|
Assert.Equal("MyTestApp", serviceDescriptor.LogName);
|
||||||
}
|
}
|
||||||
|
|
@ -173,7 +172,7 @@ $@"<service>
|
||||||
const string seedXml = "<service>"
|
const string seedXml = "<service>"
|
||||||
+ "<outfiledisabled>true</outfiledisabled>"
|
+ "<outfiledisabled>true</outfiledisabled>"
|
||||||
+ "</service>";
|
+ "</service>";
|
||||||
var serviceDescriptor = ServiceDescriptor.FromXML(seedXml);
|
var serviceDescriptor = ServiceDescriptor.FromXml(seedXml);
|
||||||
|
|
||||||
Assert.True(serviceDescriptor.OutFileDisabled);
|
Assert.True(serviceDescriptor.OutFileDisabled);
|
||||||
}
|
}
|
||||||
|
|
@ -184,7 +183,7 @@ $@"<service>
|
||||||
const string seedXml = "<service>"
|
const string seedXml = "<service>"
|
||||||
+ "<errfiledisabled>true</errfiledisabled>"
|
+ "<errfiledisabled>true</errfiledisabled>"
|
||||||
+ "</service>";
|
+ "</service>";
|
||||||
var serviceDescriptor = ServiceDescriptor.FromXML(seedXml);
|
var serviceDescriptor = ServiceDescriptor.FromXml(seedXml);
|
||||||
|
|
||||||
Assert.True(serviceDescriptor.ErrFileDisabled);
|
Assert.True(serviceDescriptor.ErrFileDisabled);
|
||||||
}
|
}
|
||||||
|
|
@ -195,7 +194,7 @@ $@"<service>
|
||||||
const string seedXml = "<service>"
|
const string seedXml = "<service>"
|
||||||
+ "<outfilepattern>.out.test.log</outfilepattern>"
|
+ "<outfilepattern>.out.test.log</outfilepattern>"
|
||||||
+ "</service>";
|
+ "</service>";
|
||||||
var serviceDescriptor = ServiceDescriptor.FromXML(seedXml);
|
var serviceDescriptor = ServiceDescriptor.FromXml(seedXml);
|
||||||
|
|
||||||
Assert.Equal(".out.test.log", serviceDescriptor.OutFilePattern);
|
Assert.Equal(".out.test.log", serviceDescriptor.OutFilePattern);
|
||||||
}
|
}
|
||||||
|
|
@ -206,7 +205,7 @@ $@"<service>
|
||||||
const string seedXml = "<service>"
|
const string seedXml = "<service>"
|
||||||
+ "<errfilepattern>.err.test.log</errfilepattern>"
|
+ "<errfilepattern>.err.test.log</errfilepattern>"
|
||||||
+ "</service>";
|
+ "</service>";
|
||||||
var serviceDescriptor = ServiceDescriptor.FromXML(seedXml);
|
var serviceDescriptor = ServiceDescriptor.FromXml(seedXml);
|
||||||
|
|
||||||
Assert.Equal(".err.test.log", serviceDescriptor.ErrFilePattern);
|
Assert.Equal(".err.test.log", serviceDescriptor.ErrFilePattern);
|
||||||
}
|
}
|
||||||
|
|
@ -222,12 +221,12 @@ $@"<service>
|
||||||
+ "</log>"
|
+ "</log>"
|
||||||
+ "</service>";
|
+ "</service>";
|
||||||
|
|
||||||
var serviceDescriptor = ServiceDescriptor.FromXML(seedXml);
|
var serviceDescriptor = ServiceDescriptor.FromXml(seedXml);
|
||||||
serviceDescriptor.BaseName = "service";
|
serviceDescriptor.BaseName = "service";
|
||||||
|
|
||||||
var logHandler = serviceDescriptor.LogHandler as SizeBasedRollingLogAppender;
|
var logHandler = serviceDescriptor.LogHandler as SizeBasedRollingLogAppender;
|
||||||
Assert.NotNull(logHandler);
|
Assert.NotNull(logHandler);
|
||||||
Assert.Equal(112 * 1024, logHandler.SizeTheshold);
|
Assert.Equal(112 * 1024, logHandler.SizeThreshold);
|
||||||
Assert.Equal(113, logHandler.FilesToKeep);
|
Assert.Equal(113, logHandler.FilesToKeep);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -242,7 +241,7 @@ $@"<service>
|
||||||
+ "</log>"
|
+ "</log>"
|
||||||
+ "</service>";
|
+ "</service>";
|
||||||
|
|
||||||
var serviceDescriptor = ServiceDescriptor.FromXML(seedXml);
|
var serviceDescriptor = ServiceDescriptor.FromXml(seedXml);
|
||||||
serviceDescriptor.BaseName = "service";
|
serviceDescriptor.BaseName = "service";
|
||||||
|
|
||||||
var logHandler = serviceDescriptor.LogHandler as TimeBasedRollingLogAppender;
|
var logHandler = serviceDescriptor.LogHandler as TimeBasedRollingLogAppender;
|
||||||
|
|
@ -263,12 +262,12 @@ $@"<service>
|
||||||
+ "</log>"
|
+ "</log>"
|
||||||
+ "</service>";
|
+ "</service>";
|
||||||
|
|
||||||
var serviceDescriptor = ServiceDescriptor.FromXML(seedXml);
|
var serviceDescriptor = ServiceDescriptor.FromXml(seedXml);
|
||||||
serviceDescriptor.BaseName = "service";
|
serviceDescriptor.BaseName = "service";
|
||||||
|
|
||||||
var logHandler = serviceDescriptor.LogHandler as RollingSizeTimeLogAppender;
|
var logHandler = serviceDescriptor.LogHandler as RollingSizeTimeLogAppender;
|
||||||
Assert.NotNull(logHandler);
|
Assert.NotNull(logHandler);
|
||||||
Assert.Equal(10240 * 1024, logHandler.SizeTheshold);
|
Assert.Equal(10240 * 1024, logHandler.SizeThreshold);
|
||||||
Assert.Equal("yyyy-MM-dd", logHandler.FilePattern);
|
Assert.Equal("yyyy-MM-dd", logHandler.FilePattern);
|
||||||
Assert.Equal((TimeSpan?)new TimeSpan(0, 0, 0), logHandler.AutoRollAtTime);
|
Assert.Equal((TimeSpan?)new TimeSpan(0, 0, 0), logHandler.AutoRollAtTime);
|
||||||
}
|
}
|
||||||
|
|
@ -284,7 +283,7 @@ $@"<service>
|
||||||
+ "<allowservicelogon>true1</allowservicelogon>"
|
+ "<allowservicelogon>true1</allowservicelogon>"
|
||||||
+ "</serviceaccount>"
|
+ "</serviceaccount>"
|
||||||
+ "</service>";
|
+ "</service>";
|
||||||
var serviceDescriptor = ServiceDescriptor.FromXML(seedXml);
|
var serviceDescriptor = ServiceDescriptor.FromXml(seedXml);
|
||||||
Assert.False(serviceDescriptor.AllowServiceAcountLogonRight);
|
Assert.False(serviceDescriptor.AllowServiceAcountLogonRight);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -298,14 +297,14 @@ $@"<service>
|
||||||
+ "<password>" + Password + "</password>"
|
+ "<password>" + Password + "</password>"
|
||||||
+ "</serviceaccount>"
|
+ "</serviceaccount>"
|
||||||
+ "</service>";
|
+ "</service>";
|
||||||
var serviceDescriptor = ServiceDescriptor.FromXML(seedXml);
|
var serviceDescriptor = ServiceDescriptor.FromXml(seedXml);
|
||||||
Assert.False(serviceDescriptor.AllowServiceAcountLogonRight);
|
Assert.False(serviceDescriptor.AllowServiceAcountLogonRight);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void VerifyWaitHint_FullXML()
|
public void VerifyWaitHint_FullXML()
|
||||||
{
|
{
|
||||||
var sd = ConfigXmlBuilder.create()
|
var sd = ConfigXmlBuilder.Create()
|
||||||
.WithTag("waithint", "20 min")
|
.WithTag("waithint", "20 min")
|
||||||
.ToServiceDescriptor(true);
|
.ToServiceDescriptor(true);
|
||||||
Assert.Equal(TimeSpan.FromMinutes(20), sd.WaitHint);
|
Assert.Equal(TimeSpan.FromMinutes(20), sd.WaitHint);
|
||||||
|
|
@ -317,7 +316,7 @@ $@"<service>
|
||||||
[Fact]
|
[Fact]
|
||||||
public void VerifyWaitHint_XMLWithoutVersion()
|
public void VerifyWaitHint_XMLWithoutVersion()
|
||||||
{
|
{
|
||||||
var sd = ConfigXmlBuilder.create(printXMLVersion: false)
|
var sd = ConfigXmlBuilder.Create(printXMLVersion: false)
|
||||||
.WithTag("waithint", "21 min")
|
.WithTag("waithint", "21 min")
|
||||||
.ToServiceDescriptor(true);
|
.ToServiceDescriptor(true);
|
||||||
Assert.Equal(TimeSpan.FromMinutes(21), sd.WaitHint);
|
Assert.Equal(TimeSpan.FromMinutes(21), sd.WaitHint);
|
||||||
|
|
@ -326,7 +325,7 @@ $@"<service>
|
||||||
[Fact]
|
[Fact]
|
||||||
public void VerifyWaitHint_XMLWithoutComment()
|
public void VerifyWaitHint_XMLWithoutComment()
|
||||||
{
|
{
|
||||||
var sd = ConfigXmlBuilder.create(xmlComment: null)
|
var sd = ConfigXmlBuilder.Create(xmlComment: null)
|
||||||
.WithTag("waithint", "22 min")
|
.WithTag("waithint", "22 min")
|
||||||
.ToServiceDescriptor(true);
|
.ToServiceDescriptor(true);
|
||||||
Assert.Equal(TimeSpan.FromMinutes(22), sd.WaitHint);
|
Assert.Equal(TimeSpan.FromMinutes(22), sd.WaitHint);
|
||||||
|
|
@ -335,7 +334,7 @@ $@"<service>
|
||||||
[Fact]
|
[Fact]
|
||||||
public void VerifyWaitHint_XMLWithoutVersionAndComment()
|
public void VerifyWaitHint_XMLWithoutVersionAndComment()
|
||||||
{
|
{
|
||||||
var sd = ConfigXmlBuilder.create(xmlComment: null, printXMLVersion: false)
|
var sd = ConfigXmlBuilder.Create(xmlComment: null, printXMLVersion: false)
|
||||||
.WithTag("waithint", "23 min")
|
.WithTag("waithint", "23 min")
|
||||||
.ToServiceDescriptor(true);
|
.ToServiceDescriptor(true);
|
||||||
Assert.Equal(TimeSpan.FromMinutes(23), sd.WaitHint);
|
Assert.Equal(TimeSpan.FromMinutes(23), sd.WaitHint);
|
||||||
|
|
@ -344,21 +343,21 @@ $@"<service>
|
||||||
[Fact]
|
[Fact]
|
||||||
public void VerifySleepTime()
|
public void VerifySleepTime()
|
||||||
{
|
{
|
||||||
var sd = ConfigXmlBuilder.create().WithTag("sleeptime", "3 hrs").ToServiceDescriptor(true);
|
var sd = ConfigXmlBuilder.Create().WithTag("sleeptime", "3 hrs").ToServiceDescriptor(true);
|
||||||
Assert.Equal(TimeSpan.FromHours(3), sd.SleepTime);
|
Assert.Equal(TimeSpan.FromHours(3), sd.SleepTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void VerifyResetFailureAfter()
|
public void VerifyResetFailureAfter()
|
||||||
{
|
{
|
||||||
var sd = ConfigXmlBuilder.create().WithTag("resetfailure", "75 sec").ToServiceDescriptor(true);
|
var sd = ConfigXmlBuilder.Create().WithTag("resetfailure", "75 sec").ToServiceDescriptor(true);
|
||||||
Assert.Equal(TimeSpan.FromSeconds(75), sd.ResetFailureAfter);
|
Assert.Equal(TimeSpan.FromSeconds(75), sd.ResetFailureAfter);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void VerifyStopTimeout()
|
public void VerifyStopTimeout()
|
||||||
{
|
{
|
||||||
var sd = ConfigXmlBuilder.create().WithTag("stoptimeout", "35 secs").ToServiceDescriptor(true);
|
var sd = ConfigXmlBuilder.Create().WithTag("stoptimeout", "35 secs").ToServiceDescriptor(true);
|
||||||
Assert.Equal(TimeSpan.FromSeconds(35), sd.StopTimeout);
|
Assert.Equal(TimeSpan.FromSeconds(35), sd.StopTimeout);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -368,14 +367,14 @@ $@"<service>
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Arguments_LegacyParam()
|
public void Arguments_LegacyParam()
|
||||||
{
|
{
|
||||||
var sd = ConfigXmlBuilder.create().WithTag("arguments", "arg").ToServiceDescriptor(true);
|
var sd = ConfigXmlBuilder.Create().WithTag("arguments", "arg").ToServiceDescriptor(true);
|
||||||
Assert.Equal("arg", sd.Arguments);
|
Assert.Equal("arg", sd.Arguments);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Arguments_NewParam_Single()
|
public void Arguments_NewParam_Single()
|
||||||
{
|
{
|
||||||
var sd = ConfigXmlBuilder.create()
|
var sd = ConfigXmlBuilder.Create()
|
||||||
.WithTag("argument", "--arg1=2")
|
.WithTag("argument", "--arg1=2")
|
||||||
.ToServiceDescriptor(true);
|
.ToServiceDescriptor(true);
|
||||||
Assert.Equal(" --arg1=2", sd.Arguments);
|
Assert.Equal(" --arg1=2", sd.Arguments);
|
||||||
|
|
@ -384,7 +383,7 @@ $@"<service>
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Arguments_NewParam_MultipleArgs()
|
public void Arguments_NewParam_MultipleArgs()
|
||||||
{
|
{
|
||||||
var sd = ConfigXmlBuilder.create()
|
var sd = ConfigXmlBuilder.Create()
|
||||||
.WithTag("argument", "--arg1=2")
|
.WithTag("argument", "--arg1=2")
|
||||||
.WithTag("argument", "--arg2=123")
|
.WithTag("argument", "--arg2=123")
|
||||||
.WithTag("argument", "--arg3=null")
|
.WithTag("argument", "--arg3=null")
|
||||||
|
|
@ -398,7 +397,7 @@ $@"<service>
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Arguments_Bothparam_Priorities()
|
public void Arguments_Bothparam_Priorities()
|
||||||
{
|
{
|
||||||
var sd = ConfigXmlBuilder.create()
|
var sd = ConfigXmlBuilder.Create()
|
||||||
.WithTag("arguments", "--arg1=2 --arg2=3")
|
.WithTag("arguments", "--arg1=2 --arg2=3")
|
||||||
.WithTag("argument", "--arg2=123")
|
.WithTag("argument", "--arg2=123")
|
||||||
.WithTag("argument", "--arg3=null")
|
.WithTag("argument", "--arg3=null")
|
||||||
|
|
@ -411,7 +410,7 @@ $@"<service>
|
||||||
[InlineData(false)]
|
[InlineData(false)]
|
||||||
public void DelayedStart_RoundTrip(bool enabled)
|
public void DelayedStart_RoundTrip(bool enabled)
|
||||||
{
|
{
|
||||||
var bldr = ConfigXmlBuilder.create();
|
var bldr = ConfigXmlBuilder.Create();
|
||||||
if (enabled)
|
if (enabled)
|
||||||
{
|
{
|
||||||
bldr = bldr.WithDelayedAutoStart();
|
bldr = bldr.WithDelayedAutoStart();
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,8 @@
|
||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using winsw;
|
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace winswTests.Util
|
namespace WinSW.Tests.Util
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Helper for WinSW CLI testing
|
/// Helper for WinSW CLI testing
|
||||||
|
|
@ -25,7 +24,7 @@ $@"<service>
|
||||||
<logpath>C:\winsw\logs</logpath>
|
<logpath>C:\winsw\logs</logpath>
|
||||||
</service>";
|
</service>";
|
||||||
|
|
||||||
public static readonly ServiceDescriptor DefaultServiceDescriptor = ServiceDescriptor.FromXML(SeedXml);
|
public static readonly ServiceDescriptor DefaultServiceDescriptor = ServiceDescriptor.FromXml(SeedXml);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Runs a simle test, which returns the output CLI
|
/// Runs a simle test, which returns the output CLI
|
||||||
|
|
@ -115,13 +114,13 @@ $@"<service>
|
||||||
|
|
||||||
public Exception Exception { get; }
|
public Exception Exception { get; }
|
||||||
|
|
||||||
public bool HasException => Exception != null;
|
public bool HasException => this.Exception != null;
|
||||||
|
|
||||||
public CLITestResult(string output, string error, Exception exception = null)
|
public CLITestResult(string output, string error, Exception exception = null)
|
||||||
{
|
{
|
||||||
Out = output;
|
this.Out = output;
|
||||||
Error = error;
|
this.Error = error;
|
||||||
Exception = exception;
|
this.Exception = exception;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,36 +1,45 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using winsw;
|
using WinSW.Plugins.RunawayProcessKiller;
|
||||||
using winsw.Plugins.RunawayProcessKiller;
|
using WinSW.Tests.Extensions;
|
||||||
using winswTests.Extensions;
|
|
||||||
|
|
||||||
namespace winswTests.Util
|
namespace WinSW.Tests.Util
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Configuration XML builder, which simplifies testing of WinSW Configuration file.
|
/// Configuration XML builder, which simplifies testing of WinSW Configuration file.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
class ConfigXmlBuilder
|
internal class ConfigXmlBuilder
|
||||||
{
|
{
|
||||||
public string Name { get; set; }
|
public string Name { get; set; }
|
||||||
|
|
||||||
public string Id { get; set; }
|
public string Id { get; set; }
|
||||||
|
|
||||||
public string Description { get; set; }
|
public string Description { get; set; }
|
||||||
|
|
||||||
public string Executable { get; set; }
|
public string Executable { get; set; }
|
||||||
public bool PrintXMLVersion { get; set; }
|
|
||||||
public string XMLComment { get; set; }
|
public bool PrintXmlVersion { get; set; }
|
||||||
public List<string> ExtensionXmls { get; private set; }
|
|
||||||
|
public string XmlComment { get; set; }
|
||||||
|
|
||||||
|
public List<string> ExtensionXmls { get; }
|
||||||
|
|
||||||
private readonly List<string> configEntries;
|
private readonly List<string> configEntries;
|
||||||
|
|
||||||
// TODO: Switch to the initializer?
|
// TODO: Switch to the initializer?
|
||||||
private ConfigXmlBuilder()
|
private ConfigXmlBuilder()
|
||||||
{
|
{
|
||||||
configEntries = new List<string>();
|
this.configEntries = new List<string>();
|
||||||
ExtensionXmls = new List<string>();
|
this.ExtensionXmls = new List<string>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ConfigXmlBuilder create(string id = null, string name = null,
|
public static ConfigXmlBuilder Create(
|
||||||
string description = null, string executable = null, bool printXMLVersion = true,
|
string id = null,
|
||||||
|
string name = null,
|
||||||
|
string description = null,
|
||||||
|
string executable = null,
|
||||||
|
bool printXMLVersion = true,
|
||||||
string xmlComment = "")
|
string xmlComment = "")
|
||||||
{
|
{
|
||||||
var config = new ConfigXmlBuilder();
|
var config = new ConfigXmlBuilder();
|
||||||
|
|
@ -38,43 +47,43 @@ namespace winswTests.Util
|
||||||
config.Name = name ?? "MyApp Service";
|
config.Name = name ?? "MyApp Service";
|
||||||
config.Description = description ?? "MyApp Service (powered by WinSW)";
|
config.Description = description ?? "MyApp Service (powered by WinSW)";
|
||||||
config.Executable = executable ?? "%BASE%\\myExecutable.exe";
|
config.Executable = executable ?? "%BASE%\\myExecutable.exe";
|
||||||
config.PrintXMLVersion = printXMLVersion;
|
config.PrintXmlVersion = printXMLVersion;
|
||||||
config.XMLComment = (xmlComment != null && xmlComment.Length == 0)
|
config.XmlComment = (xmlComment != null && xmlComment.Length == 0)
|
||||||
? "Just a sample configuration file generated by the test suite"
|
? "Just a sample configuration file generated by the test suite"
|
||||||
: xmlComment;
|
: xmlComment;
|
||||||
return config;
|
return config;
|
||||||
}
|
}
|
||||||
|
|
||||||
public string ToXMLString(bool dumpConfig = false)
|
public string ToXmlString(bool dumpConfig = false)
|
||||||
{
|
{
|
||||||
StringBuilder str = new StringBuilder();
|
StringBuilder str = new StringBuilder();
|
||||||
if (PrintXMLVersion)
|
if (this.PrintXmlVersion)
|
||||||
{
|
{
|
||||||
// TODO: The encoding is generally wrong
|
// TODO: The encoding is generally wrong
|
||||||
str.Append("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n");
|
str.Append("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (XMLComment != null)
|
if (this.XmlComment != null)
|
||||||
{
|
{
|
||||||
str.AppendFormat("<!--{0}-->\n", XMLComment);
|
str.AppendFormat("<!--{0}-->\n", this.XmlComment);
|
||||||
}
|
}
|
||||||
|
|
||||||
str.Append("<service>\n");
|
str.Append("<service>\n");
|
||||||
str.AppendFormat(" <id>{0}</id>\n", Id);
|
str.AppendFormat(" <id>{0}</id>\n", this.Id);
|
||||||
str.AppendFormat(" <name>{0}</name>\n", Name);
|
str.AppendFormat(" <name>{0}</name>\n", this.Name);
|
||||||
str.AppendFormat(" <description>{0}</description>\n", Description);
|
str.AppendFormat(" <description>{0}</description>\n", this.Description);
|
||||||
str.AppendFormat(" <executable>{0}</executable>\n", Executable);
|
str.AppendFormat(" <executable>{0}</executable>\n", this.Executable);
|
||||||
foreach (string entry in configEntries)
|
foreach (string entry in this.configEntries)
|
||||||
{
|
{
|
||||||
// We do not care much about pretty formatting here
|
// We do not care much about pretty formatting here
|
||||||
str.AppendFormat(" {0}\n", entry);
|
str.AppendFormat(" {0}\n", entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Extensions
|
// Extensions
|
||||||
if (ExtensionXmls.Count > 0)
|
if (this.ExtensionXmls.Count > 0)
|
||||||
{
|
{
|
||||||
str.Append(" <extensions>\n");
|
str.Append(" <extensions>\n");
|
||||||
foreach (string xml in ExtensionXmls)
|
foreach (string xml in this.ExtensionXmls)
|
||||||
{
|
{
|
||||||
str.Append(xml);
|
str.Append(xml);
|
||||||
}
|
}
|
||||||
|
|
@ -95,18 +104,18 @@ namespace winswTests.Util
|
||||||
|
|
||||||
public ServiceDescriptor ToServiceDescriptor(bool dumpConfig = false)
|
public ServiceDescriptor ToServiceDescriptor(bool dumpConfig = false)
|
||||||
{
|
{
|
||||||
return ServiceDescriptor.FromXML(ToXMLString(dumpConfig));
|
return ServiceDescriptor.FromXml(this.ToXmlString(dumpConfig));
|
||||||
}
|
}
|
||||||
|
|
||||||
public ConfigXmlBuilder WithRawEntry(string entry)
|
public ConfigXmlBuilder WithRawEntry(string entry)
|
||||||
{
|
{
|
||||||
configEntries.Add(entry);
|
this.configEntries.Add(entry);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ConfigXmlBuilder WithTag(string tagName, string value)
|
public ConfigXmlBuilder WithTag(string tagName, string value)
|
||||||
{
|
{
|
||||||
return WithRawEntry(string.Format("<{0}>{1}</{0}>", tagName, value));
|
return this.WithRawEntry(string.Format("<{0}>{1}</{0}>", tagName, value));
|
||||||
}
|
}
|
||||||
|
|
||||||
public ConfigXmlBuilder WithRunawayProcessKiller(RunawayProcessKillerExtension ext, string extensionId = "killRunawayProcess", bool enabled = true)
|
public ConfigXmlBuilder WithRunawayProcessKiller(RunawayProcessKillerExtension ext, string extensionId = "killRunawayProcess", bool enabled = true)
|
||||||
|
|
@ -118,7 +127,7 @@ namespace winswTests.Util
|
||||||
str.AppendFormat(" <stopTimeout>{0}</stopTimeout>\n", ext.StopTimeout.TotalMilliseconds);
|
str.AppendFormat(" <stopTimeout>{0}</stopTimeout>\n", ext.StopTimeout.TotalMilliseconds);
|
||||||
str.AppendFormat(" <checkWinSWEnvironmentVariable>{0}</checkWinSWEnvironmentVariable>\n", ext.CheckWinSWEnvironmentVariable);
|
str.AppendFormat(" <checkWinSWEnvironmentVariable>{0}</checkWinSWEnvironmentVariable>\n", ext.CheckWinSWEnvironmentVariable);
|
||||||
str.Append(" </extension>\n");
|
str.Append(" </extension>\n");
|
||||||
ExtensionXmls.Add(str.ToString());
|
this.ExtensionXmls.Add(str.ToString());
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
@ -129,10 +138,10 @@ namespace winswTests.Util
|
||||||
xml.Append($"<download from=\"{download.From}\" to=\"{download.To}\" failOnError=\"{download.FailOnError}\"");
|
xml.Append($"<download from=\"{download.From}\" to=\"{download.To}\" failOnError=\"{download.FailOnError}\"");
|
||||||
|
|
||||||
// Authentication
|
// Authentication
|
||||||
if (download.Auth != Download.AuthType.none)
|
if (download.Auth != Download.AuthType.None)
|
||||||
{
|
{
|
||||||
xml.Append($" auth=\"{download.Auth}\"");
|
xml.Append($" auth=\"{download.Auth}\"");
|
||||||
if (download.Auth == Download.AuthType.basic)
|
if (download.Auth == Download.AuthType.Basic)
|
||||||
{
|
{
|
||||||
string username = download.Username;
|
string username = download.Username;
|
||||||
if (username != null)
|
if (username != null)
|
||||||
|
|
@ -155,12 +164,12 @@ namespace winswTests.Util
|
||||||
|
|
||||||
xml.Append("/>");
|
xml.Append("/>");
|
||||||
|
|
||||||
return WithRawEntry(xml.ToString());
|
return this.WithRawEntry(xml.ToString());
|
||||||
}
|
}
|
||||||
|
|
||||||
public ConfigXmlBuilder WithDelayedAutoStart()
|
public ConfigXmlBuilder WithDelayedAutoStart()
|
||||||
{
|
{
|
||||||
return WithRawEntry("<delayedAutoStart/>");
|
return this.WithRawEntry("<delayedAutoStart/>");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace winswTests.Util
|
namespace WinSW.Tests.Util
|
||||||
{
|
{
|
||||||
internal static class DateTimeExtensions
|
internal static class DateTimeExtensions
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
|
||||||
namespace winswTests.Util
|
namespace WinSW.Tests.Util
|
||||||
{
|
{
|
||||||
class FilesystemTestHelper
|
internal class FilesystemTestHelper
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a temporary directory for testing.
|
/// Creates a temporary directory for testing.
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,14 @@
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using winsw;
|
using WinSW.Configuration;
|
||||||
using winsw.Configuration;
|
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace winswTests.Util
|
namespace WinSW.Tests.Util
|
||||||
{
|
{
|
||||||
public static class ServiceDescriptorAssert
|
public static class ServiceDescriptorAssert
|
||||||
{
|
{
|
||||||
// TODO: convert to Extension attributes once the .NET dependency is upgraded
|
// TODO: convert to Extension attributes once the .NET dependency is upgraded
|
||||||
// BTW there is a way to get them working in .NET2, but KISS
|
// BTW there is a way to get them working in .NET2, but KISS
|
||||||
|
|
||||||
public static void AssertPropertyIsDefault(ServiceDescriptor desc, string property)
|
public static void AssertPropertyIsDefault(ServiceDescriptor desc, string property)
|
||||||
{
|
{
|
||||||
PropertyInfo actualProperty = typeof(ServiceDescriptor).GetProperty(property);
|
PropertyInfo actualProperty = typeof(ServiceDescriptor).GetProperty(property);
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
"settings": {
|
||||||
|
"orderingRules": {
|
||||||
|
"usingDirectivesPlacement": "outsideNamespace"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,12 +1,13 @@
|
||||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
|
||||||
# Visual Studio 2013
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
VisualStudioVersion = 12.0.31101.0
|
# Visual Studio Version 16
|
||||||
|
VisualStudioVersion = 16.0.30128.74
|
||||||
MinimumVisualStudioVersion = 10.0.40219.1
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "winsw", "Core\ServiceWrapper\winsw.csproj", "{0DE77F55-ADE5-43C1-999A-0BC81153B039}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "winsw", "Core\ServiceWrapper\winsw.csproj", "{0DE77F55-ADE5-43C1-999A-0BC81153B039}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "winswTests", "Test\winswTests\winswTests.csproj", "{93843402-842B-44B4-B303-AEE829BE0B43}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "winswTests", "Test\winswTests\winswTests.csproj", "{93843402-842B-44B4-B303-AEE829BE0B43}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SharedDirectoryMapper", "Plugins\SharedDirectoryMapper\SharedDirectoryMapper.csproj", "{CA5C71DB-C5A8-4C27-BF83-8E6DAED9D6B5}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SharedDirectoryMapper", "Plugins\SharedDirectoryMapper\SharedDirectoryMapper.csproj", "{CA5C71DB-C5A8-4C27-BF83-8E6DAED9D6B5}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{077C2CEC-B687-4B53-86E9-C1A1BF5554E5}"
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{077C2CEC-B687-4B53-86E9-C1A1BF5554E5}"
|
||||||
EndProject
|
EndProject
|
||||||
|
|
@ -14,12 +15,13 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Plugins", "Plugins", "{BC4A
|
||||||
EndProject
|
EndProject
|
||||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Core", "Core", "{5297623A-1A95-4F89-9AAE-DA634081EC86}"
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Core", "Core", "{5297623A-1A95-4F89-9AAE-DA634081EC86}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WinSWCore", "Core\WinSWCore\WinSWCore.csproj", "{9D0C63E2-B6FF-4A85-BD36-B3E5D7F27D06}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WinSWCore", "Core\WinSWCore\WinSWCore.csproj", "{9D0C63E2-B6FF-4A85-BD36-B3E5D7F27D06}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RunawayProcessKiller", "Plugins\RunawayProcessKiller\RunawayProcessKiller.csproj", "{57284B7A-82A4-407A-B706-EBEA6BF8EA13}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RunawayProcessKiller", "Plugins\RunawayProcessKiller\RunawayProcessKiller.csproj", "{57284B7A-82A4-407A-B706-EBEA6BF8EA13}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{AA414F46-B863-473A-A0E0-C2971B3396AE}"
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{AA414F46-B863-473A-A0E0-C2971B3396AE}"
|
||||||
ProjectSection(SolutionItems) = preProject
|
ProjectSection(SolutionItems) = preProject
|
||||||
|
.editorconfig = .editorconfig
|
||||||
..\samples\sample-complete.xml = ..\samples\sample-complete.xml
|
..\samples\sample-complete.xml = ..\samples\sample-complete.xml
|
||||||
..\samples\sample-minimal.xml = ..\samples\sample-minimal.xml
|
..\samples\sample-minimal.xml = ..\samples\sample-minimal.xml
|
||||||
EndProjectSection
|
EndProjectSection
|
||||||
|
|
@ -83,4 +85,7 @@ Global
|
||||||
{9D0C63E2-B6FF-4A85-BD36-B3E5D7F27D06} = {5297623A-1A95-4F89-9AAE-DA634081EC86}
|
{9D0C63E2-B6FF-4A85-BD36-B3E5D7F27D06} = {5297623A-1A95-4F89-9AAE-DA634081EC86}
|
||||||
{57284B7A-82A4-407A-B706-EBEA6BF8EA13} = {BC4AD891-E87E-4F30-867C-FD8084A29E5D}
|
{57284B7A-82A4-407A-B706-EBEA6BF8EA13} = {BC4AD891-E87E-4F30-867C-FD8084A29E5D}
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
|
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||||
|
SolutionGuid = {F9F8AFEA-196D-4041-8441-FABC3B730F9E}
|
||||||
|
EndGlobalSection
|
||||||
EndGlobal
|
EndGlobal
|
||||||
|
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
||||||
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
|
|
||||||
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=SW/@EntryIndexedValue">SW</s:String>
|
|
||||||
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=UNC/@EntryIndexedValue">UNC</s:String></wpf:ResourceDictionary>
|
|
||||||
Loading…
Reference in New Issue