mirror of https://github.com/winsw/winsw
Clean up styles
parent
6e9d25a7b5
commit
9808ae88e8
|
@ -1,8 +1,4 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Text;
|
||||
using winsw.Logging;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace winsw.Logging
|
||||
{
|
||||
|
@ -11,7 +7,7 @@ namespace winsw.Logging
|
|||
/// </summary>
|
||||
public class WrapperServiceEventLogProvider : IServiceEventLogProvider
|
||||
{
|
||||
public WrapperService service {get; set;}
|
||||
public WrapperService service { get; set; }
|
||||
|
||||
public EventLog locate()
|
||||
{
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Management;
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.ServiceProcess;
|
||||
using System.Text;
|
||||
|
@ -12,15 +12,13 @@ using log4net.Appender;
|
|||
using log4net.Config;
|
||||
using log4net.Core;
|
||||
using log4net.Layout;
|
||||
using log4net.Repository.Hierarchy;
|
||||
using Microsoft.Win32;
|
||||
using winsw.Extensions;
|
||||
using winsw.Logging;
|
||||
using winsw.Native;
|
||||
using winsw.Util;
|
||||
using WMI;
|
||||
using ServiceType = WMI.ServiceType;
|
||||
using winsw.Native;
|
||||
using System.Reflection;
|
||||
using winsw.Logging;
|
||||
|
||||
namespace winsw
|
||||
{
|
||||
|
@ -32,7 +30,7 @@ namespace winsw
|
|||
private readonly ServiceDescriptor _descriptor;
|
||||
private Dictionary<string, string> _envs;
|
||||
|
||||
internal WinSWExtensionManager ExtensionManager { private set; get; }
|
||||
internal WinSWExtensionManager ExtensionManager { get; private set; }
|
||||
|
||||
private static readonly ILog Log = LogManager.GetLogger("WinSW");
|
||||
private static readonly WrapperServiceEventLogProvider eventLogProvider = new WrapperServiceEventLogProvider();
|
||||
|
@ -50,17 +48,12 @@ namespace winsw
|
|||
/// <remarks>
|
||||
/// The version will be taken from <see cref="AssemblyInfo"/>
|
||||
/// </remarks>
|
||||
public static Version Version
|
||||
{
|
||||
get { return Assembly.GetExecutingAssembly().GetName().Version; }
|
||||
}
|
||||
public static Version Version => Assembly.GetExecutingAssembly().GetName().Version;
|
||||
|
||||
/// <summary>
|
||||
/// Indicates that the system is shutting down.
|
||||
/// </summary>
|
||||
public bool IsShuttingDown {
|
||||
get { return _systemShuttingdown; }
|
||||
}
|
||||
public bool IsShuttingDown => _systemShuttingdown;
|
||||
|
||||
public WrapperService(ServiceDescriptor descriptor)
|
||||
{
|
||||
|
@ -77,8 +70,8 @@ namespace winsw
|
|||
eventLogProvider.service = this;
|
||||
}
|
||||
|
||||
public WrapperService() : this (new ServiceDescriptor())
|
||||
{
|
||||
public WrapperService() : this(new ServiceDescriptor())
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -93,7 +86,7 @@ namespace winsw
|
|||
|
||||
try
|
||||
{
|
||||
using (var tr = new StreamReader(file,Encoding.UTF8))
|
||||
using (var tr = new StreamReader(file, Encoding.UTF8))
|
||||
{
|
||||
string line;
|
||||
while ((line = tr.ReadLine()) != null)
|
||||
|
@ -114,7 +107,6 @@ namespace winsw
|
|||
{
|
||||
File.Delete(file);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -199,7 +191,6 @@ namespace winsw
|
|||
{
|
||||
LogEvent("envar " + key + '=' + _envs[key]);
|
||||
}*/
|
||||
|
||||
HandleFileCopies();
|
||||
|
||||
// handle downloads
|
||||
|
@ -217,12 +208,13 @@ namespace winsw
|
|||
string errorMessage = "Failed to download " + d.From + " to " + d.To;
|
||||
LogEvent(errorMessage + ". " + e.Message);
|
||||
Log.Error(errorMessage, e);
|
||||
|
||||
// TODO: move this code into the download logic
|
||||
if (d.FailOnError)
|
||||
{
|
||||
throw new IOException(errorMessage, e);
|
||||
}
|
||||
|
||||
|
||||
// Else just keep going
|
||||
}
|
||||
}
|
||||
|
@ -254,7 +246,7 @@ namespace winsw
|
|||
|
||||
protected override void OnShutdown()
|
||||
{
|
||||
// WriteEvent("OnShutdown");
|
||||
// WriteEvent("OnShutdown");
|
||||
|
||||
try
|
||||
{
|
||||
|
@ -269,7 +261,7 @@ namespace winsw
|
|||
|
||||
protected override void OnStop()
|
||||
{
|
||||
// WriteEvent("OnStop");
|
||||
// WriteEvent("OnStop");
|
||||
|
||||
try
|
||||
{
|
||||
|
@ -318,7 +310,7 @@ namespace winsw
|
|||
executable = _descriptor.Executable;
|
||||
}
|
||||
|
||||
// TODO: Redirect logging to Log4Net once https://github.com/kohsuke/winsw/pull/213 is integrated
|
||||
// TODO: Redirect logging to Log4Net once https://github.com/kohsuke/winsw/pull/213 is integrated
|
||||
StartProcess(stopProcess, stoparguments, executable, null, false);
|
||||
|
||||
Log.Debug("WaitForProcessToExit " + _process.Id + "+" + stopProcess.Id);
|
||||
|
@ -327,10 +319,10 @@ namespace winsw
|
|||
SignalShutdownComplete();
|
||||
}
|
||||
|
||||
// Stop extensions
|
||||
// Stop extensions
|
||||
ExtensionManager.FireBeforeWrapperStopped();
|
||||
|
||||
if (_systemShuttingdown && _descriptor.BeepOnShutdown)
|
||||
if (_systemShuttingdown && _descriptor.BeepOnShutdown)
|
||||
{
|
||||
Console.Beep();
|
||||
}
|
||||
|
@ -341,11 +333,11 @@ namespace winsw
|
|||
private void WaitForProcessToExit(Process processoWait)
|
||||
{
|
||||
SignalShutdownPending();
|
||||
|
||||
|
||||
int effectiveProcessWaitSleepTime;
|
||||
if (_descriptor.SleepTime.TotalMilliseconds > Int32.MaxValue)
|
||||
{
|
||||
Log.Warn("The requested sleep time " + _descriptor.SleepTime.TotalMilliseconds + "is greater that the max value " +
|
||||
Log.Warn("The requested sleep time " + _descriptor.SleepTime.TotalMilliseconds + "is greater that the max value " +
|
||||
Int32.MaxValue + ". The value will be truncated");
|
||||
effectiveProcessWaitSleepTime = Int32.MaxValue;
|
||||
}
|
||||
|
@ -356,12 +348,12 @@ namespace winsw
|
|||
|
||||
try
|
||||
{
|
||||
// WriteEvent("WaitForProcessToExit [start]");
|
||||
// WriteEvent("WaitForProcessToExit [start]");
|
||||
|
||||
while (!processoWait.WaitForExit(effectiveProcessWaitSleepTime))
|
||||
{
|
||||
SignalShutdownPending();
|
||||
// WriteEvent("WaitForProcessToExit [repeat]");
|
||||
// WriteEvent("WaitForProcessToExit [repeat]");
|
||||
}
|
||||
}
|
||||
catch (InvalidOperationException)
|
||||
|
@ -369,7 +361,7 @@ namespace winsw
|
|||
// already terminated
|
||||
}
|
||||
|
||||
// WriteEvent("WaitForProcessToExit [finished]");
|
||||
// WriteEvent("WaitForProcessToExit [finished]");
|
||||
}
|
||||
|
||||
private void SignalShutdownPending()
|
||||
|
@ -377,7 +369,7 @@ namespace winsw
|
|||
int effectiveWaitHint;
|
||||
if (_descriptor.WaitHint.TotalMilliseconds > Int32.MaxValue)
|
||||
{
|
||||
Log.Warn("The requested WaitHint value (" + _descriptor.WaitHint.TotalMilliseconds + " ms) is greater that the max value " +
|
||||
Log.Warn("The requested WaitHint value (" + _descriptor.WaitHint.TotalMilliseconds + " ms) is greater that the max value " +
|
||||
Int32.MaxValue + ". The value will be truncated");
|
||||
effectiveWaitHint = Int32.MaxValue;
|
||||
}
|
||||
|
@ -386,11 +378,10 @@ namespace winsw
|
|||
effectiveWaitHint = (int)_descriptor.WaitHint.TotalMilliseconds;
|
||||
}
|
||||
|
||||
|
||||
IntPtr handle = ServiceHandle;
|
||||
_wrapperServiceStatus.checkPoint++;
|
||||
_wrapperServiceStatus.waitHint = effectiveWaitHint;
|
||||
// WriteEvent("SignalShutdownPending " + wrapperServiceStatus.checkPoint + ":" + wrapperServiceStatus.waitHint);
|
||||
// WriteEvent("SignalShutdownPending " + wrapperServiceStatus.checkPoint + ":" + wrapperServiceStatus.waitHint);
|
||||
_wrapperServiceStatus.currentState = (int)State.SERVICE_STOP_PENDING;
|
||||
Advapi32.SetServiceStatus(handle, ref _wrapperServiceStatus);
|
||||
}
|
||||
|
@ -399,16 +390,15 @@ namespace winsw
|
|||
{
|
||||
IntPtr handle = ServiceHandle;
|
||||
_wrapperServiceStatus.checkPoint++;
|
||||
// WriteEvent("SignalShutdownComplete " + wrapperServiceStatus.checkPoint + ":" + wrapperServiceStatus.waitHint);
|
||||
// WriteEvent("SignalShutdownComplete " + wrapperServiceStatus.checkPoint + ":" + wrapperServiceStatus.waitHint);
|
||||
_wrapperServiceStatus.currentState = (int)State.SERVICE_STOPPED;
|
||||
Advapi32.SetServiceStatus(handle, ref _wrapperServiceStatus);
|
||||
}
|
||||
|
||||
private void StartProcess(Process processToStart, string arguments, String executable, LogHandler logHandler, bool redirectStdin)
|
||||
{
|
||||
|
||||
// Define handler of the completed process
|
||||
ProcessCompletionCallback processCompletionCallback = delegate(Process proc)
|
||||
ProcessCompletionCallback processCompletionCallback = proc =>
|
||||
{
|
||||
string msg = processToStart.Id + " - " + processToStart.StartInfo.FileName + " " + processToStart.StartInfo.Arguments;
|
||||
try
|
||||
|
@ -425,6 +415,7 @@ namespace winsw
|
|||
// restart the service automatically
|
||||
if (proc.ExitCode == 0)
|
||||
SignalShutdownComplete();
|
||||
|
||||
Environment.Exit(proc.ExitCode);
|
||||
}
|
||||
}
|
||||
|
@ -485,30 +476,27 @@ namespace winsw
|
|||
throw new WmiException(ReturnValue.NoSuchService);
|
||||
}
|
||||
|
||||
|
||||
// ReSharper disable once InconsistentNaming
|
||||
/// <summary>
|
||||
/// Runs the wrapper.
|
||||
/// </summary>
|
||||
/// <param name="_args">Arguments. If empty, WinSW will behave in the service mode. Otherwise - CLI mode</param>
|
||||
/// <param name="descriptor">Service descriptor. If null, it will be initialized within the method.
|
||||
/// <param name="descriptor">Service descriptor. If null, it will be initialized within the method.
|
||||
/// In such case configs will be loaded from the XML Configuration File.</param>
|
||||
/// <exception cref="Exception">Any unhandled exception</exception>
|
||||
public static void Run(string[] _args, ServiceDescriptor descriptor = null)
|
||||
{
|
||||
bool isCLIMode = _args.Length > 0;
|
||||
|
||||
|
||||
|
||||
// If descriptor is not specified, initialize the new one (and load configs from there)
|
||||
var d = descriptor ?? new ServiceDescriptor();
|
||||
|
||||
|
||||
// Configure the wrapper-internal logging.
|
||||
// STDIN and STDOUT of the child process will be handled independently.
|
||||
InitLoggers(d, isCLIMode);
|
||||
|
||||
|
||||
if (isCLIMode) // CLI mode, in-service mode otherwise
|
||||
{
|
||||
{
|
||||
Log.Debug("Starting ServiceWrapper in the CLI mode");
|
||||
|
||||
// Get service info for the future use
|
||||
|
@ -519,7 +507,7 @@ namespace winsw
|
|||
if (args[0] == "/redirect")
|
||||
{
|
||||
// Redirect output
|
||||
// One might ask why we support this when the caller
|
||||
// One might ask why we support this when the caller
|
||||
// can redirect the output easily. The answer is for supporting UAC.
|
||||
// On UAC-enabled Windows such as Vista, SCM operation requires
|
||||
// elevated privileges, thus winsw.exe needs to be launched
|
||||
|
@ -527,7 +515,7 @@ namespace winsw
|
|||
// and among other things it makes it difficult for the caller
|
||||
// to read stdout/stderr. Thus redirection becomes handy.
|
||||
var f = new FileStream(args[1], FileMode.Create);
|
||||
var w = new StreamWriter(f) {AutoFlush = true};
|
||||
var w = new StreamWriter(f) { AutoFlush = true };
|
||||
Console.SetOut(w);
|
||||
Console.SetError(w);
|
||||
|
||||
|
@ -551,7 +539,7 @@ namespace winsw
|
|||
throw new Exception("Installation failure: Service with id '" + d.Id + "' already exists");
|
||||
}
|
||||
|
||||
string username=null, password=null;
|
||||
string username = null, password = null;
|
||||
bool setallowlogonasaserviceright = false;
|
||||
if (args.Count > 1 && args[1] == "/p")
|
||||
{
|
||||
|
@ -578,13 +566,13 @@ namespace winsw
|
|||
setallowlogonasaserviceright = d.AllowServiceAcountLogonRight;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (setallowlogonasaserviceright)
|
||||
{
|
||||
LogonAsAService.AddLogonAsAServiceRight(username);
|
||||
}
|
||||
|
||||
svc.Create (
|
||||
svc.Create(
|
||||
d.Id,
|
||||
d.Caption,
|
||||
"\"" + d.ExecutablePath + "\"",
|
||||
|
@ -616,7 +604,7 @@ namespace winsw
|
|||
using (Service sc = scm.Open(d.Id))
|
||||
{
|
||||
// Delayed auto start
|
||||
if (isDelayedAutoStart)
|
||||
if (isDelayedAutoStart)
|
||||
{
|
||||
sc.SetDelayedAutoStart(true);
|
||||
}
|
||||
|
@ -629,8 +617,10 @@ namespace winsw
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (args[0] == "uninstall")
|
||||
{
|
||||
Log.Info("Uninstalling the service with id '" + d.Id + "'");
|
||||
|
@ -639,6 +629,7 @@ namespace winsw
|
|||
Log.Warn("The service with id '" + d.Id + "' does not exist. Nothing to uninstall");
|
||||
return; // there's no such service, so consider it already uninstalled
|
||||
}
|
||||
|
||||
if (s.Started)
|
||||
{
|
||||
// We could fail the opeartion here, but it would be an incompatible change.
|
||||
|
@ -654,9 +645,9 @@ namespace winsw
|
|||
{
|
||||
if (e.ErrorCode == ReturnValue.ServiceMarkedForDeletion)
|
||||
{
|
||||
Log.Error("Failed to uninstall the service with id '" + d.Id + "'"
|
||||
Log.Error("Failed to uninstall the service with id '" + d.Id + "'"
|
||||
+ ". It has been marked for deletion.");
|
||||
|
||||
|
||||
// TODO: change the default behavior to Error?
|
||||
return; // it's already uninstalled, so consider it a success
|
||||
}
|
||||
|
@ -664,31 +655,40 @@ namespace winsw
|
|||
{
|
||||
Log.Fatal("Failed to uninstall the service with id '" + d.Id + "'. WMI Error code is '" + e.ErrorCode + "'");
|
||||
}
|
||||
|
||||
throw e;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (args[0] == "start")
|
||||
{
|
||||
Log.Info("Starting the service with id '" + d.Id + "'");
|
||||
if (s == null) ThrowNoSuchService();
|
||||
if (s == null)
|
||||
ThrowNoSuchService();
|
||||
|
||||
s.StartService();
|
||||
return;
|
||||
}
|
||||
|
||||
if (args[0] == "stop")
|
||||
{
|
||||
Log.Info("Stopping the service with id '" + d.Id + "'");
|
||||
if (s == null) ThrowNoSuchService();
|
||||
if (s == null)
|
||||
ThrowNoSuchService();
|
||||
|
||||
s.StopService();
|
||||
return;
|
||||
}
|
||||
|
||||
if (args[0] == "restart")
|
||||
{
|
||||
Log.Info("Restarting the service with id '" + d.Id + "'");
|
||||
if (s == null)
|
||||
if (s == null)
|
||||
ThrowNoSuchService();
|
||||
|
||||
if(s.Started)
|
||||
if (s.Started)
|
||||
s.StopService();
|
||||
|
||||
while (s.Started)
|
||||
|
@ -700,22 +700,24 @@ namespace winsw
|
|||
s.StartService();
|
||||
return;
|
||||
}
|
||||
|
||||
if (args[0] == "restart!")
|
||||
{
|
||||
Log.Info("Restarting the service with id '" + d.Id + "'");
|
||||
|
||||
// run restart from another process group. see README.md for why this is useful.
|
||||
|
||||
STARTUPINFO si = new STARTUPINFO();
|
||||
PROCESS_INFORMATION pi = new PROCESS_INFORMATION();
|
||||
STARTUPINFO si = default;
|
||||
|
||||
bool result = Kernel32.CreateProcess(null, d.ExecutablePath+" restart", IntPtr.Zero, IntPtr.Zero, false, 0x200/*CREATE_NEW_PROCESS_GROUP*/, IntPtr.Zero, null, ref si, out pi);
|
||||
bool result = Kernel32.CreateProcess(null, d.ExecutablePath + " restart", IntPtr.Zero, IntPtr.Zero, false, 0x200/*CREATE_NEW_PROCESS_GROUP*/, IntPtr.Zero, null, ref si, out PROCESS_INFORMATION pi);
|
||||
if (!result)
|
||||
{
|
||||
throw new Exception("Failed to invoke restart: "+Marshal.GetLastWin32Error());
|
||||
throw new Exception("Failed to invoke restart: " + Marshal.GetLastWin32Error());
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (args[0] == "status")
|
||||
{
|
||||
Log.Debug("User requested the status of the process with id '" + d.Id + "'");
|
||||
|
@ -727,6 +729,7 @@ namespace winsw
|
|||
Console.WriteLine("Stopped");
|
||||
return;
|
||||
}
|
||||
|
||||
if (args[0] == "test")
|
||||
{
|
||||
WrapperService wsvc = new WrapperService(d);
|
||||
|
@ -735,6 +738,7 @@ namespace winsw
|
|||
wsvc.OnStop();
|
||||
return;
|
||||
}
|
||||
|
||||
if (args[0] == "testwait")
|
||||
{
|
||||
WrapperService wsvc = new WrapperService(d);
|
||||
|
@ -744,27 +748,29 @@ namespace winsw
|
|||
wsvc.OnStop();
|
||||
return;
|
||||
}
|
||||
if (args[0] == "help" || args[0] == "--help" || args[0] == "-h"
|
||||
|
||||
if (args[0] == "help" || args[0] == "--help" || args[0] == "-h"
|
||||
|| args[0] == "-?" || args[0] == "/?")
|
||||
{
|
||||
printHelp();
|
||||
return;
|
||||
}
|
||||
|
||||
if (args[0] == "version")
|
||||
{
|
||||
printVersion();
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
Console.WriteLine("Unknown command: " + args[0]);
|
||||
printAvailableCommandsInfo();
|
||||
throw new Exception("Unknown command: " + args[0]);
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.Info("Starting ServiceWrapper in the service mode");
|
||||
}
|
||||
|
||||
Run(new WrapperService(d));
|
||||
}
|
||||
|
||||
|
@ -774,7 +780,7 @@ namespace winsw
|
|||
Level logLevel = 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;
|
||||
Level consoleLogLevel = Level.Info;
|
||||
Level eventLogLevel = Level.Warn;
|
||||
|
||||
// Legacy format from winsw-1.x: (DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + " - " + message);
|
||||
|
@ -812,11 +818,11 @@ namespace winsw
|
|||
}
|
||||
|
||||
// System log
|
||||
var systemEventLogger = new ServiceEventLogAppender
|
||||
var systemEventLogger = new ServiceEventLogAppender
|
||||
{
|
||||
Name = "System event log",
|
||||
Threshold = eventLogLevel,
|
||||
provider = eventLogProvider
|
||||
provider = eventLogProvider
|
||||
};
|
||||
systemEventLogger.ActivateOptions();
|
||||
appenders.Add(systemEventLogger);
|
||||
|
@ -851,21 +857,21 @@ namespace winsw
|
|||
private static void printHelp()
|
||||
{
|
||||
Console.WriteLine("A wrapper binary that can be used to host executables as Windows services");
|
||||
Console.WriteLine("");
|
||||
Console.WriteLine();
|
||||
Console.WriteLine("Usage: winsw [/redirect file] <command> [<args>]");
|
||||
Console.WriteLine(" Missing arguments trigger the service mode");
|
||||
Console.WriteLine("");
|
||||
Console.WriteLine();
|
||||
printAvailableCommandsInfo();
|
||||
Console.WriteLine("");
|
||||
Console.WriteLine();
|
||||
Console.WriteLine("Extra options:");
|
||||
Console.WriteLine("- '/redirect' - redirect the wrapper's STDOUT and STDERR to the specified file");
|
||||
Console.WriteLine("");
|
||||
Console.WriteLine();
|
||||
printVersion();
|
||||
Console.WriteLine("More info: https://github.com/kohsuke/winsw");
|
||||
Console.WriteLine("Bug tracker: https://github.com/kohsuke/winsw/issues");
|
||||
}
|
||||
|
||||
//TODO: Rework to enum in winsw-2.0
|
||||
// TODO: Rework to enum in winsw-2.0
|
||||
private static void printAvailableCommandsInfo()
|
||||
{
|
||||
Console.WriteLine("Available commands:");
|
||||
|
|
|
@ -16,6 +16,7 @@ namespace winsw
|
|||
|
||||
[DllImport(KERNEL32)]
|
||||
private static extern bool SetConsoleCtrlHandler(ConsoleCtrlDelegate HandlerRoutine, bool Add);
|
||||
|
||||
// Delegate type to be used as the Handler Routine for SCCH
|
||||
private delegate Boolean ConsoleCtrlDelegate(CtrlTypes CtrlType);
|
||||
|
||||
|
@ -43,15 +44,15 @@ namespace winsw
|
|||
{
|
||||
if (AttachConsole((uint)process.Id))
|
||||
{
|
||||
//Disable Ctrl-C handling for our program
|
||||
// Disable Ctrl-C handling for our program
|
||||
SetConsoleCtrlHandler(null, true);
|
||||
GenerateConsoleCtrlEvent(CtrlTypes.CTRL_C_EVENT, 0);
|
||||
|
||||
process.WaitForExit((int)shutdownTimeout.TotalMilliseconds);
|
||||
|
||||
return process.HasExited;
|
||||
return process.HasExited;
|
||||
}
|
||||
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,7 +3,6 @@ using System.Collections.Generic;
|
|||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Xml;
|
||||
using WMI;
|
||||
|
||||
|
@ -14,11 +13,11 @@ namespace winsw.Configuration
|
|||
/// </summary>
|
||||
public sealed class DefaultWinSWSettings : IWinSWConfiguration
|
||||
{
|
||||
public string Id { get { return null; } }
|
||||
public string Caption { get { return null; } }
|
||||
public string Description { get { return null; } }
|
||||
public string Executable { get { return null; } }
|
||||
public bool HideWindow { get { return false; } }
|
||||
public string Id => null;
|
||||
public string Caption => null;
|
||||
public string Description => null;
|
||||
public string Executable => null;
|
||||
public bool HideWindow => false;
|
||||
|
||||
public string ExecutablePath
|
||||
{
|
||||
|
@ -32,47 +31,47 @@ namespace winsw.Configuration
|
|||
}
|
||||
|
||||
// Installation
|
||||
public bool AllowServiceAcountLogonRight { get { return false; } }
|
||||
public string ServiceAccountPassword { get { return null; } }
|
||||
public string ServiceAccountUser { get { return "NULL\\NULL"; } }
|
||||
public List<winsw.Native.SC_ACTION> FailureActions { get { return new List<winsw.Native.SC_ACTION>(); } }
|
||||
public TimeSpan ResetFailureAfter { get { return TimeSpan.FromDays(1); } }
|
||||
public bool AllowServiceAcountLogonRight => false;
|
||||
public string ServiceAccountPassword => null;
|
||||
public string ServiceAccountUser => "NULL\\NULL";
|
||||
public List<Native.SC_ACTION> FailureActions => new List<Native.SC_ACTION>();
|
||||
public TimeSpan ResetFailureAfter => TimeSpan.FromDays(1);
|
||||
|
||||
// Executable management
|
||||
public string Arguments { get { return ""; } }
|
||||
public string Startarguments { get { return null; } }
|
||||
public string StopExecutable { get { return null; } }
|
||||
public string Stoparguments { get { return null; } }
|
||||
public string WorkingDirectory { get { return Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); } }
|
||||
public ProcessPriorityClass Priority { get { return ProcessPriorityClass.Normal; } }
|
||||
public TimeSpan StopTimeout { get { return TimeSpan.FromSeconds(15); } }
|
||||
public bool StopParentProcessFirst { get { return false; } }
|
||||
public string Arguments => string.Empty;
|
||||
public string Startarguments => null;
|
||||
public string StopExecutable => null;
|
||||
public string Stoparguments => null;
|
||||
public string WorkingDirectory => Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
|
||||
public ProcessPriorityClass Priority => ProcessPriorityClass.Normal;
|
||||
public TimeSpan StopTimeout => TimeSpan.FromSeconds(15);
|
||||
public bool StopParentProcessFirst => false;
|
||||
|
||||
// Service management
|
||||
public StartMode StartMode { get { return StartMode.Automatic; } }
|
||||
public bool DelayedAutoStart { get { return false; } }
|
||||
public string[] ServiceDependencies { get { return new string[0]; } }
|
||||
public TimeSpan WaitHint { get { return TimeSpan.FromSeconds(15); } }
|
||||
public TimeSpan SleepTime { get { return TimeSpan.FromSeconds(1); } }
|
||||
public bool Interactive { get { return false; } }
|
||||
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 { get { return Path.GetDirectoryName(ExecutablePath); } }
|
||||
public string LogMode { get { return "append"; } }
|
||||
public string LogDirectory => Path.GetDirectoryName(ExecutablePath);
|
||||
public string LogMode => "append";
|
||||
|
||||
public bool OutFileDisabled { get { return false; } }
|
||||
public bool ErrFileDisabled { get { return false; } }
|
||||
public string OutFilePattern { get { return ".out.log"; } }
|
||||
public string ErrFilePattern { get { return ".err.log"; } }
|
||||
public bool OutFileDisabled => false;
|
||||
public bool ErrFileDisabled => false;
|
||||
public string OutFilePattern => ".out.log";
|
||||
public string ErrFilePattern => ".err.log";
|
||||
|
||||
// Environment
|
||||
public List<Download> Downloads { get { return new List<Download>(); } }
|
||||
public Dictionary<string, string> EnvironmentVariables { get { return new Dictionary<string, string>(); } }
|
||||
public List<Download> Downloads => new List<Download>();
|
||||
public Dictionary<string, string> EnvironmentVariables => new Dictionary<string, string>();
|
||||
|
||||
// Misc
|
||||
public bool BeepOnShutdown { get { return false; } }
|
||||
public bool BeepOnShutdown => false;
|
||||
|
||||
// Extensions
|
||||
public XmlNode ExtensionsConfiguration { get {return null; } }
|
||||
public XmlNode ExtensionsConfiguration => null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@ namespace winsw.Configuration
|
|||
{
|
||||
public interface IWinSWConfiguration
|
||||
{
|
||||
//TODO: Document the parameters && refactor
|
||||
// TODO: Document the parameters && refactor
|
||||
|
||||
string Id { get; }
|
||||
string Caption { get; }
|
||||
|
@ -20,7 +20,7 @@ namespace winsw.Configuration
|
|||
bool AllowServiceAcountLogonRight { get; }
|
||||
string ServiceAccountPassword { get; }
|
||||
string ServiceAccountUser { get; }
|
||||
List<winsw.Native.SC_ACTION> FailureActions { get; }
|
||||
List<Native.SC_ACTION> FailureActions { get; }
|
||||
TimeSpan ResetFailureAfter { get; }
|
||||
|
||||
// Executable management
|
||||
|
@ -39,16 +39,16 @@ namespace winsw.Configuration
|
|||
TimeSpan WaitHint { get; }
|
||||
TimeSpan SleepTime { get; }
|
||||
bool Interactive { get; }
|
||||
|
||||
|
||||
// Logging
|
||||
string LogDirectory { get; }
|
||||
//TODO: replace by enum
|
||||
// TODO: replace by enum
|
||||
string LogMode { get; }
|
||||
|
||||
|
||||
// Environment
|
||||
List<Download> Downloads { get; }
|
||||
Dictionary<string, string> EnvironmentVariables { get; }
|
||||
|
||||
|
||||
// Misc
|
||||
bool BeepOnShutdown { get; }
|
||||
|
||||
|
|
|
@ -13,7 +13,12 @@ namespace winsw
|
|||
/// </summary>
|
||||
public class Download
|
||||
{
|
||||
public enum AuthType { none = 0, sspi, basic }
|
||||
public enum AuthType
|
||||
{
|
||||
none = 0,
|
||||
sspi,
|
||||
basic
|
||||
}
|
||||
|
||||
public readonly string From;
|
||||
public readonly string To;
|
||||
|
@ -23,10 +28,16 @@ namespace winsw
|
|||
public readonly bool UnsecureAuth;
|
||||
public readonly bool FailOnError;
|
||||
|
||||
public string ShortId { get { return String.Format("(download from {0})", From); } }
|
||||
public string ShortId => $"(download from {From})";
|
||||
|
||||
public Download(string from, string to, bool failOnError = false, AuthType auth = AuthType.none,
|
||||
string username = null, string password = null, bool unsecureAuth = false)
|
||||
public Download(
|
||||
string from,
|
||||
string to,
|
||||
bool failOnError = false,
|
||||
AuthType auth = AuthType.none,
|
||||
string username = null,
|
||||
string password = null,
|
||||
bool unsecureAuth = false)
|
||||
{
|
||||
From = from;
|
||||
To = to;
|
||||
|
@ -48,19 +59,19 @@ namespace winsw
|
|||
To = XmlHelper.SingleAttribute<String>(n, "to");
|
||||
|
||||
// All arguments below are optional
|
||||
FailOnError = XmlHelper.SingleAttribute<bool>(n, "failOnError", false);
|
||||
FailOnError = XmlHelper.SingleAttribute(n, "failOnError", false);
|
||||
|
||||
Auth = XmlHelper.EnumAttribute<AuthType>(n, "auth", AuthType.none);
|
||||
Auth = XmlHelper.EnumAttribute(n, "auth", AuthType.none);
|
||||
Username = XmlHelper.SingleAttribute<String>(n, "user", null);
|
||||
Password = XmlHelper.SingleAttribute<String>(n, "password", null);
|
||||
UnsecureAuth = XmlHelper.SingleAttribute<bool>(n, "unsecureAuth", false);
|
||||
UnsecureAuth = XmlHelper.SingleAttribute(n, "unsecureAuth", false);
|
||||
|
||||
if (Auth == AuthType.basic)
|
||||
{
|
||||
// Allow it only for HTTPS or for UnsecureAuth
|
||||
// Allow it only for HTTPS or for UnsecureAuth
|
||||
if (!From.StartsWith("https:") && !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 " + ShortId +
|
||||
"If you really want this you must enable 'unsecureAuth' in the configuration");
|
||||
}
|
||||
|
||||
|
@ -69,6 +80,7 @@ namespace winsw
|
|||
{
|
||||
throw new InvalidDataException("Basic Auth is enabled, but username is not specified " + ShortId);
|
||||
}
|
||||
|
||||
if (Password == null)
|
||||
{
|
||||
throw new InvalidDataException("Basic Auth is enabled, but password is not specified " + ShortId);
|
||||
|
@ -87,7 +99,7 @@ namespace winsw
|
|||
/// <summary>
|
||||
/// Downloads the requested file and puts it to the specified target.
|
||||
/// </summary>
|
||||
/// <exception cref="System.Net.WebException">
|
||||
/// <exception cref="WebException">
|
||||
/// Download failure. FailOnError flag should be processed outside.
|
||||
/// </exception>
|
||||
public void Perform()
|
||||
|
@ -120,6 +132,7 @@ namespace winsw
|
|||
// only after we successfully downloaded a file, overwrite the existing one
|
||||
if (File.Exists(To))
|
||||
File.Delete(To);
|
||||
|
||||
File.Move(To + ".tmp", To);
|
||||
}
|
||||
|
||||
|
@ -129,9 +142,12 @@ namespace winsw
|
|||
while (true)
|
||||
{
|
||||
int len = i.Read(buf, 0, buf.Length);
|
||||
if (len <= 0) break;
|
||||
if (len <= 0)
|
||||
break;
|
||||
|
||||
o.Write(buf, 0, len);
|
||||
}
|
||||
|
||||
i.Close();
|
||||
o.Close();
|
||||
}
|
||||
|
|
|
@ -7,8 +7,8 @@ using System.Threading;
|
|||
namespace DynamicProxy
|
||||
{
|
||||
/// <summary>
|
||||
/// Interface that a user defined proxy handler needs to implement. This interface
|
||||
/// defines one method that gets invoked by the generated proxy.
|
||||
/// Interface that a user defined proxy handler needs to implement. This interface
|
||||
/// defines one method that gets invoked by the generated proxy.
|
||||
/// </summary>
|
||||
public interface IProxyInvocationHandler
|
||||
{
|
||||
|
@ -96,7 +96,7 @@ namespace DynamicProxy
|
|||
private const string MODULE_NAME = "ProxyModule";
|
||||
private const string HANDLER_NAME = "handler";
|
||||
|
||||
// Initialize the value type mapper. This is needed for methods with intrinsic
|
||||
// Initialize the value type mapper. This is needed for methods with intrinsic
|
||||
// return types, used in the Emit process.
|
||||
static ProxyFactory()
|
||||
{
|
||||
|
@ -182,7 +182,7 @@ namespace DynamicProxy
|
|||
// create a new assembly for this proxy, one that isn't presisted on the file system
|
||||
AssemblyBuilder assemblyBuilder = domain.DefineDynamicAssembly(
|
||||
assemblyName, AssemblyBuilderAccess.Run);
|
||||
// assemblyName, AssemblyBuilderAccess.RunAndSave,"."); // to save it to the disk
|
||||
// assemblyName, AssemblyBuilderAccess.RunAndSave,"."); // to save it to the disk
|
||||
|
||||
// create a new module for this proxy
|
||||
ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule(MODULE_NAME);
|
||||
|
@ -200,9 +200,8 @@ namespace DynamicProxy
|
|||
FieldBuilder handlerField = typeBuilder.DefineField(
|
||||
HANDLER_NAME, handlerType, FieldAttributes.Private);
|
||||
|
||||
|
||||
// build a constructor that takes the delegate object as the only argument
|
||||
//ConstructorInfo defaultObjConstructor = objType.GetConstructor( new Type[0] );
|
||||
// ConstructorInfo defaultObjConstructor = objType.GetConstructor( new Type[0] );
|
||||
ConstructorInfo superConstructor = objType.GetConstructor(new Type[0]);
|
||||
ConstructorBuilder delegateConstructor = typeBuilder.DefineConstructor(
|
||||
MethodAttributes.Public, CallingConventions.Standard, new Type[] { handlerType });
|
||||
|
@ -224,8 +223,8 @@ namespace DynamicProxy
|
|||
constructorIL.Emit(OpCodes.Ret);
|
||||
#endregion
|
||||
|
||||
// for every method that the interfaces define, build a corresponding
|
||||
// method in the dynamic type that calls the handlers invoke method.
|
||||
// for every method that the interfaces define, build a corresponding
|
||||
// method in the dynamic type that calls the handlers invoke method.
|
||||
foreach (Type interfaceType in interfaces)
|
||||
{
|
||||
GenerateMethod(interfaceType, handlerField, typeBuilder);
|
||||
|
@ -242,106 +241,122 @@ namespace DynamicProxy
|
|||
private static readonly MethodInfo INVOKE_METHOD = typeof(IProxyInvocationHandler).GetMethod("Invoke");
|
||||
private static readonly MethodInfo GET_METHODINFO_METHOD = typeof(MetaDataFactory).GetMethod("GetMethod", new Type[] { typeof(string), typeof(int) });
|
||||
|
||||
private void GenerateMethod( Type interfaceType, FieldBuilder handlerField, TypeBuilder typeBuilder ) {
|
||||
MetaDataFactory.Add( interfaceType );
|
||||
private void GenerateMethod(Type interfaceType, FieldBuilder handlerField, TypeBuilder typeBuilder)
|
||||
{
|
||||
MetaDataFactory.Add(interfaceType);
|
||||
MethodInfo[] interfaceMethods = interfaceType.GetMethods();
|
||||
PropertyInfo[] props = interfaceType.GetProperties();
|
||||
|
||||
for ( int i = 0; i < interfaceMethods.Length; i++ ) {
|
||||
for (int i = 0; i < interfaceMethods.Length; i++)
|
||||
{
|
||||
MethodInfo methodInfo = interfaceMethods[i];
|
||||
|
||||
// Get the method parameters since we need to create an array
|
||||
// of parameter types
|
||||
ParameterInfo[] methodParams = methodInfo.GetParameters();
|
||||
int numOfParams = methodParams.Length;
|
||||
Type[] methodParameters = new Type[ numOfParams ];
|
||||
Type[] methodParameters = new Type[numOfParams];
|
||||
|
||||
// convert the ParameterInfo objects into Type
|
||||
for ( int j = 0; j < numOfParams; j++ ) {
|
||||
for (int j = 0; j < numOfParams; j++)
|
||||
{
|
||||
methodParameters[j] = methodParams[j].ParameterType;
|
||||
}
|
||||
|
||||
// create a new builder for the method in the interface
|
||||
MethodBuilder methodBuilder = typeBuilder.DefineMethod(
|
||||
methodInfo.Name,
|
||||
/*MethodAttributes.Public | MethodAttributes.Virtual | */ methodInfo.Attributes&~MethodAttributes.Abstract,
|
||||
methodInfo.Name,
|
||||
/*MethodAttributes.Public | MethodAttributes.Virtual | */ methodInfo.Attributes & ~MethodAttributes.Abstract,
|
||||
CallingConventions.Standard,
|
||||
methodInfo.ReturnType, methodParameters );
|
||||
methodInfo.ReturnType, methodParameters);
|
||||
|
||||
#region( "Handler Method IL Code" )
|
||||
ILGenerator methodIL = methodBuilder.GetILGenerator();
|
||||
|
||||
|
||||
// load "this"
|
||||
methodIL.Emit( OpCodes.Ldarg_0 );
|
||||
methodIL.Emit(OpCodes.Ldarg_0);
|
||||
// load the handler
|
||||
methodIL.Emit( OpCodes.Ldfld, handlerField );
|
||||
methodIL.Emit(OpCodes.Ldfld, handlerField);
|
||||
// load "this" since its needed for the call to invoke
|
||||
methodIL.Emit( OpCodes.Ldarg_0 );
|
||||
methodIL.Emit(OpCodes.Ldarg_0);
|
||||
// load the name of the interface, used to get the MethodInfo object
|
||||
// from MetaDataFactory
|
||||
methodIL.Emit( OpCodes.Ldstr, interfaceType.FullName );
|
||||
// load the index, used to get the MethodInfo object
|
||||
// from MetaDataFactory
|
||||
methodIL.Emit( OpCodes.Ldc_I4, i );
|
||||
methodIL.Emit(OpCodes.Ldstr, interfaceType.FullName);
|
||||
// load the index, used to get the MethodInfo object
|
||||
// from MetaDataFactory
|
||||
methodIL.Emit(OpCodes.Ldc_I4, i);
|
||||
// invoke GetMethod in MetaDataFactory
|
||||
methodIL.Emit( OpCodes.Call, GET_METHODINFO_METHOD);
|
||||
methodIL.Emit(OpCodes.Call, GET_METHODINFO_METHOD);
|
||||
|
||||
// load the number of parameters onto the stack
|
||||
methodIL.Emit( OpCodes.Ldc_I4, numOfParams );
|
||||
methodIL.Emit(OpCodes.Ldc_I4, numOfParams);
|
||||
// create a new array, using the size that was just pused on the stack
|
||||
methodIL.Emit( OpCodes.Newarr, typeof(object) );
|
||||
|
||||
methodIL.Emit(OpCodes.Newarr, typeof(object));
|
||||
|
||||
// if we have any parameters, then iterate through and set the values
|
||||
// of each element to the corresponding arguments
|
||||
for ( int j = 0; j < numOfParams; j++ ) {
|
||||
methodIL.Emit( OpCodes.Dup ); // this copies the array
|
||||
methodIL.Emit( OpCodes.Ldc_I4, j );
|
||||
methodIL.Emit( OpCodes.Ldarg, j + 1 );
|
||||
if ( methodParameters[j].IsValueType ) {
|
||||
methodIL.Emit( OpCodes.Box, methodParameters[j] );
|
||||
for (int j = 0; j < numOfParams; j++)
|
||||
{
|
||||
methodIL.Emit(OpCodes.Dup); // this copies the array
|
||||
methodIL.Emit(OpCodes.Ldc_I4, j);
|
||||
methodIL.Emit(OpCodes.Ldarg, j + 1);
|
||||
if (methodParameters[j].IsValueType)
|
||||
{
|
||||
methodIL.Emit(OpCodes.Box, methodParameters[j]);
|
||||
}
|
||||
methodIL.Emit( OpCodes.Stelem_Ref );
|
||||
|
||||
methodIL.Emit(OpCodes.Stelem_Ref);
|
||||
}
|
||||
|
||||
// call the Invoke method
|
||||
methodIL.Emit( OpCodes.Callvirt, INVOKE_METHOD );
|
||||
|
||||
if ( methodInfo.ReturnType != typeof(void) ) {
|
||||
methodIL.Emit(OpCodes.Callvirt, INVOKE_METHOD);
|
||||
|
||||
if (methodInfo.ReturnType != typeof(void))
|
||||
{
|
||||
// if the return type if a value type, then unbox the return value
|
||||
// so that we don't get junk.
|
||||
if ( methodInfo.ReturnType.IsValueType ) {
|
||||
methodIL.Emit( OpCodes.Unbox, methodInfo.ReturnType );
|
||||
if ( methodInfo.ReturnType.IsEnum ) {
|
||||
methodIL.Emit( OpCodes.Ldind_I4 );
|
||||
} else if ( !methodInfo.ReturnType.IsPrimitive ) {
|
||||
methodIL.Emit( OpCodes.Ldobj, methodInfo.ReturnType );
|
||||
} else {
|
||||
methodIL.Emit( (OpCode) OpCodeTypeMapper[ methodInfo.ReturnType ] );
|
||||
if (methodInfo.ReturnType.IsValueType)
|
||||
{
|
||||
methodIL.Emit(OpCodes.Unbox, methodInfo.ReturnType);
|
||||
if (methodInfo.ReturnType.IsEnum)
|
||||
{
|
||||
methodIL.Emit(OpCodes.Ldind_I4);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// pop the return value that Invoke returned from the stack since
|
||||
// the method's return type is void.
|
||||
methodIL.Emit( OpCodes.Pop );
|
||||
else if (!methodInfo.ReturnType.IsPrimitive)
|
||||
{
|
||||
methodIL.Emit(OpCodes.Ldobj, methodInfo.ReturnType);
|
||||
}
|
||||
else
|
||||
{
|
||||
methodIL.Emit((OpCode)OpCodeTypeMapper[methodInfo.ReturnType]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
// pop the return value that Invoke returned from the stack since
|
||||
// the method's return type is void.
|
||||
methodIL.Emit(OpCodes.Pop);
|
||||
}
|
||||
|
||||
// Return
|
||||
methodIL.Emit( OpCodes.Ret );
|
||||
methodIL.Emit(OpCodes.Ret);
|
||||
#endregion
|
||||
}
|
||||
|
||||
//for (int i = 0; i < props.Length; i++)
|
||||
//{
|
||||
// PropertyInfo p = props[i];
|
||||
// for (int i = 0; i < props.Length; i++)
|
||||
// {
|
||||
// PropertyInfo p = props[i];
|
||||
|
||||
// PropertyBuilder pb = typeBuilder.DefineProperty(p.Name, p.Attributes, p.PropertyType, new Type[] { p.PropertyType });
|
||||
// pb.SetGetMethod((MethodBuilder)methodTable[p.GetGetMethod()]);
|
||||
// pb.SetSetMethod((MethodBuilder)methodTable[p.GetSetMethod()]);
|
||||
//}
|
||||
// PropertyBuilder pb = typeBuilder.DefineProperty(p.Name, p.Attributes, p.PropertyType, new Type[] { p.PropertyType });
|
||||
// pb.SetGetMethod((MethodBuilder)methodTable[p.GetGetMethod()]);
|
||||
// pb.SetSetMethod((MethodBuilder)methodTable[p.GetSetMethod()]);
|
||||
// }
|
||||
|
||||
// Iterate through the parent interfaces and recursively call this method
|
||||
foreach ( Type parentType in interfaceType.GetInterfaces() ) {
|
||||
GenerateMethod( parentType, handlerField, typeBuilder );
|
||||
foreach (Type parentType in interfaceType.GetInterfaces())
|
||||
{
|
||||
GenerateMethod(parentType, handlerField, typeBuilder);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
using System;
|
||||
using System.Xml;
|
||||
using winsw.Util;
|
||||
|
||||
namespace winsw.Extensions
|
||||
{
|
||||
public abstract class AbstractWinSWExtension : IWinSWExtension
|
||||
public abstract class AbstractWinSWExtension : IWinSWExtension
|
||||
{
|
||||
public abstract String DisplayName { get; }
|
||||
|
||||
public WinSWExtensionDescriptor Descriptor { get; set; }
|
||||
|
||||
public virtual void Configure(ServiceDescriptor descriptor, XmlNode node)
|
||||
|
|
|
@ -18,12 +18,6 @@ namespace winsw.Extensions
|
|||
ExtensionId = extensionName;
|
||||
}
|
||||
|
||||
public override string Message
|
||||
{
|
||||
get
|
||||
{
|
||||
return ExtensionId + ": " + base.Message;
|
||||
}
|
||||
}
|
||||
public override string Message => ExtensionId + ": " + base.Message;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,4 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace winsw.Extensions
|
||||
namespace winsw.Extensions
|
||||
{
|
||||
/// <summary>
|
||||
/// This attribute is used to identify extension points within the code
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
using System;
|
||||
using System.Xml;
|
||||
using winsw.Util;
|
||||
|
||||
namespace winsw.Extensions
|
||||
{
|
||||
|
@ -8,7 +7,7 @@ namespace winsw.Extensions
|
|||
/// Interface for Win Service Wrapper Extension
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// All implementations should provide the default empty constructor.
|
||||
/// All implementations should provide the default empty constructor.
|
||||
/// The initialization will be performed by Init methods.
|
||||
/// Binary comparibility of the class is not guaranteed in WinSW 2.
|
||||
/// </remarks>
|
||||
|
|
|
@ -1,17 +1,16 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Xml;
|
||||
using System.Reflection;
|
||||
using System.Diagnostics;
|
||||
using winsw.Util;
|
||||
using System.Xml;
|
||||
using log4net;
|
||||
|
||||
namespace winsw.Extensions
|
||||
{
|
||||
public class WinSWExtensionManager
|
||||
{
|
||||
public Dictionary<string, IWinSWExtension> Extensions { private set; get; }
|
||||
public ServiceDescriptor ServiceDescriptor { private set; get; }
|
||||
public Dictionary<string, IWinSWExtension> Extensions { get; private set; }
|
||||
|
||||
public ServiceDescriptor ServiceDescriptor { get; private set; }
|
||||
|
||||
private static readonly ILog Log = LogManager.GetLogger(typeof(WinSWExtensionManager));
|
||||
|
||||
|
@ -27,7 +26,7 @@ namespace winsw.Extensions
|
|||
/// If any extensions fails, WinSW startup should be interrupted.
|
||||
/// </summary>
|
||||
/// <exception cref="Exception">Start failure</exception>
|
||||
public void FireOnWrapperStarted()
|
||||
public void FireOnWrapperStarted()
|
||||
{
|
||||
foreach (var ext in Extensions)
|
||||
{
|
||||
|
@ -50,7 +49,7 @@ namespace winsw.Extensions
|
|||
public void FireBeforeWrapperStopped()
|
||||
{
|
||||
foreach (var ext in Extensions)
|
||||
{
|
||||
{
|
||||
try
|
||||
{
|
||||
ext.Value.BeforeWrapperStopped();
|
||||
|
@ -100,10 +99,9 @@ namespace winsw.Extensions
|
|||
}
|
||||
}
|
||||
|
||||
//TODO: Implement loading of external extensions. Current version supports internal hack
|
||||
// TODO: Implement loading of external extensions. Current version supports internal hack
|
||||
#region Extension load management
|
||||
|
||||
|
||||
/// Loads extensions according to the configuration file.
|
||||
/// </summary>
|
||||
/// <param name="logger">Logger</param>
|
||||
|
@ -111,7 +109,7 @@ namespace winsw.Extensions
|
|||
public void LoadExtensions()
|
||||
{
|
||||
var extensionIds = ServiceDescriptor.ExtensionIds;
|
||||
foreach (String extensionId in extensionIds)
|
||||
foreach (String extensionId in extensionIds)
|
||||
{
|
||||
LoadExtension(extensionId);
|
||||
}
|
||||
|
@ -131,7 +129,7 @@ namespace winsw.Extensions
|
|||
}
|
||||
|
||||
var extensionsConfig = ServiceDescriptor.ExtensionsConfiguration;
|
||||
XmlElement configNode =(extensionsConfig != null) ? extensionsConfig.SelectSingleNode("extension[@id='"+id+"'][1]") as XmlElement : null;
|
||||
XmlElement configNode = (extensionsConfig != null) ? extensionsConfig.SelectSingleNode("extension[@id='" + id + "'][1]") as XmlElement : null;
|
||||
if (configNode == null)
|
||||
{
|
||||
throw new ExtensionException(id, "Cannot get the configuration entry");
|
||||
|
@ -151,6 +149,7 @@ namespace winsw.Extensions
|
|||
Log.Fatal("Failed to configure the extension " + id, ex);
|
||||
throw ex;
|
||||
}
|
||||
|
||||
Extensions.Add(id, extension);
|
||||
Log.Info("Extension loaded: " + id);
|
||||
}
|
||||
|
@ -158,7 +157,6 @@ namespace winsw.Extensions
|
|||
{
|
||||
Log.Warn("Extension is disabled: " + id);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private IWinSWExtension CreateExtensionInstance(string id, string className)
|
||||
|
@ -166,29 +164,30 @@ namespace winsw.Extensions
|
|||
ActivationContext ac = AppDomain.CurrentDomain.ActivationContext;
|
||||
Assembly assembly = Assembly.GetCallingAssembly();
|
||||
Object created;
|
||||
|
||||
|
||||
try
|
||||
{
|
||||
Type t = Type.GetType(className);
|
||||
if (t == null)
|
||||
{
|
||||
throw new ExtensionException(id, "Class "+className+" does not exist");
|
||||
throw new ExtensionException(id, "Class " + className + " does not exist");
|
||||
}
|
||||
|
||||
created = Activator.CreateInstance(t);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new ExtensionException(id, "Cannot load the class by name: "+className, ex);
|
||||
throw new ExtensionException(id, "Cannot load the class by name: " + className, ex);
|
||||
}
|
||||
|
||||
var extension = created as IWinSWExtension;
|
||||
if (extension == null)
|
||||
|
||||
if (!(created is IWinSWExtension extension))
|
||||
{
|
||||
throw new ExtensionException(id, "The loaded class is not a WinSW extension: " + className + ". Type is " + created.GetType());
|
||||
}
|
||||
|
||||
return extension;
|
||||
}
|
||||
|
||||
#endregion
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,6 @@ using System;
|
|||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using System.Timers;
|
||||
using ICSharpCode.SharpZipLib.Zip;
|
||||
|
||||
namespace winsw
|
||||
|
@ -11,6 +10,7 @@ namespace winsw
|
|||
public interface EventLogger
|
||||
{
|
||||
void LogEvent(string message);
|
||||
|
||||
void LogEvent(string message, EventLogEntryType type);
|
||||
}
|
||||
|
||||
|
@ -25,7 +25,7 @@ namespace winsw
|
|||
/// <summary>
|
||||
/// Error and information about logging should be reported here.
|
||||
/// </summary>
|
||||
public EventLogger EventLogger { set; get; }
|
||||
public EventLogger EventLogger { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Convenience method to copy stuff from StreamReader to StreamWriter
|
||||
|
@ -36,10 +36,13 @@ namespace winsw
|
|||
while (true)
|
||||
{
|
||||
int sz = i.Read(buf, 0, buf.Length);
|
||||
if (sz == 0) break;
|
||||
if (sz == 0)
|
||||
break;
|
||||
|
||||
o.Write(buf, 0, sz);
|
||||
o.Flush();
|
||||
}
|
||||
|
||||
i.Close();
|
||||
o.Close();
|
||||
}
|
||||
|
@ -66,11 +69,11 @@ namespace winsw
|
|||
/// </summary>
|
||||
public abstract class AbstractFileLogAppender : LogHandler
|
||||
{
|
||||
protected string BaseLogFileName { private set; get; }
|
||||
protected bool OutFileDisabled { private set; get; }
|
||||
protected bool ErrFileDisabled { private set; get; }
|
||||
protected string OutFilePattern { private set; get; }
|
||||
protected string ErrFilePattern { private set; get; }
|
||||
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)
|
||||
{
|
||||
|
@ -84,9 +87,9 @@ namespace winsw
|
|||
|
||||
public abstract class SimpleLogAppender : AbstractFileLogAppender
|
||||
{
|
||||
public FileMode FileMode { private set; get; }
|
||||
public string OutputLogFileName { private set; get; }
|
||||
public string ErrorLogFileName { private set; get; }
|
||||
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)
|
||||
|
@ -98,8 +101,11 @@ namespace winsw
|
|||
|
||||
public override void log(Stream outputStream, Stream errorStream)
|
||||
{
|
||||
if (!OutFileDisabled) new Thread(delegate() { CopyStream(outputStream, new FileStream(OutputLogFileName, FileMode)); }).Start();
|
||||
if (!ErrFileDisabled) new Thread(delegate() { CopyStream(errorStream, new FileStream(ErrorLogFileName, FileMode)); }).Start();
|
||||
if (!OutFileDisabled)
|
||||
new Thread(() => CopyStream(outputStream, new FileStream(OutputLogFileName, FileMode))).Start();
|
||||
|
||||
if (!ErrFileDisabled)
|
||||
new Thread(() => CopyStream(errorStream, new FileStream(ErrorLogFileName, FileMode))).Start();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -118,7 +124,7 @@ namespace winsw
|
|||
{
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// LogHandler that throws away output
|
||||
/// </summary>
|
||||
|
@ -126,8 +132,8 @@ namespace winsw
|
|||
{
|
||||
public override void log(Stream outputStream, Stream errorStream)
|
||||
{
|
||||
new Thread(delegate() { CopyStream(outputStream, Stream.Null); }).Start();
|
||||
new Thread(delegate() { CopyStream(errorStream, Stream.Null); }).Start();
|
||||
new Thread(() => CopyStream(outputStream, Stream.Null)).Start();
|
||||
new Thread(() => CopyStream(errorStream, Stream.Null)).Start();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -145,8 +151,11 @@ namespace winsw
|
|||
|
||||
public override void log(Stream outputStream, Stream errorStream)
|
||||
{
|
||||
if (!OutFileDisabled) new Thread(delegate() { CopyStreamWithDateRotation(outputStream, OutFilePattern); }).Start();
|
||||
if (!ErrFileDisabled) new Thread(delegate() { CopyStreamWithDateRotation(errorStream, ErrFilePattern); }).Start();
|
||||
if (!OutFileDisabled)
|
||||
new Thread(() => CopyStreamWithDateRotation(outputStream, OutFilePattern)).Start();
|
||||
|
||||
if (!ErrFileDisabled)
|
||||
new Thread(() => CopyStreamWithDateRotation(errorStream, ErrFilePattern)).Start();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -162,16 +171,19 @@ namespace winsw
|
|||
while (true)
|
||||
{
|
||||
int len = data.Read(buf, 0, buf.Length);
|
||||
if (len == 0) break; // EOF
|
||||
if (len == 0)
|
||||
break; // EOF
|
||||
|
||||
if (periodicRollingCalendar.shouldRoll)
|
||||
{// rotate at the line boundary
|
||||
{
|
||||
// rotate at the line boundary
|
||||
int offset = 0;
|
||||
bool rolled = false;
|
||||
for (int i = 0; i < len; i++)
|
||||
{
|
||||
if (buf[i] == 0x0A)
|
||||
{// at the line boundary.
|
||||
{
|
||||
// at the line boundary.
|
||||
// time to rotate.
|
||||
w.Write(buf, offset, i + 1);
|
||||
w.Close();
|
||||
|
@ -189,24 +201,25 @@ namespace winsw
|
|||
}
|
||||
|
||||
if (!rolled)
|
||||
{// we didn't roll - most likely as we didnt find a line boundary, so we should log what we read and roll anyway.
|
||||
{
|
||||
// we didn't roll - most likely as we didnt find a line boundary, so we should log what we read and roll anyway.
|
||||
w.Write(buf, 0, len);
|
||||
w.Close();
|
||||
w = new FileStream(BaseLogFileName + "_" + periodicRollingCalendar.format + ext, FileMode.Create);
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{// typical case. write the whole thing into the current file
|
||||
{
|
||||
// typical case. write the whole thing into the current file
|
||||
w.Write(buf, 0, len);
|
||||
}
|
||||
|
||||
w.Flush();
|
||||
}
|
||||
|
||||
data.Close();
|
||||
w.Close();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public class SizeBasedRollingLogAppender : AbstractFileLogAppender
|
||||
|
@ -220,9 +233,9 @@ namespace winsw
|
|||
// ReSharper disable once InconsistentNaming
|
||||
public static int DEFAULT_FILES_TO_KEEP = 8;
|
||||
|
||||
public int SizeTheshold { private set; get; }
|
||||
public int SizeTheshold { get; private set; }
|
||||
|
||||
public int FilesToKeep { private set; get; }
|
||||
public int FilesToKeep { get; private set; }
|
||||
|
||||
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)
|
||||
|
@ -236,8 +249,11 @@ namespace winsw
|
|||
|
||||
public override void log(Stream outputStream, Stream errorStream)
|
||||
{
|
||||
if (!OutFileDisabled) new Thread(delegate() { CopyStreamWithRotation(outputStream, OutFilePattern); }).Start();
|
||||
if (!ErrFileDisabled) new Thread(delegate() { CopyStreamWithRotation(errorStream, ErrFilePattern); }).Start();
|
||||
if (!OutFileDisabled)
|
||||
new Thread(() => CopyStreamWithRotation(outputStream, OutFilePattern)).Start();
|
||||
|
||||
if (!ErrFileDisabled)
|
||||
new Thread(() => CopyStreamWithRotation(errorStream, ErrFilePattern)).Start();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -252,9 +268,12 @@ namespace winsw
|
|||
while (true)
|
||||
{
|
||||
int len = data.Read(buf, 0, buf.Length);
|
||||
if (len == 0) break; // EOF
|
||||
if (len == 0)
|
||||
break; // EOF
|
||||
|
||||
if (sz + len < SizeTheshold)
|
||||
{// typical case. write the whole thing into the current file
|
||||
{
|
||||
// typical case. write the whole thing into the current file
|
||||
w.Write(buf, 0, len);
|
||||
sz += len;
|
||||
}
|
||||
|
@ -264,8 +283,11 @@ namespace winsw
|
|||
int s = 0;
|
||||
for (int i = 0; i < len; i++)
|
||||
{
|
||||
if (buf[i] != 0x0A) continue;
|
||||
if (sz + i < SizeTheshold) continue;
|
||||
if (buf[i] != 0x0A)
|
||||
continue;
|
||||
|
||||
if (sz + i < SizeTheshold)
|
||||
continue;
|
||||
|
||||
// at the line boundary and exceeded the rotation unit.
|
||||
// time to rotate.
|
||||
|
@ -281,9 +303,11 @@ namespace winsw
|
|||
string src = 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);
|
||||
}
|
||||
catch (IOException e)
|
||||
|
@ -300,6 +324,7 @@ namespace winsw
|
|||
|
||||
w.Flush();
|
||||
}
|
||||
|
||||
data.Close();
|
||||
w.Close();
|
||||
}
|
||||
|
@ -317,8 +342,12 @@ namespace winsw
|
|||
|
||||
public override void log(Stream outputStream, Stream errorStream)
|
||||
{
|
||||
if (!OutFileDisabled) CopyFile(OutputLogFileName, OutputLogFileName + ".old");
|
||||
if (!ErrFileDisabled) CopyFile(ErrorLogFileName, ErrorLogFileName + ".old");
|
||||
if (!OutFileDisabled)
|
||||
CopyFile(OutputLogFileName, OutputLogFileName + ".old");
|
||||
|
||||
if (!ErrFileDisabled)
|
||||
CopyFile(ErrorLogFileName, ErrorLogFileName + ".old");
|
||||
|
||||
base.log(outputStream, errorStream);
|
||||
}
|
||||
}
|
||||
|
@ -326,11 +355,11 @@ namespace winsw
|
|||
public class RollingSizeTimeLogAppender : AbstractFileLogAppender
|
||||
{
|
||||
public static int BYTES_PER_KB = 1024;
|
||||
public int SizeTheshold { private set; get; }
|
||||
public string FilePattern { private set; get; }
|
||||
public TimeSpan? AutoRollAtTime { private set; get; }
|
||||
public int? ZipOlderThanNumDays { private set; get; }
|
||||
public string ZipDateFormat { private set; get; }
|
||||
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(string logDirectory, string baseName, bool outFileDisabled, bool errFileDisabled, string outFilePattern, string errFilePattern, int sizeThreshold, string filePattern, TimeSpan? autoRollAtTime, int? zipolderthannumdays, string zipdateformat)
|
||||
: base(logDirectory, baseName, outFileDisabled, errFileDisabled, outFilePattern, errFilePattern)
|
||||
|
@ -344,8 +373,11 @@ namespace winsw
|
|||
|
||||
public override void log(Stream outputStream, Stream errorStream)
|
||||
{
|
||||
if (!OutFileDisabled) new Thread(delegate () { CopyStreamWithRotation(outputStream, OutFilePattern); }).Start();
|
||||
if (!ErrFileDisabled) new Thread(delegate () { CopyStreamWithRotation(errorStream, ErrFilePattern); }).Start();
|
||||
if (!OutFileDisabled)
|
||||
new Thread(() => CopyStreamWithRotation(outputStream, OutFilePattern)).Start();
|
||||
|
||||
if (!ErrFileDisabled)
|
||||
new Thread(() => CopyStreamWithRotation(errorStream, ErrFilePattern)).Start();
|
||||
}
|
||||
|
||||
private void CopyStreamWithRotation(Stream data, string ext)
|
||||
|
@ -379,7 +411,7 @@ namespace winsw
|
|||
|
||||
var now = DateTime.Now.AddDays(-1);
|
||||
var nextFileNumber = GetNextFileNumber(ext, baseDirectory, baseFileName, now);
|
||||
var nextFileName = Path.Combine(baseDirectory, string.Format("{0}.{1}.#{2:D4}{3}", baseFileName, now.ToString(FilePattern), nextFileNumber, ext));
|
||||
var nextFileName = Path.Combine(baseDirectory, string.Format("{0}.{1}.#{2:D4}{3}", baseFileName, now.ToString(FilePattern), nextFileNumber, ext));
|
||||
File.Move(logFile, nextFileName);
|
||||
|
||||
w = new FileStream(logFile, FileMode.Create);
|
||||
|
@ -402,10 +434,13 @@ namespace winsw
|
|||
};
|
||||
timer.Start();
|
||||
}
|
||||
|
||||
while (true)
|
||||
{
|
||||
var len = data.Read(buf, 0, buf.Length);
|
||||
if (len == 0) break; // EOF
|
||||
if (len == 0)
|
||||
break; // EOF
|
||||
|
||||
lock (fileLock)
|
||||
{
|
||||
if (sz + len < SizeTheshold)
|
||||
|
@ -422,8 +457,11 @@ namespace winsw
|
|||
int s = 0;
|
||||
for (int i = 0; i < len; i++)
|
||||
{
|
||||
if (buf[i] != 0x0A) continue;
|
||||
if (sz + i < SizeTheshold) continue;
|
||||
if (buf[i] != 0x0A)
|
||||
continue;
|
||||
|
||||
if (sz + i < SizeTheshold)
|
||||
continue;
|
||||
|
||||
// at the line boundary and exceeded the rotation unit.
|
||||
// time to rotate.
|
||||
|
@ -450,16 +488,19 @@ namespace winsw
|
|||
EventLogger.LogEvent(string.Format("Failed to roll size time log: {0}", e.Message));
|
||||
}
|
||||
}
|
||||
|
||||
w.Flush();
|
||||
}
|
||||
}
|
||||
|
||||
data.Close();
|
||||
w.Close();
|
||||
}
|
||||
|
||||
private void ZipFiles(string path, string fileExt, string baseZipfilename)
|
||||
{
|
||||
if (ZipOlderThanNumDays == null || !(ZipOlderThanNumDays > 0)) return;
|
||||
if (ZipOlderThanNumDays == null || !(ZipOlderThanNumDays > 0))
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
|
@ -467,7 +508,8 @@ namespace winsw
|
|||
foreach (var file in files)
|
||||
{
|
||||
var fi = new FileInfo(file);
|
||||
if (fi.LastWriteTimeUtc >= DateTime.UtcNow.AddDays(-ZipOlderThanNumDays.Value)) continue;
|
||||
if (fi.LastWriteTimeUtc >= DateTime.UtcNow.AddDays(-ZipOlderThanNumDays.Value))
|
||||
continue;
|
||||
|
||||
// lets archive this bugger
|
||||
ZipTheFile(file, path, fi.LastWriteTimeUtc.ToString(ZipDateFormat), baseZipfilename);
|
||||
|
@ -487,7 +529,6 @@ namespace winsw
|
|||
bool commited = false;
|
||||
try
|
||||
{
|
||||
|
||||
if (File.Exists(zipfilename))
|
||||
{
|
||||
zipFile = new ZipFile(zipfilename);
|
||||
|
@ -539,11 +580,11 @@ namespace winsw
|
|||
{
|
||||
var nowTime = DateTime.Now;
|
||||
var scheduledTime = new DateTime(nowTime.Year, nowTime.Month, nowTime.Day, AutoRollAtTime.Value.Hours,
|
||||
AutoRollAtTime.Value.Minutes, AutoRollAtTime.Value.Seconds, 0); //Specify your time HH,MM,SS
|
||||
AutoRollAtTime.Value.Minutes, AutoRollAtTime.Value.Seconds, 0); // Specify your time HH,MM,SS
|
||||
if (nowTime > scheduledTime)
|
||||
scheduledTime = scheduledTime.AddDays(1);
|
||||
|
||||
double tickTime = (double) (scheduledTime - DateTime.Now).TotalMilliseconds;
|
||||
double tickTime = (scheduledTime - DateTime.Now).TotalMilliseconds;
|
||||
return tickTime;
|
||||
}
|
||||
|
||||
|
@ -564,24 +605,29 @@ 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);
|
||||
int lastNumber = 0;
|
||||
if (int.TryParse(lastNumberAsString, out lastNumber))
|
||||
// var lastNumberAsString = filenameOnly.Substring(filenameOnly.Length - 4, 4);
|
||||
if (int.TryParse(lastNumberAsString, out int lastNumber))
|
||||
{
|
||||
if (lastNumber > nextFileNumber)
|
||||
nextFileNumber = lastNumber;
|
||||
}
|
||||
else
|
||||
throw new IOException(string.Format("File {0} does not follow the pattern provided",f));
|
||||
{
|
||||
throw new IOException(string.Format("File {0} does not follow the pattern provided", f));
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw new IOException(string.Format("Failed to process file {0} due to error {1}",f, e.Message), e);
|
||||
throw new IOException(string.Format("Failed to process file {0} due to error {1}", f, e.Message), e);
|
||||
}
|
||||
}
|
||||
if (nextFileNumber == 0) throw new IOException("Cannot roll the file because matching pattern not found");
|
||||
|
||||
if (nextFileNumber == 0)
|
||||
throw new IOException("Cannot roll the file because matching pattern not found");
|
||||
|
||||
nextFileNumber++;
|
||||
}
|
||||
|
||||
return nextFileNumber;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,4 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Text;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace winsw.Logging
|
||||
{
|
||||
|
|
|
@ -1,9 +1,6 @@
|
|||
using log4net.Appender;
|
||||
using System.Diagnostics;
|
||||
using log4net.Appender;
|
||||
using log4net.Core;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Text;
|
||||
|
||||
namespace winsw.Logging
|
||||
{
|
||||
|
@ -31,6 +28,7 @@ namespace winsw.Logging
|
|||
{
|
||||
return EventLogEntryType.Error;
|
||||
}
|
||||
|
||||
if (level.Value >= Level.Warn.Value)
|
||||
{
|
||||
return EventLogEntryType.Warning;
|
||||
|
|
|
@ -28,6 +28,7 @@ namespace winsw.Native
|
|||
{
|
||||
throw new Exception(String.Format("Error opening service for modifying. Error returned was: 0x{0:X}", Marshal.GetLastWin32Error()));
|
||||
}
|
||||
|
||||
return new Service(svcHandle);
|
||||
}
|
||||
|
||||
|
@ -52,9 +53,9 @@ namespace winsw.Native
|
|||
{
|
||||
SERVICE_FAILURE_ACTIONS sfa = new SERVICE_FAILURE_ACTIONS
|
||||
{
|
||||
dwResetPeriod = (int) failureResetPeriod.TotalSeconds,
|
||||
lpRebootMsg = "",
|
||||
lpCommand = ""
|
||||
dwResetPeriod = (int)failureResetPeriod.TotalSeconds,
|
||||
lpRebootMsg = string.Empty,
|
||||
lpCommand = string.Empty
|
||||
};
|
||||
// delete message
|
||||
// delete the command to run
|
||||
|
@ -101,7 +102,7 @@ namespace winsw.Native
|
|||
|
||||
public void Dispose()
|
||||
{
|
||||
if (Handle!=IntPtr.Zero)
|
||||
if (Handle != IntPtr.Zero)
|
||||
Advapi32.CloseServiceHandle(Handle);
|
||||
Handle = IntPtr.Zero;
|
||||
}
|
||||
|
@ -111,14 +112,14 @@ namespace winsw.Native
|
|||
{
|
||||
public static void AddLogonAsAServiceRight(string username)
|
||||
{
|
||||
//Needs to be at least XP or 2003 server
|
||||
//https://msdn.microsoft.com/en-us/library/windows/desktop/ms724832%28v=vs.85%29.aspx
|
||||
// Needs to be at least XP or 2003 server
|
||||
// https://msdn.microsoft.com/en-us/library/windows/desktop/ms724832%28v=vs.85%29.aspx
|
||||
OperatingSystem osInfo = Environment.OSVersion;
|
||||
|
||||
if (osInfo.Version.Major >= 5 && osInfo.Version.Minor >= 1)
|
||||
{
|
||||
var newuser = GetLocalAccountIfLocalAccount(username);
|
||||
//Trace.WriteLine("Username for Logon as A Service: " + newuser);
|
||||
// Trace.WriteLine("Username for Logon as A Service: " + newuser);
|
||||
long rightexitcode = SetRight(newuser, PrivlegeRights.SeServiceLogonRight.ToString());
|
||||
if (rightexitcode != 0)
|
||||
{
|
||||
|
@ -155,6 +156,7 @@ namespace winsw.Native
|
|||
{
|
||||
return GetLogin(username);
|
||||
}
|
||||
|
||||
return username;
|
||||
}
|
||||
|
||||
|
@ -164,32 +166,32 @@ namespace winsw.Native
|
|||
/// <returns>The windows error code returned by LsaAddAccountRights</returns>
|
||||
private static long SetRight(String accountName, String privilegeName)
|
||||
{
|
||||
long winErrorCode = 0; //contains the last error
|
||||
long winErrorCode; // contains the last error
|
||||
|
||||
//pointer an size for the SID
|
||||
// pointer an size for the SID
|
||||
IntPtr sid = IntPtr.Zero;
|
||||
int sidSize = 0;
|
||||
//StringBuilder and size for the domain name
|
||||
// StringBuilder and size for the domain name
|
||||
StringBuilder domainName = new StringBuilder();
|
||||
int nameSize = 0;
|
||||
//account-type variable for lookup
|
||||
// account-type variable for lookup
|
||||
int accountType = 0;
|
||||
|
||||
//get required buffer size
|
||||
// get required buffer size
|
||||
Advapi32.LookupAccountName(String.Empty, accountName, sid, ref sidSize, domainName, ref nameSize, ref accountType);
|
||||
|
||||
//allocate buffers
|
||||
// allocate buffers
|
||||
domainName = new StringBuilder(nameSize);
|
||||
sid = Marshal.AllocHGlobal(sidSize);
|
||||
|
||||
//lookup the SID for the account
|
||||
// lookup the SID for the account
|
||||
bool result = Advapi32.LookupAccountName(String.Empty, accountName, sid, ref sidSize, domainName, ref nameSize,
|
||||
ref accountType);
|
||||
|
||||
//say what you're doing
|
||||
//Console.WriteLine("LookupAccountName result = " + result);
|
||||
//Console.WriteLine("IsValidSid: " + Advapi32.IsValidSid(sid));
|
||||
//Console.WriteLine("LookupAccountName domainName: " + domainName.ToString());
|
||||
// say what you're doing
|
||||
// Console.WriteLine("LookupAccountName result = " + result);
|
||||
// Console.WriteLine("IsValidSid: " + Advapi32.IsValidSid(sid));
|
||||
// Console.WriteLine("LookupAccountName domainName: " + domainName.ToString());
|
||||
|
||||
if (!result)
|
||||
{
|
||||
|
@ -198,10 +200,9 @@ namespace winsw.Native
|
|||
}
|
||||
else
|
||||
{
|
||||
|
||||
//initialize an empty unicode-string
|
||||
LSA_UNICODE_STRING systemName = new LSA_UNICODE_STRING();
|
||||
//combine all policies
|
||||
// initialize an empty unicode-string
|
||||
LSA_UNICODE_STRING systemName = default;
|
||||
// combine all policies
|
||||
const int access = (int)(
|
||||
LSA_AccessPolicy.POLICY_AUDIT_LOG_ADMIN |
|
||||
LSA_AccessPolicy.POLICY_CREATE_ACCOUNT |
|
||||
|
@ -217,10 +218,9 @@ namespace winsw.Native
|
|||
LSA_AccessPolicy.POLICY_VIEW_AUDIT_INFORMATION |
|
||||
LSA_AccessPolicy.POLICY_VIEW_LOCAL_INFORMATION
|
||||
);
|
||||
//initialize a pointer for the policy handle
|
||||
IntPtr policyHandle = IntPtr.Zero;
|
||||
// initialize a pointer for the policy handle
|
||||
|
||||
//these attributes are not used, but LsaOpenPolicy wants them to exists
|
||||
// these attributes are not used, but LsaOpenPolicy wants them to exists
|
||||
LSA_OBJECT_ATTRIBUTES objectAttributes = new LSA_OBJECT_ATTRIBUTES
|
||||
{
|
||||
Length = 0,
|
||||
|
@ -230,8 +230,8 @@ namespace winsw.Native
|
|||
SecurityQualityOfService = IntPtr.Zero
|
||||
};
|
||||
|
||||
//get a policy handle
|
||||
uint resultPolicy = Advapi32.LsaOpenPolicy(ref systemName, ref objectAttributes, access, out policyHandle);
|
||||
// get a policy handle
|
||||
uint resultPolicy = Advapi32.LsaOpenPolicy(ref systemName, ref objectAttributes, access, out IntPtr policyHandle);
|
||||
winErrorCode = Advapi32.LsaNtStatusToWinError(resultPolicy);
|
||||
|
||||
if (winErrorCode != 0)
|
||||
|
@ -240,17 +240,17 @@ namespace winsw.Native
|
|||
}
|
||||
else
|
||||
{
|
||||
//Now that we have the SID an the policy,
|
||||
//we can add rights to the account.
|
||||
// Now that we have the SID an the policy,
|
||||
// we can add rights to the account.
|
||||
|
||||
//initialize an unicode-string for the privilege name
|
||||
// initialize an unicode-string for the privilege name
|
||||
LSA_UNICODE_STRING[] userRights = new LSA_UNICODE_STRING[1];
|
||||
userRights[0] = new LSA_UNICODE_STRING();
|
||||
userRights[0] = default;
|
||||
userRights[0].Buffer = Marshal.StringToHGlobalUni(privilegeName);
|
||||
userRights[0].Length = (UInt16)(privilegeName.Length * UnicodeEncoding.CharSize);
|
||||
userRights[0].MaximumLength = (UInt16)((privilegeName.Length + 1) * UnicodeEncoding.CharSize);
|
||||
|
||||
//add the right to the account
|
||||
// add the right to the account
|
||||
uint res = Advapi32.LsaAddAccountRights(policyHandle, sid, userRights, 1);
|
||||
winErrorCode = Advapi32.LsaNtStatusToWinError(res);
|
||||
if (winErrorCode != 0)
|
||||
|
@ -260,6 +260,7 @@ namespace winsw.Native
|
|||
|
||||
Advapi32.LsaClose(policyHandle);
|
||||
}
|
||||
|
||||
Advapi32.FreeSid(sid);
|
||||
}
|
||||
|
||||
|
@ -290,7 +291,7 @@ namespace winsw.Native
|
|||
|
||||
[DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
|
||||
internal static extern IntPtr OpenService(IntPtr hSCManager, string lpServiceName, uint dwDesiredAccess);
|
||||
|
||||
|
||||
[DllImport("advapi32.dll", SetLastError = true)]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
internal static extern bool CloseServiceHandle(IntPtr hSCObject);
|
||||
|
@ -309,7 +310,7 @@ namespace winsw.Native
|
|||
internal static extern void FreeSid(IntPtr pSid);
|
||||
|
||||
[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true, PreserveSig = true)]
|
||||
internal static extern bool LookupAccountName(string lpSystemName, string lpAccountName, IntPtr psid, ref int cbsid, StringBuilder domainName,
|
||||
internal static extern bool LookupAccountName(string lpSystemName, string lpAccountName, IntPtr psid, ref int cbsid, StringBuilder domainName,
|
||||
ref int cbdomainLength, ref int use);
|
||||
|
||||
[DllImport("advapi32.dll")]
|
||||
|
@ -320,22 +321,21 @@ namespace winsw.Native
|
|||
|
||||
[DllImport("advapi32.dll", SetLastError = false)]
|
||||
internal static extern uint LsaNtStatusToWinError(uint status);
|
||||
|
||||
}
|
||||
|
||||
//http://msdn.microsoft.com/en-us/library/windows/desktop/bb545671(v=vs.85).aspx
|
||||
// http://msdn.microsoft.com/en-us/library/windows/desktop/bb545671(v=vs.85).aspx
|
||||
internal enum PrivlegeRights
|
||||
{
|
||||
SeServiceLogonRight, //Required for an account to log on using the service logon type.
|
||||
SeRemoteInteractiveLogonRight, //Required for an account to log on remotely using the interactive logon type.
|
||||
SeNetworkLogonRight, //Required for an account to log on using the network logon type.
|
||||
SeInteractiveLogonRight, //Required for an account to log on using the interactive logon type.
|
||||
SeDenyServiceLogonRight, //Explicitly denies an account the right to log on using the service logon type.
|
||||
SeDenyRemoteInteractiveLogonRight, //Explicitly denies an account the right to log on remotely using the interactive logon type.
|
||||
SeDenyNetworkLogonRight, //Explicitly denies an account the right to log on using the network logon type.
|
||||
SeDenyInteractiveLogonRight, //Explicitly denies an account the right to log on using the interactive logon type.
|
||||
SeDenyBatchLogonRight, //Explicitly denies an account the right to log on using the batch logon type.
|
||||
SeBatchLogonRight //Required for an account to log on using the batch logon type.
|
||||
SeServiceLogonRight, // Required for an account to log on using the service logon type.
|
||||
SeRemoteInteractiveLogonRight, // Required for an account to log on remotely using the interactive logon type.
|
||||
SeNetworkLogonRight, // Required for an account to log on using the network logon type.
|
||||
SeInteractiveLogonRight, // Required for an account to log on using the interactive logon type.
|
||||
SeDenyServiceLogonRight, // Explicitly denies an account the right to log on using the service logon type.
|
||||
SeDenyRemoteInteractiveLogonRight, // Explicitly denies an account the right to log on remotely using the interactive logon type.
|
||||
SeDenyNetworkLogonRight, // Explicitly denies an account the right to log on using the network logon type.
|
||||
SeDenyInteractiveLogonRight, // Explicitly denies an account the right to log on using the interactive logon type.
|
||||
SeDenyBatchLogonRight, // Explicitly denies an account the right to log on using the batch logon type.
|
||||
SeBatchLogonRight // Required for an account to log on using the batch logon type.
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
|
@ -381,7 +381,7 @@ namespace winsw.Native
|
|||
/// <summary>
|
||||
/// Required to connect to the service control manager.
|
||||
/// </summary>
|
||||
|
||||
|
||||
SC_MANAGER_CONNECT = 0x00001,
|
||||
|
||||
/// <summary>
|
||||
|
@ -391,19 +391,19 @@ namespace winsw.Native
|
|||
SC_MANAGER_CREATE_SERVICE = 0x00002,
|
||||
|
||||
/// <summary>
|
||||
/// Required to call the EnumServicesStatusEx function to list the
|
||||
/// Required to call the EnumServicesStatusEx function to list the
|
||||
/// services that are in the database.
|
||||
/// </summary>
|
||||
SC_MANAGER_ENUMERATE_SERVICE = 0x00004,
|
||||
|
||||
/// <summary>
|
||||
/// Required to call the LockServiceDatabase function to acquire a
|
||||
/// Required to call the LockServiceDatabase function to acquire a
|
||||
/// lock on the database.
|
||||
/// </summary>
|
||||
SC_MANAGER_LOCK = 0x00008,
|
||||
|
||||
/// <summary>
|
||||
/// Required to call the QueryServiceLockStatus function to retrieve
|
||||
/// Required to call the QueryServiceLockStatus function to retrieve
|
||||
/// the lock status information for the database.
|
||||
/// </summary>
|
||||
SC_MANAGER_QUERY_LOCK_STATUS = 0x00010,
|
||||
|
@ -414,7 +414,7 @@ namespace winsw.Native
|
|||
SC_MANAGER_MODIFY_BOOT_CONFIG = 0x00020,
|
||||
|
||||
/// <summary>
|
||||
/// Includes STANDARD_RIGHTS_REQUIRED, in addition to all access
|
||||
/// Includes STANDARD_RIGHTS_REQUIRED, in addition to all access
|
||||
/// rights in this table.
|
||||
/// </summary>
|
||||
SC_MANAGER_ALL_ACCESS = ACCESS_MASK.STANDARD_RIGHTS_REQUIRED |
|
||||
|
@ -452,7 +452,7 @@ namespace winsw.Native
|
|||
SERVICE_PAUSE_CONTINUE = 0x00040,
|
||||
SERVICE_INTERROGATE = 0x00080,
|
||||
SERVICE_USER_DEFINED_CONTROL = 0x00100,
|
||||
SERVICE_ALL_ACCESS = (STANDARD_RIGHTS_REQUIRED |
|
||||
SERVICE_ALL_ACCESS = STANDARD_RIGHTS_REQUIRED |
|
||||
SERVICE_QUERY_CONFIG |
|
||||
SERVICE_CHANGE_CONFIG |
|
||||
SERVICE_QUERY_STATUS |
|
||||
|
@ -461,7 +461,7 @@ namespace winsw.Native
|
|||
SERVICE_STOP |
|
||||
SERVICE_PAUSE_CONTINUE |
|
||||
SERVICE_INTERROGATE |
|
||||
SERVICE_USER_DEFINED_CONTROL)
|
||||
SERVICE_USER_DEFINED_CONTROL
|
||||
}
|
||||
|
||||
[Flags]
|
||||
|
@ -536,7 +536,6 @@ namespace winsw.Native
|
|||
SERVICE_PAUSE_PENDING = 0x00000006,
|
||||
SERVICE_PAUSED = 0x00000007,
|
||||
}
|
||||
|
||||
|
||||
// http://msdn.microsoft.com/en-us/library/windows/desktop/ms685126(v=vs.85).aspx
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
|
@ -577,7 +576,7 @@ namespace winsw.Native
|
|||
}
|
||||
|
||||
// http://msdn.microsoft.com/en-us/library/windows/desktop/ms685939(v=vs.85).aspx
|
||||
[StructLayout(LayoutKind.Sequential,CharSet=CharSet.Unicode)]
|
||||
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
|
||||
public struct SERVICE_FAILURE_ACTIONS
|
||||
{
|
||||
/// <summary>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
using Microsoft.Win32.SafeHandles;
|
||||
using System;
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using Microsoft.Win32.SafeHandles;
|
||||
|
||||
namespace winsw.Native
|
||||
{
|
||||
|
@ -8,7 +8,7 @@ namespace winsw.Native
|
|||
/// kernel32.dll P/Invoke wrappers
|
||||
/// </summary>
|
||||
public class Kernel32
|
||||
{
|
||||
{
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
public static extern bool SetStdHandle(int nStdHandle, SafeFileHandle handle);
|
||||
|
||||
|
|
|
@ -29,10 +29,16 @@ namespace winsw
|
|||
|
||||
public enum PeriodicityType
|
||||
{
|
||||
ERRONEOUS, TOP_OF_MILLISECOND, TOP_OF_SECOND, TOP_OF_MINUTE, TOP_OF_HOUR, TOP_OF_DAY
|
||||
ERRONEOUS,
|
||||
TOP_OF_MILLISECOND,
|
||||
TOP_OF_SECOND,
|
||||
TOP_OF_MINUTE,
|
||||
TOP_OF_HOUR,
|
||||
TOP_OF_DAY
|
||||
}
|
||||
|
||||
private static readonly PeriodicityType[] VALID_ORDERED_LIST = {
|
||||
private static readonly PeriodicityType[] VALID_ORDERED_LIST =
|
||||
{
|
||||
PeriodicityType.TOP_OF_MILLISECOND, PeriodicityType.TOP_OF_SECOND, PeriodicityType.TOP_OF_MINUTE, PeriodicityType.TOP_OF_HOUR, PeriodicityType.TOP_OF_DAY
|
||||
};
|
||||
|
||||
|
@ -54,6 +60,7 @@ namespace winsw
|
|||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return PeriodicityType.ERRONEOUS;
|
||||
}
|
||||
|
||||
|
@ -100,17 +107,11 @@ namespace winsw
|
|||
_nextRoll = nextTriggeringTime(now, _period);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public string format
|
||||
{
|
||||
get
|
||||
{
|
||||
return _currentRoll.ToString(_format);
|
||||
}
|
||||
}
|
||||
|
||||
public string format => _currentRoll.ToString(_format);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,7 +3,6 @@ using System.Collections;
|
|||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using System.Xml;
|
||||
using winsw.Configuration;
|
||||
using winsw.Native;
|
||||
|
@ -20,40 +19,35 @@ namespace winsw
|
|||
// ReSharper disable once InconsistentNaming
|
||||
protected readonly XmlDocument dom = new XmlDocument();
|
||||
|
||||
private static readonly DefaultWinSWSettings defaults = new DefaultWinSWSettings();
|
||||
public static DefaultWinSWSettings Defaults { get { return defaults; } }
|
||||
public static DefaultWinSWSettings Defaults { get; } = new DefaultWinSWSettings();
|
||||
|
||||
/// <summary>
|
||||
/// Where did we find the configuration file?
|
||||
///
|
||||
///
|
||||
/// This string is "c:\abc\def\ghi" when the configuration XML is "c:\abc\def\ghi.xml"
|
||||
/// </summary>
|
||||
public string BasePath { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The file name portion of the configuration file.
|
||||
///
|
||||
///
|
||||
/// In the above example, this would be "ghi".
|
||||
/// </summary>
|
||||
public string BaseName { get; set; }
|
||||
|
||||
public virtual string ExecutablePath
|
||||
{
|
||||
get
|
||||
{
|
||||
// Currently there is no opportunity to alter the executable path
|
||||
return Defaults.ExecutablePath;
|
||||
}
|
||||
}
|
||||
// Currently there is no opportunity to alter the executable path
|
||||
public virtual string ExecutablePath => Defaults.ExecutablePath;
|
||||
|
||||
public ServiceDescriptor()
|
||||
{
|
||||
// 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
|
||||
// Get the first parent to go into the recursive loop
|
||||
string p = ExecutablePath;
|
||||
string baseName = Path.GetFileNameWithoutExtension(p);
|
||||
if (baseName.EndsWith(".vshost")) baseName = baseName.Substring(0, baseName.Length - 7);
|
||||
if (baseName.EndsWith(".vshost"))
|
||||
baseName = baseName.Substring(0, baseName.Length - 7);
|
||||
|
||||
DirectoryInfo d = new DirectoryInfo(Path.GetDirectoryName(p));
|
||||
while (true)
|
||||
{
|
||||
|
@ -61,7 +55,7 @@ namespace winsw
|
|||
break;
|
||||
|
||||
if (d.Parent == null)
|
||||
throw new FileNotFoundException("Unable to locate "+baseName+".xml file within executable directory or any parents");
|
||||
throw new FileNotFoundException("Unable to locate " + baseName + ".xml file within executable directory or any parents");
|
||||
|
||||
d = d.Parent;
|
||||
}
|
||||
|
@ -73,11 +67,13 @@ namespace winsw
|
|||
|
||||
// register the base directory as environment variable so that future expansions can refer to this.
|
||||
Environment.SetEnvironmentVariable("BASE", d.FullName);
|
||||
|
||||
// ditto for ID
|
||||
Environment.SetEnvironmentVariable("SERVICE_ID", Id);
|
||||
|
||||
// New name
|
||||
Environment.SetEnvironmentVariable(WinSWSystem.ENVVAR_NAME_EXECUTABLE_PATH, ExecutablePath);
|
||||
|
||||
// Also inject system environment variables
|
||||
Environment.SetEnvironmentVariable(WinSWSystem.ENVVAR_NAME_SERVICE_ID, Id);
|
||||
}
|
||||
|
@ -106,7 +102,9 @@ namespace winsw
|
|||
private string SingleElement(string tagName, bool optional)
|
||||
{
|
||||
var n = dom.SelectSingleNode("//" + tagName);
|
||||
if (n == null && !optional) throw new InvalidDataException("<" + tagName + "> is missing in configuration XML");
|
||||
if (n == null && !optional)
|
||||
throw new InvalidDataException("<" + tagName + "> is missing in configuration XML");
|
||||
|
||||
return n == null ? null : Environment.ExpandEnvironmentVariables(n.InnerText);
|
||||
}
|
||||
|
||||
|
@ -147,51 +145,36 @@ namespace winsw
|
|||
return TimeSpan.FromMilliseconds(int.Parse(v.Substring(0, v.Length - s.Key.Length).Trim()) * s.Value);
|
||||
}
|
||||
}
|
||||
|
||||
return TimeSpan.FromMilliseconds(int.Parse(v));
|
||||
}
|
||||
|
||||
private static readonly Dictionary<string,long> Suffix = new Dictionary<string,long> {
|
||||
{ "ms", 1 },
|
||||
private static readonly Dictionary<string, long> Suffix = new Dictionary<string, long>
|
||||
{
|
||||
{ "ms", 1 },
|
||||
{ "sec", 1000L },
|
||||
{ "secs", 1000L },
|
||||
{ "min", 1000L*60L },
|
||||
{ "mins", 1000L*60L },
|
||||
{ "hr", 1000L*60L*60L },
|
||||
{ "hrs", 1000L*60L*60L },
|
||||
{ "hour", 1000L*60L*60L },
|
||||
{ "hours", 1000L*60L*60L },
|
||||
{ "day", 1000L*60L*60L*24L },
|
||||
{ "days", 1000L*60L*60L*24L }
|
||||
{ "min", 1000L * 60L },
|
||||
{ "mins", 1000L * 60L },
|
||||
{ "hr", 1000L * 60L * 60L },
|
||||
{ "hrs", 1000L * 60L * 60L },
|
||||
{ "hour", 1000L * 60L * 60L },
|
||||
{ "hours", 1000L * 60L * 60L },
|
||||
{ "day", 1000L * 60L * 60L * 24L },
|
||||
{ "days", 1000L * 60L * 60L * 24L }
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Path to the executable.
|
||||
/// </summary>
|
||||
public string Executable
|
||||
{
|
||||
get
|
||||
{
|
||||
return SingleElement("executable");
|
||||
}
|
||||
}
|
||||
public string Executable => SingleElement("executable");
|
||||
|
||||
public bool HideWindow
|
||||
{
|
||||
get {
|
||||
return SingleBoolElement("hidewindow", Defaults.HideWindow);
|
||||
}
|
||||
}
|
||||
public bool HideWindow => SingleBoolElement("hidewindow", Defaults.HideWindow);
|
||||
|
||||
/// <summary>
|
||||
/// Optionally specify a different Path to an executable to shutdown the service.
|
||||
/// </summary>
|
||||
public string StopExecutable
|
||||
{
|
||||
get
|
||||
{
|
||||
return SingleElement("stopexecutable", true);
|
||||
}
|
||||
}
|
||||
public string StopExecutable => SingleElement("stopexecutable", true);
|
||||
|
||||
/// <summary>
|
||||
/// Arguments or multiple optional argument elements which overrule the arguments element.
|
||||
|
@ -223,28 +206,17 @@ namespace winsw
|
|||
/// <summary>
|
||||
/// Multiple optional startargument elements.
|
||||
/// </summary>
|
||||
public string Startarguments
|
||||
{
|
||||
get
|
||||
{
|
||||
return AppendTags("startargument", Defaults.Startarguments);
|
||||
}
|
||||
}
|
||||
public string Startarguments => AppendTags("startargument", Defaults.Startarguments);
|
||||
|
||||
/// <summary>
|
||||
/// Multiple optional stopargument elements.
|
||||
/// </summary>
|
||||
public string Stoparguments
|
||||
public string Stoparguments => AppendTags("stopargument", Defaults.Stoparguments);
|
||||
|
||||
public string WorkingDirectory
|
||||
{
|
||||
get
|
||||
{
|
||||
return AppendTags("stopargument", Defaults.Stoparguments);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public string WorkingDirectory {
|
||||
get {
|
||||
var wd = SingleElement("workingdirectory", true);
|
||||
return String.IsNullOrEmpty(wd) ? Defaults.WorkingDirectory : wd;
|
||||
}
|
||||
|
@ -257,8 +229,8 @@ namespace winsw
|
|||
List<string> res = new List<string>();
|
||||
|
||||
XmlNode argumentNode = ExtensionsConfiguration;
|
||||
XmlNodeList extensions = argumentNode != null ? argumentNode.SelectNodes("extension") : null;
|
||||
if ( extensions != null)
|
||||
XmlNodeList extensions = argumentNode?.SelectNodes("extension");
|
||||
if (extensions != null)
|
||||
{
|
||||
foreach (XmlNode e in extensions)
|
||||
{
|
||||
|
@ -272,14 +244,7 @@ namespace winsw
|
|||
}
|
||||
}
|
||||
|
||||
public XmlNode ExtensionsConfiguration
|
||||
{
|
||||
get
|
||||
{
|
||||
XmlNode argumentNode = dom.SelectSingleNode("//extensions");
|
||||
return argumentNode;
|
||||
}
|
||||
}
|
||||
public XmlNode ExtensionsConfiguration => dom.SelectSingleNode("//extensions");
|
||||
|
||||
/// <summary>
|
||||
/// Combines the contents of all the elements of the given name,
|
||||
|
@ -295,7 +260,7 @@ namespace winsw
|
|||
}
|
||||
else
|
||||
{
|
||||
string arguments = "";
|
||||
string arguments = string.Empty;
|
||||
|
||||
foreach (XmlElement argument in dom.SelectNodes("//" + tagName))
|
||||
{
|
||||
|
@ -314,6 +279,7 @@ namespace winsw
|
|||
token = '"' + token + '"';
|
||||
}
|
||||
}
|
||||
|
||||
arguments += " " + token;
|
||||
}
|
||||
|
||||
|
@ -365,6 +331,7 @@ namespace winsw
|
|||
{
|
||||
mode = Defaults.LogMode;
|
||||
}
|
||||
|
||||
return mode;
|
||||
}
|
||||
}
|
||||
|
@ -379,18 +346,9 @@ namespace winsw
|
|||
}
|
||||
}
|
||||
|
||||
public bool OutFileDisabled
|
||||
{
|
||||
get { return SingleBoolElement("outfiledisabled", Defaults.OutFileDisabled); }
|
||||
}
|
||||
public bool OutFileDisabled => SingleBoolElement("outfiledisabled", Defaults.OutFileDisabled);
|
||||
|
||||
public bool ErrFileDisabled
|
||||
{
|
||||
get
|
||||
{
|
||||
return SingleBoolElement("errfiledisabled", Defaults.ErrFileDisabled);
|
||||
}
|
||||
}
|
||||
public bool ErrFileDisabled => SingleBoolElement("errfiledisabled", Defaults.ErrFileDisabled);
|
||||
|
||||
public string OutFilePattern
|
||||
{
|
||||
|
@ -414,7 +372,6 @@ namespace winsw
|
|||
|
||||
public LogHandler LogHandler
|
||||
{
|
||||
|
||||
get
|
||||
{
|
||||
XmlElement e = (XmlElement)dom.SelectSingleNode("//logmode");
|
||||
|
@ -423,6 +380,7 @@ namespace winsw
|
|||
// this is more modern way, to support nested elements as configuration
|
||||
e = (XmlElement)dom.SelectSingleNode("//log");
|
||||
}
|
||||
|
||||
int sizeThreshold;
|
||||
switch (LogMode)
|
||||
{
|
||||
|
@ -444,13 +402,14 @@ namespace winsw
|
|||
{
|
||||
throw new InvalidDataException("Time Based rolling policy is specified but no pattern can be found in configuration XML.");
|
||||
}
|
||||
|
||||
var pattern = patternNode.InnerText;
|
||||
int period = SingleIntElement(e,"period",1);
|
||||
int period = SingleIntElement(e, "period", 1);
|
||||
return new TimeBasedRollingLogAppender(LogDirectory, LogName, OutFileDisabled, ErrFileDisabled, OutFilePattern, 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);
|
||||
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);
|
||||
|
||||
case "append":
|
||||
|
@ -463,29 +422,29 @@ namespace winsw
|
|||
{
|
||||
throw new InvalidDataException("Roll-Size-Time Based rolling policy is specified but no pattern can be found in configuration XML.");
|
||||
}
|
||||
|
||||
XmlNode autoRollAtTimeNode = e.SelectSingleNode("autoRollAtTime");
|
||||
TimeSpan? autoRollAtTime = null;
|
||||
if (autoRollAtTimeNode != null)
|
||||
{
|
||||
TimeSpan autoRollAtTimeValue;
|
||||
// validate it
|
||||
if (!TimeSpan.TryParse(autoRollAtTimeNode.InnerText, out autoRollAtTimeValue))
|
||||
if (!TimeSpan.TryParse(autoRollAtTimeNode.InnerText, out TimeSpan autoRollAtTimeValue))
|
||||
throw new InvalidDataException("Roll-Size-Time Based rolling policy is specified but autoRollAtTime does not match the TimeSpan format HH:mm:ss found in configuration XML.");
|
||||
autoRollAtTime = autoRollAtTimeValue;
|
||||
}
|
||||
|
||||
XmlNode zipolderthannumdaysNode = e.SelectSingleNode("zipOlderThanNumDays");
|
||||
int? zipolderthannumdays = null;
|
||||
if (zipolderthannumdaysNode != null)
|
||||
{
|
||||
int zipolderthannumdaysValue;
|
||||
// validate it
|
||||
if (!int.TryParse(zipolderthannumdaysNode.InnerText, out zipolderthannumdaysValue))
|
||||
if (!int.TryParse(zipolderthannumdaysNode.InnerText, out int zipolderthannumdaysValue))
|
||||
throw new InvalidDataException("Roll-Size-Time Based rolling policy is specified but zipOlderThanNumDays does not match the int format found in configuration XML.");
|
||||
zipolderthannumdays = zipolderthannumdaysValue;
|
||||
}
|
||||
|
||||
XmlNode zipdateformatNode = e.SelectSingleNode("zipDateFormat");
|
||||
string zipdateformat = null;
|
||||
string zipdateformat;
|
||||
if (zipdateformatNode == null)
|
||||
{
|
||||
zipdateformat = "yyyyMM";
|
||||
|
@ -501,7 +460,6 @@ namespace winsw
|
|||
throw new InvalidDataException("Undefined logging mode: " + LogMode);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -519,35 +477,19 @@ namespace winsw
|
|||
{
|
||||
serviceDependencies.Add(depend.InnerText);
|
||||
}
|
||||
|
||||
return (string[])serviceDependencies.ToArray(typeof(string));
|
||||
}
|
||||
|
||||
return Defaults.ServiceDependencies;
|
||||
}
|
||||
}
|
||||
|
||||
public string Id
|
||||
{
|
||||
get
|
||||
{
|
||||
return SingleElement("id");
|
||||
}
|
||||
}
|
||||
public string Id => SingleElement("id");
|
||||
|
||||
public string Caption
|
||||
{
|
||||
get
|
||||
{
|
||||
return SingleElement("name");
|
||||
}
|
||||
}
|
||||
public string Caption => SingleElement("name");
|
||||
|
||||
public string Description
|
||||
{
|
||||
get
|
||||
{
|
||||
return SingleElement("description");
|
||||
}
|
||||
}
|
||||
public string Description => SingleElement("description");
|
||||
|
||||
/// <summary>
|
||||
/// Start mode of the Service
|
||||
|
@ -557,7 +499,9 @@ namespace winsw
|
|||
get
|
||||
{
|
||||
var p = SingleElement("startmode", true);
|
||||
if (p == null) return Defaults.StartMode;
|
||||
if (p == null)
|
||||
return Defaults.StartMode;
|
||||
|
||||
try
|
||||
{
|
||||
return (StartMode)Enum.Parse(typeof(StartMode), p, true);
|
||||
|
@ -569,6 +513,7 @@ namespace winsw
|
|||
{
|
||||
Console.WriteLine(sm);
|
||||
}
|
||||
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
@ -578,64 +523,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
|
||||
{
|
||||
get
|
||||
{
|
||||
return dom.SelectSingleNode("//delayedAutoStart") != null;
|
||||
}
|
||||
}
|
||||
public bool DelayedAutoStart => 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
|
||||
{
|
||||
get
|
||||
{
|
||||
return dom.SelectSingleNode("//beeponshutdown") != null;
|
||||
}
|
||||
}
|
||||
|
||||
public bool BeepOnShutdown => 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
|
||||
/// 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
|
||||
{
|
||||
get
|
||||
{
|
||||
return SingleTimeSpanElement(dom, "waithint", Defaults.WaitHint);
|
||||
}
|
||||
}
|
||||
|
||||
public TimeSpan WaitHint => SingleTimeSpanElement(dom, "waithint", Defaults.WaitHint);
|
||||
|
||||
/// <summary>
|
||||
/// The time before the service should make its next call to the SetServiceStatus function
|
||||
/// The time before the service should make its next call to the SetServiceStatus function
|
||||
/// with an incremented checkPoint value (default 1 sec).
|
||||
/// 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
|
||||
{
|
||||
get
|
||||
{
|
||||
return SingleTimeSpanElement(dom, "sleeptime", Defaults.SleepTime);
|
||||
}
|
||||
}
|
||||
public TimeSpan SleepTime => SingleTimeSpanElement(dom, "sleeptime", Defaults.SleepTime);
|
||||
|
||||
/// <summary>
|
||||
/// True if the service can interact with the desktop.
|
||||
/// </summary>
|
||||
public bool Interactive
|
||||
{
|
||||
get
|
||||
{
|
||||
return dom.SelectSingleNode("//interactive") != null;
|
||||
}
|
||||
}
|
||||
public bool Interactive => dom.SelectSingleNode("//interactive") != null;
|
||||
|
||||
/// <summary>
|
||||
/// Environment variable overrides
|
||||
|
@ -653,6 +566,7 @@ namespace winsw
|
|||
|
||||
Environment.SetEnvironmentVariable(key, value);
|
||||
}
|
||||
|
||||
return map;
|
||||
}
|
||||
}
|
||||
|
@ -666,7 +580,7 @@ namespace winsw
|
|||
get
|
||||
{
|
||||
var xmlNodeList = dom.SelectNodes("//download");
|
||||
if (xmlNodeList == null)
|
||||
if (xmlNodeList == null)
|
||||
{
|
||||
return Defaults.Downloads;
|
||||
}
|
||||
|
@ -674,12 +588,12 @@ namespace winsw
|
|||
List<Download> r = new List<Download>();
|
||||
foreach (XmlNode n in xmlNodeList)
|
||||
{
|
||||
XmlElement el = n as XmlElement;
|
||||
if (el != null)
|
||||
if (n is XmlElement el)
|
||||
{
|
||||
r.Add(new Download(el));
|
||||
}
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
@ -710,25 +624,21 @@ namespace winsw
|
|||
default:
|
||||
throw new Exception("Invalid failure action: " + action);
|
||||
}
|
||||
|
||||
XmlAttribute delay = n.Attributes["delay"];
|
||||
r.Add(new SC_ACTION(type, delay != null ? ParseTimeSpan(delay.Value) : TimeSpan.Zero));
|
||||
}
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
public TimeSpan ResetFailureAfter
|
||||
{
|
||||
get
|
||||
{
|
||||
return SingleTimeSpanElement(dom, "resetfailure", Defaults.ResetFailureAfter);
|
||||
}
|
||||
}
|
||||
public TimeSpan ResetFailureAfter => SingleTimeSpanElement(dom, "resetfailure", Defaults.ResetFailureAfter);
|
||||
|
||||
protected string GetServiceAccountPart(string subNodeName)
|
||||
{
|
||||
var node = dom.SelectSingleNode("//serviceaccount");
|
||||
{
|
||||
var node = dom.SelectSingleNode("//serviceaccount");
|
||||
|
||||
if (node != null)
|
||||
{
|
||||
|
@ -736,56 +646,28 @@ namespace winsw
|
|||
if (subNode != null)
|
||||
{
|
||||
return subNode.InnerText;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
protected string AllowServiceLogon
|
||||
{
|
||||
get
|
||||
{
|
||||
return GetServiceAccountPart("allowservicelogon");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// ReSharper disable once InconsistentNaming
|
||||
protected string serviceAccountDomain
|
||||
{
|
||||
get
|
||||
{
|
||||
return GetServiceAccountPart("domain");
|
||||
}
|
||||
}
|
||||
protected string AllowServiceLogon => GetServiceAccountPart("allowservicelogon");
|
||||
|
||||
// ReSharper disable once InconsistentNaming
|
||||
protected string serviceAccountName
|
||||
{
|
||||
get
|
||||
{
|
||||
return GetServiceAccountPart("user");
|
||||
}
|
||||
}
|
||||
protected string serviceAccountDomain => GetServiceAccountPart("domain");
|
||||
|
||||
public string ServiceAccountPassword
|
||||
{
|
||||
get
|
||||
{
|
||||
return GetServiceAccountPart("password");
|
||||
}
|
||||
}
|
||||
// ReSharper disable once InconsistentNaming
|
||||
protected string serviceAccountName => GetServiceAccountPart("user");
|
||||
|
||||
public string ServiceAccountUser
|
||||
{
|
||||
get { return (serviceAccountDomain ?? "NULL") + @"\" + (serviceAccountName ?? "NULL"); }
|
||||
}
|
||||
public string ServiceAccountPassword => GetServiceAccountPart("password");
|
||||
|
||||
public bool HasServiceAccount()
|
||||
{
|
||||
return !string.IsNullOrEmpty(serviceAccountDomain) && !string.IsNullOrEmpty(serviceAccountName);
|
||||
}
|
||||
public string ServiceAccountUser => (serviceAccountDomain ?? "NULL") + @"\" + (serviceAccountName ?? "NULL");
|
||||
|
||||
public bool HasServiceAccount()
|
||||
{
|
||||
return !string.IsNullOrEmpty(serviceAccountDomain) && !string.IsNullOrEmpty(serviceAccountName);
|
||||
}
|
||||
|
||||
public bool AllowServiceAcountLogonRight
|
||||
{
|
||||
|
@ -793,37 +675,31 @@ namespace winsw
|
|||
{
|
||||
if (AllowServiceLogon != null)
|
||||
{
|
||||
bool parsedvalue;
|
||||
if (Boolean.TryParse(AllowServiceLogon, out parsedvalue))
|
||||
if (Boolean.TryParse(AllowServiceLogon, out bool parsedvalue))
|
||||
{
|
||||
return parsedvalue;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Time to wait for the service to gracefully shutdown the executable before we forcibly kill it
|
||||
/// </summary>
|
||||
public TimeSpan StopTimeout
|
||||
{
|
||||
get
|
||||
{
|
||||
return SingleTimeSpanElement(dom, "stoptimeout", Defaults.StopTimeout);
|
||||
}
|
||||
}
|
||||
/// <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 bool StopParentProcessFirst
|
||||
{
|
||||
get
|
||||
{
|
||||
var value = SingleElement("stopparentprocessfirst", true);
|
||||
bool result;
|
||||
if (bool.TryParse(value, out result))
|
||||
if (bool.TryParse(value, out bool result))
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
return Defaults.StopParentProcessFirst;
|
||||
}
|
||||
}
|
||||
|
@ -835,8 +711,9 @@ namespace winsw
|
|||
{
|
||||
get
|
||||
{
|
||||
var p = SingleElement("priority",true);
|
||||
if (p == null) return Defaults.Priority;
|
||||
var p = SingleElement("priority", true);
|
||||
if (p == null)
|
||||
return Defaults.Priority;
|
||||
|
||||
return (ProcessPriorityClass)Enum.Parse(typeof(ProcessPriorityClass), p, true);
|
||||
}
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
using log4net;
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Management;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using log4net;
|
||||
|
||||
namespace winsw.Util
|
||||
{
|
||||
|
@ -24,8 +23,9 @@ namespace winsw.Util
|
|||
public static List<int> GetChildPids(int pid)
|
||||
{
|
||||
var childPids = new List<int>();
|
||||
|
||||
try {
|
||||
|
||||
try
|
||||
{
|
||||
var searcher = new ManagementObjectSearcher("Select * From Win32_Process Where ParentProcessID=" + pid);
|
||||
foreach (var mo in searcher.Get())
|
||||
{
|
||||
|
@ -38,7 +38,7 @@ namespace winsw.Util
|
|||
{
|
||||
Logger.Warn("Failed to locate children of the process with PID=" + pid + ". Child processes won't be terminated", ex);
|
||||
}
|
||||
|
||||
|
||||
return childPids;
|
||||
}
|
||||
|
||||
|
@ -87,7 +87,7 @@ namespace winsw.Util
|
|||
}
|
||||
}
|
||||
|
||||
//TODO: Propagate error if process kill fails? Currently we use the legacy behavior
|
||||
// TODO: Propagate error if process kill fails? Currently we use the legacy behavior
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -100,7 +100,7 @@ namespace winsw.Util
|
|||
public static void StopProcessAndChildren(int pid, TimeSpan stopTimeout, bool stopParentProcessFirst)
|
||||
{
|
||||
if (!stopParentProcessFirst)
|
||||
{
|
||||
{
|
||||
foreach (var childPid in GetChildPids(pid))
|
||||
{
|
||||
StopProcessAndChildren(childPid, stopTimeout, stopParentProcessFirst);
|
||||
|
@ -149,7 +149,7 @@ namespace winsw.Util
|
|||
foreach (string key in envVars.Keys)
|
||||
{
|
||||
Environment.SetEnvironmentVariable(key, envVars[key]);
|
||||
// DONTDO: ps.EnvironmentVariables[key] = envs[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)
|
||||
}
|
||||
}
|
||||
|
@ -157,8 +157,8 @@ namespace winsw.Util
|
|||
processToStart.Start();
|
||||
Logger.Info("Started process " + processToStart.Id);
|
||||
|
||||
if (priority != null && priority.Value != ProcessPriorityClass.Normal)
|
||||
{
|
||||
if (priority != null && priority.Value != ProcessPriorityClass.Normal)
|
||||
{
|
||||
processToStart.PriorityClass = priority.Value;
|
||||
}
|
||||
|
||||
|
@ -172,7 +172,7 @@ namespace winsw.Util
|
|||
// monitor the completion of the process
|
||||
if (callback != null)
|
||||
{
|
||||
StartThread(delegate
|
||||
StartThread(() =>
|
||||
{
|
||||
processToStart.WaitForExit();
|
||||
callback(processToStart);
|
||||
|
@ -187,7 +187,7 @@ namespace winsw.Util
|
|||
/// </summary>
|
||||
public static void StartThread(ThreadStart main)
|
||||
{
|
||||
new Thread(delegate()
|
||||
new Thread(() =>
|
||||
{
|
||||
try
|
||||
{
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
using log4net;
|
||||
using System;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.InteropServices;
|
||||
using log4net;
|
||||
using winsw.Native;
|
||||
|
||||
namespace winsw.Util
|
||||
|
@ -20,6 +20,7 @@ namespace winsw.Util
|
|||
|
||||
[DllImport(KERNEL32)]
|
||||
private static extern bool SetConsoleCtrlHandler(ConsoleCtrlDelegate HandlerRoutine, bool Add);
|
||||
|
||||
// Delegate type to be used as the Handler Routine for SCCH
|
||||
private delegate Boolean ConsoleCtrlDelegate(CtrlTypes CtrlType);
|
||||
|
||||
|
@ -47,7 +48,7 @@ namespace winsw.Util
|
|||
{
|
||||
if (AttachConsole((uint)process.Id))
|
||||
{
|
||||
//Disable Ctrl-C handling for our program
|
||||
// Disable Ctrl-C handling for our program
|
||||
SetConsoleCtrlHandler(null, true);
|
||||
GenerateConsoleCtrlEvent(CtrlTypes.CTRL_C_EVENT, 0);
|
||||
|
||||
|
@ -61,9 +62,9 @@ namespace winsw.Util
|
|||
Logger.Warn("Failed to detach from console. Error code: " + errorCode);
|
||||
}
|
||||
|
||||
return process.HasExited;
|
||||
return process.HasExited;
|
||||
}
|
||||
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,15 +1,13 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Xml;
|
||||
using System.IO;
|
||||
using System.Xml;
|
||||
|
||||
namespace winsw.Util
|
||||
{
|
||||
public class XmlHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// Retrieves a single string element
|
||||
/// Retrieves a single string element
|
||||
/// </summary>
|
||||
/// <param name="node">Parent node</param>
|
||||
/// <param name="tagName">Element name</param>
|
||||
|
@ -19,7 +17,9 @@ namespace winsw.Util
|
|||
public static string SingleElement(XmlNode node, string tagName, Boolean optional)
|
||||
{
|
||||
var n = node.SelectSingleNode(tagName);
|
||||
if (n == null && !optional) throw new InvalidDataException("<" + tagName + "> is missing in configuration XML");
|
||||
if (n == null && !optional)
|
||||
throw new InvalidDataException("<" + tagName + "> is missing in configuration XML");
|
||||
|
||||
return n == null ? null : Environment.ExpandEnvironmentVariables(n.InnerText);
|
||||
}
|
||||
|
||||
|
@ -34,7 +34,9 @@ namespace winsw.Util
|
|||
public static XmlNode SingleNode(XmlNode node, string tagName, Boolean optional)
|
||||
{
|
||||
var n = node.SelectSingleNode(tagName);
|
||||
if (n == null && !optional) throw new InvalidDataException("<" + tagName + "> is missing in configuration XML");
|
||||
if (n == null && !optional)
|
||||
throw new InvalidDataException("<" + tagName + "> is missing in configuration XML");
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
|
@ -45,14 +47,14 @@ namespace winsw.Util
|
|||
/// <param name="attributeName">Attribute name</param>
|
||||
/// <returns>Attribute value</returns>
|
||||
/// <exception cref="InvalidDataException">The required attribute is missing</exception>
|
||||
public static TAttributeType SingleAttribute <TAttributeType> (XmlElement node, string attributeName)
|
||||
public static TAttributeType SingleAttribute<TAttributeType>(XmlElement node, string attributeName)
|
||||
{
|
||||
if (!node.HasAttribute(attributeName))
|
||||
{
|
||||
throw new InvalidDataException("Attribute <" + attributeName + "> is missing in configuration XML");
|
||||
}
|
||||
|
||||
return SingleAttribute<TAttributeType>(node, attributeName, default(TAttributeType));
|
||||
return SingleAttribute(node, attributeName, default(TAttributeType));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -64,12 +66,13 @@ namespace winsw.Util
|
|||
/// <returns>Attribute value (or default)</returns>
|
||||
public static TAttributeType SingleAttribute<TAttributeType>(XmlElement node, string attributeName, TAttributeType defaultValue)
|
||||
{
|
||||
if (!node.HasAttribute(attributeName)) return defaultValue;
|
||||
if (!node.HasAttribute(attributeName))
|
||||
return defaultValue;
|
||||
|
||||
string rawValue = node.GetAttribute(attributeName);
|
||||
string substitutedValue = Environment.ExpandEnvironmentVariables(rawValue);
|
||||
var value = (TAttributeType)Convert.ChangeType(substitutedValue, typeof(TAttributeType));
|
||||
return value;
|
||||
string rawValue = node.GetAttribute(attributeName);
|
||||
string substitutedValue = Environment.ExpandEnvironmentVariables(rawValue);
|
||||
var value = (TAttributeType)Convert.ChangeType(substitutedValue, typeof(TAttributeType));
|
||||
return value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -83,7 +86,8 @@ namespace winsw.Util
|
|||
/// <exception cref="InvalidDataException">Wrong enum value</exception>
|
||||
public static TAttributeType EnumAttribute<TAttributeType>(XmlElement node, string attributeName, TAttributeType defaultValue)
|
||||
{
|
||||
if (!node.HasAttribute(attributeName)) return defaultValue;
|
||||
if (!node.HasAttribute(attributeName))
|
||||
return defaultValue;
|
||||
|
||||
string rawValue = node.GetAttribute(attributeName);
|
||||
string substitutedValue = Environment.ExpandEnvironmentVariables(rawValue);
|
||||
|
@ -94,7 +98,7 @@ namespace winsw.Util
|
|||
}
|
||||
catch (Exception ex) // Most likely ArgumentException
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace winsw
|
||||
{
|
||||
|
|
|
@ -1,8 +1,4 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace winsw
|
||||
namespace winsw
|
||||
{
|
||||
/// <summary>
|
||||
/// Class, which contains generic information about WinSW runtime.
|
||||
|
@ -19,11 +15,11 @@ namespace winsw
|
|||
/// Variable, which points to the service ID.
|
||||
/// It may be used to determine runaway processes.
|
||||
/// </summary>
|
||||
public static string ENVVAR_NAME_SERVICE_ID { get { return SYSTEM_EVNVVAR_PREFIX + "SERVICE_ID"; } }
|
||||
public static string ENVVAR_NAME_SERVICE_ID => SYSTEM_EVNVVAR_PREFIX + "SERVICE_ID";
|
||||
|
||||
/// <summary>
|
||||
/// Variable, which specifies path to the executable being launched by WinSW.
|
||||
/// </summary>
|
||||
public static string ENVVAR_NAME_EXECUTABLE_PATH { get { return SYSTEM_EVNVVAR_PREFIX + "EXECUTABLE"; } }
|
||||
public static string ENVVAR_NAME_EXECUTABLE_PATH => SYSTEM_EVNVVAR_PREFIX + "EXECUTABLE";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@ using DynamicProxy;
|
|||
|
||||
namespace WMI
|
||||
{
|
||||
//Reference: http://msdn2.microsoft.com/en-us/library/aa389390(VS.85).aspx
|
||||
// Reference: http://msdn2.microsoft.com/en-us/library/aa389390(VS.85).aspx
|
||||
|
||||
public enum ReturnValue
|
||||
{
|
||||
|
@ -63,13 +63,14 @@ namespace WMI
|
|||
public class WmiClassName : Attribute
|
||||
{
|
||||
public readonly string Name;
|
||||
public WmiClassName(string name) { Name = name; }
|
||||
|
||||
public WmiClassName(string name) => 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
|
||||
|
@ -141,6 +142,7 @@ namespace WMI
|
|||
_mo[method.Name.Substring(4)] = args[0];
|
||||
return null;
|
||||
}
|
||||
|
||||
if (method.Name.StartsWith("get_"))
|
||||
{
|
||||
return _mo[method.Name.Substring(4)];
|
||||
|
@ -180,7 +182,9 @@ namespace WMI
|
|||
string query = "SELECT * FROM " + _wmiClass + " WHERE ";
|
||||
for (int i = 0; i < args.Length; i++)
|
||||
{
|
||||
if (i != 0) query += " AND ";
|
||||
if (i != 0)
|
||||
query += " AND ";
|
||||
|
||||
query += ' ' + Capitalize(methodArgs[i].Name) + " = '" + args[i] + "'";
|
||||
}
|
||||
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Xml;
|
||||
using System.Collections.Specialized;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Xml;
|
||||
using log4net;
|
||||
using winsw.Extensions;
|
||||
using winsw.Util;
|
||||
using log4net;
|
||||
using System.Collections.Specialized;
|
||||
using System.Text;
|
||||
|
||||
namespace winsw.Plugins.RunawayProcessKiller
|
||||
{
|
||||
|
@ -34,7 +34,7 @@ namespace winsw.Plugins.RunawayProcessKiller
|
|||
/// </summary>
|
||||
public bool CheckWinSWEnvironmentVariable { get; private set; }
|
||||
|
||||
public override String DisplayName { get { return "Runaway Process Killer"; } }
|
||||
public override String DisplayName => "Runaway Process Killer";
|
||||
|
||||
private String ServiceId { get; set; }
|
||||
|
||||
|
@ -61,7 +61,7 @@ namespace winsw.Plugins.RunawayProcessKiller
|
|||
StopTimeout = TimeSpan.FromMilliseconds(Int32.Parse(XmlHelper.SingleElement(node, "stopTimeout", false)));
|
||||
StopParentProcessFirst = Boolean.Parse(XmlHelper.SingleElement(node, "stopParentFirst", false));
|
||||
ServiceId = descriptor.Id;
|
||||
//TODO: Consider making it documented
|
||||
// TODO: Consider making it documented
|
||||
var checkWinSWEnvironmentVariable = XmlHelper.SingleElement(node, "checkWinSWEnvironmentVariable", true);
|
||||
CheckWinSWEnvironmentVariable = checkWinSWEnvironmentVariable != null ? Boolean.Parse(checkWinSWEnvironmentVariable) : true;
|
||||
}
|
||||
|
@ -74,17 +74,19 @@ namespace winsw.Plugins.RunawayProcessKiller
|
|||
{
|
||||
// Read PID file from the disk
|
||||
int pid;
|
||||
if (System.IO.File.Exists(Pidfile)) {
|
||||
if (File.Exists(Pidfile))
|
||||
{
|
||||
string pidstring;
|
||||
try
|
||||
{
|
||||
pidstring = System.IO.File.ReadAllText(Pidfile);
|
||||
pidstring = File.ReadAllText(Pidfile);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Error("Cannot read PID file from " + Pidfile, ex);
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
pid = Int32.Parse(pidstring);
|
||||
|
@ -108,7 +110,7 @@ namespace winsw.Plugins.RunawayProcessKiller
|
|||
{
|
||||
proc = Process.GetProcessById(pid);
|
||||
}
|
||||
catch (ArgumentException ex)
|
||||
catch (ArgumentException)
|
||||
{
|
||||
Logger.Debug("No runaway process with PID=" + pid + ". The process has been already stopped.");
|
||||
return;
|
||||
|
@ -127,14 +129,16 @@ namespace winsw.Plugins.RunawayProcessKiller
|
|||
}
|
||||
else if (CheckWinSWEnvironmentVariable)
|
||||
{
|
||||
Logger.Warn("The process " + pid + " has no " + expectedEnvVarName + " environment variable defined. "
|
||||
Logger.Warn("The process " + pid + " has no " + expectedEnvVarName + " environment variable defined. "
|
||||
+ "The process has not been started by WinSW, hence it won't be terminated.");
|
||||
if (Logger.IsDebugEnabled) {
|
||||
//TODO replace by String.Join() in .NET 4
|
||||
if (Logger.IsDebugEnabled)
|
||||
{
|
||||
// TODO replace by String.Join() in .NET 4
|
||||
String[] keys = new String[previousProcessEnvVars.Count];
|
||||
previousProcessEnvVars.Keys.CopyTo(keys, 0);
|
||||
Logger.DebugFormat("Env vars of the process with PID={0}: {1}", new Object[] {pid, String.Join(",", keys)});
|
||||
Logger.DebugFormat("Env vars of the process with PID={0}: {1}", new Object[] { pid, String.Join(",", keys) });
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
else
|
||||
|
@ -155,11 +159,13 @@ 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 (!CheckWinSWEnvironmentVariable)
|
||||
{
|
||||
bldr.Append("not ");
|
||||
}
|
||||
|
||||
bldr.Append("checked, affiliated service ID: ");
|
||||
bldr.Append(affiliatedServiceId != null ? affiliatedServiceId : "undefined");
|
||||
bldr.Append(affiliatedServiceId ?? "undefined");
|
||||
bldr.Append(", process to kill: ");
|
||||
bldr.Append(proc);
|
||||
|
||||
|
@ -171,12 +177,12 @@ namespace winsw.Plugins.RunawayProcessKiller
|
|||
/// Records the started process PID for the future use in OnStart() after the restart.
|
||||
/// </summary>
|
||||
/// <param name="process"></param>
|
||||
public override void OnProcessStarted(System.Diagnostics.Process process)
|
||||
public override void OnProcessStarted(Process process)
|
||||
{
|
||||
Logger.Info("Recording PID of the started process:" + process.Id + ". PID file destination is " + Pidfile);
|
||||
try
|
||||
{
|
||||
System.IO.File.WriteAllText(Pidfile, process.Id.ToString());
|
||||
File.WriteAllText(Pidfile, process.Id.ToString());
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Xml;
|
||||
using System.Diagnostics;
|
||||
using log4net;
|
||||
using winsw.Extensions;
|
||||
using winsw.Util;
|
||||
using log4net;
|
||||
|
||||
namespace winsw.Plugins.SharedDirectoryMapper
|
||||
{
|
||||
|
@ -13,7 +12,7 @@ namespace winsw.Plugins.SharedDirectoryMapper
|
|||
private readonly SharedDirectoryMappingHelper _mapper = new SharedDirectoryMappingHelper();
|
||||
private readonly List<SharedDirectoryMapperConfig> _entries = new List<SharedDirectoryMapperConfig>();
|
||||
|
||||
public override String DisplayName { get { return "Shared Directory Mapper"; } }
|
||||
public override String DisplayName => "Shared Directory Mapper";
|
||||
|
||||
private static readonly ILog Logger = LogManager.GetLogger(typeof(SharedDirectoryMapper));
|
||||
|
||||
|
@ -34,8 +33,7 @@ namespace winsw.Plugins.SharedDirectoryMapper
|
|||
{
|
||||
foreach (XmlNode mapNode in nodes)
|
||||
{
|
||||
var mapElement = mapNode as XmlElement;
|
||||
if (mapElement != null)
|
||||
if (mapNode is XmlElement mapElement)
|
||||
{
|
||||
var config = SharedDirectoryMapperConfig.FromXml(mapElement);
|
||||
_entries.Add(config);
|
||||
|
@ -85,8 +83,9 @@ namespace winsw.Plugins.SharedDirectoryMapper
|
|||
}
|
||||
}
|
||||
|
||||
private void HandleMappingError(SharedDirectoryMapperConfig config, MapperException ex) {
|
||||
Logger.Error("Mapping of " + config.Label + " failed. STDOUT: " + ex.Process.StandardOutput.ReadToEnd()
|
||||
private void HandleMappingError(SharedDirectoryMapperConfig config, MapperException ex)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
using System;
|
||||
using System.Diagnostics;
|
||||
using winsw.Util;
|
||||
|
||||
namespace winsw.Plugins.SharedDirectoryMapper
|
||||
{
|
||||
|
|
|
@ -1,9 +1,6 @@
|
|||
using NUnit.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.IO;
|
||||
using System.Xml;
|
||||
using NUnit.Framework;
|
||||
using winsw;
|
||||
using winswTests.Util;
|
||||
|
||||
|
@ -16,11 +13,10 @@ namespace winswTests.Configuration
|
|||
[TestFixture]
|
||||
class ExamplesTest
|
||||
{
|
||||
|
||||
[Test]
|
||||
public void allOptionsConfigShouldDeclareDefaults()
|
||||
public void AllOptionsConfigShouldDeclareDefaults()
|
||||
{
|
||||
ServiceDescriptor d = doLoad("allOptions");
|
||||
ServiceDescriptor d = DoLoad("allOptions");
|
||||
|
||||
Assert.AreEqual("myapp", d.Id);
|
||||
Assert.AreEqual("MyApp Service (powered by WinSW)", d.Caption);
|
||||
|
@ -31,9 +27,9 @@ namespace winswTests.Configuration
|
|||
}
|
||||
|
||||
[Test]
|
||||
public void minimalConfigShouldDeclareDefaults()
|
||||
public void MinimalConfigShouldDeclareDefaults()
|
||||
{
|
||||
ServiceDescriptor d = doLoad("minimal");
|
||||
ServiceDescriptor d = DoLoad("minimal");
|
||||
|
||||
Assert.AreEqual("myapp", d.Id);
|
||||
Assert.AreEqual("MyApp Service (powered by WinSW)", d.Caption);
|
||||
|
@ -43,13 +39,15 @@ namespace winswTests.Configuration
|
|||
ServiceDescriptorAssert.AssertAllOptionalPropertiesAreDefault(d);
|
||||
}
|
||||
|
||||
private ServiceDescriptor doLoad(string exampleName) {
|
||||
private ServiceDescriptor DoLoad(string exampleName)
|
||||
{
|
||||
var dir = Directory.GetCurrentDirectory();
|
||||
string path = Path.GetFullPath(dir + "\\..\\..\\..\\..\\..\\..\\examples\\sample-" + exampleName + ".xml");
|
||||
if (!File.Exists(path))
|
||||
{
|
||||
throw new FileNotFoundException("Cannot find the XML file " + path, path);
|
||||
}
|
||||
|
||||
XmlDocument dom = new XmlDocument();
|
||||
dom.Load(path);
|
||||
return new ServiceDescriptor(dom);
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
using NUnit.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using NUnit.Framework;
|
||||
using winsw;
|
||||
using winswTests.Util;
|
||||
|
||||
|
@ -22,7 +20,7 @@ namespace winswTests
|
|||
var sd = ConfigXmlBuilder.create()
|
||||
.WithDownload(d)
|
||||
.ToServiceDescriptor(true);
|
||||
var loaded = getSingleEntry(sd);
|
||||
var loaded = GetSingleEntry(sd);
|
||||
|
||||
// Check default values
|
||||
Assert.That(loaded.FailOnError, Is.EqualTo(false));
|
||||
|
@ -40,7 +38,7 @@ namespace winswTests
|
|||
var sd = ConfigXmlBuilder.create()
|
||||
.WithDownload(d)
|
||||
.ToServiceDescriptor(true);
|
||||
var loaded = getSingleEntry(sd);
|
||||
var loaded = GetSingleEntry(sd);
|
||||
|
||||
// Check default values
|
||||
Assert.That(loaded.FailOnError, Is.EqualTo(true));
|
||||
|
@ -58,7 +56,7 @@ namespace winswTests
|
|||
var sd = ConfigXmlBuilder.create()
|
||||
.WithDownload(d)
|
||||
.ToServiceDescriptor(true);
|
||||
var loaded = getSingleEntry(sd);
|
||||
var loaded = GetSingleEntry(sd);
|
||||
|
||||
// Check default values
|
||||
Assert.That(loaded.FailOnError, Is.EqualTo(false));
|
||||
|
@ -67,7 +65,7 @@ namespace winswTests
|
|||
Assert.That(loaded.Password, Is.Null);
|
||||
Assert.That(loaded.UnsecureAuth, Is.EqualTo(false));
|
||||
}
|
||||
|
||||
|
||||
[TestCase("http://")]
|
||||
[TestCase("ftp://")]
|
||||
[TestCase("file:///")]
|
||||
|
@ -77,19 +75,19 @@ namespace winswTests
|
|||
{
|
||||
var d = new Download(protocolPrefix + "myServer.com:8080/file.txt", To,
|
||||
auth: Download.AuthType.basic, username: "aUser", password: "aPassword");
|
||||
assertInitializationFails(d, "you're sending your credentials in clear text to the server");
|
||||
AssertInitializationFails(d, "you're sending your credentials in clear text to the server");
|
||||
}
|
||||
|
||||
public void ShouldRejectBasicAuth_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");
|
||||
AssertInitializationFails(d, "Basic Auth is enabled, but username is not specified");
|
||||
}
|
||||
|
||||
public void ShouldRejectBasicAuth_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");
|
||||
AssertInitializationFails(d, "Basic Auth is enabled, but password is not specified");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -105,7 +103,7 @@ namespace winswTests
|
|||
.WithDownload(d)
|
||||
.ToServiceDescriptor(true);
|
||||
|
||||
var loaded = getSingleEntry(sd);
|
||||
var loaded = 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");
|
||||
|
@ -121,7 +119,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 = GetSingleEntry(sd);
|
||||
Assert.That(loaded.FailOnError, Is.False);
|
||||
}
|
||||
|
||||
|
@ -134,7 +132,7 @@ 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);
|
||||
var loaded = GetSingleEntry(sd);
|
||||
Assert.That(loaded.Auth, Is.EqualTo(Download.AuthType.sspi));
|
||||
}
|
||||
|
||||
|
@ -146,27 +144,28 @@ namespace winswTests
|
|||
.WithRawEntry("<download from=\"http://www.nosuchhostexists.foo.myorg/foo.xml\" to=\"%BASE%\\foo.xml\" auth=\"digest\"/>")
|
||||
.ToServiceDescriptor(true);
|
||||
|
||||
ExceptionHelper.assertFails("Cannot parse <auth> Enum value from string 'digest'", typeof(InvalidDataException), delegate {
|
||||
var d = getSingleEntry(sd);
|
||||
ExceptionHelper.assertFails("Cannot parse <auth> Enum value from string 'digest'", typeof(InvalidDataException), () =>
|
||||
{
|
||||
var d = GetSingleEntry(sd);
|
||||
});
|
||||
}
|
||||
|
||||
private Download getSingleEntry(ServiceDescriptor sd)
|
||||
private Download GetSingleEntry(ServiceDescriptor sd)
|
||||
{
|
||||
var downloads = sd.Downloads.ToArray();
|
||||
Assert.That(downloads.Length, Is.EqualTo(1), "Service Descriptor is expected to have only one entry");
|
||||
return downloads[0];
|
||||
}
|
||||
|
||||
private void assertInitializationFails(Download download, String expectedMessagePart = null, Type expectedExceptionType = null)
|
||||
private void AssertInitializationFails(Download download, String expectedMessagePart = null, Type expectedExceptionType = null)
|
||||
{
|
||||
var sd = ConfigXmlBuilder.create()
|
||||
.WithDownload(download)
|
||||
.ToServiceDescriptor(true);
|
||||
|
||||
ExceptionHelper.assertFails(expectedMessagePart, expectedExceptionType ?? typeof(InvalidDataException), delegate
|
||||
ExceptionHelper.assertFails(expectedMessagePart, expectedExceptionType ?? typeof(InvalidDataException), () =>
|
||||
{
|
||||
var d = getSingleEntry(sd);
|
||||
var d = GetSingleEntry(sd);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace winswTests.Extensions
|
||||
{
|
||||
|
@ -15,7 +13,7 @@ namespace winswTests.Extensions
|
|||
/// </summary>
|
||||
/// <param name="type">Type of the extension</param>
|
||||
/// <returns>String for Type locator, which includes class and assembly names</returns>
|
||||
public static String getExtensionClassNameWithAssembly(Type type)
|
||||
public static String GetExtensionClassNameWithAssembly(Type type)
|
||||
{
|
||||
return type.ToString() + ", " + type.Assembly;
|
||||
}
|
||||
|
|
|
@ -1,14 +1,13 @@
|
|||
using winsw;
|
||||
using NUnit.Framework;
|
||||
using winsw.Extensions;
|
||||
using winsw.Plugins.SharedDirectoryMapper;
|
||||
using winsw.Plugins.RunawayProcessKiller;
|
||||
using winswTests.Util;
|
||||
using System.IO;
|
||||
using System.Diagnostics;
|
||||
using winsw.Util;
|
||||
using System;
|
||||
using System;
|
||||
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 winswTests.Util;
|
||||
|
||||
namespace winswTests.Extensions
|
||||
{
|
||||
|
@ -17,8 +16,8 @@ namespace winswTests.Extensions
|
|||
{
|
||||
ServiceDescriptor _testServiceDescriptor;
|
||||
|
||||
string testExtension = getExtensionClassNameWithAssembly(typeof(RunawayProcessKillerExtension));
|
||||
|
||||
string testExtension = GetExtensionClassNameWithAssembly(typeof(RunawayProcessKillerExtension));
|
||||
|
||||
[SetUp]
|
||||
public void SetUp()
|
||||
{
|
||||
|
@ -71,7 +70,7 @@ namespace winswTests.Extensions
|
|||
var winswId = "myAppWithRunaway";
|
||||
var extensionId = "runawayProcessKiller";
|
||||
var tmpDir = FilesystemTestHelper.CreateTmpDirectory();
|
||||
|
||||
|
||||
// Prepare the env var
|
||||
String varName = WinSWSystem.ENVVAR_NAME_SERVICE_ID;
|
||||
var env = new Dictionary<string, string>();
|
||||
|
@ -99,7 +98,7 @@ namespace winswTests.Extensions
|
|||
Assert.IsNotNull(extension, "RunawayProcessKillerExtension should be loaded");
|
||||
Assert.AreEqual(pidfile, extension.Pidfile, "PidFile should have been retained during the config roundtrip");
|
||||
|
||||
// Inject PID
|
||||
// Inject PID
|
||||
File.WriteAllText(pidfile, proc.Id.ToString());
|
||||
|
||||
// Try to terminate
|
||||
|
@ -119,7 +118,7 @@ namespace winswTests.Extensions
|
|||
Console.Error.WriteLine("Test: ProcessHelper failed to properly terminate process with ID=" + proc.Id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
using winsw;
|
||||
using NUnit.Framework;
|
||||
using NUnit.Framework;
|
||||
using winsw;
|
||||
using winsw.Extensions;
|
||||
using winsw.Plugins.SharedDirectoryMapper;
|
||||
|
||||
|
@ -10,12 +10,11 @@ namespace winswTests.Extensions
|
|||
{
|
||||
ServiceDescriptor _testServiceDescriptor;
|
||||
|
||||
string testExtension = getExtensionClassNameWithAssembly(typeof(SharedDirectoryMapper));
|
||||
string testExtension = GetExtensionClassNameWithAssembly(typeof(SharedDirectoryMapper));
|
||||
|
||||
[SetUp]
|
||||
public void SetUp()
|
||||
{
|
||||
|
||||
string seedXml = "<?xml version=\"1.0\" encoding=\"utf-8\" ?>"
|
||||
+ "<service> "
|
||||
+ " <id>SERVICE_NAME</id> "
|
||||
|
|
|
@ -7,7 +7,6 @@ namespace winswTests
|
|||
[TestFixture]
|
||||
class MainTest
|
||||
{
|
||||
|
||||
[Test]
|
||||
public void PrintVersion()
|
||||
{
|
||||
|
@ -21,12 +20,12 @@ namespace winswTests
|
|||
{
|
||||
string expectedVersion = WrapperService.Version.ToString();
|
||||
string cliOut = CLITestHelper.CLITest(new[] { "help" });
|
||||
|
||||
|
||||
StringAssert.Contains(expectedVersion, cliOut, "Expected that help contains " + expectedVersion);
|
||||
StringAssert.Contains("start", cliOut, "Expected that help refers start command");
|
||||
StringAssert.Contains("help", cliOut, "Expected that help refers help command");
|
||||
StringAssert.Contains("version", cliOut, "Expected that help refers version command");
|
||||
//TODO: check all commands after the migration of ccommands to enum
|
||||
// TODO: check all commands after the migration of ccommands to enum
|
||||
|
||||
// Extra options
|
||||
StringAssert.Contains("/redirect", cliOut, "Expected that help message refers the redirect message");
|
||||
|
@ -37,8 +36,8 @@ namespace winswTests
|
|||
{
|
||||
const string commandName = "nonExistentCommand";
|
||||
string expectedMessage = "Unknown command: " + commandName.ToLower();
|
||||
CLITestResult res = CLITestHelper.CLIErrorTest(new[] {commandName});
|
||||
|
||||
CLITestResult res = CLITestHelper.CLIErrorTest(new[] { commandName });
|
||||
|
||||
Assert.True(res.HasException, "Expected an exception due to the wrong command");
|
||||
StringAssert.Contains(expectedMessage, res.Out, "Expected the message about unknown command");
|
||||
// ReSharper disable once PossibleNullReferenceException
|
||||
|
|
|
@ -1,18 +1,16 @@
|
|||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Diagnostics;
|
||||
using NUnit.Framework;
|
||||
using winsw;
|
||||
|
||||
namespace winswTests
|
||||
{
|
||||
using System;
|
||||
using WMI;
|
||||
using winswTests.Util;
|
||||
using WMI;
|
||||
|
||||
[TestFixture]
|
||||
public class ServiceDescriptorTests
|
||||
{
|
||||
|
||||
private ServiceDescriptor _extendedServiceDescriptor;
|
||||
|
||||
private const string ExpectedWorkingDirectory = @"Z:\Path\SubPath";
|
||||
|
@ -32,9 +30,9 @@ namespace winswTests
|
|||
+ "<arguments>My Arguments</arguments>"
|
||||
+ "<logmode>rotate</logmode>"
|
||||
+ "<serviceaccount>"
|
||||
+ "<domain>" + Domain + "</domain>"
|
||||
+ "<user>" + Username + "</user>"
|
||||
+ "<password>" + Password + "</password>"
|
||||
+ "<domain>" + Domain + "</domain>"
|
||||
+ "<user>" + Username + "</user>"
|
||||
+ "<password>" + Password + "</password>"
|
||||
+ "<allowservicelogon>" + AllowServiceAccountLogonRight + "</allowservicelogon>"
|
||||
+ "</serviceaccount>"
|
||||
+ "<workingdirectory>"
|
||||
|
@ -52,7 +50,7 @@ namespace winswTests
|
|||
}
|
||||
|
||||
[Test]
|
||||
[ExpectedException(typeof(System.ArgumentException))]
|
||||
[ExpectedException(typeof(ArgumentException))]
|
||||
public void IncorrectStartMode()
|
||||
{
|
||||
const string SeedXml = "<service>"
|
||||
|
@ -105,6 +103,7 @@ namespace winswTests
|
|||
_extendedServiceDescriptor = ServiceDescriptor.FromXML(SeedXml);
|
||||
Assert.That(_extendedServiceDescriptor.StartMode, Is.EqualTo(StartMode.Manual));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void VerifyWorkingDirectory()
|
||||
{
|
||||
|
@ -154,8 +153,8 @@ namespace winswTests
|
|||
[Test]
|
||||
public void CanParseStopParentProcessFirst()
|
||||
{
|
||||
const string seedXml = "<service>"
|
||||
+ "<stopparentprocessfirst>true</stopparentprocessfirst>"
|
||||
const string seedXml = "<service>"
|
||||
+ "<stopparentprocessfirst>true</stopparentprocessfirst>"
|
||||
+ "</service>";
|
||||
var serviceDescriptor = ServiceDescriptor.FromXML(seedXml);
|
||||
|
||||
|
@ -165,8 +164,8 @@ namespace winswTests
|
|||
[Test]
|
||||
public void CanParseStopTimeout()
|
||||
{
|
||||
const string seedXml = "<service>"
|
||||
+ "<stoptimeout>60sec</stoptimeout>"
|
||||
const string seedXml = "<service>"
|
||||
+ "<stoptimeout>60sec</stoptimeout>"
|
||||
+ "</service>";
|
||||
var serviceDescriptor = ServiceDescriptor.FromXML(seedXml);
|
||||
|
||||
|
@ -176,8 +175,8 @@ namespace winswTests
|
|||
[Test]
|
||||
public void CanParseStopTimeoutFromMinutes()
|
||||
{
|
||||
const string seedXml = "<service>"
|
||||
+ "<stoptimeout>10min</stoptimeout>"
|
||||
const string seedXml = "<service>"
|
||||
+ "<stoptimeout>10min</stoptimeout>"
|
||||
+ "</service>";
|
||||
var serviceDescriptor = ServiceDescriptor.FromXML(seedXml);
|
||||
|
||||
|
@ -242,14 +241,14 @@ namespace winswTests
|
|||
[Test]
|
||||
public void LogModeRollBySize()
|
||||
{
|
||||
const string seedXml = "<service>"
|
||||
const string seedXml = "<service>"
|
||||
+ "<logpath>c:\\</logpath>"
|
||||
+ "<log mode=\"roll-by-size\">"
|
||||
+ "<sizeThreshold>112</sizeThreshold>"
|
||||
+ "<keepFiles>113</keepFiles>"
|
||||
+ "<sizeThreshold>112</sizeThreshold>"
|
||||
+ "<keepFiles>113</keepFiles>"
|
||||
+ "</log>"
|
||||
+ "</service>";
|
||||
|
||||
|
||||
var serviceDescriptor = ServiceDescriptor.FromXML(seedXml);
|
||||
serviceDescriptor.BaseName = "service";
|
||||
|
||||
|
@ -265,8 +264,8 @@ namespace winswTests
|
|||
const string seedXml = "<service>"
|
||||
+ "<logpath>c:\\</logpath>"
|
||||
+ "<log mode=\"roll-by-time\">"
|
||||
+ "<period>7</period>"
|
||||
+ "<pattern>log pattern</pattern>"
|
||||
+ "<period>7</period>"
|
||||
+ "<pattern>log pattern</pattern>"
|
||||
+ "</log>"
|
||||
+ "</service>";
|
||||
|
||||
|
@ -298,23 +297,24 @@ namespace winswTests
|
|||
Assert.NotNull(logHandler);
|
||||
Assert.That(logHandler.SizeTheshold, Is.EqualTo(10240 * 1024));
|
||||
Assert.That(logHandler.FilePattern, Is.EqualTo("yyyy-MM-dd"));
|
||||
Assert.That(logHandler.AutoRollAtTime, Is.EqualTo((TimeSpan?)new TimeSpan(0,0,0)));
|
||||
Assert.That(logHandler.AutoRollAtTime, Is.EqualTo((TimeSpan?)new TimeSpan(0, 0, 0)));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void VerifyServiceLogonRightGraceful()
|
||||
{
|
||||
const string seedXml="<service>"
|
||||
const string seedXml = "<service>"
|
||||
+ "<serviceaccount>"
|
||||
+ "<domain>" + Domain + "</domain>"
|
||||
+ "<user>" + Username + "</user>"
|
||||
+ "<password>" + Password + "</password>"
|
||||
+ "<domain>" + Domain + "</domain>"
|
||||
+ "<user>" + Username + "</user>"
|
||||
+ "<password>" + Password + "</password>"
|
||||
+ "<allowservicelogon>true1</allowservicelogon>"
|
||||
+ "</serviceaccount>"
|
||||
+ "</serviceaccount>"
|
||||
+ "</service>";
|
||||
var serviceDescriptor = ServiceDescriptor.FromXML(seedXml);
|
||||
Assert.That(serviceDescriptor.AllowServiceAcountLogonRight, Is.EqualTo(false));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void VerifyServiceLogonRightOmitted()
|
||||
{
|
||||
|
@ -358,7 +358,7 @@ namespace winswTests
|
|||
.ToServiceDescriptor(true);
|
||||
Assert.That(sd.WaitHint, Is.EqualTo(TimeSpan.FromMinutes(22)));
|
||||
}
|
||||
|
||||
|
||||
[Test]
|
||||
public void VerifyWaitHint_XMLWithoutVersionAndComment()
|
||||
{
|
||||
|
@ -438,10 +438,12 @@ namespace winswTests
|
|||
public void DelayedStart_RoundTrip(bool enabled)
|
||||
{
|
||||
var bldr = ConfigXmlBuilder.create();
|
||||
if (enabled) {
|
||||
if (enabled)
|
||||
{
|
||||
bldr = bldr.WithDelayedAutoStart();
|
||||
}
|
||||
var sd = bldr.ToServiceDescriptor();
|
||||
|
||||
var sd = bldr.ToServiceDescriptor();
|
||||
Assert.That(sd.DelayedAutoStart, Is.EqualTo(enabled));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,6 +22,7 @@ namespace winswTests.Util
|
|||
+ "</workingdirectory>"
|
||||
+ @"<logpath>C:\winsw\logs</logpath>"
|
||||
+ "</service>";
|
||||
|
||||
private static readonly ServiceDescriptor DefaultServiceDescriptor = ServiceDescriptor.FromXML(SeedXml);
|
||||
|
||||
/// <summary>
|
||||
|
@ -61,8 +62,9 @@ namespace winswTests.Util
|
|||
|
||||
using (swOut = new StringWriter())
|
||||
using (swErr = new StringWriter())
|
||||
{
|
||||
try
|
||||
{
|
||||
{
|
||||
Console.SetOut(swOut);
|
||||
Console.SetError(swErr);
|
||||
WrapperService.Run(args, descriptor ?? DefaultServiceDescriptor);
|
||||
|
@ -85,7 +87,8 @@ namespace winswTests.Util
|
|||
Console.WriteLine(testEx);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return new CLITestResult(swOut.ToString(), swErr.ToString(), testEx);
|
||||
}
|
||||
}
|
||||
|
@ -97,14 +100,14 @@ namespace winswTests.Util
|
|||
{
|
||||
[NotNull]
|
||||
public String Out { get; private set; }
|
||||
|
||||
|
||||
[NotNull]
|
||||
public String Err { get; private set; }
|
||||
|
||||
|
||||
[CanBeNull]
|
||||
public Exception Exception { get; private set; }
|
||||
|
||||
public bool HasException { get { return Exception != null; } }
|
||||
public bool HasException => Exception != null;
|
||||
|
||||
public CLITestResult(String output, String err, Exception exception = null)
|
||||
{
|
||||
|
|
|
@ -17,7 +17,7 @@ namespace winswTests.Util
|
|||
public string Description { get; set; }
|
||||
public string Executable { get; set; }
|
||||
public bool PrintXMLVersion { get; set; }
|
||||
public string XMLComment { get; set; }
|
||||
public string XMLComment { get; set; }
|
||||
public List<string> ExtensionXmls { get; private set; }
|
||||
|
||||
private List<String> configEntries;
|
||||
|
@ -29,9 +29,9 @@ namespace winswTests.Util
|
|||
ExtensionXmls = new List<string>();
|
||||
}
|
||||
|
||||
public static ConfigXmlBuilder create(string id = null, string name = null,
|
||||
string description = null, string executable = null, bool printXMLVersion = true,
|
||||
string xmlComment = "")
|
||||
public static ConfigXmlBuilder create(string id = null, string name = null,
|
||||
string description = null, string executable = null, bool printXMLVersion = true,
|
||||
string xmlComment = "")
|
||||
{
|
||||
var config = new ConfigXmlBuilder();
|
||||
config.Id = id ?? "myapp";
|
||||
|
@ -39,8 +39,8 @@ namespace winswTests.Util
|
|||
config.Description = description ?? "MyApp Service (powered by WinSW)";
|
||||
config.Executable = executable ?? "%BASE%\\myExecutable.exe";
|
||||
config.PrintXMLVersion = printXMLVersion;
|
||||
config.XMLComment = (xmlComment != null && xmlComment.Length == 0)
|
||||
? "Just a sample configuration file generated by the test suite"
|
||||
config.XMLComment = (xmlComment != null && xmlComment.Length == 0)
|
||||
? "Just a sample configuration file generated by the test suite"
|
||||
: xmlComment;
|
||||
return config;
|
||||
}
|
||||
|
@ -53,10 +53,12 @@ namespace winswTests.Util
|
|||
// TODO: The encoding is generally wrong
|
||||
str.Append("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n");
|
||||
}
|
||||
|
||||
if (XMLComment != null)
|
||||
{
|
||||
str.AppendFormat("<!--{0}-->\n", XMLComment);
|
||||
}
|
||||
|
||||
str.Append("<service>\n");
|
||||
str.AppendFormat(" <id>{0}</id>\n", Id);
|
||||
str.AppendFormat(" <name>{0}</name>\n", Name);
|
||||
|
@ -69,13 +71,14 @@ namespace winswTests.Util
|
|||
}
|
||||
|
||||
// Extensions
|
||||
if (ExtensionXmls.Count > 0)
|
||||
if (ExtensionXmls.Count > 0)
|
||||
{
|
||||
str.Append(" <extensions>\n");
|
||||
foreach (string xml in ExtensionXmls)
|
||||
foreach (string xml in ExtensionXmls)
|
||||
{
|
||||
str.Append(xml);
|
||||
}
|
||||
|
||||
str.Append(" </extensions>\n");
|
||||
}
|
||||
|
||||
|
@ -86,13 +89,14 @@ namespace winswTests.Util
|
|||
Console.Out.WriteLine("Produced config:");
|
||||
Console.Out.WriteLine(res);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
public ServiceDescriptor ToServiceDescriptor(bool dumpConfig = false)
|
||||
{
|
||||
return ServiceDescriptor.FromXML(ToXMLString(dumpConfig));
|
||||
}
|
||||
}
|
||||
|
||||
public ConfigXmlBuilder WithRawEntry(string entry)
|
||||
{
|
||||
|
@ -101,20 +105,20 @@ namespace winswTests.Util
|
|||
}
|
||||
|
||||
public ConfigXmlBuilder WithTag(string tagName, string value)
|
||||
{
|
||||
{
|
||||
return WithRawEntry(String.Format("<{0}>{1}</{0}>", tagName, value));
|
||||
}
|
||||
|
||||
public ConfigXmlBuilder WithRunawayProcessKiller(RunawayProcessKillerExtension ext, string extensionId = "killRunawayProcess", bool enabled = true)
|
||||
{
|
||||
var fullyQualifiedExtensionName = ExtensionTestBase.getExtensionClassNameWithAssembly(typeof(RunawayProcessKillerExtension));
|
||||
var fullyQualifiedExtensionName = ExtensionTestBase.GetExtensionClassNameWithAssembly(typeof(RunawayProcessKillerExtension));
|
||||
StringBuilder str = new StringBuilder();
|
||||
str.AppendFormat(" <extension enabled=\"{0}\" className=\"{1}\" id=\"{2}\">\n", new Object[] { enabled, fullyQualifiedExtensionName, extensionId});
|
||||
str.AppendFormat(" <extension enabled=\"{0}\" className=\"{1}\" id=\"{2}\">\n", new Object[] { enabled, fullyQualifiedExtensionName, extensionId });
|
||||
str.AppendFormat(" <pidfile>{0}</pidfile>\n", ext.Pidfile);
|
||||
str.AppendFormat(" <stopTimeout>{0}</stopTimeout>\n", ext.StopTimeout.TotalMilliseconds);
|
||||
str.AppendFormat(" <stopParentFirst>{0}</stopParentFirst>\n", ext.StopParentProcessFirst);
|
||||
str.AppendFormat(" <checkWinSWEnvironmentVariable>{0}</checkWinSWEnvironmentVariable>\n", ext.CheckWinSWEnvironmentVariable);
|
||||
str.Append( " </extension>\n");
|
||||
str.Append(" </extension>\n");
|
||||
ExtensionXmls.Add(str.ToString());
|
||||
|
||||
return this;
|
||||
|
@ -123,8 +127,8 @@ namespace winswTests.Util
|
|||
public ConfigXmlBuilder WithDownload(Download download)
|
||||
{
|
||||
StringBuilder str = new StringBuilder();
|
||||
str.AppendFormat("<download from=\"{0}\" to=\"{1}\" failOnError=\"{2}\"", new Object[] { download.From, download.To, download.FailOnError});
|
||||
|
||||
str.AppendFormat("<download from=\"{0}\" to=\"{1}\" failOnError=\"{2}\"", new Object[] { download.From, download.To, download.FailOnError });
|
||||
|
||||
// Authentication
|
||||
if (download.Auth != Download.AuthType.none)
|
||||
{
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
using NUnit.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace winswTests.Util
|
||||
{
|
||||
|
@ -28,8 +26,6 @@ namespace winswTests.Util
|
|||
|
||||
Assert.Fail("Expected exception " + expectedExceptionType + " to be thrown by the operation");
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
public delegate void ExceptionHelperExecutionBody();
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
using NUnit.Framework;
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace winswTests.Util
|
||||
{
|
||||
|
@ -14,7 +13,7 @@ namespace winswTests.Util
|
|||
/// <returns>tmp Dir</returns>
|
||||
public static string CreateTmpDirectory(String testName = null)
|
||||
{
|
||||
string tempDirectory = Path.Combine(Path.GetTempPath(), "winswTests_" + (testName ?? "") + Path.GetRandomFileName());
|
||||
string tempDirectory = Path.Combine(Path.GetTempPath(), "winswTests_" + (testName ?? string.Empty) + Path.GetRandomFileName());
|
||||
Directory.CreateDirectory(tempDirectory);
|
||||
Console.Out.WriteLine("Created the temporary directory: {0}", tempDirectory);
|
||||
return tempDirectory;
|
||||
|
@ -29,7 +28,8 @@ namespace winswTests.Util
|
|||
{
|
||||
Dictionary<string, string> res = new Dictionary<string, string>();
|
||||
var lines = File.ReadAllLines(filePath);
|
||||
foreach(var line in lines) {
|
||||
foreach (var line in lines)
|
||||
{
|
||||
var parsed = line.Split("=".ToCharArray(), 2);
|
||||
if (parsed.Length == 2)
|
||||
{
|
||||
|
@ -40,6 +40,7 @@ namespace winswTests.Util
|
|||
Assert.Fail("Wrong line in the parsed Set output file: " + line);
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,14 +1,12 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using NUnit.Framework;
|
||||
using winsw;
|
||||
using System.IO;
|
||||
using NUnit.Framework;
|
||||
using winsw.Util;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace winswTests.Util
|
||||
{
|
||||
|
||||
namespace winswTests.Util
|
||||
{
|
||||
[TestFixture]
|
||||
class ProcessHelperTest
|
||||
{
|
||||
|
@ -25,7 +23,6 @@ namespace winswTests.Util
|
|||
String scriptFile = Path.Combine(tmpDir, "printenv.bat");
|
||||
File.WriteAllText(scriptFile, "set > " + envFile);
|
||||
|
||||
|
||||
Process proc = new Process();
|
||||
var ps = proc.StartInfo;
|
||||
ps.FileName = scriptFile;
|
||||
|
@ -48,35 +45,36 @@ namespace winswTests.Util
|
|||
Assert.That(!envVars.ContainsKey("test_key"), "Test error: the environment parsing logic is case-insensitive");
|
||||
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ShouldNotHangWhenWritingLargeStringToStdOut()
|
||||
{
|
||||
var tmpDir = FilesystemTestHelper.CreateTmpDirectory();
|
||||
String scriptFile = Path.Combine(tmpDir, "print_lots_to_stdout.bat");
|
||||
var lotsOfStdOut = string.Join("", _Range(1,1000));
|
||||
File.WriteAllText(scriptFile, string.Format("echo \"{0}\"", lotsOfStdOut));
|
||||
|
||||
Process proc = new Process();
|
||||
var ps = proc.StartInfo;
|
||||
ps.FileName = scriptFile;
|
||||
|
||||
ProcessHelper.StartProcessAndCallbackForExit(proc);
|
||||
var exited = proc.WaitForExit(5000);
|
||||
if (!exited)
|
||||
{
|
||||
Assert.Fail("Process " + proc + " didn't exit after 5 seconds");
|
||||
}
|
||||
}
|
||||
|
||||
private string[] _Range(int start, int limit)
|
||||
{
|
||||
var range = new List<string>();
|
||||
for(var i = start; i<limit; i++)
|
||||
{
|
||||
range.Add(i.ToString());
|
||||
}
|
||||
return range.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ShouldNotHangWhenWritingLargeStringToStdOut()
|
||||
{
|
||||
var tmpDir = FilesystemTestHelper.CreateTmpDirectory();
|
||||
String scriptFile = Path.Combine(tmpDir, "print_lots_to_stdout.bat");
|
||||
var lotsOfStdOut = string.Join(string.Empty, _Range(1, 1000));
|
||||
File.WriteAllText(scriptFile, string.Format("echo \"{0}\"", lotsOfStdOut));
|
||||
|
||||
Process proc = new Process();
|
||||
var ps = proc.StartInfo;
|
||||
ps.FileName = scriptFile;
|
||||
|
||||
ProcessHelper.StartProcessAndCallbackForExit(proc);
|
||||
var exited = proc.WaitForExit(5000);
|
||||
if (!exited)
|
||||
{
|
||||
Assert.Fail("Process " + proc + " didn't exit after 5 seconds");
|
||||
}
|
||||
}
|
||||
|
||||
private string[] _Range(int start, int limit)
|
||||
{
|
||||
var range = new List<string>();
|
||||
for (var i = start; i < limit; i++)
|
||||
{
|
||||
range.Add(i.ToString());
|
||||
}
|
||||
|
||||
return range.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
using NUnit.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using NUnit.Framework;
|
||||
using winsw;
|
||||
using winsw.Configuration;
|
||||
|
||||
|
@ -13,7 +11,7 @@ namespace winswTests.Util
|
|||
// TODO: convert to Extension attributes once the .NET dependency is upgraded
|
||||
// BTW there is a way to get them working in .NET2, but KISS
|
||||
|
||||
public static void AssertPropertyIsDefault(ServiceDescriptor d, string property)
|
||||
public static void AssertPropertyIsDefault(ServiceDescriptor d, string property)
|
||||
{
|
||||
PropertyInfo actualProperty = typeof(ServiceDescriptor).GetProperty(property);
|
||||
Assert.IsNotNull(actualProperty, "Cannot find property " + property + " in the service descriptor" + d);
|
||||
|
@ -37,14 +35,17 @@ namespace winswTests.Util
|
|||
AssertPropertyIsDefault(d, AllOptionalProperties);
|
||||
}
|
||||
|
||||
private static List<string> AllProperties {
|
||||
get {
|
||||
private static List<string> AllProperties
|
||||
{
|
||||
get
|
||||
{
|
||||
var res = new List<string>();
|
||||
var properties = typeof(IWinSWConfiguration).GetProperties();
|
||||
foreach (var prop in properties)
|
||||
{
|
||||
res.Add(prop.Name);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
@ -62,6 +63,4 @@ namespace winswTests.Util
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue