mirror of https://github.com/winsw/winsw
Standardize coding styles
parent
d2e33f1749
commit
e116368fc4
|
@ -1,8 +1,13 @@
|
|||
<Project>
|
||||
|
||||
<PropertyGroup>
|
||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||
<DebugType>full</DebugType>
|
||||
<ArtifactsDir>$(MSBuildThisFileDirectory)artifacts\</ArtifactsDir>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<AdditionalFiles Include="$(MSBuildThisFileDirectory)src\stylecop.json" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
|
@ -0,0 +1,64 @@
|
|||
[*.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
|
||||
|
||||
# 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
|
||||
|
||||
# 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;
|
||||
|
||||
namespace winsw.Logging
|
||||
namespace WinSW.Logging
|
||||
{
|
||||
/// <summary>
|
||||
/// Implements caching of the WindowsService reference in WinSW.
|
||||
/// </summary>
|
||||
public class WrapperServiceEventLogProvider : IServiceEventLogProvider
|
||||
{
|
||||
public WrapperService? service { get; set; }
|
||||
public WrapperService? Service { get; set; }
|
||||
|
||||
public EventLog? locate()
|
||||
public EventLog? Locate()
|
||||
{
|
||||
WrapperService? _service = service;
|
||||
if (_service != null && !_service.IsShuttingDown)
|
||||
WrapperService? service = this.Service;
|
||||
if (service != null && !service.IsShuttingDown)
|
||||
{
|
||||
return _service.EventLog;
|
||||
return service.EventLog;
|
||||
}
|
||||
|
||||
// By default return null
|
||||
|
|
|
@ -18,13 +18,13 @@ using log4net.Appender;
|
|||
using log4net.Config;
|
||||
using log4net.Core;
|
||||
using log4net.Layout;
|
||||
using winsw.Logging;
|
||||
using winsw.Native;
|
||||
using winsw.Util;
|
||||
using WinSW.Logging;
|
||||
using WinSW.Native;
|
||||
using WinSW.Util;
|
||||
using WMI;
|
||||
using ServiceType = WMI.ServiceType;
|
||||
|
||||
namespace winsw
|
||||
namespace WinSW
|
||||
{
|
||||
public static class Program
|
||||
{
|
||||
|
@ -86,8 +86,8 @@ namespace winsw
|
|||
}
|
||||
|
||||
// Get service info for the future use
|
||||
Win32Services svcs = new WmiRoot().GetCollection<Win32Services>();
|
||||
Win32Service? svc = svcs.Select(descriptor.Id);
|
||||
IWin32Services svcs = new WmiRoot().GetCollection<IWin32Services>();
|
||||
IWin32Service? svc = svcs.Select(descriptor.Id);
|
||||
|
||||
var args = new List<string>(Array.AsReadOnly(argsArray));
|
||||
if (args[0] == "/redirect")
|
||||
|
@ -452,7 +452,6 @@ namespace winsw
|
|||
Log.Info("Restarting the service with id '" + descriptor.Id + "'");
|
||||
|
||||
// run restart from another process group. see README.md for why this is useful.
|
||||
|
||||
bool result = ProcessApis.CreateProcess(null, descriptor.ExecutablePath + " restart", IntPtr.Zero, IntPtr.Zero, false, ProcessApis.CREATE_NEW_PROCESS_GROUP, IntPtr.Zero, null, default, out _);
|
||||
if (!result)
|
||||
{
|
||||
|
@ -537,6 +536,7 @@ namespace winsw
|
|||
{
|
||||
// TODO: Make logging levels configurable
|
||||
Level fileLogLevel = Level.Debug;
|
||||
|
||||
// TODO: Debug should not be printed to console by default. Otherwise commands like 'status' will be pollutted
|
||||
// This is a workaround till there is a better command line parsing, which will allow determining
|
||||
Level consoleLogLevel = Level.Info;
|
||||
|
@ -581,7 +581,7 @@ namespace winsw
|
|||
{
|
||||
Name = "Wrapper event log",
|
||||
Threshold = eventLogLevel,
|
||||
provider = WrapperService.eventLogProvider,
|
||||
Provider = WrapperService.eventLogProvider,
|
||||
};
|
||||
systemEventLogger.ActivateOptions();
|
||||
appenders.Add(systemEventLogger);
|
||||
|
|
|
@ -10,20 +10,20 @@ using System.Text.RegularExpressions;
|
|||
using System.Threading.Tasks;
|
||||
#endif
|
||||
using log4net;
|
||||
using winsw.Extensions;
|
||||
using winsw.Logging;
|
||||
using winsw.Native;
|
||||
using winsw.Util;
|
||||
using WinSW.Extensions;
|
||||
using WinSW.Logging;
|
||||
using WinSW.Native;
|
||||
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 ServiceDescriptor _descriptor;
|
||||
private Dictionary<string, string>? _envs;
|
||||
private readonly Process process = new Process();
|
||||
private readonly ServiceDescriptor descriptor;
|
||||
private Dictionary<string, string>? envs;
|
||||
|
||||
internal WinSWExtensionManager ExtensionManager { get; private set; }
|
||||
|
||||
|
@ -32,14 +32,15 @@ namespace winsw
|
|||
Assembly.GetExecutingAssembly(),
|
||||
#endif
|
||||
"WinSW");
|
||||
|
||||
internal static readonly WrapperServiceEventLogProvider eventLogProvider = new WrapperServiceEventLogProvider();
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
private bool _orderlyShutdown;
|
||||
private bool _systemShuttingdown;
|
||||
private bool orderlyShutdown;
|
||||
private bool systemShuttingdown;
|
||||
|
||||
/// <summary>
|
||||
/// Version of Windows service wrapper
|
||||
|
@ -52,24 +53,25 @@ namespace winsw
|
|||
/// <summary>
|
||||
/// Indicates that the system is shutting down.
|
||||
/// </summary>
|
||||
public bool IsShuttingDown => _systemShuttingdown;
|
||||
public bool IsShuttingDown => this.systemShuttingdown;
|
||||
|
||||
public WrapperService(ServiceDescriptor descriptor)
|
||||
{
|
||||
_descriptor = descriptor;
|
||||
ServiceName = _descriptor.Id;
|
||||
ExtensionManager = new WinSWExtensionManager(_descriptor);
|
||||
CanShutdown = true;
|
||||
CanStop = true;
|
||||
CanPauseAndContinue = false;
|
||||
AutoLog = true;
|
||||
_systemShuttingdown = false;
|
||||
this.descriptor = descriptor;
|
||||
this.ServiceName = this.descriptor.Id;
|
||||
this.ExtensionManager = new WinSWExtensionManager(this.descriptor);
|
||||
this.CanShutdown = true;
|
||||
this.CanStop = true;
|
||||
this.CanPauseAndContinue = false;
|
||||
this.AutoLog = true;
|
||||
this.systemShuttingdown = false;
|
||||
|
||||
// Register the event log provider
|
||||
eventLogProvider.service = this;
|
||||
eventLogProvider.Service = this;
|
||||
}
|
||||
|
||||
public WrapperService() : this(new ServiceDescriptor())
|
||||
public WrapperService()
|
||||
: this(new ServiceDescriptor())
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -79,9 +81,11 @@ namespace winsw
|
|||
/// </summary>
|
||||
private void HandleFileCopies()
|
||||
{
|
||||
var file = _descriptor.BasePath + ".copies";
|
||||
var file = this.descriptor.BasePath + ".copies";
|
||||
if (!File.Exists(file))
|
||||
{
|
||||
return; // nothing to handle
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
|
@ -89,15 +93,15 @@ namespace winsw
|
|||
string? line;
|
||||
while ((line = tr.ReadLine()) != null)
|
||||
{
|
||||
LogEvent("Handling copy: " + line);
|
||||
this.LogEvent("Handling copy: " + line);
|
||||
string[] tokens = line.Split('>');
|
||||
if (tokens.Length > 2)
|
||||
{
|
||||
LogEvent("Too many delimiters in " + line);
|
||||
this.LogEvent("Too many delimiters in " + line);
|
||||
continue;
|
||||
}
|
||||
|
||||
MoveFile(tokens[0], tokens[1]);
|
||||
this.MoveFile(tokens[0], tokens[1]);
|
||||
}
|
||||
}
|
||||
finally
|
||||
|
@ -117,7 +121,7 @@ namespace winsw
|
|||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
LogEvent("Failed to move :" + sourceFileName + " to " + destFileName + " because " + e.Message);
|
||||
this.LogEvent("Failed to move :" + sourceFileName + " to " + destFileName + " because " + e.Message);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -127,21 +131,21 @@ namespace winsw
|
|||
/// <returns>Log Handler, which should be used for the spawned process</returns>
|
||||
private LogHandler CreateExecutableLogHandler()
|
||||
{
|
||||
string logDirectory = _descriptor.LogDirectory;
|
||||
string logDirectory = this.descriptor.LogDirectory;
|
||||
|
||||
if (!Directory.Exists(logDirectory))
|
||||
{
|
||||
Directory.CreateDirectory(logDirectory);
|
||||
}
|
||||
|
||||
LogHandler logAppender = _descriptor.LogHandler;
|
||||
LogHandler logAppender = this.descriptor.LogHandler;
|
||||
logAppender.EventLogger = this;
|
||||
return logAppender;
|
||||
}
|
||||
|
||||
public void LogEvent(string message)
|
||||
{
|
||||
if (_systemShuttingdown)
|
||||
if (this.systemShuttingdown)
|
||||
{
|
||||
/* NOP - cannot call EventLog because of shutdown. */
|
||||
}
|
||||
|
@ -149,7 +153,7 @@ namespace winsw
|
|||
{
|
||||
try
|
||||
{
|
||||
EventLog.WriteEntry(message);
|
||||
this.EventLog.WriteEntry(message);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
|
@ -160,7 +164,7 @@ namespace winsw
|
|||
|
||||
public void LogEvent(string message, EventLogEntryType type)
|
||||
{
|
||||
if (_systemShuttingdown)
|
||||
if (this.systemShuttingdown)
|
||||
{
|
||||
/* NOP - cannot call EventLog because of shutdown. */
|
||||
}
|
||||
|
@ -168,7 +172,7 @@ namespace winsw
|
|||
{
|
||||
try
|
||||
{
|
||||
EventLog.WriteEntry(message, type);
|
||||
this.EventLog.WriteEntry(message, type);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
|
@ -179,7 +183,8 @@ namespace winsw
|
|||
|
||||
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
|
||||
/**
|
||||
|
@ -187,17 +192,17 @@ namespace winsw
|
|||
{
|
||||
LogEvent("envar " + key + '=' + _envs[key]);
|
||||
}*/
|
||||
HandleFileCopies();
|
||||
this.HandleFileCopies();
|
||||
|
||||
// handle downloads
|
||||
#if VNEXT
|
||||
List<Download> downloads = _descriptor.Downloads;
|
||||
List<Download> downloads = this.descriptor.Downloads;
|
||||
Task[] tasks = new Task[downloads.Count];
|
||||
for (int i = 0; i < downloads.Count; i++)
|
||||
{
|
||||
Download download = downloads[i];
|
||||
string downloadMessage = $"Downloading: {download.From} to {download.To}. failOnError={download.FailOnError.ToString()}";
|
||||
LogEvent(downloadMessage);
|
||||
this.LogEvent(downloadMessage);
|
||||
Log.Info(downloadMessage);
|
||||
tasks[i] = download.PerformAsync();
|
||||
}
|
||||
|
@ -216,7 +221,7 @@ namespace winsw
|
|||
Download download = downloads[i];
|
||||
string errorMessage = $"Failed to download {download.From} to {download.To}";
|
||||
AggregateException exception = tasks[i].Exception!;
|
||||
LogEvent($"{errorMessage}. {exception.Message}");
|
||||
this.LogEvent($"{errorMessage}. {exception.Message}");
|
||||
Log.Error(errorMessage, exception);
|
||||
|
||||
// TODO: move this code into the download logic
|
||||
|
@ -230,10 +235,10 @@ namespace winsw
|
|||
throw new AggregateException(exceptions);
|
||||
}
|
||||
#else
|
||||
foreach (Download download in _descriptor.Downloads)
|
||||
foreach (Download download in this.descriptor.Downloads)
|
||||
{
|
||||
string downloadMessage = $"Downloading: {download.From} to {download.To}. failOnError={download.FailOnError.ToString()}";
|
||||
LogEvent(downloadMessage);
|
||||
this.LogEvent(downloadMessage);
|
||||
Log.Info(downloadMessage);
|
||||
try
|
||||
{
|
||||
|
@ -242,7 +247,7 @@ namespace winsw
|
|||
catch (Exception e)
|
||||
{
|
||||
string errorMessage = $"Failed to download {download.From} to {download.To}";
|
||||
LogEvent($"{errorMessage}. {e.Message}");
|
||||
this.LogEvent($"{errorMessage}. {e.Message}");
|
||||
Log.Error(errorMessage, e);
|
||||
|
||||
// TODO: move this code into the download logic
|
||||
|
@ -256,34 +261,34 @@ namespace winsw
|
|||
}
|
||||
#endif
|
||||
|
||||
string? startArguments = _descriptor.StartArguments;
|
||||
string? startArguments = this.descriptor.StartArguments;
|
||||
|
||||
if (startArguments is null)
|
||||
{
|
||||
startArguments = _descriptor.Arguments;
|
||||
startArguments = this.descriptor.Arguments;
|
||||
}
|
||||
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
|
||||
// in the xml for readability.
|
||||
startArguments = Regex.Replace(startArguments, @"\s*[\n\r]+\s*", " ");
|
||||
|
||||
LogEvent("Starting " + _descriptor.Executable + ' ' + startArguments);
|
||||
Log.Info("Starting " + _descriptor.Executable + ' ' + startArguments);
|
||||
this.LogEvent("Starting " + this.descriptor.Executable + ' ' + startArguments);
|
||||
Log.Info("Starting " + this.descriptor.Executable + ' ' + startArguments);
|
||||
|
||||
// Load and start extensions
|
||||
ExtensionManager.LoadExtensions();
|
||||
ExtensionManager.FireOnWrapperStarted();
|
||||
this.ExtensionManager.LoadExtensions();
|
||||
this.ExtensionManager.FireOnWrapperStarted();
|
||||
|
||||
LogHandler executableLogHandler = CreateExecutableLogHandler();
|
||||
StartProcess(_process, startArguments, _descriptor.Executable, executableLogHandler, true);
|
||||
ExtensionManager.FireOnProcessStarted(_process);
|
||||
LogHandler executableLogHandler = this.CreateExecutableLogHandler();
|
||||
this.StartProcess(this.process, startArguments, this.descriptor.Executable, executableLogHandler, true);
|
||||
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()
|
||||
|
@ -292,8 +297,8 @@ namespace winsw
|
|||
|
||||
try
|
||||
{
|
||||
_systemShuttingdown = true;
|
||||
StopIt();
|
||||
this.systemShuttingdown = true;
|
||||
this.StopIt();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
@ -307,7 +312,7 @@ namespace winsw
|
|||
|
||||
try
|
||||
{
|
||||
StopIt();
|
||||
this.StopIt();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
@ -324,18 +329,18 @@ namespace winsw
|
|||
/// </summary>
|
||||
private void StopIt()
|
||||
{
|
||||
string? stopArguments = _descriptor.StopArguments;
|
||||
LogEvent("Stopping " + _descriptor.Id);
|
||||
Log.Info("Stopping " + _descriptor.Id);
|
||||
_orderlyShutdown = true;
|
||||
string? stopArguments = this.descriptor.StopArguments;
|
||||
this.LogEvent("Stopping " + this.descriptor.Id);
|
||||
Log.Info("Stopping " + this.descriptor.Id);
|
||||
this.orderlyShutdown = true;
|
||||
|
||||
if (stopArguments is null)
|
||||
{
|
||||
try
|
||||
{
|
||||
Log.Debug("ProcessKill " + _process.Id);
|
||||
ProcessHelper.StopProcessAndChildren(_process.Id, _descriptor.StopTimeout, _descriptor.StopParentProcessFirst);
|
||||
ExtensionManager.FireOnProcessTerminated(_process);
|
||||
Log.Debug("ProcessKill " + this.process.Id);
|
||||
ProcessHelper.StopProcessAndChildren(this.process.Id, this.descriptor.StopTimeout, this.descriptor.StopParentProcessFirst);
|
||||
this.ExtensionManager.FireOnProcessTerminated(this.process);
|
||||
}
|
||||
catch (InvalidOperationException)
|
||||
{
|
||||
|
@ -344,48 +349,48 @@ namespace winsw
|
|||
}
|
||||
else
|
||||
{
|
||||
SignalShutdownPending();
|
||||
this.SignalShutdownPending();
|
||||
|
||||
stopArguments += " " + _descriptor.Arguments;
|
||||
stopArguments += " " + this.descriptor.Arguments;
|
||||
|
||||
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
|
||||
StartProcess(stopProcess, stopArguments, executable, null, false);
|
||||
this.StartProcess(stopProcess, stopArguments, executable, null, false);
|
||||
|
||||
Log.Debug("WaitForProcessToExit " + _process.Id + "+" + stopProcess.Id);
|
||||
WaitForProcessToExit(_process);
|
||||
WaitForProcessToExit(stopProcess);
|
||||
Log.Debug("WaitForProcessToExit " + this.process.Id + "+" + stopProcess.Id);
|
||||
this.WaitForProcessToExit(this.process);
|
||||
this.WaitForProcessToExit(stopProcess);
|
||||
}
|
||||
|
||||
// Stop extensions
|
||||
ExtensionManager.FireBeforeWrapperStopped();
|
||||
this.ExtensionManager.FireBeforeWrapperStopped();
|
||||
|
||||
if (_systemShuttingdown && _descriptor.BeepOnShutdown)
|
||||
if (this.systemShuttingdown && this.descriptor.BeepOnShutdown)
|
||||
{
|
||||
Console.Beep();
|
||||
}
|
||||
|
||||
Log.Info("Finished " + _descriptor.Id);
|
||||
Log.Info("Finished " + this.descriptor.Id);
|
||||
}
|
||||
|
||||
private void WaitForProcessToExit(Process processoWait)
|
||||
{
|
||||
SignalShutdownPending();
|
||||
this.SignalShutdownPending();
|
||||
|
||||
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");
|
||||
effectiveProcessWaitSleepTime = int.MaxValue;
|
||||
}
|
||||
else
|
||||
{
|
||||
effectiveProcessWaitSleepTime = (int)_descriptor.SleepTime.TotalMilliseconds;
|
||||
effectiveProcessWaitSleepTime = (int)this.descriptor.SleepTime.TotalMilliseconds;
|
||||
}
|
||||
|
||||
try
|
||||
|
@ -394,7 +399,7 @@ namespace winsw
|
|||
|
||||
while (!processoWait.WaitForExit(effectiveProcessWaitSleepTime))
|
||||
{
|
||||
SignalShutdownPending();
|
||||
this.SignalShutdownPending();
|
||||
// WriteEvent("WaitForProcessToExit [repeat]");
|
||||
}
|
||||
}
|
||||
|
@ -409,27 +414,27 @@ namespace winsw
|
|||
private void SignalShutdownPending()
|
||||
{
|
||||
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");
|
||||
effectiveWaitHint = int.MaxValue;
|
||||
}
|
||||
else
|
||||
{
|
||||
effectiveWaitHint = (int)_descriptor.WaitHint.TotalMilliseconds;
|
||||
effectiveWaitHint = (int)this.descriptor.WaitHint.TotalMilliseconds;
|
||||
}
|
||||
|
||||
RequestAdditionalTime(effectiveWaitHint);
|
||||
this.RequestAdditionalTime(effectiveWaitHint);
|
||||
}
|
||||
|
||||
private void SignalShutdownComplete()
|
||||
{
|
||||
IntPtr handle = ServiceHandle;
|
||||
_wrapperServiceStatus.CheckPoint++;
|
||||
IntPtr handle = this.ServiceHandle;
|
||||
this.wrapperServiceStatus.CheckPoint++;
|
||||
// WriteEvent("SignalShutdownComplete " + wrapperServiceStatus.checkPoint + ":" + wrapperServiceStatus.waitHint);
|
||||
_wrapperServiceStatus.CurrentState = ServiceApis.ServiceState.STOPPED;
|
||||
ServiceApis.SetServiceStatus(handle, _wrapperServiceStatus);
|
||||
this.wrapperServiceStatus.CurrentState = ServiceApis.ServiceState.STOPPED;
|
||||
ServiceApis.SetServiceStatus(handle, this.wrapperServiceStatus);
|
||||
}
|
||||
|
||||
private void StartProcess(Process processToStart, string arguments, string executable, LogHandler? logHandler, bool redirectStdin)
|
||||
|
@ -440,25 +445,28 @@ namespace winsw
|
|||
string msg = processToStart.Id + " - " + processToStart.StartInfo.FileName + " " + processToStart.StartInfo.Arguments;
|
||||
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
|
||||
{
|
||||
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.
|
||||
// by not reporting unclean shutdown, we let Windows SCM to decide if it wants to
|
||||
// restart the service automatically
|
||||
if (proc.ExitCode == 0)
|
||||
SignalShutdownComplete();
|
||||
{
|
||||
this.SignalShutdownComplete();
|
||||
}
|
||||
|
||||
Environment.Exit(proc.ExitCode);
|
||||
}
|
||||
}
|
||||
catch (InvalidOperationException ioe)
|
||||
{
|
||||
LogEvent("WaitForExit " + ioe.Message);
|
||||
this.LogEvent("WaitForExit " + ioe.Message);
|
||||
}
|
||||
finally
|
||||
{
|
||||
|
@ -471,13 +479,13 @@ namespace winsw
|
|||
processToStart: processToStart,
|
||||
executable: executable,
|
||||
arguments: arguments,
|
||||
envVars: _envs,
|
||||
workingDirectory: _descriptor.WorkingDirectory,
|
||||
priority: _descriptor.Priority,
|
||||
envVars: this.envs,
|
||||
workingDirectory: this.descriptor.WorkingDirectory,
|
||||
priority: this.descriptor.Priority,
|
||||
callback: OnProcessCompleted,
|
||||
logHandler: logHandler,
|
||||
redirectStdin: redirectStdin,
|
||||
hideWindow: _descriptor.HideWindow);
|
||||
hideWindow: this.descriptor.HideWindow);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,57 +5,82 @@ using System.IO;
|
|||
using System.Xml;
|
||||
using WMI;
|
||||
|
||||
namespace winsw.Configuration
|
||||
namespace WinSW.Configuration
|
||||
{
|
||||
/// <summary>
|
||||
/// Default WinSW settings
|
||||
/// Default WinSW settings.
|
||||
/// </summary>
|
||||
public sealed class DefaultWinSWSettings : IWinSWConfiguration
|
||||
{
|
||||
public string Id => throw new InvalidOperationException(nameof(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 Executable => throw new InvalidOperationException(nameof(Executable) + " must be specified.");
|
||||
public string Id => throw new InvalidOperationException(nameof(this.Id) + " must be specified.");
|
||||
|
||||
public string Caption => throw new InvalidOperationException(nameof(this.Caption) + " 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 string ExecutablePath => Process.GetCurrentProcess().MainModule.FileName;
|
||||
|
||||
// Installation
|
||||
public bool AllowServiceAcountLogonRight => false;
|
||||
|
||||
public string? ServiceAccountPassword => null;
|
||||
|
||||
public string? ServiceAccountUser => null;
|
||||
|
||||
public Native.SC_ACTION[] FailureActions => new Native.SC_ACTION[0];
|
||||
|
||||
public TimeSpan ResetFailureAfter => TimeSpan.FromDays(1);
|
||||
|
||||
// Executable management
|
||||
public string Arguments => string.Empty;
|
||||
|
||||
public string? StartArguments => null;
|
||||
|
||||
public string? StopExecutable => null;
|
||||
|
||||
public string? StopArguments => null;
|
||||
public string WorkingDirectory => Path.GetDirectoryName(ExecutablePath)!;
|
||||
|
||||
public string WorkingDirectory => Path.GetDirectoryName(this.ExecutablePath)!;
|
||||
|
||||
public ProcessPriorityClass Priority => ProcessPriorityClass.Normal;
|
||||
|
||||
public TimeSpan StopTimeout => TimeSpan.FromSeconds(15);
|
||||
|
||||
public bool StopParentProcessFirst => false;
|
||||
|
||||
// Service management
|
||||
public StartMode StartMode => StartMode.Automatic;
|
||||
|
||||
public bool DelayedAutoStart => false;
|
||||
|
||||
public string[] ServiceDependencies => new string[0];
|
||||
|
||||
public TimeSpan WaitHint => TimeSpan.FromSeconds(15);
|
||||
|
||||
public TimeSpan SleepTime => TimeSpan.FromSeconds(1);
|
||||
|
||||
public bool Interactive => false;
|
||||
|
||||
// Logging
|
||||
public string LogDirectory => Path.GetDirectoryName(ExecutablePath)!;
|
||||
public string LogDirectory => Path.GetDirectoryName(this.ExecutablePath)!;
|
||||
|
||||
public string LogMode => "append";
|
||||
|
||||
public bool OutFileDisabled => false;
|
||||
|
||||
public bool ErrFileDisabled => false;
|
||||
|
||||
public string OutFilePattern => ".out.log";
|
||||
|
||||
public string ErrFilePattern => ".err.log";
|
||||
|
||||
// Environment
|
||||
public List<Download> Downloads => new List<Download>(0);
|
||||
|
||||
public Dictionary<string, string> EnvironmentVariables => new Dictionary<string, string>(0);
|
||||
|
||||
// Misc
|
||||
|
|
|
@ -4,50 +4,71 @@ using System.Diagnostics;
|
|||
using System.Xml;
|
||||
using WMI;
|
||||
|
||||
namespace winsw.Configuration
|
||||
namespace WinSW.Configuration
|
||||
{
|
||||
public interface IWinSWConfiguration
|
||||
{
|
||||
// TODO: Document the parameters && refactor
|
||||
|
||||
string Id { get; }
|
||||
|
||||
string Caption { get; }
|
||||
|
||||
string Description { get; }
|
||||
|
||||
string Executable { get; }
|
||||
|
||||
string ExecutablePath { get; }
|
||||
|
||||
bool HideWindow { get; }
|
||||
|
||||
// Installation
|
||||
bool AllowServiceAcountLogonRight { get; }
|
||||
|
||||
string? ServiceAccountPassword { get; }
|
||||
|
||||
string? ServiceAccountUser { get; }
|
||||
|
||||
Native.SC_ACTION[] FailureActions { get; }
|
||||
|
||||
TimeSpan ResetFailureAfter { get; }
|
||||
|
||||
// Executable management
|
||||
string Arguments { get; }
|
||||
|
||||
string? StartArguments { get; }
|
||||
|
||||
string? StopExecutable { get; }
|
||||
|
||||
string? StopArguments { get; }
|
||||
|
||||
string WorkingDirectory { get; }
|
||||
|
||||
ProcessPriorityClass Priority { get; }
|
||||
|
||||
TimeSpan StopTimeout { get; }
|
||||
|
||||
bool StopParentProcessFirst { get; }
|
||||
|
||||
// Service management
|
||||
StartMode StartMode { get; }
|
||||
|
||||
string[] ServiceDependencies { get; }
|
||||
|
||||
TimeSpan WaitHint { get; }
|
||||
|
||||
TimeSpan SleepTime { get; }
|
||||
|
||||
bool Interactive { get; }
|
||||
|
||||
// Logging
|
||||
string LogDirectory { get; }
|
||||
|
||||
// TODO: replace by enum
|
||||
string LogMode { get; }
|
||||
|
||||
// Environment
|
||||
List<Download> Downloads { get; }
|
||||
|
||||
Dictionary<string, string> EnvironmentVariables { get; }
|
||||
|
||||
// Misc
|
||||
|
|
|
@ -10,9 +10,9 @@ using System.Threading.Tasks;
|
|||
#endif
|
||||
using System.Xml;
|
||||
using log4net;
|
||||
using winsw.Util;
|
||||
using WinSW.Util;
|
||||
|
||||
namespace winsw
|
||||
namespace WinSW
|
||||
{
|
||||
/// <summary>
|
||||
/// Specify the download activities prior to the launch.
|
||||
|
@ -22,9 +22,9 @@ namespace winsw
|
|||
{
|
||||
public enum AuthType
|
||||
{
|
||||
none = 0,
|
||||
sspi,
|
||||
basic
|
||||
None = 0,
|
||||
Sspi,
|
||||
Basic
|
||||
}
|
||||
|
||||
private static readonly ILog Logger = LogManager.GetLogger(typeof(Download));
|
||||
|
@ -38,7 +38,7 @@ namespace winsw
|
|||
public readonly bool FailOnError;
|
||||
public readonly string? Proxy;
|
||||
|
||||
public string ShortId => $"(download from {From})";
|
||||
public string ShortId => $"(download from {this.From})";
|
||||
|
||||
static Download()
|
||||
{
|
||||
|
@ -73,59 +73,59 @@ namespace winsw
|
|||
string from,
|
||||
string to,
|
||||
bool failOnError = false,
|
||||
AuthType auth = AuthType.none,
|
||||
AuthType auth = AuthType.None,
|
||||
string? username = null,
|
||||
string? password = null,
|
||||
bool unsecureAuth = false,
|
||||
string? proxy = null)
|
||||
{
|
||||
From = from;
|
||||
To = to;
|
||||
FailOnError = failOnError;
|
||||
Proxy = proxy;
|
||||
Auth = auth;
|
||||
Username = username;
|
||||
Password = password;
|
||||
UnsecureAuth = unsecureAuth;
|
||||
this.From = from;
|
||||
this.To = to;
|
||||
this.FailOnError = failOnError;
|
||||
this.Proxy = proxy;
|
||||
this.Auth = auth;
|
||||
this.Username = username;
|
||||
this.Password = password;
|
||||
this.UnsecureAuth = unsecureAuth;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructs the download setting sfrom the XML entry
|
||||
/// </summary>
|
||||
/// <param name="n">XML element</param>
|
||||
/// <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)
|
||||
{
|
||||
From = XmlHelper.SingleAttribute<string>(n, "from");
|
||||
To = XmlHelper.SingleAttribute<string>(n, "to");
|
||||
this.From = XmlHelper.SingleAttribute<string>(n, "from");
|
||||
this.To = XmlHelper.SingleAttribute<string>(n, "to");
|
||||
|
||||
// All arguments below are optional
|
||||
FailOnError = XmlHelper.SingleAttribute(n, "failOnError", false);
|
||||
Proxy = XmlHelper.SingleAttribute<string>(n, "proxy", null);
|
||||
this.FailOnError = XmlHelper.SingleAttribute(n, "failOnError", false);
|
||||
this.Proxy = XmlHelper.SingleAttribute<string>(n, "proxy", null);
|
||||
|
||||
Auth = XmlHelper.EnumAttribute(n, "auth", AuthType.none);
|
||||
Username = XmlHelper.SingleAttribute<string>(n, "user", null);
|
||||
Password = XmlHelper.SingleAttribute<string>(n, "password", null);
|
||||
UnsecureAuth = XmlHelper.SingleAttribute(n, "unsecureAuth", false);
|
||||
this.Auth = XmlHelper.EnumAttribute(n, "auth", AuthType.None);
|
||||
this.Username = XmlHelper.SingleAttribute<string>(n, "user", null);
|
||||
this.Password = XmlHelper.SingleAttribute<string>(n, "password", null);
|
||||
this.UnsecureAuth = XmlHelper.SingleAttribute(n, "unsecureAuth", false);
|
||||
|
||||
if (Auth == AuthType.basic)
|
||||
if (this.Auth == AuthType.Basic)
|
||||
{
|
||||
// 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");
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -150,10 +150,10 @@ namespace winsw
|
|||
public void Perform()
|
||||
#endif
|
||||
{
|
||||
WebRequest request = WebRequest.Create(From);
|
||||
if (!string.IsNullOrEmpty(Proxy))
|
||||
WebRequest request = WebRequest.Create(this.From);
|
||||
if (!string.IsNullOrEmpty(this.Proxy))
|
||||
{
|
||||
CustomProxyInformation proxyInformation = new CustomProxyInformation(Proxy);
|
||||
CustomProxyInformation proxyInformation = new CustomProxyInformation(this.Proxy!);
|
||||
if (proxyInformation.Credentials != null)
|
||||
{
|
||||
request.Proxy = new WebProxy(proxyInformation.ServerAddress, false, null, proxyInformation.Credentials);
|
||||
|
@ -164,35 +164,35 @@ namespace winsw
|
|||
}
|
||||
}
|
||||
|
||||
switch (Auth)
|
||||
switch (this.Auth)
|
||||
{
|
||||
case AuthType.none:
|
||||
case AuthType.None:
|
||||
// Do nothing
|
||||
break;
|
||||
|
||||
case AuthType.sspi:
|
||||
case AuthType.Sspi:
|
||||
request.UseDefaultCredentials = true;
|
||||
request.PreAuthenticate = true;
|
||||
request.Credentials = CredentialCache.DefaultCredentials;
|
||||
break;
|
||||
|
||||
case AuthType.basic:
|
||||
SetBasicAuthHeader(request, Username!, Password!);
|
||||
case AuthType.Basic:
|
||||
this.SetBasicAuthHeader(request, this.Username!, this.Password!);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new WebException("Code defect. Unsupported authentication type: " + Auth);
|
||||
throw new WebException("Code defect. Unsupported authentication type: " + this.Auth);
|
||||
}
|
||||
|
||||
bool supportsIfModifiedSince = false;
|
||||
if (request is HttpWebRequest httpRequest && File.Exists(To))
|
||||
if (request is HttpWebRequest httpRequest && File.Exists(this.To))
|
||||
{
|
||||
supportsIfModifiedSince = true;
|
||||
httpRequest.IfModifiedSince = File.GetLastWriteTime(To);
|
||||
httpRequest.IfModifiedSince = File.GetLastWriteTime(this.To);
|
||||
}
|
||||
|
||||
DateTime lastModified = default;
|
||||
string tmpFilePath = To + ".tmp";
|
||||
string tmpFilePath = this.To + ".tmp";
|
||||
try
|
||||
{
|
||||
#if VNEXT
|
||||
|
@ -217,18 +217,18 @@ namespace winsw
|
|||
#endif
|
||||
}
|
||||
|
||||
FileHelper.MoveOrReplaceFile(To + ".tmp", To);
|
||||
FileHelper.MoveOrReplaceFile(this.To + ".tmp", this.To);
|
||||
|
||||
if (supportsIfModifiedSince)
|
||||
{
|
||||
File.SetLastWriteTime(To, lastModified);
|
||||
File.SetLastWriteTime(this.To, lastModified);
|
||||
}
|
||||
}
|
||||
catch (WebException e)
|
||||
{
|
||||
if (supportsIfModifiedSince && ((HttpWebResponse)e.Response).StatusCode == HttpStatusCode.NotModified)
|
||||
{
|
||||
Logger.Info($"Skipped downloading unmodified resource '{From}'");
|
||||
Logger.Info($"Skipped downloading unmodified resource '{this.From}'");
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -253,6 +253,7 @@ namespace winsw
|
|||
public class CustomProxyInformation
|
||||
{
|
||||
public string ServerAddress { get; set; }
|
||||
|
||||
public NetworkCredential? Credentials { get; set; }
|
||||
|
||||
public CustomProxyInformation(string proxy)
|
||||
|
@ -267,12 +268,12 @@ namespace winsw
|
|||
|
||||
string username = completeCredsStr.Substring(0, credsSeparator);
|
||||
string password = completeCredsStr.Substring(credsSeparator + 1);
|
||||
Credentials = new NetworkCredential(username, password);
|
||||
ServerAddress = proxy.Replace(completeCredsStr + "@", "");
|
||||
this.Credentials = new NetworkCredential(username, password);
|
||||
this.ServerAddress = proxy.Replace(completeCredsStr + "@", string.Empty);
|
||||
}
|
||||
else
|
||||
{
|
||||
ServerAddress = proxy;
|
||||
this.ServerAddress = proxy;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -80,14 +80,19 @@ namespace DynamicProxy
|
|||
|
||||
// Load "this"
|
||||
constructorIL.Emit(OpCodes.Ldarg_0);
|
||||
|
||||
// Load first constructor parameter
|
||||
constructorIL.Emit(OpCodes.Ldarg_1);
|
||||
|
||||
// Set the first parameter into the handler field
|
||||
constructorIL.Emit(OpCodes.Stfld, handlerField);
|
||||
|
||||
// Load "this"
|
||||
constructorIL.Emit(OpCodes.Ldarg_0);
|
||||
|
||||
// Call the super constructor
|
||||
constructorIL.Emit(OpCodes.Call, baseConstructor);
|
||||
|
||||
// Constructor return
|
||||
constructorIL.Emit(OpCodes.Ret);
|
||||
|
||||
|
@ -136,7 +141,8 @@ namespace DynamicProxy
|
|||
methodInfo.Name,
|
||||
/*MethodAttributes.Public | MethodAttributes.Virtual | */ methodInfo.Attributes & ~MethodAttributes.Abstract,
|
||||
CallingConventions.Standard,
|
||||
methodInfo.ReturnType, methodParameters);
|
||||
methodInfo.ReturnType,
|
||||
methodParameters);
|
||||
|
||||
ILGenerator methodIL = methodBuilder.GetILGenerator();
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
using System.Xml;
|
||||
|
||||
namespace winsw.Extensions
|
||||
namespace WinSW.Extensions
|
||||
{
|
||||
public abstract class AbstractWinSWExtension : IWinSWExtension
|
||||
{
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
using System;
|
||||
|
||||
namespace winsw.Extensions
|
||||
namespace WinSW.Extensions
|
||||
{
|
||||
public class ExtensionException : WinSWException
|
||||
{
|
||||
|
@ -9,15 +9,15 @@ namespace winsw.Extensions
|
|||
public ExtensionException(string extensionName, string message)
|
||||
: base(message)
|
||||
{
|
||||
ExtensionId = extensionName;
|
||||
this.ExtensionId = extensionName;
|
||||
}
|
||||
|
||||
public ExtensionException(string extensionName, string message, Exception 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,4 +1,4 @@
|
|||
namespace winsw.Extensions
|
||||
namespace WinSW.Extensions
|
||||
{
|
||||
/// <summary>
|
||||
/// This attribute is used to identify extension points within the code
|
||||
|
@ -6,7 +6,7 @@
|
|||
/// <remarks>
|
||||
/// Each extension point implements its own entry type.
|
||||
/// </remarks>
|
||||
class ExtensionPointAttribute
|
||||
internal class ExtensionPointAttribute
|
||||
{
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
using System.Xml;
|
||||
|
||||
namespace winsw.Extensions
|
||||
namespace WinSW.Extensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Interface for Win Service Wrapper Extension
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
using System.Xml;
|
||||
using winsw.Util;
|
||||
using WinSW.Util;
|
||||
|
||||
namespace winsw.Extensions
|
||||
namespace WinSW.Extensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Describes WinSW extensions in <see cref="IWinSWExtension"/>
|
||||
|
@ -28,9 +28,9 @@ namespace winsw.Extensions
|
|||
|
||||
private WinSWExtensionDescriptor(string id, string className, bool enabled)
|
||||
{
|
||||
Id = id;
|
||||
Enabled = enabled;
|
||||
ClassName = className;
|
||||
this.Id = id;
|
||||
this.Enabled = enabled;
|
||||
this.ClassName = className;
|
||||
}
|
||||
|
||||
public static WinSWExtensionDescriptor FromXml(XmlElement node)
|
||||
|
|
|
@ -3,7 +3,7 @@ using System.Collections.Generic;
|
|||
using System.Xml;
|
||||
using log4net;
|
||||
|
||||
namespace winsw.Extensions
|
||||
namespace WinSW.Extensions
|
||||
{
|
||||
public class WinSWExtensionManager
|
||||
{
|
||||
|
@ -15,8 +15,8 @@ namespace winsw.Extensions
|
|||
|
||||
public WinSWExtensionManager(ServiceDescriptor serviceDescriptor)
|
||||
{
|
||||
ServiceDescriptor = serviceDescriptor;
|
||||
Extensions = new Dictionary<string, IWinSWExtension>();
|
||||
this.ServiceDescriptor = serviceDescriptor;
|
||||
this.Extensions = new Dictionary<string, IWinSWExtension>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -27,7 +27,7 @@ namespace winsw.Extensions
|
|||
/// <exception cref="Exception">Start failure</exception>
|
||||
public void FireOnWrapperStarted()
|
||||
{
|
||||
foreach (var ext in Extensions)
|
||||
foreach (var ext in this.Extensions)
|
||||
{
|
||||
try
|
||||
{
|
||||
|
@ -47,7 +47,7 @@ namespace winsw.Extensions
|
|||
/// </summary>
|
||||
public void FireBeforeWrapperStopped()
|
||||
{
|
||||
foreach (var ext in Extensions)
|
||||
foreach (var ext in this.Extensions)
|
||||
{
|
||||
try
|
||||
{
|
||||
|
@ -66,7 +66,7 @@ namespace winsw.Extensions
|
|||
/// <param name="process">Process</param>
|
||||
public void FireOnProcessStarted(System.Diagnostics.Process process)
|
||||
{
|
||||
foreach (var ext in Extensions)
|
||||
foreach (var ext in this.Extensions)
|
||||
{
|
||||
try
|
||||
{
|
||||
|
@ -85,7 +85,7 @@ namespace winsw.Extensions
|
|||
/// <param name="process">Process</param>
|
||||
public void FireOnProcessTerminated(System.Diagnostics.Process process)
|
||||
{
|
||||
foreach (var ext in Extensions)
|
||||
foreach (var ext in this.Extensions)
|
||||
{
|
||||
try
|
||||
{
|
||||
|
@ -101,16 +101,16 @@ namespace winsw.Extensions
|
|||
// TODO: Implement loading of external extensions. Current version supports internal hack
|
||||
#region Extension load management
|
||||
|
||||
/// <summary>
|
||||
/// Loads extensions according to the configuration file.
|
||||
/// </summary>
|
||||
/// <param name="logger">Logger</param>
|
||||
/// <exception cref="Exception">Loading failure</exception>
|
||||
public void LoadExtensions()
|
||||
{
|
||||
var extensionIds = ServiceDescriptor.ExtensionIds;
|
||||
var extensionIds = this.ServiceDescriptor.ExtensionIds;
|
||||
foreach (string extensionId in extensionIds)
|
||||
{
|
||||
LoadExtension(extensionId);
|
||||
this.LoadExtension(extensionId);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -118,16 +118,15 @@ namespace winsw.Extensions
|
|||
/// Loads extensions from the configuration file
|
||||
/// </summary>
|
||||
/// <param name="id">Extension ID</param>
|
||||
/// <param name="logger">Logger</param>
|
||||
/// <exception cref="Exception">Loading failure</exception>
|
||||
private void LoadExtension(string id)
|
||||
{
|
||||
if (Extensions.ContainsKey(id))
|
||||
if (this.Extensions.ContainsKey(id))
|
||||
{
|
||||
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;
|
||||
if (configNode is null)
|
||||
{
|
||||
|
@ -137,11 +136,11 @@ namespace winsw.Extensions
|
|||
var descriptor = WinSWExtensionDescriptor.FromXml(configNode);
|
||||
if (descriptor.Enabled)
|
||||
{
|
||||
IWinSWExtension extension = CreateExtensionInstance(descriptor.Id, descriptor.ClassName);
|
||||
IWinSWExtension extension = this.CreateExtensionInstance(descriptor.Id, descriptor.ClassName);
|
||||
extension.Descriptor = descriptor;
|
||||
try
|
||||
{
|
||||
extension.Configure(ServiceDescriptor, configNode);
|
||||
extension.Configure(this.ServiceDescriptor, configNode);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{ // Consider any unexpected exception as fatal
|
||||
|
@ -149,7 +148,7 @@ namespace winsw.Extensions
|
|||
throw ex;
|
||||
}
|
||||
|
||||
Extensions.Add(id, extension);
|
||||
this.Extensions.Add(id, extension);
|
||||
Log.Info("Extension loaded: " + id);
|
||||
}
|
||||
else
|
||||
|
|
|
@ -8,12 +8,11 @@ using System.Threading;
|
|||
#if !VNEXT
|
||||
using ICSharpCode.SharpZipLib.Zip;
|
||||
#endif
|
||||
using winsw.Util;
|
||||
using WinSW.Util;
|
||||
|
||||
namespace winsw
|
||||
namespace WinSW
|
||||
{
|
||||
// ReSharper disable once InconsistentNaming
|
||||
public interface EventLogger
|
||||
public interface IEventLogger
|
||||
{
|
||||
void LogEvent(string message);
|
||||
|
||||
|
@ -25,14 +24,13 @@ namespace winsw
|
|||
/// </summary>
|
||||
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>
|
||||
/// Error and information about logging should be reported here.
|
||||
/// </summary>
|
||||
#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.
|
||||
|
||||
/// <summary>
|
||||
|
@ -61,7 +59,7 @@ namespace winsw
|
|||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -72,21 +70,25 @@ namespace winsw
|
|||
public abstract class AbstractFileLogAppender : LogHandler
|
||||
{
|
||||
protected string BaseLogFileName { get; private set; }
|
||||
|
||||
protected bool OutFileDisabled { get; private set; }
|
||||
|
||||
protected bool ErrFileDisabled { get; private set; }
|
||||
|
||||
protected string OutFilePattern { get; private set; }
|
||||
|
||||
protected string ErrFilePattern { get; private set; }
|
||||
|
||||
protected AbstractFileLogAppender(string logDirectory, string baseName, bool outFileDisabled, bool errFileDisabled, string outFilePattern, string errFilePattern)
|
||||
{
|
||||
BaseLogFileName = Path.Combine(logDirectory, baseName);
|
||||
OutFileDisabled = outFileDisabled;
|
||||
OutFilePattern = outFilePattern;
|
||||
ErrFileDisabled = errFileDisabled;
|
||||
ErrFilePattern = errFilePattern;
|
||||
this.BaseLogFileName = Path.Combine(logDirectory, baseName);
|
||||
this.OutFileDisabled = outFileDisabled;
|
||||
this.OutFilePattern = outFilePattern;
|
||||
this.ErrFileDisabled = errFileDisabled;
|
||||
this.ErrFilePattern = errFilePattern;
|
||||
}
|
||||
|
||||
public override void log(StreamReader outputReader, StreamReader errorReader)
|
||||
public override void Log(StreamReader outputReader, StreamReader errorReader)
|
||||
{
|
||||
if (this.OutFileDisabled)
|
||||
{
|
||||
|
@ -117,25 +119,27 @@ namespace winsw
|
|||
public abstract class SimpleLogAppender : AbstractFileLogAppender
|
||||
{
|
||||
public FileMode FileMode { get; private set; }
|
||||
|
||||
public string OutputLogFileName { get; private set; }
|
||||
|
||||
public string ErrorLogFileName { get; private set; }
|
||||
|
||||
protected SimpleLogAppender(string logDirectory, string baseName, FileMode fileMode, bool outFileDisabled, bool errFileDisabled, string outFilePattern, string errFilePattern)
|
||||
: base(logDirectory, baseName, outFileDisabled, errFileDisabled, outFilePattern, errFilePattern)
|
||||
{
|
||||
FileMode = fileMode;
|
||||
OutputLogFileName = BaseLogFileName + ".out.log";
|
||||
ErrorLogFileName = BaseLogFileName + ".err.log";
|
||||
this.FileMode = fileMode;
|
||||
this.OutputLogFileName = this.BaseLogFileName + ".out.log";
|
||||
this.ErrorLogFileName = this.BaseLogFileName + ".err.log";
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
new Thread(() => CopyStream(errorReader, CreateWriter(new FileStream(ErrorLogFileName, FileMode)))).Start();
|
||||
new Thread(() => this.CopyStream(errorReader, this.CreateWriter(new FileStream(this.ErrorLogFileName, this.FileMode)))).Start();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -160,7 +164,7 @@ namespace winsw
|
|||
/// </summary>
|
||||
public class IgnoreLogAppender : LogHandler
|
||||
{
|
||||
public override void log(StreamReader outputReader, StreamReader errorReader)
|
||||
public override void Log(StreamReader outputReader, StreamReader errorReader)
|
||||
{
|
||||
outputReader.Dispose();
|
||||
errorReader.Dispose();
|
||||
|
@ -170,23 +174,24 @@ namespace winsw
|
|||
public class TimeBasedRollingLogAppender : AbstractFileLogAppender
|
||||
{
|
||||
public string Pattern { get; private set; }
|
||||
|
||||
public int Period { get; private set; }
|
||||
|
||||
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)
|
||||
{
|
||||
Pattern = pattern;
|
||||
Period = period;
|
||||
this.Pattern = pattern;
|
||||
this.Period = period;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
new Thread(() => CopyStreamWithDateRotation(errorReader, ErrFilePattern)).Start();
|
||||
new Thread(() => this.CopyStreamWithDateRotation(errorReader, this.ErrFilePattern)).Start();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -194,17 +199,17 @@ namespace winsw
|
|||
/// </summary>
|
||||
private void CopyStreamWithDateRotation(StreamReader reader, string ext)
|
||||
{
|
||||
PeriodicRollingCalendar periodicRollingCalendar = new PeriodicRollingCalendar(Pattern, Period);
|
||||
periodicRollingCalendar.init();
|
||||
PeriodicRollingCalendar periodicRollingCalendar = new PeriodicRollingCalendar(this.Pattern, this.Period);
|
||||
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;
|
||||
while ((line = reader.ReadLine()) != null)
|
||||
{
|
||||
if (periodicRollingCalendar.shouldRoll)
|
||||
if (periodicRollingCalendar.ShouldRoll)
|
||||
{
|
||||
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);
|
||||
|
@ -217,14 +222,10 @@ namespace winsw
|
|||
|
||||
public class SizeBasedRollingLogAppender : AbstractFileLogAppender
|
||||
{
|
||||
// ReSharper disable once InconsistentNaming
|
||||
public static int BYTES_PER_KB = 1024;
|
||||
// ReSharper disable once InconsistentNaming
|
||||
public static int BYTES_PER_MB = 1024 * BYTES_PER_KB;
|
||||
// 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 static int BytesPerKB = 1024;
|
||||
public static int BytesPerMB = 1024 * BytesPerKB;
|
||||
public static int DefaultSizeThreshold = 10 * BytesPerMB; // roll every 10MB.
|
||||
public static int DefaultFilesToKeep = 8;
|
||||
|
||||
public int SizeTheshold { get; private set; }
|
||||
|
||||
|
@ -233,21 +234,23 @@ namespace winsw
|
|||
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)
|
||||
{
|
||||
SizeTheshold = sizeThreshold;
|
||||
FilesToKeep = filesToKeep;
|
||||
this.SizeTheshold = sizeThreshold;
|
||||
this.FilesToKeep = filesToKeep;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
new Thread(() => CopyStreamWithRotation(outputReader, OutFilePattern)).Start();
|
||||
new Thread(() => this.CopyStreamWithRotation(outputReader, this.OutFilePattern)).Start();
|
||||
}
|
||||
|
||||
protected override void LogError(StreamReader errorReader)
|
||||
{
|
||||
new Thread(() => CopyStreamWithRotation(errorReader, ErrFilePattern)).Start();
|
||||
new Thread(() => this.CopyStreamWithRotation(errorReader, this.ErrFilePattern)).Start();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -255,41 +258,45 @@ namespace winsw
|
|||
/// </summary>
|
||||
private void CopyStreamWithRotation(StreamReader reader, string ext)
|
||||
{
|
||||
StreamWriter writer = CreateWriter(new FileStream(BaseLogFileName + ext, FileMode.Append));
|
||||
long fileLength = new FileInfo(BaseLogFileName + ext).Length;
|
||||
StreamWriter writer = this.CreateWriter(new FileStream(this.BaseLogFileName + ext, FileMode.Append));
|
||||
long fileLength = new FileInfo(this.BaseLogFileName + ext).Length;
|
||||
|
||||
string? line;
|
||||
while ((line = reader.ReadLine()) != null)
|
||||
{
|
||||
int lengthToWrite = (line.Length + Environment.NewLine.Length) * sizeof(char);
|
||||
if (fileLength + lengthToWrite > SizeTheshold)
|
||||
if (fileLength + lengthToWrite > this.SizeTheshold)
|
||||
{
|
||||
writer.Dispose();
|
||||
|
||||
try
|
||||
{
|
||||
for (int j = FilesToKeep; j >= 1; j--)
|
||||
for (int j = this.FilesToKeep; j >= 1; j--)
|
||||
{
|
||||
string dst = BaseLogFileName + "." + (j - 1) + ext;
|
||||
string src = BaseLogFileName + "." + (j - 2) + ext;
|
||||
string dst = this.BaseLogFileName + "." + (j - 1) + ext;
|
||||
string src = this.BaseLogFileName + "." + (j - 2) + ext;
|
||||
if (File.Exists(dst))
|
||||
{
|
||||
File.Delete(dst);
|
||||
}
|
||||
|
||||
if (File.Exists(src))
|
||||
{
|
||||
File.Move(src, dst);
|
||||
}
|
||||
}
|
||||
|
||||
File.Move(BaseLogFileName + ext, BaseLogFileName + ".0" + ext);
|
||||
File.Move(this.BaseLogFileName + ext, this.BaseLogFileName + ".0" + ext);
|
||||
}
|
||||
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
|
||||
// we'll infinitely try to roll.
|
||||
writer = CreateWriter(new FileStream(BaseLogFileName + ext, FileMode.Create));
|
||||
fileLength = new FileInfo(BaseLogFileName + ext).Length;
|
||||
writer = this.CreateWriter(new FileStream(this.BaseLogFileName + ext, FileMode.Create));
|
||||
fileLength = new FileInfo(this.BaseLogFileName + ext).Length;
|
||||
}
|
||||
|
||||
writer.WriteLine(line);
|
||||
|
@ -311,25 +318,34 @@ namespace winsw
|
|||
{
|
||||
}
|
||||
|
||||
public override void log(StreamReader outputReader, StreamReader errorReader)
|
||||
public override void Log(StreamReader outputReader, StreamReader errorReader)
|
||||
{
|
||||
if (!OutFileDisabled)
|
||||
MoveFile(OutputLogFileName, OutputLogFileName + ".old");
|
||||
if (!this.OutFileDisabled)
|
||||
{
|
||||
this.MoveFile(this.OutputLogFileName, this.OutputLogFileName + ".old");
|
||||
}
|
||||
|
||||
if (!ErrFileDisabled)
|
||||
MoveFile(ErrorLogFileName, ErrorLogFileName + ".old");
|
||||
if (!this.ErrFileDisabled)
|
||||
{
|
||||
this.MoveFile(this.ErrorLogFileName, this.ErrorLogFileName + ".old");
|
||||
}
|
||||
|
||||
base.log(outputReader, errorReader);
|
||||
base.Log(outputReader, errorReader);
|
||||
}
|
||||
}
|
||||
|
||||
public class RollingSizeTimeLogAppender : AbstractFileLogAppender
|
||||
{
|
||||
public static int BYTES_PER_KB = 1024;
|
||||
public static int BytesPerKB = 1024;
|
||||
|
||||
public int SizeTheshold { get; private set; }
|
||||
|
||||
public string FilePattern { get; private set; }
|
||||
|
||||
public TimeSpan? AutoRollAtTime { get; private set; }
|
||||
|
||||
public int? ZipOlderThanNumDays { get; private set; }
|
||||
|
||||
public string ZipDateFormat { get; private set; }
|
||||
|
||||
public RollingSizeTimeLogAppender(
|
||||
|
@ -346,21 +362,21 @@ namespace winsw
|
|||
string zipdateformat)
|
||||
: base(logDirectory, baseName, outFileDisabled, errFileDisabled, outFilePattern, errFilePattern)
|
||||
{
|
||||
SizeTheshold = sizeThreshold;
|
||||
FilePattern = filePattern;
|
||||
AutoRollAtTime = autoRollAtTime;
|
||||
ZipOlderThanNumDays = zipolderthannumdays;
|
||||
ZipDateFormat = zipdateformat;
|
||||
this.SizeTheshold = sizeThreshold;
|
||||
this.FilePattern = filePattern;
|
||||
this.AutoRollAtTime = autoRollAtTime;
|
||||
this.ZipOlderThanNumDays = zipolderthannumdays;
|
||||
this.ZipDateFormat = zipdateformat;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
new Thread(() => CopyStreamWithRotation(errorReader, ErrFilePattern)).Start();
|
||||
new Thread(() => this.CopyStreamWithRotation(errorReader, this.ErrFilePattern)).Start();
|
||||
}
|
||||
|
||||
private void CopyStreamWithRotation(StreamReader reader, string extension)
|
||||
|
@ -368,18 +384,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
|
||||
var fileLock = new object();
|
||||
|
||||
var baseDirectory = Path.GetDirectoryName(BaseLogFileName)!;
|
||||
var baseFileName = Path.GetFileName(BaseLogFileName);
|
||||
var logFile = BaseLogFileName + extension;
|
||||
var baseDirectory = Path.GetDirectoryName(this.BaseLogFileName)!;
|
||||
var baseFileName = Path.GetFileName(this.BaseLogFileName);
|
||||
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;
|
||||
|
||||
// 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
|
||||
var tickTime = SetupRollTimer(autoRollAtTime);
|
||||
var tickTime = this.SetupRollTimer(autoRollAtTime);
|
||||
var timer = new System.Timers.Timer(tickTime);
|
||||
timer.Elapsed += (s, e) =>
|
||||
{
|
||||
|
@ -391,25 +407,25 @@ namespace winsw
|
|||
writer.Dispose();
|
||||
|
||||
var now = DateTime.Now.AddDays(-1);
|
||||
var nextFileNumber = GetNextFileNumber(extension, baseDirectory, baseFileName, now);
|
||||
var nextFileName = Path.Combine(baseDirectory, string.Format("{0}.{1}.#{2:D4}{3}", baseFileName, now.ToString(FilePattern), nextFileNumber, extension));
|
||||
var nextFileNumber = this.GetNextFileNumber(extension, baseDirectory, baseFileName, now);
|
||||
var nextFileName = Path.Combine(baseDirectory, string.Format("{0}.{1}.#{2:D4}{3}", baseFileName, now.ToString(this.FilePattern), nextFileNumber, extension));
|
||||
File.Move(logFile, nextFileName);
|
||||
|
||||
writer = CreateWriter(new FileStream(logFile, FileMode.Create));
|
||||
writer = this.CreateWriter(new FileStream(logFile, FileMode.Create));
|
||||
fileLength = new FileInfo(logFile).Length;
|
||||
}
|
||||
|
||||
// Next day so check if file can be zipped
|
||||
ZipFiles(baseDirectory, extension, baseFileName);
|
||||
this.ZipFiles(baseDirectory, extension, baseFileName);
|
||||
}
|
||||
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
|
||||
{
|
||||
// Recalculate the next interval
|
||||
timer.Interval = SetupRollTimer(autoRollAtTime);
|
||||
timer.Interval = this.SetupRollTimer(autoRollAtTime);
|
||||
timer.Start();
|
||||
}
|
||||
};
|
||||
|
@ -422,26 +438,26 @@ namespace winsw
|
|||
lock (fileLock)
|
||||
{
|
||||
int lengthToWrite = (line.Length + Environment.NewLine.Length) * sizeof(char);
|
||||
if (fileLength + lengthToWrite > SizeTheshold)
|
||||
if (fileLength + lengthToWrite > this.SizeTheshold)
|
||||
{
|
||||
try
|
||||
{
|
||||
// roll file
|
||||
var now = DateTime.Now;
|
||||
var nextFileNumber = GetNextFileNumber(extension, baseDirectory, baseFileName, now);
|
||||
var nextFileName =
|
||||
Path.Combine(baseDirectory,
|
||||
string.Format("{0}.{1}.#{2:D4}{3}", baseFileName, now.ToString(FilePattern), nextFileNumber, extension));
|
||||
var nextFileNumber = this.GetNextFileNumber(extension, baseDirectory, baseFileName, now);
|
||||
var nextFileName = Path.Combine(
|
||||
baseDirectory,
|
||||
string.Format("{0}.{1}.#{2:D4}{3}", baseFileName, now.ToString(this.FilePattern), nextFileNumber, extension));
|
||||
File.Move(logFile, nextFileName);
|
||||
|
||||
// even if the log rotation fails, create a new one, or else
|
||||
// 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;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
EventLogger.LogEvent($"Failed to roll size time log: {e.Message}");
|
||||
this.EventLogger.LogEvent($"Failed to roll size time log: {e.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -456,28 +472,32 @@ namespace winsw
|
|||
|
||||
private void ZipFiles(string directory, string fileExtension, string zipFileBaseName)
|
||||
{
|
||||
if (ZipOlderThanNumDays is null || ZipOlderThanNumDays <= 0)
|
||||
if (this.ZipOlderThanNumDays is null || this.ZipOlderThanNumDays <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
foreach (string path in Directory.GetFiles(directory, "*" + fileExtension))
|
||||
{
|
||||
var fileInfo = new FileInfo(path);
|
||||
if (fileInfo.LastWriteTimeUtc >= DateTime.UtcNow.AddDays(-ZipOlderThanNumDays.Value))
|
||||
if (fileInfo.LastWriteTimeUtc >= DateTime.UtcNow.AddDays(-this.ZipOlderThanNumDays.Value))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
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");
|
||||
ZipOneFile(path, sourceFileName, zipFilePath);
|
||||
this.ZipOneFile(path, sourceFileName, zipFilePath);
|
||||
|
||||
File.Delete(path);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
EventLogger.LogEvent($"Failed to Zip files. Error {e.Message}");
|
||||
this.EventLogger.LogEvent($"Failed to Zip files. Error {e.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -496,7 +516,7 @@ namespace winsw
|
|||
}
|
||||
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
|
||||
{
|
||||
|
@ -521,7 +541,7 @@ namespace winsw
|
|||
}
|
||||
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}");
|
||||
zipFile?.AbortUpdate();
|
||||
}
|
||||
finally
|
||||
|
@ -543,7 +563,9 @@ namespace winsw
|
|||
autoRollAtTime.Seconds,
|
||||
0);
|
||||
if (nowTime > scheduledTime)
|
||||
{
|
||||
scheduledTime = scheduledTime.AddDays(1);
|
||||
}
|
||||
|
||||
double tickTime = (scheduledTime - DateTime.Now).TotalMilliseconds;
|
||||
return tickTime;
|
||||
|
@ -552,7 +574,7 @@ namespace winsw
|
|||
private int GetNextFileNumber(string ext, string baseDirectory, string baseFileName, DateTime now)
|
||||
{
|
||||
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)
|
||||
{
|
||||
nextFileNumber = 1;
|
||||
|
@ -566,11 +588,12 @@ namespace winsw
|
|||
var filenameOnly = Path.GetFileNameWithoutExtension(f);
|
||||
var hashIndex = filenameOnly.IndexOf('#');
|
||||
var lastNumberAsString = filenameOnly.Substring(hashIndex + 1, 4);
|
||||
// var lastNumberAsString = filenameOnly.Substring(filenameOnly.Length - 4, 4);
|
||||
if (int.TryParse(lastNumberAsString, out int lastNumber))
|
||||
{
|
||||
if (lastNumber > nextFileNumber)
|
||||
{
|
||||
nextFileNumber = lastNumber;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -584,7 +607,9 @@ namespace winsw
|
|||
}
|
||||
|
||||
if (nextFileNumber == 0)
|
||||
{
|
||||
throw new IOException("Cannot roll the file because matching pattern not found");
|
||||
}
|
||||
|
||||
nextFileNumber++;
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
using System.Diagnostics;
|
||||
|
||||
namespace winsw.Logging
|
||||
namespace WinSW.Logging
|
||||
{
|
||||
/// <summary>
|
||||
/// Indicates that the class may reference the event log
|
||||
|
@ -11,6 +11,6 @@ namespace winsw.Logging
|
|||
/// Locates Event Log for the service.
|
||||
/// </summary>
|
||||
/// <returns>Event Log or null if it is not avilable</returns>
|
||||
EventLog? locate();
|
||||
EventLog? Locate();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
using log4net.Appender;
|
||||
using log4net.Core;
|
||||
|
||||
namespace winsw.Logging
|
||||
namespace WinSW.Logging
|
||||
{
|
||||
/// <summary>
|
||||
/// Implementes service Event log appender for log4j.
|
||||
|
@ -11,18 +11,18 @@ namespace winsw.Logging
|
|||
public class ServiceEventLogAppender : AppenderSkeleton
|
||||
{
|
||||
#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.
|
||||
|
||||
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
|
||||
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)
|
||||
{
|
||||
|
|
|
@ -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
|
||||
{
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
namespace winsw.Native
|
||||
#pragma warning disable SA1310 // Field names should not contain underscore
|
||||
|
||||
namespace WinSW.Native
|
||||
{
|
||||
internal static class Errors
|
||||
{
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace winsw.Native
|
||||
namespace WinSW.Native
|
||||
{
|
||||
internal static class HandleApis
|
||||
{
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
using System.Runtime.InteropServices;
|
||||
using Microsoft.Win32.SafeHandles;
|
||||
|
||||
namespace winsw.Native
|
||||
namespace WinSW.Native
|
||||
{
|
||||
internal static class Kernel32
|
||||
{
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
namespace winsw.Native
|
||||
namespace WinSW.Native
|
||||
{
|
||||
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.Security.Principal;
|
||||
|
||||
namespace winsw.Native
|
||||
namespace WinSW.Native
|
||||
{
|
||||
internal static class ProcessApis
|
||||
{
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Runtime.InteropServices;
|
||||
using static winsw.Native.SecurityApis;
|
||||
using static WinSW.Native.SecurityApis;
|
||||
|
||||
namespace winsw.Native
|
||||
namespace WinSW.Native
|
||||
{
|
||||
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;
|
||||
|
||||
namespace winsw.Native
|
||||
namespace WinSW.Native
|
||||
{
|
||||
internal static class SecurityApis
|
||||
{
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
using System;
|
||||
using System.Security.AccessControl;
|
||||
using static winsw.Native.ServiceApis;
|
||||
using static WinSW.Native.ServiceApis;
|
||||
|
||||
namespace winsw.Native
|
||||
namespace WinSW.Native
|
||||
{
|
||||
public enum SC_ACTION_TYPE
|
||||
{
|
||||
|
@ -106,7 +106,8 @@ namespace winsw.Native
|
|||
{
|
||||
fixed (SC_ACTION* actionsPtr = actions)
|
||||
{
|
||||
if (!ChangeServiceConfig2(this.handle,
|
||||
if (!ChangeServiceConfig2(
|
||||
this.handle,
|
||||
ServiceConfigInfoLevels.FAILURE_ACTIONS,
|
||||
new SERVICE_FAILURE_ACTIONS
|
||||
{
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
using System.Runtime.InteropServices;
|
||||
using System.Security.AccessControl;
|
||||
|
||||
namespace winsw.Native
|
||||
namespace WinSW.Native
|
||||
{
|
||||
internal static class ServiceApis
|
||||
{
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace winsw.Native
|
||||
namespace WinSW.Native
|
||||
{
|
||||
internal static class Throw
|
||||
{
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#if !NETCOREAPP
|
||||
#pragma warning disable SA1502 // Element should not be on a single line
|
||||
namespace System.Diagnostics.CodeAnalysis
|
||||
{
|
||||
/// <summary>Specifies that null is allowed as an input even if the corresponding type disallows it.</summary>
|
||||
|
|
|
@ -1,33 +1,29 @@
|
|||
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
|
||||
{
|
||||
private readonly string _format;
|
||||
private readonly long _period;
|
||||
private DateTime _currentRoll;
|
||||
private DateTime _nextRoll;
|
||||
private readonly string format;
|
||||
private readonly long period;
|
||||
private DateTime currentRoll;
|
||||
private DateTime nextRoll;
|
||||
|
||||
public PeriodicRollingCalendar(string format, long period)
|
||||
{
|
||||
_format = format;
|
||||
_period = period;
|
||||
_currentRoll = DateTime.Now;
|
||||
this.format = format;
|
||||
this.period = period;
|
||||
this.currentRoll = DateTime.Now;
|
||||
}
|
||||
|
||||
public void init()
|
||||
public void Init()
|
||||
{
|
||||
periodicityType = determinePeriodicityType();
|
||||
_nextRoll = nextTriggeringTime(_currentRoll, _period);
|
||||
this.PeriodicityType = this.DeterminePeriodicityType();
|
||||
this.nextRoll = this.NextTriggeringTime(this.currentRoll, this.period);
|
||||
}
|
||||
|
||||
public enum PeriodicityType
|
||||
public enum Periodicity
|
||||
{
|
||||
ERRONEOUS,
|
||||
TOP_OF_MILLISECOND,
|
||||
|
@ -37,23 +33,23 @@ namespace winsw
|
|||
TOP_OF_DAY
|
||||
}
|
||||
|
||||
private static readonly PeriodicityType[] VALID_ORDERED_LIST =
|
||||
private static readonly Periodicity[] ValidOrderedList =
|
||||
{
|
||||
PeriodicityType.TOP_OF_MILLISECOND, PeriodicityType.TOP_OF_SECOND, PeriodicityType.TOP_OF_MINUTE, PeriodicityType.TOP_OF_HOUR, PeriodicityType.TOP_OF_DAY
|
||||
Periodicity.TOP_OF_MILLISECOND, Periodicity.TOP_OF_SECOND, Periodicity.TOP_OF_MINUTE, Periodicity.TOP_OF_HOUR, Periodicity.TOP_OF_DAY
|
||||
};
|
||||
|
||||
private PeriodicityType determinePeriodicityType()
|
||||
private Periodicity DeterminePeriodicityType()
|
||||
{
|
||||
PeriodicRollingCalendar periodicRollingCalendar = new PeriodicRollingCalendar(_format, _period);
|
||||
PeriodicRollingCalendar periodicRollingCalendar = new PeriodicRollingCalendar(this.format, this.period);
|
||||
DateTime epoch = new DateTime(1970, 1, 1);
|
||||
|
||||
foreach (PeriodicityType i in VALID_ORDERED_LIST)
|
||||
foreach (Periodicity i in ValidOrderedList)
|
||||
{
|
||||
string r0 = epoch.ToString(_format);
|
||||
periodicRollingCalendar.periodicityType = i;
|
||||
string r0 = epoch.ToString(this.format);
|
||||
periodicRollingCalendar.PeriodicityType = i;
|
||||
|
||||
DateTime next = periodicRollingCalendar.nextTriggeringTime(epoch, 1);
|
||||
string r1 = next.ToString(_format);
|
||||
DateTime next = periodicRollingCalendar.NextTriggeringTime(epoch, 1);
|
||||
string r1 = next.ToString(this.format);
|
||||
|
||||
if (r0 != r1)
|
||||
{
|
||||
|
@ -61,45 +57,45 @@ namespace winsw
|
|||
}
|
||||
}
|
||||
|
||||
return PeriodicityType.ERRONEOUS;
|
||||
return Periodicity.ERRONEOUS;
|
||||
}
|
||||
|
||||
private DateTime nextTriggeringTime(DateTime input, long increment) => periodicityType switch
|
||||
private DateTime NextTriggeringTime(DateTime input, long increment) => this.PeriodicityType switch
|
||||
{
|
||||
PeriodicityType.TOP_OF_MILLISECOND =>
|
||||
Periodicity.TOP_OF_MILLISECOND =>
|
||||
new DateTime(input.Year, input.Month, input.Day, input.Hour, input.Minute, input.Second, input.Millisecond)
|
||||
.AddMilliseconds(increment),
|
||||
|
||||
PeriodicityType.TOP_OF_SECOND =>
|
||||
Periodicity.TOP_OF_SECOND =>
|
||||
new DateTime(input.Year, input.Month, input.Day, input.Hour, input.Minute, input.Second)
|
||||
.AddSeconds(increment),
|
||||
|
||||
PeriodicityType.TOP_OF_MINUTE =>
|
||||
Periodicity.TOP_OF_MINUTE =>
|
||||
new DateTime(input.Year, input.Month, input.Day, input.Hour, input.Minute, 0)
|
||||
.AddMinutes(increment),
|
||||
|
||||
PeriodicityType.TOP_OF_HOUR =>
|
||||
Periodicity.TOP_OF_HOUR =>
|
||||
new DateTime(input.Year, input.Month, input.Day, input.Hour, 0, 0)
|
||||
.AddHours(increment),
|
||||
|
||||
PeriodicityType.TOP_OF_DAY =>
|
||||
Periodicity.TOP_OF_DAY =>
|
||||
new DateTime(input.Year, input.Month, input.Day)
|
||||
.AddDays(increment),
|
||||
|
||||
_ => throw new Exception("invalid periodicity type: " + periodicityType),
|
||||
_ => throw new Exception("invalid periodicity type: " + this.PeriodicityType),
|
||||
};
|
||||
|
||||
public PeriodicityType periodicityType { get; set; }
|
||||
public Periodicity PeriodicityType { get; set; }
|
||||
|
||||
public bool shouldRoll
|
||||
public bool ShouldRoll
|
||||
{
|
||||
get
|
||||
{
|
||||
DateTime now = DateTime.Now;
|
||||
if (now > _nextRoll)
|
||||
if (now > this.nextRoll)
|
||||
{
|
||||
_currentRoll = now;
|
||||
_nextRoll = nextTriggeringTime(now, _period);
|
||||
this.currentRoll = now;
|
||||
this.nextRoll = this.NextTriggeringTime(now, this.period);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -107,6 +103,6 @@ namespace winsw
|
|||
}
|
||||
}
|
||||
|
||||
public string format => _currentRoll.ToString(_format);
|
||||
public string Format => this.currentRoll.ToString(this.format);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,19 +4,18 @@ using System.Diagnostics;
|
|||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Xml;
|
||||
using winsw.Configuration;
|
||||
using winsw.Native;
|
||||
using winsw.Util;
|
||||
using WinSW.Configuration;
|
||||
using WinSW.Native;
|
||||
using WinSW.Util;
|
||||
using WMI;
|
||||
|
||||
namespace winsw
|
||||
namespace WinSW
|
||||
{
|
||||
/// <summary>
|
||||
/// In-memory representation of the configuration file.
|
||||
/// </summary>
|
||||
public class ServiceDescriptor : IWinSWConfiguration
|
||||
{
|
||||
// ReSharper disable once InconsistentNaming
|
||||
protected readonly XmlDocument dom = new XmlDocument();
|
||||
|
||||
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,
|
||||
// as well as trimming off ".vshost" suffix (which is used during debugging)
|
||||
// Get the first parent to go into the recursive loop
|
||||
string p = ExecutablePath;
|
||||
string p = this.ExecutablePath;
|
||||
string baseName = Path.GetFileNameWithoutExtension(p);
|
||||
if (baseName.EndsWith(".vshost"))
|
||||
{
|
||||
baseName = baseName.Substring(0, baseName.Length - 7);
|
||||
}
|
||||
|
||||
DirectoryInfo d = new DirectoryInfo(Path.GetDirectoryName(p));
|
||||
while (true)
|
||||
{
|
||||
if (File.Exists(Path.Combine(d.FullName, baseName + ".xml")))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (d.Parent is null)
|
||||
{
|
||||
throw new FileNotFoundException("Unable to locate " + baseName + ".xml file within executable directory or any parents");
|
||||
}
|
||||
|
||||
d = d.Parent;
|
||||
}
|
||||
|
||||
BaseName = baseName;
|
||||
BasePath = Path.Combine(d.FullName, BaseName);
|
||||
this.BaseName = baseName;
|
||||
this.BasePath = Path.Combine(d.FullName, this.BaseName);
|
||||
|
||||
try
|
||||
{
|
||||
dom.Load(BasePath + ".xml");
|
||||
this.dom.Load(this.BasePath + ".xml");
|
||||
}
|
||||
catch (XmlException e)
|
||||
{
|
||||
|
@ -78,13 +83,13 @@ namespace winsw
|
|||
Environment.SetEnvironmentVariable("BASE", d.FullName);
|
||||
|
||||
// ditto for ID
|
||||
Environment.SetEnvironmentVariable("SERVICE_ID", Id);
|
||||
Environment.SetEnvironmentVariable("SERVICE_ID", this.Id);
|
||||
|
||||
// New name
|
||||
Environment.SetEnvironmentVariable(WinSWSystem.ENVVAR_NAME_EXECUTABLE_PATH, ExecutablePath);
|
||||
Environment.SetEnvironmentVariable(WinSWSystem.EnvVarNameExecutablePath, this.ExecutablePath);
|
||||
|
||||
// Also inject system environment variables
|
||||
Environment.SetEnvironmentVariable(WinSWSystem.ENVVAR_NAME_SERVICE_ID, Id);
|
||||
Environment.SetEnvironmentVariable(WinSWSystem.EnvVarNameServiceId, this.Id);
|
||||
|
||||
this.environmentVariables = this.LoadEnvironmentVariables();
|
||||
}
|
||||
|
@ -101,7 +106,6 @@ namespace winsw
|
|||
this.environmentVariables = this.LoadEnvironmentVariables();
|
||||
}
|
||||
|
||||
// ReSharper disable once InconsistentNaming
|
||||
public static ServiceDescriptor FromXML(string xml)
|
||||
{
|
||||
var dom = new XmlDocument();
|
||||
|
@ -111,21 +115,23 @@ namespace winsw
|
|||
|
||||
private string SingleElement(string tagName)
|
||||
{
|
||||
return SingleElement(tagName, false)!;
|
||||
return this.SingleElement(tagName, false)!;
|
||||
}
|
||||
|
||||
private string? SingleElement(string tagName, bool optional)
|
||||
{
|
||||
XmlNode? n = dom.SelectSingleNode("//" + tagName);
|
||||
XmlNode? n = this.dom.SelectSingleNode("//" + tagName);
|
||||
if (n is null && !optional)
|
||||
{
|
||||
throw new InvalidDataException("<" + tagName + "> is missing in configuration XML");
|
||||
}
|
||||
|
||||
return n is null ? null : Environment.ExpandEnvironmentVariables(n.InnerText);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
@ -139,8 +145,8 @@ namespace winsw
|
|||
|
||||
private TimeSpan SingleTimeSpanElement(XmlNode parent, string tagName, TimeSpan defaultValue)
|
||||
{
|
||||
string? value = SingleElement(tagName, true);
|
||||
return value is null ? defaultValue : ParseTimeSpan(value);
|
||||
string? value = this.SingleElement(tagName, true);
|
||||
return value is null ? defaultValue : this.ParseTimeSpan(value);
|
||||
}
|
||||
|
||||
private TimeSpan ParseTimeSpan(string v)
|
||||
|
@ -175,14 +181,14 @@ namespace winsw
|
|||
/// <summary>
|
||||
/// Path to the executable.
|
||||
/// </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>
|
||||
/// Optionally specify a different Path to an executable to shutdown the service.
|
||||
/// </summary>
|
||||
public string? StopExecutable => SingleElement("stopexecutable", true);
|
||||
public string? StopExecutable => this.SingleElement("stopexecutable", true);
|
||||
|
||||
/// <summary>
|
||||
/// <c>arguments</c> or multiple optional <c>argument</c> elements which overrule the arguments element.
|
||||
|
@ -191,14 +197,14 @@ namespace winsw
|
|||
{
|
||||
get
|
||||
{
|
||||
string? arguments = AppendTags("argument", null);
|
||||
string? arguments = this.AppendTags("argument", null);
|
||||
|
||||
if (!(arguments is null))
|
||||
{
|
||||
return arguments;
|
||||
}
|
||||
|
||||
XmlNode? argumentsNode = dom.SelectSingleNode("//arguments");
|
||||
XmlNode? argumentsNode = this.dom.SelectSingleNode("//arguments");
|
||||
|
||||
return argumentsNode is null ? Defaults.Arguments : Environment.ExpandEnvironmentVariables(argumentsNode.InnerText);
|
||||
}
|
||||
|
@ -211,14 +217,14 @@ namespace winsw
|
|||
{
|
||||
get
|
||||
{
|
||||
string? startArguments = AppendTags("startargument", null);
|
||||
string? startArguments = this.AppendTags("startargument", null);
|
||||
|
||||
if (!(startArguments is null))
|
||||
{
|
||||
return startArguments;
|
||||
}
|
||||
|
||||
XmlNode? startArgumentsNode = dom.SelectSingleNode("//startarguments");
|
||||
XmlNode? startArgumentsNode = this.dom.SelectSingleNode("//startarguments");
|
||||
|
||||
return startArgumentsNode is null ? null : Environment.ExpandEnvironmentVariables(startArgumentsNode.InnerText);
|
||||
}
|
||||
|
@ -231,14 +237,14 @@ namespace winsw
|
|||
{
|
||||
get
|
||||
{
|
||||
string? stopArguments = AppendTags("stopargument", null);
|
||||
string? stopArguments = this.AppendTags("stopargument", null);
|
||||
|
||||
if (!(stopArguments is null))
|
||||
{
|
||||
return stopArguments;
|
||||
}
|
||||
|
||||
XmlNode? stopArgumentsNode = dom.SelectSingleNode("//stoparguments");
|
||||
XmlNode? stopArgumentsNode = this.dom.SelectSingleNode("//stoparguments");
|
||||
|
||||
return stopArgumentsNode is null ? null : Environment.ExpandEnvironmentVariables(stopArgumentsNode.InnerText);
|
||||
}
|
||||
|
@ -248,7 +254,7 @@ namespace winsw
|
|||
{
|
||||
get
|
||||
{
|
||||
var wd = SingleElement("workingdirectory", true);
|
||||
var wd = this.SingleElement("workingdirectory", true);
|
||||
return string.IsNullOrEmpty(wd) ? Defaults.WorkingDirectory : wd!;
|
||||
}
|
||||
}
|
||||
|
@ -257,7 +263,7 @@ namespace winsw
|
|||
{
|
||||
get
|
||||
{
|
||||
XmlNode? argumentNode = ExtensionsConfiguration;
|
||||
XmlNode? argumentNode = this.ExtensionsConfiguration;
|
||||
XmlNodeList? extensions = argumentNode?.SelectNodes("extension");
|
||||
if (extensions is null)
|
||||
{
|
||||
|
@ -274,7 +280,7 @@ namespace winsw
|
|||
}
|
||||
}
|
||||
|
||||
public XmlNode? ExtensionsConfiguration => dom.SelectSingleNode("//extensions");
|
||||
public XmlNode? ExtensionsConfiguration => this.dom.SelectSingleNode("//extensions");
|
||||
|
||||
/// <summary>
|
||||
/// Combines the contents of all the elements of the given name,
|
||||
|
@ -282,7 +288,7 @@ namespace winsw
|
|||
/// </summary>
|
||||
private string? AppendTags(string tagName, string? defaultValue = null)
|
||||
{
|
||||
XmlNode? argumentNode = dom.SelectSingleNode("//" + tagName);
|
||||
XmlNode? argumentNode = this.dom.SelectSingleNode("//" + tagName);
|
||||
if (argumentNode is null)
|
||||
{
|
||||
return defaultValue;
|
||||
|
@ -290,7 +296,7 @@ namespace winsw
|
|||
|
||||
StringBuilder arguments = new StringBuilder();
|
||||
|
||||
XmlNodeList argumentNodeList = dom.SelectNodes("//" + tagName);
|
||||
XmlNodeList argumentNodeList = this.dom.SelectNodes("//" + tagName);
|
||||
for (int i = 0; i < argumentNodeList.Count; i++)
|
||||
{
|
||||
arguments.Append(' ');
|
||||
|
@ -325,7 +331,7 @@ namespace winsw
|
|||
{
|
||||
get
|
||||
{
|
||||
XmlNode? loggingNode = dom.SelectSingleNode("//logpath");
|
||||
XmlNode? loggingNode = this.dom.SelectSingleNode("//logpath");
|
||||
|
||||
return loggingNode is null
|
||||
? Defaults.LogDirectory
|
||||
|
@ -340,7 +346,7 @@ namespace winsw
|
|||
string? mode = null;
|
||||
|
||||
// first, backward compatibility with older configuration
|
||||
XmlElement? e = (XmlElement?)dom.SelectSingleNode("//logmode");
|
||||
XmlElement? e = (XmlElement?)this.dom.SelectSingleNode("//logmode");
|
||||
if (e != null)
|
||||
{
|
||||
mode = e.InnerText;
|
||||
|
@ -348,9 +354,11 @@ namespace winsw
|
|||
else
|
||||
{
|
||||
// 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)
|
||||
{
|
||||
mode = e.GetAttribute("mode");
|
||||
}
|
||||
}
|
||||
|
||||
return mode ?? Defaults.LogMode;
|
||||
|
@ -361,21 +369,21 @@ namespace winsw
|
|||
{
|
||||
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
|
||||
{
|
||||
get
|
||||
{
|
||||
XmlNode? loggingName = dom.SelectSingleNode("//outfilepattern");
|
||||
XmlNode? loggingName = this.dom.SelectSingleNode("//outfilepattern");
|
||||
|
||||
return loggingName is null ? Defaults.OutFilePattern : Environment.ExpandEnvironmentVariables(loggingName.InnerText);
|
||||
}
|
||||
|
@ -385,7 +393,7 @@ namespace winsw
|
|||
{
|
||||
get
|
||||
{
|
||||
XmlNode? loggingName = dom.SelectSingleNode("//errfilepattern");
|
||||
XmlNode? loggingName = this.dom.SelectSingleNode("//errfilepattern");
|
||||
|
||||
return loggingName is null ? Defaults.ErrFilePattern : Environment.ExpandEnvironmentVariables(loggingName.InnerText);
|
||||
}
|
||||
|
@ -395,25 +403,25 @@ namespace winsw
|
|||
{
|
||||
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
|
||||
e ??= (XmlElement?)dom.SelectSingleNode("//log")!; // WARNING: NRE
|
||||
e ??= (XmlElement?)this.dom.SelectSingleNode("//log")!; // WARNING: NRE
|
||||
|
||||
int sizeThreshold;
|
||||
switch (LogMode)
|
||||
switch (this.LogMode)
|
||||
{
|
||||
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":
|
||||
return new IgnoreLogAppender();
|
||||
|
||||
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":
|
||||
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":
|
||||
XmlNode? patternNode = e.SelectSingleNode("pattern");
|
||||
|
@ -423,19 +431,19 @@ namespace winsw
|
|||
}
|
||||
|
||||
var pattern = patternNode.InnerText;
|
||||
int period = SingleIntElement(e, "period", 1);
|
||||
return new TimeBasedRollingLogAppender(LogDirectory, LogName, OutFileDisabled, ErrFileDisabled, OutFilePattern, ErrFilePattern, pattern, period);
|
||||
int period = this.SingleIntElement(e, "period", 1);
|
||||
return new TimeBasedRollingLogAppender(this.LogDirectory, this.LogName, this.OutFileDisabled, this.ErrFileDisabled, this.OutFilePattern, this.ErrFilePattern, pattern, period);
|
||||
|
||||
case "roll-by-size":
|
||||
sizeThreshold = SingleIntElement(e, "sizeThreshold", 10 * 1024) * SizeBasedRollingLogAppender.BYTES_PER_KB;
|
||||
int keepFiles = SingleIntElement(e, "keepFiles", SizeBasedRollingLogAppender.DEFAULT_FILES_TO_KEEP);
|
||||
return new SizeBasedRollingLogAppender(LogDirectory, LogName, OutFileDisabled, ErrFileDisabled, OutFilePattern, ErrFilePattern, sizeThreshold, keepFiles);
|
||||
sizeThreshold = this.SingleIntElement(e, "sizeThreshold", 10 * 1024) * SizeBasedRollingLogAppender.BytesPerKB;
|
||||
int keepFiles = this.SingleIntElement(e, "keepFiles", SizeBasedRollingLogAppender.DefaultFilesToKeep);
|
||||
return new SizeBasedRollingLogAppender(this.LogDirectory, this.LogName, this.OutFileDisabled, this.ErrFileDisabled, this.OutFilePattern, this.ErrFilePattern, sizeThreshold, keepFiles);
|
||||
|
||||
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":
|
||||
sizeThreshold = SingleIntElement(e, "sizeThreshold", 10 * 1024) * RollingSizeTimeLogAppender.BYTES_PER_KB;
|
||||
sizeThreshold = this.SingleIntElement(e, "sizeThreshold", 10 * 1024) * RollingSizeTimeLogAppender.BytesPerKB;
|
||||
XmlNode? filePatternNode = e.SelectSingleNode("pattern");
|
||||
if (filePatternNode is null)
|
||||
{
|
||||
|
@ -448,7 +456,9 @@ namespace winsw
|
|||
{
|
||||
// validate it
|
||||
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.");
|
||||
}
|
||||
|
||||
autoRollAtTime = autoRollAtTimeValue;
|
||||
}
|
||||
|
@ -459,7 +469,9 @@ namespace winsw
|
|||
{
|
||||
// validate it
|
||||
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.");
|
||||
}
|
||||
|
||||
zipolderthannumdays = zipolderthannumdaysValue;
|
||||
}
|
||||
|
@ -467,10 +479,10 @@ namespace winsw
|
|||
XmlNode? zipdateformatNode = e.SelectSingleNode("zipDateFormat");
|
||||
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:
|
||||
throw new InvalidDataException("Undefined logging mode: " + LogMode);
|
||||
throw new InvalidDataException("Undefined logging mode: " + this.LogMode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -482,7 +494,7 @@ namespace winsw
|
|||
{
|
||||
get
|
||||
{
|
||||
XmlNodeList? nodeList = dom.SelectNodes("//depend");
|
||||
XmlNodeList? nodeList = this.dom.SelectNodes("//depend");
|
||||
if (nodeList is null)
|
||||
{
|
||||
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>
|
||||
/// Start mode of the Service
|
||||
|
@ -511,9 +523,11 @@ namespace winsw
|
|||
{
|
||||
get
|
||||
{
|
||||
string? p = SingleElement("startmode", true);
|
||||
string? p = this.SingleElement("startmode", true);
|
||||
if (p is null)
|
||||
{
|
||||
return Defaults.StartMode;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
|
@ -536,32 +550,32 @@ namespace winsw
|
|||
/// 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.
|
||||
/// </summary>
|
||||
public bool DelayedAutoStart => dom.SelectSingleNode("//delayedAutoStart") != null;
|
||||
public bool DelayedAutoStart => this.dom.SelectSingleNode("//delayedAutoStart") != null;
|
||||
|
||||
/// <summary>
|
||||
/// 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
|
||||
/// </summary>
|
||||
public bool BeepOnShutdown => dom.SelectSingleNode("//beeponshutdown") != null;
|
||||
public bool BeepOnShutdown => this.dom.SelectSingleNode("//beeponshutdown") != null;
|
||||
|
||||
/// <summary>
|
||||
/// 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
|
||||
/// with either an incremented checkPoint value or a change in currentState. (see http://msdn.microsoft.com/en-us/library/ms685996.aspx)
|
||||
/// </summary>
|
||||
public TimeSpan WaitHint => SingleTimeSpanElement(dom, "waithint", Defaults.WaitHint);
|
||||
public TimeSpan WaitHint => this.SingleTimeSpanElement(this.dom, "waithint", Defaults.WaitHint);
|
||||
|
||||
/// <summary>
|
||||
/// The time before the service should make its next call to the SetServiceStatus function
|
||||
/// 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.
|
||||
/// </summary>
|
||||
public TimeSpan SleepTime => SingleTimeSpanElement(dom, "sleeptime", Defaults.SleepTime);
|
||||
public TimeSpan SleepTime => this.SingleTimeSpanElement(this.dom, "sleeptime", Defaults.SleepTime);
|
||||
|
||||
/// <summary>
|
||||
/// True if the service can interact with the desktop.
|
||||
/// </summary>
|
||||
public bool Interactive => dom.SelectSingleNode("//interactive") != null;
|
||||
public bool Interactive => this.dom.SelectSingleNode("//interactive") != null;
|
||||
|
||||
/// <summary>
|
||||
/// Environment variable overrides
|
||||
|
@ -576,7 +590,7 @@ namespace winsw
|
|||
{
|
||||
get
|
||||
{
|
||||
XmlNodeList? nodeList = dom.SelectNodes("//download");
|
||||
XmlNodeList? nodeList = this.dom.SelectNodes("//download");
|
||||
if (nodeList is null)
|
||||
{
|
||||
return Defaults.Downloads;
|
||||
|
@ -599,7 +613,7 @@ namespace winsw
|
|||
{
|
||||
get
|
||||
{
|
||||
XmlNodeList? childNodes = dom.SelectNodes("//onfailure");
|
||||
XmlNodeList? childNodes = this.dom.SelectNodes("//onfailure");
|
||||
if (childNodes is null)
|
||||
{
|
||||
return new SC_ACTION[0];
|
||||
|
@ -618,18 +632,18 @@ namespace winsw
|
|||
_ => throw new Exception("Invalid failure action: " + action)
|
||||
};
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
public TimeSpan ResetFailureAfter => SingleTimeSpanElement(dom, "resetfailure", Defaults.ResetFailureAfter);
|
||||
public TimeSpan ResetFailureAfter => this.SingleTimeSpanElement(this.dom, "resetfailure", Defaults.ResetFailureAfter);
|
||||
|
||||
protected string? GetServiceAccountPart(string subNodeName)
|
||||
{
|
||||
XmlNode? node = dom.SelectSingleNode("//serviceaccount");
|
||||
XmlNode? node = this.dom.SelectSingleNode("//serviceaccount");
|
||||
|
||||
if (node != null)
|
||||
{
|
||||
|
@ -643,28 +657,28 @@ namespace winsw
|
|||
return null;
|
||||
}
|
||||
|
||||
protected string? AllowServiceLogon => GetServiceAccountPart("allowservicelogon");
|
||||
protected string? AllowServiceLogon => this.GetServiceAccountPart("allowservicelogon");
|
||||
|
||||
protected internal string? ServiceAccountDomain => GetServiceAccountPart("domain");
|
||||
protected internal string? ServiceAccountDomain => this.GetServiceAccountPart("domain");
|
||||
|
||||
protected internal string? ServiceAccountName => GetServiceAccountPart("user");
|
||||
protected internal string? ServiceAccountName => this.GetServiceAccountPart("user");
|
||||
|
||||
public string? ServiceAccountPassword => GetServiceAccountPart("password");
|
||||
public string? ServiceAccountPassword => this.GetServiceAccountPart("password");
|
||||
|
||||
public string? ServiceAccountUser => ServiceAccountName is null ? null : (ServiceAccountDomain ?? ".") + "\\" + ServiceAccountName;
|
||||
public string? ServiceAccountUser => this.ServiceAccountName is null ? null : (this.ServiceAccountDomain ?? ".") + "\\" + this.ServiceAccountName;
|
||||
|
||||
public bool HasServiceAccount()
|
||||
{
|
||||
return !string.IsNullOrEmpty(ServiceAccountName);
|
||||
return !string.IsNullOrEmpty(this.ServiceAccountName);
|
||||
}
|
||||
|
||||
public bool AllowServiceAcountLogonRight
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
@ -677,13 +691,13 @@ namespace winsw
|
|||
/// <summary>
|
||||
/// Time to wait for the service to gracefully shutdown the executable before we forcibly kill it
|
||||
/// </summary>
|
||||
public TimeSpan StopTimeout => SingleTimeSpanElement(dom, "stoptimeout", Defaults.StopTimeout);
|
||||
public TimeSpan StopTimeout => this.SingleTimeSpanElement(this.dom, "stoptimeout", Defaults.StopTimeout);
|
||||
|
||||
public bool StopParentProcessFirst
|
||||
{
|
||||
get
|
||||
{
|
||||
var value = SingleElement("stopparentprocessfirst", true);
|
||||
var value = this.SingleElement("stopparentprocessfirst", true);
|
||||
if (bool.TryParse(value, out bool result))
|
||||
{
|
||||
return result;
|
||||
|
@ -700,19 +714,21 @@ namespace winsw
|
|||
{
|
||||
get
|
||||
{
|
||||
string? p = SingleElement("priority", true);
|
||||
string? p = this.SingleElement("priority", true);
|
||||
if (p is null)
|
||||
{
|
||||
return Defaults.Priority;
|
||||
}
|
||||
|
||||
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()
|
||||
{
|
||||
XmlNodeList nodeList = dom.SelectNodes("//env");
|
||||
XmlNodeList nodeList = this.dom.SelectNodes("//env");
|
||||
Dictionary<string, string> environment = new Dictionary<string, string>(nodeList.Count);
|
||||
for (int i = 0; i < nodeList.Count; i++)
|
||||
{
|
||||
|
|
|
@ -6,7 +6,7 @@ using System.IO;
|
|||
using System.Runtime.InteropServices;
|
||||
#endif
|
||||
|
||||
namespace winsw.Util
|
||||
namespace WinSW.Util
|
||||
{
|
||||
public static class FileHelper
|
||||
{
|
||||
|
|
|
@ -5,7 +5,7 @@ using System.Management;
|
|||
using System.Threading;
|
||||
using log4net;
|
||||
|
||||
namespace winsw.Util
|
||||
namespace WinSW.Util
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides helper classes for Process Management
|
||||
|
@ -76,6 +76,7 @@ namespace winsw.Util
|
|||
{
|
||||
Logger.Warn("Process " + pid + " did not respond to Ctrl+C signal - Killing as fallback");
|
||||
}
|
||||
|
||||
proc.Kill();
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
@ -126,14 +127,14 @@ namespace winsw.Util
|
|||
/// Once the process exits, the callback will be invoked.
|
||||
/// </summary>
|
||||
/// <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="arguments">Arguments to be passed</param>
|
||||
/// <param name="envVars">Additional environment variables</param>
|
||||
/// <param name="workingDirectory">Working directory</param>
|
||||
/// <param name="priority">Priority</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="logHandler">Log handler. If enabled, logs will be redirected to the process and then reported</param>
|
||||
public static void StartProcessAndCallbackForExit(
|
||||
Process processToStart,
|
||||
string? executable = null,
|
||||
|
@ -161,6 +162,7 @@ namespace winsw.Util
|
|||
foreach (string key in envVars.Keys)
|
||||
{
|
||||
Environment.SetEnvironmentVariable(key, envVars[key]);
|
||||
|
||||
// DONTDO: ps.EnvironmentVariables[key] = envs[key];
|
||||
// bugged (lower cases all variable names due to StringDictionary being used, see http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=326163)
|
||||
}
|
||||
|
@ -178,7 +180,7 @@ namespace winsw.Util
|
|||
if (logHandler != null)
|
||||
{
|
||||
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
|
||||
|
|
|
@ -4,9 +4,9 @@ using System.ComponentModel;
|
|||
using System.Diagnostics;
|
||||
using System.Runtime.InteropServices;
|
||||
using log4net;
|
||||
using winsw.Native;
|
||||
using WinSW.Native;
|
||||
|
||||
namespace winsw.Util
|
||||
namespace WinSW.Util
|
||||
{
|
||||
internal static class SignalHelper
|
||||
{
|
||||
|
|
|
@ -3,7 +3,7 @@ using System.Diagnostics.CodeAnalysis;
|
|||
using System.IO;
|
||||
using System.Xml;
|
||||
|
||||
namespace winsw.Util
|
||||
namespace WinSW.Util
|
||||
{
|
||||
public class XmlHelper
|
||||
{
|
||||
|
@ -19,7 +19,9 @@ namespace winsw.Util
|
|||
{
|
||||
XmlNode? n = node.SelectSingleNode(tagName);
|
||||
if (n is null && !optional)
|
||||
{
|
||||
throw new InvalidDataException("<" + tagName + "> is missing in configuration XML");
|
||||
}
|
||||
|
||||
return n is null ? null : Environment.ExpandEnvironmentVariables(n.InnerText);
|
||||
}
|
||||
|
@ -36,7 +38,9 @@ namespace winsw.Util
|
|||
{
|
||||
XmlNode? n = node.SelectSingleNode(tagName);
|
||||
if (n is null && !optional)
|
||||
{
|
||||
throw new InvalidDataException("<" + tagName + "> is missing in configuration XML");
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
|
@ -69,7 +73,9 @@ namespace winsw.Util
|
|||
public static TAttributeType SingleAttribute<TAttributeType>(XmlElement node, string attributeName, [AllowNull] TAttributeType defaultValue)
|
||||
{
|
||||
if (!node.HasAttribute(attributeName))
|
||||
{
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
string rawValue = node.GetAttribute(attributeName);
|
||||
string substitutedValue = Environment.ExpandEnvironmentVariables(rawValue);
|
||||
|
@ -90,7 +96,9 @@ namespace winsw.Util
|
|||
where TAttributeType : struct
|
||||
{
|
||||
if (!node.HasAttribute(attributeName))
|
||||
{
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
string rawValue = node.GetAttribute(attributeName);
|
||||
string substitutedValue = Environment.ExpandEnvironmentVariables(rawValue);
|
||||
|
@ -102,7 +110,8 @@ namespace winsw.Util
|
|||
}
|
||||
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);
|
||||
}
|
||||
#else
|
||||
|
|
|
@ -11,6 +11,10 @@
|
|||
|
||||
<ItemGroup>
|
||||
<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 Condition="'$(TargetFramework)' == 'netcoreapp3.1'">
|
||||
|
|
|
@ -1,15 +1,17 @@
|
|||
using System;
|
||||
|
||||
namespace winsw
|
||||
namespace WinSW
|
||||
{
|
||||
public class WinSWException : Exception
|
||||
{
|
||||
public WinSWException(string message)
|
||||
: base(message)
|
||||
{ }
|
||||
{
|
||||
}
|
||||
|
||||
public WinSWException(string message, Exception innerException)
|
||||
: base(message, innerException)
|
||||
{ }
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
namespace winsw
|
||||
namespace WinSW
|
||||
{
|
||||
/// <summary>
|
||||
/// Class, which contains generic information about WinSW runtime.
|
||||
|
@ -9,17 +9,17 @@
|
|||
/// <summary>
|
||||
/// Prefix for all environment variables being injected for WinSW
|
||||
/// </summary>
|
||||
public static readonly string SYSTEM_EVNVVAR_PREFIX = "WINSW_";
|
||||
public static readonly string SystemEnvVarPrefix = "WINSW_";
|
||||
|
||||
/// <summary>
|
||||
/// Variable, which points to the service ID.
|
||||
/// It may be used to determine runaway processes.
|
||||
/// </summary>
|
||||
public static string ENVVAR_NAME_SERVICE_ID => SYSTEM_EVNVVAR_PREFIX + "SERVICE_ID";
|
||||
public static string EnvVarNameServiceId => SystemEnvVarPrefix + "SERVICE_ID";
|
||||
|
||||
/// <summary>
|
||||
/// Variable, which specifies path to the executable being launched by WinSW.
|
||||
/// </summary>
|
||||
public static string ENVVAR_NAME_EXECUTABLE_PATH => SYSTEM_EVNVVAR_PREFIX + "EXECUTABLE";
|
||||
public static string EnvVarNameExecutablePath => SystemEnvVarPrefix + "EXECUTABLE";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -48,7 +48,7 @@ namespace WMI
|
|||
public WmiException(string message, ReturnValue code)
|
||||
: base(message)
|
||||
{
|
||||
ErrorCode = code;
|
||||
this.ErrorCode = code;
|
||||
}
|
||||
|
||||
public WmiException(ReturnValue code)
|
||||
|
@ -64,13 +64,15 @@ namespace WMI
|
|||
{
|
||||
public readonly string Name;
|
||||
|
||||
public WmiClassName(string name) => Name = name;
|
||||
public WmiClassName(string name) => this.Name = name;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Marker interface to denote a collection in WMI.
|
||||
/// </summary>
|
||||
public interface IWmiCollection { }
|
||||
public interface IWmiCollection
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Marker interface to denote an individual managed object
|
||||
|
@ -109,7 +111,9 @@ namespace WMI
|
|||
{
|
||||
uint code = (uint)result["returnValue"];
|
||||
if (code != 0)
|
||||
{
|
||||
throw new WmiException((ReturnValue)code);
|
||||
}
|
||||
}
|
||||
|
||||
protected ManagementBaseObject GetMethodParameters(ManagementObject wmiObject, string methodName, ParameterInfo[] methodParameters, object[] arguments)
|
||||
|
@ -174,20 +178,23 @@ namespace WMI
|
|||
{
|
||||
ParameterInfo[] methodParameters = method.GetParameters();
|
||||
|
||||
if (method.Name == nameof(Win32Services.Select))
|
||||
if (method.Name == nameof(IWin32Services.Select))
|
||||
{
|
||||
// select method to find instances
|
||||
StringBuilder query = new StringBuilder("SELECT * FROM ").Append(this.className).Append(" WHERE ");
|
||||
for (int i = 0; i < arguments.Length; i++)
|
||||
{
|
||||
if (i != 0)
|
||||
{
|
||||
query.Append(" AND ");
|
||||
}
|
||||
|
||||
query.Append(' ').Append(Capitalize(methodParameters[i].Name!)).Append(" = '").Append(arguments[i]).Append('\'');
|
||||
}
|
||||
|
||||
using ManagementObjectSearcher searcher = new ManagementObjectSearcher(this.wmiClass.Scope, new ObjectQuery(query.ToString()));
|
||||
using ManagementObjectCollection results = searcher.Get();
|
||||
|
||||
// TODO: support collections
|
||||
foreach (ManagementObject wmiObject in results)
|
||||
{
|
||||
|
@ -209,7 +216,8 @@ namespace WMI
|
|||
/// <summary>
|
||||
/// Obtains an object that corresponds to a table in WMI, which is a collection of a managed object.
|
||||
/// </summary>
|
||||
public T GetCollection<T>() where T : IWmiCollection
|
||||
public T GetCollection<T>()
|
||||
where T : IWmiCollection
|
||||
{
|
||||
WmiClassName className = (WmiClassName)typeof(T).GetCustomAttributes(typeof(WmiClassName), false)[0];
|
||||
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
|
||||
namespace WMI
|
||||
namespace WMI
|
||||
{
|
||||
public enum ServiceType
|
||||
{
|
||||
|
@ -26,18 +25,22 @@ namespace WMI
|
|||
/// Device driver started by the operating system loader. This value is valid only for driver services.
|
||||
/// </summary>
|
||||
Boot,
|
||||
|
||||
/// <summary>
|
||||
/// Device driver started by the operating system initialization process. This value is valid only for driver services.
|
||||
/// </summary>
|
||||
System,
|
||||
|
||||
/// <summary>
|
||||
/// Service to be started automatically by the Service Control Manager during system startup.
|
||||
/// </summary>
|
||||
Automatic,
|
||||
|
||||
/// <summary>
|
||||
/// Service to be started by the Service Control Manager when a process calls the StartService method.
|
||||
/// </summary>
|
||||
Manual,
|
||||
|
||||
/// <summary>
|
||||
/// Service that can no longer be started.
|
||||
/// </summary>
|
||||
|
@ -45,22 +48,25 @@ namespace WMI
|
|||
}
|
||||
|
||||
[WmiClassName("Win32_Service")]
|
||||
public interface Win32Services : IWmiCollection
|
||||
public interface IWin32Services : IWmiCollection
|
||||
{
|
||||
// ReturnValue Create(bool desktopInteract, string displayName, int errorControl, string loadOrderGroup, string loadOrderGroupDependencies, string name, string pathName, string serviceDependencies, string serviceType, string startMode, string startName, string startPassword);
|
||||
void Create(string name, string displayName, string pathName, ServiceType serviceType, ErrorControl errorControl, string startMode, bool desktopInteract, string? startName, string? startPassword, string[] serviceDependencies);
|
||||
|
||||
void Create(string name, string displayName, string pathName, ServiceType serviceType, ErrorControl errorControl, string startMode, bool desktopInteract, string[] serviceDependencies);
|
||||
|
||||
Win32Service? Select(string name);
|
||||
IWin32Service? Select(string name);
|
||||
}
|
||||
|
||||
// https://docs.microsoft.com/windows/win32/cimwin32prov/win32-service
|
||||
public interface Win32Service : IWmiObject
|
||||
public interface IWin32Service : IWmiObject
|
||||
{
|
||||
bool Started { get; }
|
||||
|
||||
void Delete();
|
||||
|
||||
void StartService();
|
||||
|
||||
void StopService();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace winsw.Plugins.RunawayProcessKiller
|
||||
namespace WinSW.Plugins.RunawayProcessKiller
|
||||
{
|
||||
public partial class RunawayProcessKillerExtension
|
||||
{
|
||||
|
|
|
@ -5,11 +5,11 @@ using System.IO;
|
|||
using System.Text;
|
||||
using System.Xml;
|
||||
using log4net;
|
||||
using winsw.Extensions;
|
||||
using winsw.Util;
|
||||
using static winsw.Plugins.RunawayProcessKiller.RunawayProcessKillerExtension.NativeMethods;
|
||||
using WinSW.Extensions;
|
||||
using WinSW.Util;
|
||||
using static WinSW.Plugins.RunawayProcessKiller.RunawayProcessKillerExtension.NativeMethods;
|
||||
|
||||
namespace winsw.Plugins.RunawayProcessKiller
|
||||
namespace WinSW.Plugins.RunawayProcessKiller
|
||||
{
|
||||
public partial class RunawayProcessKillerExtension : AbstractWinSWExtension
|
||||
{
|
||||
|
@ -183,13 +183,13 @@ namespace winsw.Plugins.RunawayProcessKiller
|
|||
{
|
||||
// We expect the upper logic to process any errors
|
||||
// TODO: a better parser API for types would be useful
|
||||
Pidfile = XmlHelper.SingleElement(node, "pidfile", false)!;
|
||||
StopTimeout = TimeSpan.FromMilliseconds(int.Parse(XmlHelper.SingleElement(node, "stopTimeout", false)!));
|
||||
StopParentProcessFirst = bool.Parse(XmlHelper.SingleElement(node, "stopParentFirst", false)!);
|
||||
ServiceId = descriptor.Id;
|
||||
this.Pidfile = XmlHelper.SingleElement(node, "pidfile", false)!;
|
||||
this.StopTimeout = TimeSpan.FromMilliseconds(int.Parse(XmlHelper.SingleElement(node, "stopTimeout", false)!));
|
||||
this.StopParentProcessFirst = bool.Parse(XmlHelper.SingleElement(node, "stopParentFirst", false)!);
|
||||
this.ServiceId = descriptor.Id;
|
||||
// TODO: Consider making it documented
|
||||
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>
|
||||
|
@ -200,16 +200,16 @@ namespace winsw.Plugins.RunawayProcessKiller
|
|||
{
|
||||
// Read PID file from the disk
|
||||
int pid;
|
||||
if (File.Exists(Pidfile))
|
||||
if (File.Exists(this.Pidfile))
|
||||
{
|
||||
string pidstring;
|
||||
try
|
||||
{
|
||||
pidstring = File.ReadAllText(Pidfile);
|
||||
pidstring = File.ReadAllText(this.Pidfile);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Error("Cannot read PID file from " + Pidfile, ex);
|
||||
Logger.Error("Cannot read PID file from " + this.Pidfile, ex);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -219,13 +219,13 @@ namespace winsw.Plugins.RunawayProcessKiller
|
|||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -243,9 +243,9 @@ namespace winsw.Plugins.RunawayProcessKiller
|
|||
}
|
||||
|
||||
// Ensure the process references the service
|
||||
string expectedEnvVarName = WinSWSystem.ENVVAR_NAME_SERVICE_ID;
|
||||
string expectedEnvVarName = WinSWSystem.EnvVarNameServiceId;
|
||||
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. "
|
||||
+ "The process has not been started by WinSW, hence it won't be terminated.");
|
||||
|
@ -254,10 +254,10 @@ namespace winsw.Plugins.RunawayProcessKiller
|
|||
}
|
||||
|
||||
// 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 + "'. "
|
||||
+ "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;
|
||||
}
|
||||
|
||||
|
@ -265,7 +265,7 @@ namespace winsw.Plugins.RunawayProcessKiller
|
|||
StringBuilder bldr = new StringBuilder("Stopping the runaway process (pid=");
|
||||
bldr.Append(pid);
|
||||
bldr.Append(") and its children. Environment was ");
|
||||
if (!CheckWinSWEnvironmentVariable)
|
||||
if (!this.CheckWinSWEnvironmentVariable)
|
||||
{
|
||||
bldr.Append("not ");
|
||||
}
|
||||
|
@ -285,14 +285,14 @@ namespace winsw.Plugins.RunawayProcessKiller
|
|||
/// <param name="process"></param>
|
||||
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
|
||||
{
|
||||
File.WriteAllText(Pidfile, process.Id.ToString());
|
||||
File.WriteAllText(this.Pidfile, process.Id.ToString());
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Error("Cannot update the PID file " + Pidfile, ex);
|
||||
Logger.Error("Cannot update the PID file " + this.Pidfile, ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Xml;
|
||||
using log4net;
|
||||
using winsw.Extensions;
|
||||
using winsw.Util;
|
||||
using WinSW.Extensions;
|
||||
using WinSW.Util;
|
||||
|
||||
namespace winsw.Plugins.SharedDirectoryMapper
|
||||
namespace WinSW.Plugins.SharedDirectoryMapper
|
||||
{
|
||||
public class SharedDirectoryMapper : AbstractWinSWExtension
|
||||
{
|
||||
|
@ -22,7 +22,7 @@ namespace winsw.Plugins.SharedDirectoryMapper
|
|||
public SharedDirectoryMapper(bool enableMapping, string directoryUNC, string driveLabel)
|
||||
{
|
||||
SharedDirectoryMapperConfig config = new SharedDirectoryMapperConfig(enableMapping, driveLabel, directoryUNC);
|
||||
_entries.Add(config);
|
||||
this._entries.Add(config);
|
||||
}
|
||||
|
||||
public override void Configure(ServiceDescriptor descriptor, XmlNode node)
|
||||
|
@ -35,7 +35,7 @@ namespace winsw.Plugins.SharedDirectoryMapper
|
|||
if (mapNodes[i] is XmlElement mapElement)
|
||||
{
|
||||
var config = SharedDirectoryMapperConfig.FromXml(mapElement);
|
||||
_entries.Add(config);
|
||||
this._entries.Add(config);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -43,40 +43,40 @@ namespace winsw.Plugins.SharedDirectoryMapper
|
|||
|
||||
public override void OnWrapperStarted()
|
||||
{
|
||||
foreach (SharedDirectoryMapperConfig config in _entries)
|
||||
foreach (SharedDirectoryMapperConfig config in this._entries)
|
||||
{
|
||||
if (config.EnableMapping)
|
||||
{
|
||||
Logger.Info(DisplayName + ": Mapping shared directory " + config.UNCPath + " to " + config.Label);
|
||||
Logger.Info(this.DisplayName + ": Mapping shared directory " + config.UNCPath + " to " + config.Label);
|
||||
try
|
||||
{
|
||||
_mapper.MapDirectory(config.Label, config.UNCPath);
|
||||
this._mapper.MapDirectory(config.Label, config.UNCPath);
|
||||
}
|
||||
catch (MapperException ex)
|
||||
{
|
||||
HandleMappingError(config, ex);
|
||||
this.HandleMappingError(config, ex);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Warn(DisplayName + ": Mapping of " + config.Label + " is disabled");
|
||||
Logger.Warn(this.DisplayName + ": Mapping of " + config.Label + " is disabled");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void BeforeWrapperStopped()
|
||||
{
|
||||
foreach (SharedDirectoryMapperConfig config in _entries)
|
||||
foreach (SharedDirectoryMapperConfig config in this._entries)
|
||||
{
|
||||
if (config.EnableMapping)
|
||||
{
|
||||
try
|
||||
{
|
||||
_mapper.UnmapDirectory(config.Label);
|
||||
this._mapper.UnmapDirectory(config.Label);
|
||||
}
|
||||
catch (MapperException ex)
|
||||
{
|
||||
HandleMappingError(config, ex);
|
||||
this.HandleMappingError(config, ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -86,7 +86,7 @@ namespace winsw.Plugins.SharedDirectoryMapper
|
|||
{
|
||||
Logger.Error("Mapping of " + config.Label + " failed. STDOUT: " + ex.Process.StandardOutput.ReadToEnd()
|
||||
+ " \r\nSTDERR: " + ex.Process.StandardError.ReadToEnd(), ex);
|
||||
throw new ExtensionException(Descriptor.Id, DisplayName + ": Mapping of " + config.Label + "failed", ex);
|
||||
throw new ExtensionException(this.Descriptor.Id, this.DisplayName + ": Mapping of " + config.Label + "failed", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
using System.Xml;
|
||||
using winsw.Util;
|
||||
using WinSW.Util;
|
||||
|
||||
namespace winsw.Plugins.SharedDirectoryMapper
|
||||
namespace WinSW.Plugins.SharedDirectoryMapper
|
||||
{
|
||||
/// <summary>
|
||||
/// Stores configuration entries for SharedDirectoryMapper extension.
|
||||
|
@ -14,9 +14,9 @@ namespace winsw.Plugins.SharedDirectoryMapper
|
|||
|
||||
public SharedDirectoryMapperConfig(bool enableMapping, string label, string uncPath)
|
||||
{
|
||||
EnableMapping = enableMapping;
|
||||
Label = label;
|
||||
UNCPath = uncPath;
|
||||
this.EnableMapping = enableMapping;
|
||||
this.Label = label;
|
||||
this.UNCPath = uncPath;
|
||||
}
|
||||
|
||||
public static SharedDirectoryMapperConfig FromXml(XmlElement node)
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
using System.Diagnostics;
|
||||
|
||||
namespace winsw.Plugins.SharedDirectoryMapper
|
||||
namespace WinSW.Plugins.SharedDirectoryMapper
|
||||
{
|
||||
class SharedDirectoryMappingHelper
|
||||
{
|
||||
|
@ -42,7 +42,7 @@ namespace winsw.Plugins.SharedDirectoryMapper
|
|||
/// <exception cref="MapperException">Operation failure</exception>
|
||||
public void MapDirectory(string label, string uncPath)
|
||||
{
|
||||
InvokeCommand("net.exe", " use " + label + " " + uncPath);
|
||||
this.InvokeCommand("net.exe", " use " + label + " " + uncPath);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -52,7 +52,7 @@ namespace winsw.Plugins.SharedDirectoryMapper
|
|||
/// <exception cref="MapperException">Operation failure</exception>
|
||||
public void UnmapDirectory(string label)
|
||||
{
|
||||
InvokeCommand("net.exe", " use /DELETE /YES " + label);
|
||||
this.InvokeCommand("net.exe", " use /DELETE /YES " + label);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -64,8 +64,8 @@ namespace winsw.Plugins.SharedDirectoryMapper
|
|||
public MapperException(Process process, string command, string args)
|
||||
: base("Command " + command + " " + args + " failed with code " + process.ExitCode)
|
||||
{
|
||||
Call = command + " " + args;
|
||||
Process = process;
|
||||
this.Call = command + " " + args;
|
||||
this.Process = process;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
using System.Reflection;
|
||||
using System.Xml;
|
||||
using NUnit.Framework;
|
||||
using winsw;
|
||||
using WinSW;
|
||||
using winswTests.Util;
|
||||
|
||||
namespace winswTests.Configuration
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
using System.IO;
|
||||
using NUnit.Framework;
|
||||
using winsw;
|
||||
using WinSW;
|
||||
using winswTests.Util;
|
||||
|
||||
namespace winswTests
|
||||
|
@ -19,11 +19,11 @@ namespace winswTests
|
|||
var sd = ConfigXmlBuilder.create()
|
||||
.WithDownload(d)
|
||||
.ToServiceDescriptor(true);
|
||||
var loaded = GetSingleEntry(sd);
|
||||
var loaded = this.GetSingleEntry(sd);
|
||||
|
||||
// Check default values
|
||||
Assert.That(loaded.FailOnError, Is.False);
|
||||
Assert.That(loaded.Auth, Is.EqualTo(Download.AuthType.none));
|
||||
Assert.That(loaded.Auth, Is.EqualTo(Download.AuthType.None));
|
||||
Assert.That(loaded.Username, Is.Null);
|
||||
Assert.That(loaded.Password, Is.Null);
|
||||
Assert.That(loaded.UnsecureAuth, Is.False);
|
||||
|
@ -33,15 +33,15 @@ namespace winswTests
|
|||
public void Roundtrip_BasicAuth()
|
||||
{
|
||||
// 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()
|
||||
.WithDownload(d)
|
||||
.ToServiceDescriptor(true);
|
||||
var loaded = GetSingleEntry(sd);
|
||||
var loaded = this.GetSingleEntry(sd);
|
||||
|
||||
// Check default values
|
||||
Assert.That(loaded.FailOnError, Is.True);
|
||||
Assert.That(loaded.Auth, Is.EqualTo(Download.AuthType.basic));
|
||||
Assert.That(loaded.Auth, Is.EqualTo(Download.AuthType.Basic));
|
||||
Assert.That(loaded.Username, Is.EqualTo("aUser"));
|
||||
Assert.That(loaded.Password, Is.EqualTo("aPassword"));
|
||||
Assert.That(loaded.UnsecureAuth, Is.True);
|
||||
|
@ -51,15 +51,15 @@ namespace winswTests
|
|||
public void Roundtrip_SSPI()
|
||||
{
|
||||
// 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()
|
||||
.WithDownload(d)
|
||||
.ToServiceDescriptor(true);
|
||||
var loaded = GetSingleEntry(sd);
|
||||
var loaded = this.GetSingleEntry(sd);
|
||||
|
||||
// Check default values
|
||||
Assert.That(loaded.FailOnError, Is.False);
|
||||
Assert.That(loaded.Auth, Is.EqualTo(Download.AuthType.sspi));
|
||||
Assert.That(loaded.Auth, Is.EqualTo(Download.AuthType.Sspi));
|
||||
Assert.That(loaded.Username, Is.Null);
|
||||
Assert.That(loaded.Password, Is.Null);
|
||||
Assert.That(loaded.UnsecureAuth, Is.False);
|
||||
|
@ -73,22 +73,22 @@ namespace winswTests
|
|||
public void RejectBasicAuth_With_UnsecureProtocol(string protocolPrefix)
|
||||
{
|
||||
string unsecureFrom = protocolPrefix + "myServer.com:8080/file.txt";
|
||||
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");
|
||||
var d = new Download(unsecureFrom, To, auth: Download.AuthType.Basic, username: "aUser", password: "aPassword");
|
||||
this.AssertInitializationFails(d, "Warning: you're sending your credentials in clear text to the server");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void RejectBasicAuth_Without_Username()
|
||||
{
|
||||
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");
|
||||
var d = new Download(From, To, auth: Download.AuthType.Basic, username: null, password: "aPassword");
|
||||
this.AssertInitializationFails(d, "Basic Auth is enabled, but username is not specified");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void RejectBasicAuth_Without_Password()
|
||||
{
|
||||
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");
|
||||
var d = new Download(From, To, auth: Download.AuthType.Basic, username: "aUser", password: null);
|
||||
this.AssertInitializationFails(d, "Basic Auth is enabled, but password is not specified");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -104,7 +104,7 @@ namespace winswTests
|
|||
.WithDownload(d)
|
||||
.ToServiceDescriptor(true);
|
||||
|
||||
var loaded = GetSingleEntry(sd);
|
||||
var loaded = this.GetSingleEntry(sd);
|
||||
Assert.That(loaded.From, Is.EqualTo(From));
|
||||
Assert.That(loaded.To, Is.EqualTo(To));
|
||||
Assert.That(loaded.FailOnError, Is.EqualTo(failOnError), "Unexpected FailOnError value");
|
||||
|
@ -120,7 +120,7 @@ namespace winswTests
|
|||
.WithRawEntry("<download from=\"http://www.nosuchhostexists.foo.myorg/foo.xml\" to=\"%BASE%\\foo.xml\"/>")
|
||||
.ToServiceDescriptor(true);
|
||||
|
||||
var loaded = GetSingleEntry(sd);
|
||||
var loaded = this.GetSingleEntry(sd);
|
||||
Assert.That(loaded.FailOnError, Is.False);
|
||||
}
|
||||
|
||||
|
@ -133,8 +133,8 @@ namespace winswTests
|
|||
var sd = ConfigXmlBuilder.create()
|
||||
.WithRawEntry("<download from=\"http://www.nosuchhostexists.foo.myorg/foo.xml\" to=\"%BASE%\\foo.xml\" auth=\"" + authType + "\"/>")
|
||||
.ToServiceDescriptor(true);
|
||||
var loaded = GetSingleEntry(sd);
|
||||
Assert.That(loaded.Auth, Is.EqualTo(Download.AuthType.sspi));
|
||||
var loaded = this.GetSingleEntry(sd);
|
||||
Assert.That(loaded.Auth, Is.EqualTo(Download.AuthType.Sspi));
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
@ -145,7 +145,7 @@ namespace winswTests
|
|||
.WithRawEntry("<download from=\"http://www.nosuchhostexists.foo.myorg/foo.xml\" to=\"%BASE%\\foo.xml\" auth=\"digest\"/>")
|
||||
.ToServiceDescriptor(true);
|
||||
|
||||
Assert.That(() => GetSingleEntry(sd), Throws.TypeOf<InvalidDataException>().With.Message.StartsWith("Cannot parse <auth> Enum value from string 'digest'"));
|
||||
Assert.That(() => this.GetSingleEntry(sd), Throws.TypeOf<InvalidDataException>().With.Message.StartsWith("Cannot parse <auth> Enum value from string 'digest'"));
|
||||
}
|
||||
|
||||
[TestCase("http://", "127.0.0.1:80", "egarcia", "Passw0rd")]
|
||||
|
@ -190,7 +190,7 @@ namespace winswTests
|
|||
.WithDownload(download)
|
||||
.ToServiceDescriptor(true);
|
||||
|
||||
Assert.That(() => GetSingleEntry(sd), Throws.TypeOf<InvalidDataException>().With.Message.StartsWith(expectedMessagePart));
|
||||
Assert.That(() => this.GetSingleEntry(sd), Throws.TypeOf<InvalidDataException>().With.Message.StartsWith(expectedMessagePart));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@ using System.Net.Sockets;
|
|||
using System.Runtime.CompilerServices;
|
||||
using System.Threading.Tasks;
|
||||
using NUnit.Framework;
|
||||
using winsw;
|
||||
using WinSW;
|
||||
using winswTests.Util;
|
||||
|
||||
namespace winswTests
|
||||
|
@ -111,7 +111,7 @@ namespace winswTests
|
|||
await this.TestClientServerAsync(
|
||||
async (source, dest) =>
|
||||
{
|
||||
await new Download(source, dest, false, Download.AuthType.none).PerformAsync();
|
||||
await new Download(source, dest, false, Download.AuthType.None).PerformAsync();
|
||||
Assert.That(File.ReadAllBytes(dest), Is.EqualTo(this.contents));
|
||||
},
|
||||
context =>
|
||||
|
@ -136,7 +136,7 @@ namespace winswTests
|
|||
await this.TestClientServerAsync(
|
||||
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.That(File.ReadAllBytes(dest), Is.EqualTo(this.contents));
|
||||
},
|
||||
context =>
|
||||
|
|
|
@ -3,10 +3,10 @@ using System.Collections.Generic;
|
|||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using NUnit.Framework;
|
||||
using winsw;
|
||||
using winsw.Extensions;
|
||||
using winsw.Plugins.RunawayProcessKiller;
|
||||
using winsw.Util;
|
||||
using WinSW;
|
||||
using WinSW.Extensions;
|
||||
using WinSW.Plugins.RunawayProcessKiller;
|
||||
using WinSW.Util;
|
||||
using winswTests.Util;
|
||||
|
||||
namespace winswTests.Extensions
|
||||
|
@ -30,20 +30,20 @@ $@"<service>
|
|||
<arguments>-Xrs -jar \""%BASE%\slave.jar\"" -jnlpUrl ...</arguments>
|
||||
<log mode=""roll""></log>
|
||||
<extensions>
|
||||
<extension enabled=""true"" className=""{testExtension}"" id=""killRunawayProcess"">
|
||||
<extension enabled=""true"" className=""{this.testExtension}"" id=""killRunawayProcess"">
|
||||
<pidfile>foo/bar/pid.txt</pidfile>
|
||||
<stopTimeout>5000</stopTimeout>
|
||||
<stopParentFirst>true</stopParentFirst>
|
||||
</extension>
|
||||
</extensions>
|
||||
</service>";
|
||||
_testServiceDescriptor = ServiceDescriptor.FromXML(seedXml);
|
||||
this._testServiceDescriptor = ServiceDescriptor.FromXML(seedXml);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void LoadExtensions()
|
||||
{
|
||||
WinSWExtensionManager manager = new WinSWExtensionManager(_testServiceDescriptor);
|
||||
WinSWExtensionManager manager = new WinSWExtensionManager(this._testServiceDescriptor);
|
||||
manager.LoadExtensions();
|
||||
Assert.AreEqual(1, manager.Extensions.Count, "One extension should be loaded");
|
||||
|
||||
|
@ -58,7 +58,7 @@ $@"<service>
|
|||
[Test]
|
||||
public void StartStopExtension()
|
||||
{
|
||||
WinSWExtensionManager manager = new WinSWExtensionManager(_testServiceDescriptor);
|
||||
WinSWExtensionManager manager = new WinSWExtensionManager(this._testServiceDescriptor);
|
||||
manager.LoadExtensions();
|
||||
manager.FireOnWrapperStarted();
|
||||
manager.FireBeforeWrapperStopped();
|
||||
|
@ -80,7 +80,7 @@ $@"<service>
|
|||
ps.Arguments = "/c pause";
|
||||
ps.UseShellExecute = false;
|
||||
ps.RedirectStandardOutput = true;
|
||||
ps.EnvironmentVariables[WinSWSystem.ENVVAR_NAME_SERVICE_ID] = winswId;
|
||||
ps.EnvironmentVariables[WinSWSystem.EnvVarNameServiceId] = winswId;
|
||||
proc.Start();
|
||||
|
||||
try
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
using NUnit.Framework;
|
||||
using winsw;
|
||||
using winsw.Extensions;
|
||||
using winsw.Plugins.SharedDirectoryMapper;
|
||||
using WinSW;
|
||||
using WinSW.Extensions;
|
||||
using WinSW.Plugins.SharedDirectoryMapper;
|
||||
|
||||
namespace winswTests.Extensions
|
||||
{
|
||||
|
@ -24,13 +24,13 @@ $@"<service>
|
|||
<arguments>-Xrs -jar \""%BASE%\slave.jar\"" -jnlpUrl ...</arguments>
|
||||
<log mode=""roll""></log>
|
||||
<extensions>
|
||||
<extension enabled=""true"" className=""{testExtension}"" id=""mapNetworDirs"">
|
||||
<extension enabled=""true"" className=""{this.testExtension}"" id=""mapNetworDirs"">
|
||||
<mapping>
|
||||
<map enabled=""false"" label=""N:"" uncpath=""\\UNC""/>
|
||||
<map enabled=""false"" label=""M:"" uncpath=""\\UNC2""/>
|
||||
</mapping>
|
||||
</extension>
|
||||
<extension enabled=""true"" className=""{testExtension}"" id=""mapNetworDirs2"">
|
||||
<extension enabled=""true"" className=""{this.testExtension}"" id=""mapNetworDirs2"">
|
||||
<mapping>
|
||||
<map enabled=""false"" label=""X:"" uncpath=""\\UNC""/>
|
||||
<map enabled=""false"" label=""Y:"" uncpath=""\\UNC2""/>
|
||||
|
@ -38,13 +38,13 @@ $@"<service>
|
|||
</extension>
|
||||
</extensions>
|
||||
</service>";
|
||||
_testServiceDescriptor = ServiceDescriptor.FromXML(seedXml);
|
||||
this._testServiceDescriptor = ServiceDescriptor.FromXML(seedXml);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void LoadExtensions()
|
||||
{
|
||||
WinSWExtensionManager manager = new WinSWExtensionManager(_testServiceDescriptor);
|
||||
WinSWExtensionManager manager = new WinSWExtensionManager(this._testServiceDescriptor);
|
||||
manager.LoadExtensions();
|
||||
Assert.AreEqual(2, manager.Extensions.Count, "Two extensions should be loaded");
|
||||
}
|
||||
|
@ -52,7 +52,7 @@ $@"<service>
|
|||
[Test]
|
||||
public void StartStopExtension()
|
||||
{
|
||||
WinSWExtensionManager manager = new WinSWExtensionManager(_testServiceDescriptor);
|
||||
WinSWExtensionManager manager = new WinSWExtensionManager(this._testServiceDescriptor);
|
||||
manager.LoadExtensions();
|
||||
manager.FireOnWrapperStarted();
|
||||
manager.FireBeforeWrapperStopped();
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
using System;
|
||||
using System.ServiceProcess;
|
||||
using NUnit.Framework;
|
||||
using winsw;
|
||||
using WinSW;
|
||||
using winswTests.Util;
|
||||
|
||||
namespace winswTests
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
using System;
|
||||
using System.Diagnostics;
|
||||
using NUnit.Framework;
|
||||
using winsw;
|
||||
using WinSW;
|
||||
using winswTests.Util;
|
||||
using WMI;
|
||||
|
||||
|
@ -38,13 +38,13 @@ $@"<service>
|
|||
<workingdirectory>{ExpectedWorkingDirectory}</workingdirectory>
|
||||
<logpath>C:\logs</logpath>
|
||||
</service>";
|
||||
_extendedServiceDescriptor = ServiceDescriptor.FromXML(seedXml);
|
||||
this._extendedServiceDescriptor = ServiceDescriptor.FromXML(seedXml);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void DefaultStartMode()
|
||||
{
|
||||
Assert.That(_extendedServiceDescriptor.StartMode, Is.EqualTo(StartMode.Automatic));
|
||||
Assert.That(this._extendedServiceDescriptor.StartMode, Is.EqualTo(StartMode.Automatic));
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
@ -69,8 +69,8 @@ $@"<service>
|
|||
<logpath>C:\logs</logpath>
|
||||
</service>";
|
||||
|
||||
_extendedServiceDescriptor = ServiceDescriptor.FromXML(seedXml);
|
||||
Assert.That(() => _extendedServiceDescriptor.StartMode, Throws.ArgumentException);
|
||||
this._extendedServiceDescriptor = ServiceDescriptor.FromXML(seedXml);
|
||||
Assert.That(() => this._extendedServiceDescriptor.StartMode, Throws.ArgumentException);
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
@ -95,35 +95,35 @@ $@"<service>
|
|||
<logpath>C:\logs</logpath>
|
||||
</service>";
|
||||
|
||||
_extendedServiceDescriptor = ServiceDescriptor.FromXML(seedXml);
|
||||
Assert.That(_extendedServiceDescriptor.StartMode, Is.EqualTo(StartMode.Manual));
|
||||
this._extendedServiceDescriptor = ServiceDescriptor.FromXML(seedXml);
|
||||
Assert.That(this._extendedServiceDescriptor.StartMode, Is.EqualTo(StartMode.Manual));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void VerifyWorkingDirectory()
|
||||
{
|
||||
Debug.WriteLine("_extendedServiceDescriptor.WorkingDirectory :: " + _extendedServiceDescriptor.WorkingDirectory);
|
||||
Assert.That(_extendedServiceDescriptor.WorkingDirectory, Is.EqualTo(ExpectedWorkingDirectory));
|
||||
Debug.WriteLine("_extendedServiceDescriptor.WorkingDirectory :: " + this._extendedServiceDescriptor.WorkingDirectory);
|
||||
Assert.That(this._extendedServiceDescriptor.WorkingDirectory, Is.EqualTo(ExpectedWorkingDirectory));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void VerifyServiceLogonRight()
|
||||
{
|
||||
Assert.That(_extendedServiceDescriptor.AllowServiceAcountLogonRight, Is.True);
|
||||
Assert.That(this._extendedServiceDescriptor.AllowServiceAcountLogonRight, Is.True);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void VerifyUsername()
|
||||
{
|
||||
Debug.WriteLine("_extendedServiceDescriptor.WorkingDirectory :: " + _extendedServiceDescriptor.WorkingDirectory);
|
||||
Assert.That(_extendedServiceDescriptor.ServiceAccountUser, Is.EqualTo(Domain + "\\" + Username));
|
||||
Debug.WriteLine("_extendedServiceDescriptor.WorkingDirectory :: " + this._extendedServiceDescriptor.WorkingDirectory);
|
||||
Assert.That(this._extendedServiceDescriptor.ServiceAccountUser, Is.EqualTo(Domain + "\\" + Username));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void VerifyPassword()
|
||||
{
|
||||
Debug.WriteLine("_extendedServiceDescriptor.WorkingDirectory :: " + _extendedServiceDescriptor.WorkingDirectory);
|
||||
Assert.That(_extendedServiceDescriptor.ServiceAccountPassword, Is.EqualTo(Password));
|
||||
Debug.WriteLine("_extendedServiceDescriptor.WorkingDirectory :: " + this._extendedServiceDescriptor.WorkingDirectory);
|
||||
Assert.That(this._extendedServiceDescriptor.ServiceAccountPassword, Is.EqualTo(Password));
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
@ -142,7 +142,7 @@ $@"<service>
|
|||
[Test]
|
||||
public void StopParentProcessFirstIsFalseByDefault()
|
||||
{
|
||||
Assert.That(_extendedServiceDescriptor.StopParentProcessFirst, Is.False);
|
||||
Assert.That(this._extendedServiceDescriptor.StopParentProcessFirst, Is.False);
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using NUnit.Framework;
|
||||
using winsw;
|
||||
using WinSW;
|
||||
|
||||
namespace winswTests.Util
|
||||
{
|
||||
|
@ -115,13 +115,13 @@ $@"<service>
|
|||
|
||||
public Exception Exception { get; }
|
||||
|
||||
public bool HasException => Exception != null;
|
||||
public bool HasException => this.Exception != null;
|
||||
|
||||
public CLITestResult(string output, string error, Exception exception = null)
|
||||
{
|
||||
Out = output;
|
||||
Error = error;
|
||||
Exception = exception;
|
||||
this.Out = output;
|
||||
this.Error = error;
|
||||
this.Exception = exception;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using winsw;
|
||||
using winsw.Plugins.RunawayProcessKiller;
|
||||
using WinSW;
|
||||
using WinSW.Plugins.RunawayProcessKiller;
|
||||
using winswTests.Extensions;
|
||||
|
||||
namespace winswTests.Util
|
||||
|
@ -25,8 +25,8 @@ namespace winswTests.Util
|
|||
// TODO: Switch to the initializer?
|
||||
private ConfigXmlBuilder()
|
||||
{
|
||||
configEntries = new List<string>();
|
||||
ExtensionXmls = new List<string>();
|
||||
this.configEntries = new List<string>();
|
||||
this.ExtensionXmls = new List<string>();
|
||||
}
|
||||
|
||||
public static ConfigXmlBuilder create(string id = null, string name = null,
|
||||
|
@ -48,33 +48,33 @@ namespace winswTests.Util
|
|||
public string ToXMLString(bool dumpConfig = false)
|
||||
{
|
||||
StringBuilder str = new StringBuilder();
|
||||
if (PrintXMLVersion)
|
||||
if (this.PrintXMLVersion)
|
||||
{
|
||||
// TODO: The encoding is generally wrong
|
||||
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.AppendFormat(" <id>{0}</id>\n", Id);
|
||||
str.AppendFormat(" <name>{0}</name>\n", Name);
|
||||
str.AppendFormat(" <description>{0}</description>\n", Description);
|
||||
str.AppendFormat(" <executable>{0}</executable>\n", Executable);
|
||||
foreach (string entry in configEntries)
|
||||
str.AppendFormat(" <id>{0}</id>\n", this.Id);
|
||||
str.AppendFormat(" <name>{0}</name>\n", this.Name);
|
||||
str.AppendFormat(" <description>{0}</description>\n", this.Description);
|
||||
str.AppendFormat(" <executable>{0}</executable>\n", this.Executable);
|
||||
foreach (string entry in this.configEntries)
|
||||
{
|
||||
// We do not care much about pretty formatting here
|
||||
str.AppendFormat(" {0}\n", entry);
|
||||
}
|
||||
|
||||
// Extensions
|
||||
if (ExtensionXmls.Count > 0)
|
||||
if (this.ExtensionXmls.Count > 0)
|
||||
{
|
||||
str.Append(" <extensions>\n");
|
||||
foreach (string xml in ExtensionXmls)
|
||||
foreach (string xml in this.ExtensionXmls)
|
||||
{
|
||||
str.Append(xml);
|
||||
}
|
||||
|
@ -95,18 +95,18 @@ namespace winswTests.Util
|
|||
|
||||
public ServiceDescriptor ToServiceDescriptor(bool dumpConfig = false)
|
||||
{
|
||||
return ServiceDescriptor.FromXML(ToXMLString(dumpConfig));
|
||||
return ServiceDescriptor.FromXML(this.ToXMLString(dumpConfig));
|
||||
}
|
||||
|
||||
public ConfigXmlBuilder WithRawEntry(string entry)
|
||||
{
|
||||
configEntries.Add(entry);
|
||||
this.configEntries.Add(entry);
|
||||
return this;
|
||||
}
|
||||
|
||||
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)
|
||||
|
@ -119,7 +119,7 @@ namespace winswTests.Util
|
|||
str.AppendFormat(" <stopParentFirst>{0}</stopParentFirst>\n", ext.StopParentProcessFirst);
|
||||
str.AppendFormat(" <checkWinSWEnvironmentVariable>{0}</checkWinSWEnvironmentVariable>\n", ext.CheckWinSWEnvironmentVariable);
|
||||
str.Append(" </extension>\n");
|
||||
ExtensionXmls.Add(str.ToString());
|
||||
this.ExtensionXmls.Add(str.ToString());
|
||||
|
||||
return this;
|
||||
}
|
||||
|
@ -130,10 +130,10 @@ namespace winswTests.Util
|
|||
xml.Append($"<download from=\"{download.From}\" to=\"{download.To}\" failOnError=\"{download.FailOnError}\"");
|
||||
|
||||
// Authentication
|
||||
if (download.Auth != Download.AuthType.none)
|
||||
if (download.Auth != Download.AuthType.None)
|
||||
{
|
||||
xml.Append($" auth=\"{download.Auth}\"");
|
||||
if (download.Auth == Download.AuthType.basic)
|
||||
if (download.Auth == Download.AuthType.Basic)
|
||||
{
|
||||
string username = download.Username;
|
||||
if (username != null)
|
||||
|
@ -156,12 +156,12 @@ namespace winswTests.Util
|
|||
|
||||
xml.Append("/>");
|
||||
|
||||
return WithRawEntry(xml.ToString());
|
||||
return this.WithRawEntry(xml.ToString());
|
||||
}
|
||||
|
||||
public ConfigXmlBuilder WithDelayedAutoStart()
|
||||
{
|
||||
return WithRawEntry("<delayedAutoStart/>");
|
||||
return this.WithRawEntry("<delayedAutoStart/>");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@ using System.Diagnostics;
|
|||
using System.IO;
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using winsw.Util;
|
||||
using WinSW.Util;
|
||||
|
||||
namespace winswTests.Util
|
||||
{
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using NUnit.Framework;
|
||||
using winsw;
|
||||
using winsw.Configuration;
|
||||
using WinSW;
|
||||
using WinSW.Configuration;
|
||||
|
||||
namespace winswTests.Util
|
||||
{
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
using NUnit.Framework;
|
||||
using winsw;
|
||||
using WinSW;
|
||||
|
||||
namespace winswTests.Util
|
||||
{
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"settings": {
|
||||
"orderingRules": {
|
||||
"usingDirectivesPlacement": "outsideNamespace"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -20,6 +20,7 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RunawayProcessKiller", "Plu
|
|||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{AA414F46-B863-473A-A0E0-C2971B3396AE}"
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
.editorconfig = .editorconfig
|
||||
..\examples\sample-allOptions.xml = ..\examples\sample-allOptions.xml
|
||||
..\examples\sample-minimal.xml = ..\examples\sample-minimal.xml
|
||||
EndProjectSection
|
||||
|
|
|
@ -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