From 22c3043862a53f773a3cd95f913a54a1d3d62479 Mon Sep 17 00:00:00 2001 From: NextTurn <45985406+NextTurn@users.noreply.github.com> Date: Mon, 26 Nov 2018 00:00:00 +0800 Subject: [PATCH] Introduce command exception --- src/WinSW.Core/CommandException.cs | 22 ++++++++++++++ src/WinSW.Core/Native/Security.cs | 5 ++-- src/WinSW.Core/Native/Service.cs | 37 ++++++++++++------------ src/WinSW.Core/Native/Throw.cs | 17 ++++++----- src/WinSW/Program.cs | 46 ++++++++++++++++++------------ 5 files changed, 80 insertions(+), 47 deletions(-) create mode 100644 src/WinSW.Core/CommandException.cs diff --git a/src/WinSW.Core/CommandException.cs b/src/WinSW.Core/CommandException.cs new file mode 100644 index 0000000..5bb9514 --- /dev/null +++ b/src/WinSW.Core/CommandException.cs @@ -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) + { + } + } +} diff --git a/src/WinSW.Core/Native/Security.cs b/src/WinSW.Core/Native/Security.cs index a151aa6..d91e466 100644 --- a/src/WinSW.Core/Native/Security.cs +++ b/src/WinSW.Core/Native/Security.cs @@ -7,6 +7,7 @@ namespace WinSW.Native { internal static class Security { + /// /// internal static void AddServiceLogonRight(string userName) { @@ -23,7 +24,7 @@ namespace WinSW.Native } } - /// + /// private static IntPtr GetAccountSid(string accountName) { int sidSize = 0; @@ -43,7 +44,7 @@ namespace WinSW.Native { 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; diff --git a/src/WinSW.Core/Native/Service.cs b/src/WinSW.Core/Native/Service.cs index aa2f988..85f4e19 100644 --- a/src/WinSW.Core/Native/Service.cs +++ b/src/WinSW.Core/Native/Service.cs @@ -1,5 +1,4 @@ using System; -using System.ComponentModel; using System.Security.AccessControl; using System.ServiceProcess; using System.Text; @@ -55,19 +54,19 @@ namespace WinSW.Native private ServiceManager(IntPtr handle) => this.handle = handle; - /// + /// internal static ServiceManager Open() { IntPtr handle = OpenSCManager(null, null, ServiceManagerAccess.ALL_ACCESS); 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); } - /// + /// internal Service CreateService( string serviceName, string displayName, @@ -111,19 +110,19 @@ namespace WinSW.Native password); if (handle == IntPtr.Zero) { - Throw.Win32Exception("Failed to create service."); + Throw.Command.Win32Exception("Failed to create service."); } return new Service(handle); } - /// + /// internal Service OpenService(string serviceName) { IntPtr serviceHandle = ServiceApis.OpenService(this.handle, serviceName, ServiceAccess.ALL_ACCESS); if (serviceHandle == IntPtr.Zero) { - Throw.Win32Exception("Failed to open the service."); + Throw.Command.Win32Exception("Failed to open the service."); } return new Service(serviceHandle); @@ -158,30 +157,30 @@ namespace WinSW.Native internal Service(IntPtr handle) => this.handle = handle; - /// + /// internal ServiceControllerStatus Status { get { 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; } } - /// + /// internal void Delete() { if (!DeleteService(this.handle)) { - Throw.Win32Exception("Failed to delete service."); + Throw.Command.Win32Exception("Failed to delete service."); } } - /// + /// internal void SetDescription(string description) { if (!ChangeServiceConfig2( @@ -189,11 +188,11 @@ namespace WinSW.Native ServiceConfigInfoLevels.DESCRIPTION, new SERVICE_DESCRIPTION { Description = description })) { - Throw.Win32Exception("Failed to configure the description."); + Throw.Command.Win32Exception("Failed to configure the description."); } } - /// + /// internal unsafe void SetFailureActions(TimeSpan failureResetPeriod, SC_ACTION[] actions) { fixed (SC_ACTION* actionsPtr = actions) @@ -210,12 +209,12 @@ namespace WinSW.Native Actions = actionsPtr, })) { - Throw.Win32Exception("Failed to configure the failure actions."); + Throw.Command.Win32Exception("Failed to configure the failure actions."); } } } - /// + /// internal void SetDelayedAutoStart(bool enabled) { if (!ChangeServiceConfig2( @@ -223,18 +222,18 @@ namespace WinSW.Native ServiceConfigInfoLevels.DELAYED_AUTO_START_INFO, 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."); } } - /// + /// internal void SetSecurityDescriptor(RawSecurityDescriptor securityDescriptor) { byte[] securityDescriptorBytes = new byte[securityDescriptor.BinaryLength]; securityDescriptor.GetBinaryForm(securityDescriptorBytes, 0); if (!SetServiceObjectSecurity(this.handle, SecurityInfos.DiscretionaryAcl, securityDescriptorBytes)) { - Throw.Win32Exception("Failed to configure the security descriptor."); + Throw.Command.Win32Exception("Failed to configure the security descriptor."); } } diff --git a/src/WinSW.Core/Native/Throw.cs b/src/WinSW.Core/Native/Throw.cs index 91cd3a4..b6125fa 100644 --- a/src/WinSW.Core/Native/Throw.cs +++ b/src/WinSW.Core/Native/Throw.cs @@ -6,14 +6,17 @@ namespace WinSW.Native { internal static class Throw { - /// - [MethodImpl(MethodImplOptions.NoInlining)] - internal static void Win32Exception(string message) + internal static class Command { - Win32Exception inner = new Win32Exception(); - Debug.Assert(inner.NativeErrorCode != 0); - Debug.Assert(message.EndsWith(".")); - throw new Win32Exception(inner.NativeErrorCode, message + ' ' + inner.Message); + /// + [MethodImpl(MethodImplOptions.NoInlining)] + internal static void Win32Exception(string message) + { + Win32Exception inner = new Win32Exception(); + Debug.Assert(inner.NativeErrorCode != 0); + Debug.Assert(message.EndsWith(".")); + throw new CommandException(message + ' ' + inner.Message, inner); + } } } } diff --git a/src/WinSW/Program.cs b/src/WinSW/Program.cs index 2d70599..ead3ba0 100644 --- a/src/WinSW/Program.cs +++ b/src/WinSW/Program.cs @@ -40,6 +40,20 @@ namespace WinSW Console.Error.WriteLine(message); 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) { string message = e.Message; @@ -47,13 +61,6 @@ namespace WinSW Console.Error.WriteLine(message); return e.NativeErrorCode; } - catch (UserException e) - { - string message = e.Message; - Log.Fatal(message, e); - Console.Error.WriteLine(message); - return -1; - } catch (Exception e) { Log.Fatal("Unhandled exception", e); @@ -186,7 +193,7 @@ namespace WinSW { 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"); - 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; @@ -292,7 +299,7 @@ namespace WinSW inBuffer, 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 @@ -348,7 +355,7 @@ namespace WinSW password, ref passwordLength)) { - Throw.Win32Exception("Failed to unpack auth buffer."); + Throw.Command.Win32Exception("Failed to unpack auth buffer."); } } finally @@ -404,9 +411,9 @@ namespace WinSW 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: 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 default: - Log.Fatal("Failed to uninstall the service with id '" + descriptor.Id + "'. WMI Error code is '" + e.ErrorCode + "'"); - throw e; + Log.Fatal("Failed to uninstall the service with id '" + descriptor.Id + "'. Error code is '" + inner.NativeErrorCode + "'"); + 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 _)) { - throw new Exception("Failed to invoke restart: " + Marshal.GetLastWin32Error()); + throw new CommandException("Failed to invoke restart: " + Marshal.GetLastWin32Error()); } } @@ -668,10 +675,10 @@ namespace WinSW } } - /// + /// [DoesNotReturn] [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) { @@ -733,12 +740,13 @@ namespace WinSW appenders.ToArray()); } + /// internal static bool IsProcessElevated() { IntPtr process = ProcessApis.GetCurrentProcess(); 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 @@ -752,7 +760,7 @@ namespace WinSW sizeof(SecurityApis.TOKEN_ELEVATION), out _)) { - Throw.Win32Exception("Failed to get token information."); + Throw.Command.Win32Exception("Failed to get token information."); } return elevation.TokenIsElevated != 0;