mirror of https://github.com/winsw/winsw
Introduce command exception
parent
0c393e0eec
commit
22c3043862
|
@ -0,0 +1,22 @@
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace WinSW
|
||||||
|
{
|
||||||
|
internal sealed class CommandException : Exception
|
||||||
|
{
|
||||||
|
internal CommandException(Exception inner)
|
||||||
|
: base(inner.Message, inner)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
internal CommandException(string message)
|
||||||
|
: base(message)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
internal CommandException(string message, Exception inner)
|
||||||
|
: base(message, inner)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -7,6 +7,7 @@ namespace WinSW.Native
|
||||||
{
|
{
|
||||||
internal static class Security
|
internal static class Security
|
||||||
{
|
{
|
||||||
|
/// <exception cref="CommandException" />
|
||||||
/// <exception cref="Win32Exception" />
|
/// <exception cref="Win32Exception" />
|
||||||
internal static void AddServiceLogonRight(string userName)
|
internal static void AddServiceLogonRight(string userName)
|
||||||
{
|
{
|
||||||
|
@ -23,7 +24,7 @@ namespace WinSW.Native
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <exception cref="Win32Exception" />
|
/// <exception cref="CommandException" />
|
||||||
private static IntPtr GetAccountSid(string accountName)
|
private static IntPtr GetAccountSid(string accountName)
|
||||||
{
|
{
|
||||||
int sidSize = 0;
|
int sidSize = 0;
|
||||||
|
@ -43,7 +44,7 @@ namespace WinSW.Native
|
||||||
{
|
{
|
||||||
if (!LookupAccountName(null, accountName, sid, ref sidSize, domainName, ref domainNameLength, out _))
|
if (!LookupAccountName(null, accountName, sid, ref sidSize, domainName, ref domainNameLength, out _))
|
||||||
{
|
{
|
||||||
Throw.Win32Exception("Failed to find the account.");
|
Throw.Command.Win32Exception("Failed to find the account.");
|
||||||
}
|
}
|
||||||
|
|
||||||
return sid;
|
return sid;
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
using System;
|
using System;
|
||||||
using System.ComponentModel;
|
|
||||||
using System.Security.AccessControl;
|
using System.Security.AccessControl;
|
||||||
using System.ServiceProcess;
|
using System.ServiceProcess;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
@ -55,19 +54,19 @@ namespace WinSW.Native
|
||||||
|
|
||||||
private ServiceManager(IntPtr handle) => this.handle = handle;
|
private ServiceManager(IntPtr handle) => this.handle = handle;
|
||||||
|
|
||||||
/// <exception cref="Win32Exception" />
|
/// <exception cref="CommandException" />
|
||||||
internal static ServiceManager Open()
|
internal static ServiceManager Open()
|
||||||
{
|
{
|
||||||
IntPtr handle = OpenSCManager(null, null, ServiceManagerAccess.ALL_ACCESS);
|
IntPtr handle = OpenSCManager(null, null, ServiceManagerAccess.ALL_ACCESS);
|
||||||
if (handle == IntPtr.Zero)
|
if (handle == IntPtr.Zero)
|
||||||
{
|
{
|
||||||
Throw.Win32Exception("Failed to open the service control manager database.");
|
Throw.Command.Win32Exception("Failed to open the service control manager database.");
|
||||||
}
|
}
|
||||||
|
|
||||||
return new ServiceManager(handle);
|
return new ServiceManager(handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <exception cref="Win32Exception" />
|
/// <exception cref="CommandException" />
|
||||||
internal Service CreateService(
|
internal Service CreateService(
|
||||||
string serviceName,
|
string serviceName,
|
||||||
string displayName,
|
string displayName,
|
||||||
|
@ -111,19 +110,19 @@ namespace WinSW.Native
|
||||||
password);
|
password);
|
||||||
if (handle == IntPtr.Zero)
|
if (handle == IntPtr.Zero)
|
||||||
{
|
{
|
||||||
Throw.Win32Exception("Failed to create service.");
|
Throw.Command.Win32Exception("Failed to create service.");
|
||||||
}
|
}
|
||||||
|
|
||||||
return new Service(handle);
|
return new Service(handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <exception cref="Win32Exception" />
|
/// <exception cref="CommandException" />
|
||||||
internal Service OpenService(string serviceName)
|
internal Service OpenService(string serviceName)
|
||||||
{
|
{
|
||||||
IntPtr serviceHandle = ServiceApis.OpenService(this.handle, serviceName, ServiceAccess.ALL_ACCESS);
|
IntPtr serviceHandle = ServiceApis.OpenService(this.handle, serviceName, ServiceAccess.ALL_ACCESS);
|
||||||
if (serviceHandle == IntPtr.Zero)
|
if (serviceHandle == IntPtr.Zero)
|
||||||
{
|
{
|
||||||
Throw.Win32Exception("Failed to open the service.");
|
Throw.Command.Win32Exception("Failed to open the service.");
|
||||||
}
|
}
|
||||||
|
|
||||||
return new Service(serviceHandle);
|
return new Service(serviceHandle);
|
||||||
|
@ -158,30 +157,30 @@ namespace WinSW.Native
|
||||||
|
|
||||||
internal Service(IntPtr handle) => this.handle = handle;
|
internal Service(IntPtr handle) => this.handle = handle;
|
||||||
|
|
||||||
/// <exception cref="Win32Exception" />
|
/// <exception cref="CommandException" />
|
||||||
internal ServiceControllerStatus Status
|
internal ServiceControllerStatus Status
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
if (!QueryServiceStatus(this.handle, out SERVICE_STATUS status))
|
if (!QueryServiceStatus(this.handle, out SERVICE_STATUS status))
|
||||||
{
|
{
|
||||||
Throw.Win32Exception("Failed to query service status.");
|
Throw.Command.Win32Exception("Failed to query service status.");
|
||||||
}
|
}
|
||||||
|
|
||||||
return status.CurrentState;
|
return status.CurrentState;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <exception cref="Win32Exception" />
|
/// <exception cref="CommandException" />
|
||||||
internal void Delete()
|
internal void Delete()
|
||||||
{
|
{
|
||||||
if (!DeleteService(this.handle))
|
if (!DeleteService(this.handle))
|
||||||
{
|
{
|
||||||
Throw.Win32Exception("Failed to delete service.");
|
Throw.Command.Win32Exception("Failed to delete service.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <exception cref="Win32Exception" />
|
/// <exception cref="CommandException" />
|
||||||
internal void SetDescription(string description)
|
internal void SetDescription(string description)
|
||||||
{
|
{
|
||||||
if (!ChangeServiceConfig2(
|
if (!ChangeServiceConfig2(
|
||||||
|
@ -189,11 +188,11 @@ namespace WinSW.Native
|
||||||
ServiceConfigInfoLevels.DESCRIPTION,
|
ServiceConfigInfoLevels.DESCRIPTION,
|
||||||
new SERVICE_DESCRIPTION { Description = description }))
|
new SERVICE_DESCRIPTION { Description = description }))
|
||||||
{
|
{
|
||||||
Throw.Win32Exception("Failed to configure the description.");
|
Throw.Command.Win32Exception("Failed to configure the description.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <exception cref="Win32Exception" />
|
/// <exception cref="CommandException" />
|
||||||
internal unsafe void SetFailureActions(TimeSpan failureResetPeriod, SC_ACTION[] actions)
|
internal unsafe void SetFailureActions(TimeSpan failureResetPeriod, SC_ACTION[] actions)
|
||||||
{
|
{
|
||||||
fixed (SC_ACTION* actionsPtr = actions)
|
fixed (SC_ACTION* actionsPtr = actions)
|
||||||
|
@ -210,12 +209,12 @@ namespace WinSW.Native
|
||||||
Actions = actionsPtr,
|
Actions = actionsPtr,
|
||||||
}))
|
}))
|
||||||
{
|
{
|
||||||
Throw.Win32Exception("Failed to configure the failure actions.");
|
Throw.Command.Win32Exception("Failed to configure the failure actions.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <exception cref="Win32Exception" />
|
/// <exception cref="CommandException" />
|
||||||
internal void SetDelayedAutoStart(bool enabled)
|
internal void SetDelayedAutoStart(bool enabled)
|
||||||
{
|
{
|
||||||
if (!ChangeServiceConfig2(
|
if (!ChangeServiceConfig2(
|
||||||
|
@ -223,18 +222,18 @@ namespace WinSW.Native
|
||||||
ServiceConfigInfoLevels.DELAYED_AUTO_START_INFO,
|
ServiceConfigInfoLevels.DELAYED_AUTO_START_INFO,
|
||||||
new SERVICE_DELAYED_AUTO_START_INFO { DelayedAutostart = enabled }))
|
new SERVICE_DELAYED_AUTO_START_INFO { DelayedAutostart = enabled }))
|
||||||
{
|
{
|
||||||
Throw.Win32Exception("Failed to configure the delayed auto-start setting.");
|
Throw.Command.Win32Exception("Failed to configure the delayed auto-start setting.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <exception cref="Win32Exception" />
|
/// <exception cref="CommandException" />
|
||||||
internal void SetSecurityDescriptor(RawSecurityDescriptor securityDescriptor)
|
internal void SetSecurityDescriptor(RawSecurityDescriptor securityDescriptor)
|
||||||
{
|
{
|
||||||
byte[] securityDescriptorBytes = new byte[securityDescriptor.BinaryLength];
|
byte[] securityDescriptorBytes = new byte[securityDescriptor.BinaryLength];
|
||||||
securityDescriptor.GetBinaryForm(securityDescriptorBytes, 0);
|
securityDescriptor.GetBinaryForm(securityDescriptorBytes, 0);
|
||||||
if (!SetServiceObjectSecurity(this.handle, SecurityInfos.DiscretionaryAcl, securityDescriptorBytes))
|
if (!SetServiceObjectSecurity(this.handle, SecurityInfos.DiscretionaryAcl, securityDescriptorBytes))
|
||||||
{
|
{
|
||||||
Throw.Win32Exception("Failed to configure the security descriptor.");
|
Throw.Command.Win32Exception("Failed to configure the security descriptor.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,14 +6,17 @@ namespace WinSW.Native
|
||||||
{
|
{
|
||||||
internal static class Throw
|
internal static class Throw
|
||||||
{
|
{
|
||||||
/// <exception cref="System.ComponentModel.Win32Exception" />
|
internal static class Command
|
||||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
|
||||||
internal static void Win32Exception(string message)
|
|
||||||
{
|
{
|
||||||
Win32Exception inner = new Win32Exception();
|
/// <exception cref="CommandException" />
|
||||||
Debug.Assert(inner.NativeErrorCode != 0);
|
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||||
Debug.Assert(message.EndsWith("."));
|
internal static void Win32Exception(string message)
|
||||||
throw new Win32Exception(inner.NativeErrorCode, message + ' ' + inner.Message);
|
{
|
||||||
|
Win32Exception inner = new Win32Exception();
|
||||||
|
Debug.Assert(inner.NativeErrorCode != 0);
|
||||||
|
Debug.Assert(message.EndsWith("."));
|
||||||
|
throw new CommandException(message + ' ' + inner.Message, inner);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,6 +40,20 @@ namespace WinSW
|
||||||
Console.Error.WriteLine(message);
|
Console.Error.WriteLine(message);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
catch (CommandException e)
|
||||||
|
{
|
||||||
|
string message = e.Message;
|
||||||
|
Log.Fatal(message);
|
||||||
|
Console.Error.WriteLine(message);
|
||||||
|
return e.InnerException is Win32Exception inner ? inner.NativeErrorCode : -1;
|
||||||
|
}
|
||||||
|
catch (InvalidOperationException e) when (e.InnerException is Win32Exception inner)
|
||||||
|
{
|
||||||
|
string message = e.Message;
|
||||||
|
Log.Fatal(message, e);
|
||||||
|
Console.Error.WriteLine(message);
|
||||||
|
return inner.NativeErrorCode;
|
||||||
|
}
|
||||||
catch (Win32Exception e)
|
catch (Win32Exception e)
|
||||||
{
|
{
|
||||||
string message = e.Message;
|
string message = e.Message;
|
||||||
|
@ -47,13 +61,6 @@ namespace WinSW
|
||||||
Console.Error.WriteLine(message);
|
Console.Error.WriteLine(message);
|
||||||
return e.NativeErrorCode;
|
return e.NativeErrorCode;
|
||||||
}
|
}
|
||||||
catch (UserException e)
|
|
||||||
{
|
|
||||||
string message = e.Message;
|
|
||||||
Log.Fatal(message, e);
|
|
||||||
Console.Error.WriteLine(message);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
Log.Fatal("Unhandled exception", e);
|
Log.Fatal("Unhandled exception", e);
|
||||||
|
@ -186,7 +193,7 @@ namespace WinSW
|
||||||
{
|
{
|
||||||
Console.WriteLine("Service with id '" + descriptor.Id + "' already exists");
|
Console.WriteLine("Service with id '" + descriptor.Id + "' already exists");
|
||||||
Console.WriteLine("To install the service, delete the existing one or change service Id in the configuration file");
|
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");
|
throw new CommandException("Installation failure: Service with id '" + descriptor.Id + "' already exists");
|
||||||
}
|
}
|
||||||
|
|
||||||
string? username = null;
|
string? username = null;
|
||||||
|
@ -292,7 +299,7 @@ namespace WinSW
|
||||||
inBuffer,
|
inBuffer,
|
||||||
ref inBufferSize))
|
ref inBufferSize))
|
||||||
{
|
{
|
||||||
Throw.Win32Exception("Failed to pack auth buffer.");
|
Throw.Command.Win32Exception("Failed to pack auth buffer.");
|
||||||
}
|
}
|
||||||
|
|
||||||
CredentialApis.CREDUI_INFO info = new CredentialApis.CREDUI_INFO
|
CredentialApis.CREDUI_INFO info = new CredentialApis.CREDUI_INFO
|
||||||
|
@ -348,7 +355,7 @@ namespace WinSW
|
||||||
password,
|
password,
|
||||||
ref passwordLength))
|
ref passwordLength))
|
||||||
{
|
{
|
||||||
Throw.Win32Exception("Failed to unpack auth buffer.");
|
Throw.Command.Win32Exception("Failed to unpack auth buffer.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
|
@ -404,9 +411,9 @@ namespace WinSW
|
||||||
|
|
||||||
sc.Delete();
|
sc.Delete();
|
||||||
}
|
}
|
||||||
catch (Win32Exception e)
|
catch (CommandException e) when (e.InnerException is Win32Exception inner)
|
||||||
{
|
{
|
||||||
switch (e.NativeErrorCode)
|
switch (inner.NativeErrorCode)
|
||||||
{
|
{
|
||||||
case Errors.ERROR_SERVICE_DOES_NOT_EXIST:
|
case Errors.ERROR_SERVICE_DOES_NOT_EXIST:
|
||||||
Log.Warn("The service with id '" + descriptor.Id + "' does not exist. Nothing to uninstall");
|
Log.Warn("The service with id '" + descriptor.Id + "' does not exist. Nothing to uninstall");
|
||||||
|
@ -420,8 +427,8 @@ namespace WinSW
|
||||||
break; // it's already uninstalled, so consider it a success
|
break; // it's already uninstalled, so consider it a success
|
||||||
|
|
||||||
default:
|
default:
|
||||||
Log.Fatal("Failed to uninstall the service with id '" + descriptor.Id + "'. WMI Error code is '" + e.ErrorCode + "'");
|
Log.Fatal("Failed to uninstall the service with id '" + descriptor.Id + "'. Error code is '" + inner.NativeErrorCode + "'");
|
||||||
throw e;
|
throw;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -588,7 +595,7 @@ namespace WinSW
|
||||||
|
|
||||||
if (!ProcessApis.CreateProcess(null, descriptor.ExecutablePath + " restart", IntPtr.Zero, IntPtr.Zero, false, ProcessApis.CREATE_NEW_PROCESS_GROUP, IntPtr.Zero, null, default, out _))
|
if (!ProcessApis.CreateProcess(null, descriptor.ExecutablePath + " restart", IntPtr.Zero, IntPtr.Zero, false, ProcessApis.CREATE_NEW_PROCESS_GROUP, IntPtr.Zero, null, default, out _))
|
||||||
{
|
{
|
||||||
throw new Exception("Failed to invoke restart: " + Marshal.GetLastWin32Error());
|
throw new CommandException("Failed to invoke restart: " + Marshal.GetLastWin32Error());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -668,10 +675,10 @@ namespace WinSW
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <exception cref="UserException" />
|
/// <exception cref="CommandException" />
|
||||||
[DoesNotReturn]
|
[DoesNotReturn]
|
||||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||||
private static void ThrowNoSuchService(Win32Exception inner) => throw new UserException(null, inner);
|
private static void ThrowNoSuchService(Win32Exception inner) => throw new CommandException(inner);
|
||||||
|
|
||||||
private static void InitLoggers(ServiceDescriptor descriptor, bool enableConsoleLogging)
|
private static void InitLoggers(ServiceDescriptor descriptor, bool enableConsoleLogging)
|
||||||
{
|
{
|
||||||
|
@ -733,12 +740,13 @@ namespace WinSW
|
||||||
appenders.ToArray());
|
appenders.ToArray());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <exception cref="CommandException" />
|
||||||
internal static bool IsProcessElevated()
|
internal static bool IsProcessElevated()
|
||||||
{
|
{
|
||||||
IntPtr process = ProcessApis.GetCurrentProcess();
|
IntPtr process = ProcessApis.GetCurrentProcess();
|
||||||
if (!ProcessApis.OpenProcessToken(process, TokenAccessLevels.Read, out IntPtr token))
|
if (!ProcessApis.OpenProcessToken(process, TokenAccessLevels.Read, out IntPtr token))
|
||||||
{
|
{
|
||||||
Throw.Win32Exception("Failed to open process token.");
|
Throw.Command.Win32Exception("Failed to open process token.");
|
||||||
}
|
}
|
||||||
|
|
||||||
try
|
try
|
||||||
|
@ -752,7 +760,7 @@ namespace WinSW
|
||||||
sizeof(SecurityApis.TOKEN_ELEVATION),
|
sizeof(SecurityApis.TOKEN_ELEVATION),
|
||||||
out _))
|
out _))
|
||||||
{
|
{
|
||||||
Throw.Win32Exception("Failed to get token information.");
|
Throw.Command.Win32Exception("Failed to get token information.");
|
||||||
}
|
}
|
||||||
|
|
||||||
return elevation.TokenIsElevated != 0;
|
return elevation.TokenIsElevated != 0;
|
||||||
|
|
Loading…
Reference in New Issue