mirror of https://github.com/winsw/winsw
commit
b49216fcb1
|
@ -486,300 +486,304 @@ namespace winsw
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Runs the wrapper.
|
/// Runs the wrapper.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="_args">Arguments. If empty, WinSW will behave in the service mode. Otherwise - CLI mode</param>
|
/// <param name="_args">Arguments.</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>
|
/// In such case configs will be loaded from the XML Configuration File.</param>
|
||||||
/// <exception cref="Exception">Any unhandled exception</exception>
|
/// <exception cref="Exception">Any unhandled exception</exception>
|
||||||
public static void Run(string[] _args, ServiceDescriptor? descriptor = null)
|
public static void Run(string[] _args, ServiceDescriptor? descriptor = null)
|
||||||
{
|
{
|
||||||
bool isCLIMode = _args.Length > 0;
|
bool inCliMode = Console.OpenStandardInput() != Stream.Null;
|
||||||
|
|
||||||
// If descriptor is not specified, initialize the new one (and load configs from there)
|
// If descriptor is not specified, initialize the new one (and load configs from there)
|
||||||
descriptor ??= new ServiceDescriptor();
|
descriptor ??= new ServiceDescriptor();
|
||||||
|
|
||||||
// Configure the wrapper-internal logging.
|
// Configure the wrapper-internal logging.
|
||||||
// STDIN and STDOUT of the child process will be handled independently.
|
// STDIN and STDOUT of the child process will be handled independently.
|
||||||
InitLoggers(descriptor, isCLIMode);
|
InitLoggers(descriptor, inCliMode);
|
||||||
|
|
||||||
if (isCLIMode) // CLI mode, in-service mode otherwise
|
if (!inCliMode)
|
||||||
{
|
{
|
||||||
Log.Debug("Starting ServiceWrapper in the CLI mode");
|
Log.Debug("Starting WinSW in the service mode");
|
||||||
|
Run(new WrapperService(descriptor));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Get service info for the future use
|
Log.Debug("Starting WinSW in the CLI mode");
|
||||||
Win32Services svc = new WmiRoot().GetCollection<Win32Services>();
|
|
||||||
Win32Service s = svc.Select(descriptor.Id);
|
|
||||||
|
|
||||||
var args = new List<string>(Array.AsReadOnly(_args));
|
if (_args.Length == 0)
|
||||||
if (args[0] == "/redirect")
|
{
|
||||||
|
printHelp();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get service info for the future use
|
||||||
|
Win32Services svc = new WmiRoot().GetCollection<Win32Services>();
|
||||||
|
Win32Service s = svc.Select(descriptor.Id);
|
||||||
|
|
||||||
|
var args = new List<string>(Array.AsReadOnly(_args));
|
||||||
|
if (args[0] == "/redirect")
|
||||||
|
{
|
||||||
|
// Redirect output
|
||||||
|
// 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
|
||||||
|
// accordingly. This in turn limits what the caller can do,
|
||||||
|
// 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 };
|
||||||
|
Console.SetOut(w);
|
||||||
|
Console.SetError(w);
|
||||||
|
|
||||||
|
var handle = f.SafeFileHandle;
|
||||||
|
Kernel32.SetStdHandle(-11, handle); // set stdout
|
||||||
|
Kernel32.SetStdHandle(-12, handle); // set stder
|
||||||
|
|
||||||
|
args = args.GetRange(2, args.Count - 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
args[0] = args[0].ToLower();
|
||||||
|
if (args[0] == "install")
|
||||||
|
{
|
||||||
|
Log.Info("Installing the service with id '" + descriptor.Id + "'");
|
||||||
|
|
||||||
|
// Check if the service exists
|
||||||
|
if (s != null)
|
||||||
{
|
{
|
||||||
// Redirect output
|
Console.WriteLine("Service with id '" + descriptor.Id + "' already exists");
|
||||||
// One might ask why we support this when the caller
|
Console.WriteLine("To install the service, delete the existing one or change service Id in the configuration file");
|
||||||
// can redirect the output easily. The answer is for supporting UAC.
|
throw new Exception("Installation failure: Service with id '" + descriptor.Id + "' already exists");
|
||||||
// On UAC-enabled Windows such as Vista, SCM operation requires
|
|
||||||
// elevated privileges, thus winsw.exe needs to be launched
|
|
||||||
// accordingly. This in turn limits what the caller can do,
|
|
||||||
// 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 };
|
|
||||||
Console.SetOut(w);
|
|
||||||
Console.SetError(w);
|
|
||||||
|
|
||||||
var handle = f.SafeFileHandle;
|
|
||||||
Kernel32.SetStdHandle(-11, handle); // set stdout
|
|
||||||
Kernel32.SetStdHandle(-12, handle); // set stder
|
|
||||||
|
|
||||||
args = args.GetRange(2, args.Count - 2);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
args[0] = args[0].ToLower();
|
string? username = null;
|
||||||
if (args[0] == "install")
|
string? password = null;
|
||||||
|
bool setallowlogonasaserviceright = false; // This variable is very readable.
|
||||||
|
if (args.Count > 1 && args[1] == "/p")
|
||||||
{
|
{
|
||||||
Log.Info("Installing the service with id '" + descriptor.Id + "'");
|
// we expected username/password on stdin
|
||||||
|
Console.Write("Username: ");
|
||||||
// Check if the service exists
|
username = Console.ReadLine();
|
||||||
if (s != null)
|
Console.Write("Password: ");
|
||||||
|
password = ReadPassword();
|
||||||
|
Console.WriteLine();
|
||||||
|
Console.Write("Set Account rights to allow log on as a service (y/n)?: ");
|
||||||
|
var keypressed = Console.ReadKey();
|
||||||
|
Console.WriteLine();
|
||||||
|
if (keypressed.Key == ConsoleKey.Y)
|
||||||
{
|
{
|
||||||
Console.WriteLine("Service with id '" + descriptor.Id + "' already exists");
|
setallowlogonasaserviceright = true;
|
||||||
Console.WriteLine("To install the service, delete the existing one or change service Id in the configuration file");
|
}
|
||||||
throw new Exception("Installation failure: Service with id '" + descriptor.Id + "' already exists");
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (descriptor.HasServiceAccount())
|
||||||
|
{
|
||||||
|
username = descriptor.ServiceAccountUser;
|
||||||
|
password = descriptor.ServiceAccountPassword;
|
||||||
|
setallowlogonasaserviceright = descriptor.AllowServiceAcountLogonRight;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (setallowlogonasaserviceright)
|
||||||
|
{
|
||||||
|
LogonAsAService.AddLogonAsAServiceRight(username!);
|
||||||
|
}
|
||||||
|
|
||||||
|
svc.Create(
|
||||||
|
descriptor.Id,
|
||||||
|
descriptor.Caption,
|
||||||
|
"\"" + descriptor.ExecutablePath + "\"",
|
||||||
|
ServiceType.OwnProcess,
|
||||||
|
ErrorControl.UserNotified,
|
||||||
|
descriptor.StartMode,
|
||||||
|
descriptor.Interactive,
|
||||||
|
username,
|
||||||
|
password,
|
||||||
|
descriptor.ServiceDependencies);
|
||||||
|
|
||||||
|
// update the description
|
||||||
|
/* Somehow this doesn't work, even though it doesn't report an error
|
||||||
|
Win32Service s = svc.Select(d.Id);
|
||||||
|
s.Description = d.Description;
|
||||||
|
s.Commit();
|
||||||
|
*/
|
||||||
|
|
||||||
|
// so using a classic method to set the description. Ugly.
|
||||||
|
Registry.LocalMachine
|
||||||
|
.OpenSubKey("System")
|
||||||
|
.OpenSubKey("CurrentControlSet")
|
||||||
|
.OpenSubKey("Services")
|
||||||
|
.OpenSubKey(descriptor.Id, true)
|
||||||
|
.SetValue("Description", descriptor.Description);
|
||||||
|
|
||||||
|
var actions = descriptor.FailureActions;
|
||||||
|
var isDelayedAutoStart = descriptor.StartMode == StartMode.Automatic && descriptor.DelayedAutoStart;
|
||||||
|
if (actions.Count > 0 || isDelayedAutoStart)
|
||||||
|
{
|
||||||
|
using ServiceManager scm = new ServiceManager();
|
||||||
|
using Service sc = scm.Open(descriptor.Id);
|
||||||
|
|
||||||
|
// Delayed auto start
|
||||||
|
if (isDelayedAutoStart)
|
||||||
|
{
|
||||||
|
sc.SetDelayedAutoStart(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
string? username = null;
|
// Set the failure actions
|
||||||
string? password = null;
|
if (actions.Count > 0)
|
||||||
bool setallowlogonasaserviceright = false; // This variable is very readable.
|
|
||||||
if (args.Count > 1 && args[1] == "/p")
|
|
||||||
{
|
{
|
||||||
// we expected username/password on stdin
|
sc.ChangeConfig(descriptor.ResetFailureAfter, actions);
|
||||||
Console.Write("Username: ");
|
}
|
||||||
username = Console.ReadLine();
|
}
|
||||||
Console.Write("Password: ");
|
|
||||||
password = ReadPassword();
|
return;
|
||||||
Console.WriteLine();
|
}
|
||||||
Console.Write("Set Account rights to allow log on as a service (y/n)?: ");
|
|
||||||
var keypressed = Console.ReadKey();
|
if (args[0] == "uninstall")
|
||||||
Console.WriteLine();
|
{
|
||||||
if (keypressed.Key == ConsoleKey.Y)
|
Log.Info("Uninstalling the service with id '" + descriptor.Id + "'");
|
||||||
{
|
if (s == null)
|
||||||
setallowlogonasaserviceright = true;
|
{
|
||||||
}
|
Log.Warn("The service with id '" + descriptor.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.
|
||||||
|
// So it is just a warning
|
||||||
|
Log.Warn("The service with id '" + descriptor.Id + "' is running. It may be impossible to uninstall it");
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
s.Delete();
|
||||||
|
}
|
||||||
|
catch (WmiException e)
|
||||||
|
{
|
||||||
|
if (e.ErrorCode == ReturnValue.ServiceMarkedForDeletion)
|
||||||
|
{
|
||||||
|
Log.Error("Failed to uninstall the service with id '" + descriptor.Id + "'"
|
||||||
|
+ ". It has been marked for deletion.");
|
||||||
|
|
||||||
|
// TODO: change the default behavior to Error?
|
||||||
|
return; // it's already uninstalled, so consider it a success
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (descriptor.HasServiceAccount())
|
Log.Fatal("Failed to uninstall the service with id '" + descriptor.Id + "'. WMI Error code is '" + e.ErrorCode + "'");
|
||||||
{
|
|
||||||
username = descriptor.ServiceAccountUser;
|
|
||||||
password = descriptor.ServiceAccountPassword;
|
|
||||||
setallowlogonasaserviceright = descriptor.AllowServiceAcountLogonRight;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (setallowlogonasaserviceright)
|
throw e;
|
||||||
{
|
|
||||||
LogonAsAService.AddLogonAsAServiceRight(username!);
|
|
||||||
}
|
|
||||||
|
|
||||||
svc.Create(
|
|
||||||
descriptor.Id,
|
|
||||||
descriptor.Caption,
|
|
||||||
"\"" + descriptor.ExecutablePath + "\"",
|
|
||||||
ServiceType.OwnProcess,
|
|
||||||
ErrorControl.UserNotified,
|
|
||||||
descriptor.StartMode,
|
|
||||||
descriptor.Interactive,
|
|
||||||
username,
|
|
||||||
password,
|
|
||||||
descriptor.ServiceDependencies);
|
|
||||||
|
|
||||||
// update the description
|
|
||||||
/* Somehow this doesn't work, even though it doesn't report an error
|
|
||||||
Win32Service s = svc.Select(d.Id);
|
|
||||||
s.Description = d.Description;
|
|
||||||
s.Commit();
|
|
||||||
*/
|
|
||||||
|
|
||||||
// so using a classic method to set the description. Ugly.
|
|
||||||
Registry.LocalMachine
|
|
||||||
.OpenSubKey("System")
|
|
||||||
.OpenSubKey("CurrentControlSet")
|
|
||||||
.OpenSubKey("Services")
|
|
||||||
.OpenSubKey(descriptor.Id, true)
|
|
||||||
.SetValue("Description", descriptor.Description);
|
|
||||||
|
|
||||||
var actions = descriptor.FailureActions;
|
|
||||||
var isDelayedAutoStart = descriptor.StartMode == StartMode.Automatic && descriptor.DelayedAutoStart;
|
|
||||||
if (actions.Count > 0 || isDelayedAutoStart)
|
|
||||||
{
|
|
||||||
using ServiceManager scm = new ServiceManager();
|
|
||||||
using Service sc = scm.Open(descriptor.Id);
|
|
||||||
|
|
||||||
// Delayed auto start
|
|
||||||
if (isDelayedAutoStart)
|
|
||||||
{
|
|
||||||
sc.SetDelayedAutoStart(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set the failure actions
|
|
||||||
if (actions.Count > 0)
|
|
||||||
{
|
|
||||||
sc.ChangeConfig(descriptor.ResetFailureAfter, actions);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (args[0] == "uninstall")
|
return;
|
||||||
{
|
}
|
||||||
Log.Info("Uninstalling the service with id '" + descriptor.Id + "'");
|
|
||||||
if (s == null)
|
|
||||||
{
|
|
||||||
Log.Warn("The service with id '" + descriptor.Id + "' does not exist. Nothing to uninstall");
|
|
||||||
return; // there's no such service, so consider it already uninstalled
|
|
||||||
}
|
|
||||||
|
|
||||||
if (s.Started)
|
if (args[0] == "start")
|
||||||
{
|
{
|
||||||
// We could fail the opeartion here, but it would be an incompatible change.
|
Log.Info("Starting the service with id '" + descriptor.Id + "'");
|
||||||
// So it is just a warning
|
if (s == null)
|
||||||
Log.Warn("The service with id '" + descriptor.Id + "' is running. It may be impossible to uninstall it");
|
ThrowNoSuchService();
|
||||||
}
|
|
||||||
|
|
||||||
try
|
s.StartService();
|
||||||
{
|
return;
|
||||||
s.Delete();
|
}
|
||||||
}
|
|
||||||
catch (WmiException e)
|
|
||||||
{
|
|
||||||
if (e.ErrorCode == ReturnValue.ServiceMarkedForDeletion)
|
|
||||||
{
|
|
||||||
Log.Error("Failed to uninstall the service with id '" + descriptor.Id + "'"
|
|
||||||
+ ". It has been marked for deletion.");
|
|
||||||
|
|
||||||
// TODO: change the default behavior to Error?
|
if (args[0] == "stop")
|
||||||
return; // it's already uninstalled, so consider it a success
|
{
|
||||||
}
|
Log.Info("Stopping the service with id '" + descriptor.Id + "'");
|
||||||
else
|
if (s == null)
|
||||||
{
|
ThrowNoSuchService();
|
||||||
Log.Fatal("Failed to uninstall the service with id '" + descriptor.Id + "'. WMI Error code is '" + e.ErrorCode + "'");
|
|
||||||
}
|
|
||||||
|
|
||||||
throw e;
|
s.StopService();
|
||||||
}
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
return;
|
if (args[0] == "restart")
|
||||||
}
|
{
|
||||||
|
Log.Info("Restarting the service with id '" + descriptor.Id + "'");
|
||||||
if (args[0] == "start")
|
if (s == null)
|
||||||
{
|
ThrowNoSuchService();
|
||||||
Log.Info("Starting the service with id '" + descriptor.Id + "'");
|
|
||||||
if (s == null)
|
|
||||||
ThrowNoSuchService();
|
|
||||||
|
|
||||||
s.StartService();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (args[0] == "stop")
|
|
||||||
{
|
|
||||||
Log.Info("Stopping the service with id '" + descriptor.Id + "'");
|
|
||||||
if (s == null)
|
|
||||||
ThrowNoSuchService();
|
|
||||||
|
|
||||||
|
if (s.Started)
|
||||||
s.StopService();
|
s.StopService();
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (args[0] == "restart")
|
while (s.Started)
|
||||||
{
|
{
|
||||||
Log.Info("Restarting the service with id '" + descriptor.Id + "'");
|
|
||||||
if (s == null)
|
|
||||||
ThrowNoSuchService();
|
|
||||||
|
|
||||||
if (s.Started)
|
|
||||||
s.StopService();
|
|
||||||
|
|
||||||
while (s.Started)
|
|
||||||
{
|
|
||||||
Thread.Sleep(1000);
|
|
||||||
s = svc.Select(descriptor.Id);
|
|
||||||
}
|
|
||||||
|
|
||||||
s.StartService();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (args[0] == "restart!")
|
|
||||||
{
|
|
||||||
Log.Info("Restarting the service with id '" + descriptor.Id + "'");
|
|
||||||
|
|
||||||
// run restart from another process group. see README.md for why this is useful.
|
|
||||||
|
|
||||||
STARTUPINFO si = default;
|
|
||||||
bool result = Kernel32.CreateProcess(null, descriptor.ExecutablePath + " restart", IntPtr.Zero, IntPtr.Zero, false, 0x200/*CREATE_NEW_PROCESS_GROUP*/, IntPtr.Zero, null, ref si, out _);
|
|
||||||
if (!result)
|
|
||||||
{
|
|
||||||
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 '" + descriptor.Id + "'");
|
|
||||||
if (s == null)
|
|
||||||
Console.WriteLine("NonExistent");
|
|
||||||
else if (s.Started)
|
|
||||||
Console.WriteLine("Started");
|
|
||||||
else
|
|
||||||
Console.WriteLine("Stopped");
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (args[0] == "test")
|
|
||||||
{
|
|
||||||
WrapperService wsvc = new WrapperService(descriptor);
|
|
||||||
wsvc.OnStart(args.ToArray());
|
|
||||||
Thread.Sleep(1000);
|
Thread.Sleep(1000);
|
||||||
wsvc.OnStop();
|
s = svc.Select(descriptor.Id);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (args[0] == "testwait")
|
s.StartService();
|
||||||
{
|
return;
|
||||||
WrapperService wsvc = new WrapperService(descriptor);
|
|
||||||
wsvc.OnStart(args.ToArray());
|
|
||||||
Console.WriteLine("Press any key to stop the service...");
|
|
||||||
Console.Read();
|
|
||||||
wsvc.OnStop();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
|
if (args[0] == "restart!")
|
||||||
{
|
{
|
||||||
Log.Info("Starting ServiceWrapper in the service mode");
|
Log.Info("Restarting the service with id '" + descriptor.Id + "'");
|
||||||
|
|
||||||
|
// run restart from another process group. see README.md for why this is useful.
|
||||||
|
|
||||||
|
STARTUPINFO si = default;
|
||||||
|
bool result = Kernel32.CreateProcess(null, descriptor.ExecutablePath + " restart", IntPtr.Zero, IntPtr.Zero, false, 0x200/*CREATE_NEW_PROCESS_GROUP*/, IntPtr.Zero, null, ref si, out _);
|
||||||
|
if (!result)
|
||||||
|
{
|
||||||
|
throw new Exception("Failed to invoke restart: " + Marshal.GetLastWin32Error());
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Run(new WrapperService(descriptor));
|
if (args[0] == "status")
|
||||||
|
{
|
||||||
|
Log.Debug("User requested the status of the process with id '" + descriptor.Id + "'");
|
||||||
|
if (s == null)
|
||||||
|
Console.WriteLine("NonExistent");
|
||||||
|
else if (s.Started)
|
||||||
|
Console.WriteLine("Started");
|
||||||
|
else
|
||||||
|
Console.WriteLine("Stopped");
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (args[0] == "test")
|
||||||
|
{
|
||||||
|
WrapperService wsvc = new WrapperService(descriptor);
|
||||||
|
wsvc.OnStart(args.ToArray());
|
||||||
|
Thread.Sleep(1000);
|
||||||
|
wsvc.OnStop();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (args[0] == "testwait")
|
||||||
|
{
|
||||||
|
WrapperService wsvc = new WrapperService(descriptor);
|
||||||
|
wsvc.OnStart(args.ToArray());
|
||||||
|
Console.WriteLine("Press any key to stop the service...");
|
||||||
|
Console.Read();
|
||||||
|
wsvc.OnStop();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
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]);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void InitLoggers(ServiceDescriptor d, bool enableCLILogging)
|
private static void InitLoggers(ServiceDescriptor d, bool enableCLILogging)
|
||||||
|
|
Loading…
Reference in New Issue