public void ChangeConfig(TimeSpan failureResetPeriod, List <SC_ACTION> actions) { SERVICE_FAILURE_ACTIONS sfa = new SERVICE_FAILURE_ACTIONS { dwResetPeriod = (int)failureResetPeriod.TotalSeconds, lpRebootMsg = "", lpCommand = "" }; // delete message // delete the command to run int len = Marshal.SizeOf(typeof(SC_ACTION)); sfa.cActions = actions.Count; sfa.lpsaActions = Marshal.AllocHGlobal(len * actions.Count); try { for (int i = 0; i < actions.Count; i++) { Marshal.StructureToPtr(actions[i], new IntPtr(sfa.lpsaActions.ToInt64() + i * len), false); } if (!Advapi32.ChangeServiceConfig2(Handle, SERVICE_CONFIG_INFOLEVEL.SERVICE_CONFIG_FAILURE_ACTIONS, ref sfa)) { throw new Exception("Failed to change the failure actions", new Win32Exception()); } } finally { Marshal.FreeHGlobal(sfa.lpsaActions); } }
public void Dispose() { if (Handle != IntPtr.Zero) { Advapi32.CloseServiceHandle(Handle); } Handle = IntPtr.Zero; }
public ServiceManager() { _handle = Advapi32.OpenSCManager(null, null, (uint)SCM_ACCESS.SC_MANAGER_ALL_ACCESS); if (_handle == IntPtr.Zero) { throw new Exception(String.Format("Error connecting to Service Control Manager. Error provided was: 0x{0:X}", Marshal.GetLastWin32Error())); } }
private void SignalShutdownComplete() { IntPtr handle = ServiceHandle; _wrapperServiceStatus.checkPoint++; // WriteEvent("SignalShutdownComplete " + wrapperServiceStatus.checkPoint + ":" + wrapperServiceStatus.waitHint); _wrapperServiceStatus.currentState = (int)State.SERVICE_STOPPED; Advapi32.SetServiceStatus(handle, ref _wrapperServiceStatus); }
private void SignalShutdownPending() { IntPtr handle = ServiceHandle; _wrapperServiceStatus.checkPoint++; _wrapperServiceStatus.waitHint = _descriptor.WaitHint.Milliseconds; // WriteEvent("SignalShutdownPending " + wrapperServiceStatus.checkPoint + ":" + wrapperServiceStatus.waitHint); _wrapperServiceStatus.currentState = (int)State.SERVICE_STOP_PENDING; Advapi32.SetServiceStatus(handle, ref _wrapperServiceStatus); }
public Service Open(string serviceName) { IntPtr svcHandle = Advapi32.OpenService(_handle, serviceName, (int)SERVICE_ACCESS.SERVICE_ALL_ACCESS); if (svcHandle == IntPtr.Zero) { throw new Exception(String.Format("Error opening service for modifying. Error returned was: 0x{0:X}", Marshal.GetLastWin32Error())); } return(new Service(svcHandle)); }
private void SignalShutdownPending() { int effectiveWaitHint; if (_descriptor.WaitHint.TotalMilliseconds > int.MaxValue) { Log.Warn("The requested WaitHint value (" + _descriptor.WaitHint.TotalMilliseconds + " ms) is greater that the max value " + int.MaxValue + ". The value will be truncated"); effectiveWaitHint = int.MaxValue; } else { effectiveWaitHint = (int)_descriptor.WaitHint.TotalMilliseconds; } IntPtr handle = ServiceHandle; _wrapperServiceStatus.checkPoint++; _wrapperServiceStatus.waitHint = effectiveWaitHint; // WriteEvent("SignalShutdownPending " + wrapperServiceStatus.checkPoint + ":" + wrapperServiceStatus.waitHint); _wrapperServiceStatus.currentState = (int)State.SERVICE_STOP_PENDING; Advapi32.SetServiceStatus(handle, _wrapperServiceStatus); }
// ReSharper disable once InconsistentNaming /// <summary> /// Runs the wrapper. /// </summary> /// <param name="_args">Arguments.</param> /// <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 inCliMode = _args.Length > 0; // If descriptor is not specified, initialize the new one (and load configs from there) descriptor ??= new ServiceDescriptor(); // Configure the wrapper-internal logging. // STDIN and STDOUT of the child process will be handled independently. InitLoggers(descriptor, inCliMode); if (!inCliMode) { Log.Debug("Starting WinSW in the service mode"); Run(new WrapperService(descriptor)); return; } Log.Debug("Starting WinSW in the CLI mode"); if (_args.Length == 0) { 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) { 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"); } string?username = null; string?password = null; bool setallowlogonasaserviceright = false; // This variable is very readable. if (args.Count > 1 && args[1] == "/p") { // we expected username/password on stdin Console.Write("Username: "******"Password: "******"Set Account rights to allow log on as a service (y/n)?: "); var keypressed = Console.ReadKey(); Console.WriteLine(); if (keypressed.Key == ConsoleKey.Y) { setallowlogonasaserviceright = true; } } 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.ToString(), descriptor.Interactive, username, password, descriptor.ServiceDependencies); using ServiceManager scm = new ServiceManager(); using Service sc = scm.Open(descriptor.Id); sc.SetDescription(descriptor.Description); var actions = descriptor.FailureActions; if (actions.Count > 0) { sc.SetFailureActions(descriptor.ResetFailureAfter, actions); } var isDelayedAutoStart = descriptor.StartMode == StartMode.Automatic && descriptor.DelayedAutoStart; if (isDelayedAutoStart) { sc.SetDelayedAutoStart(true); } if (descriptor.SecurityDescriptor != null) { // throws ArgumentException RawSecurityDescriptor rawSecurityDescriptor = new RawSecurityDescriptor(descriptor.SecurityDescriptor); byte[] securityDescriptorBytes = new byte[rawSecurityDescriptor.BinaryLength]; rawSecurityDescriptor.GetBinaryForm(securityDescriptorBytes, 0); _ = Advapi32.SetServiceObjectSecurity(sc.Handle, SecurityInfos.DiscretionaryAcl, securityDescriptorBytes); } return; } if (args[0] == "uninstall") { Log.Info("Uninstalling the service with id '" + descriptor.Id + "'"); if (s is 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) { // 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 { Log.Fatal("Failed to uninstall the service with id '" + descriptor.Id + "'. WMI Error code is '" + e.ErrorCode + "'"); } throw e; } return; } if (args[0] == "start") { Log.Info("Starting the service with id '" + descriptor.Id + "'"); if (s is null) { ThrowNoSuchService(); } try { s.StartService(); } catch (WmiException e) { if (e.ErrorCode == ReturnValue.ServiceAlreadyRunning) { Log.Info($"The service with ID '{descriptor.Id}' has already been started"); } else { throw; } } return; } if (args[0] == "stop") { Log.Info("Stopping the service with id '" + descriptor.Id + "'"); if (s is null) { ThrowNoSuchService(); } try { s.StopService(); } catch (WmiException e) { if (e.ErrorCode == ReturnValue.ServiceCannotAcceptControl) { Log.Info($"The service with ID '{descriptor.Id}' is not running"); } else { throw; } } return; } if (args[0] == "restart") { Log.Info("Restarting the service with id '" + descriptor.Id + "'"); if (s is 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. bool result = Kernel32.CreateProcess(null, descriptor.ExecutablePath + " restart", IntPtr.Zero, IntPtr.Zero, false, Kernel32.CREATE_NEW_PROCESS_GROUP, IntPtr.Zero, null, default, out _);
/// <summary>Adds a privilege to an account</summary> /// <param name="accountName">Name of an account - "domain\account" or only "account"</param> /// <param name="privilegeName">Name ofthe privilege</param> /// <returns>The windows error code returned by LsaAddAccountRights</returns> private static long SetRight(String accountName, String privilegeName) { long winErrorCode = 0; //contains the last error //pointer an size for the SID IntPtr sid = IntPtr.Zero; int sidSize = 0; //StringBuilder and size for the domain name StringBuilder domainName = new StringBuilder(); int nameSize = 0; //account-type variable for lookup int accountType = 0; //get required buffer size Advapi32.LookupAccountName(String.Empty, accountName, sid, ref sidSize, domainName, ref nameSize, ref accountType); //allocate buffers domainName = new StringBuilder(nameSize); sid = Marshal.AllocHGlobal(sidSize); //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()); if (!result) { winErrorCode = Kernel32.GetLastError(); Console.WriteLine("LookupAccountName failed: " + winErrorCode); } else { //initialize an empty unicode-string LSA_UNICODE_STRING systemName = new LSA_UNICODE_STRING(); //combine all policies const int access = (int)( LSA_AccessPolicy.POLICY_AUDIT_LOG_ADMIN | LSA_AccessPolicy.POLICY_CREATE_ACCOUNT | LSA_AccessPolicy.POLICY_CREATE_PRIVILEGE | LSA_AccessPolicy.POLICY_CREATE_SECRET | LSA_AccessPolicy.POLICY_GET_PRIVATE_INFORMATION | LSA_AccessPolicy.POLICY_LOOKUP_NAMES | LSA_AccessPolicy.POLICY_NOTIFICATION | LSA_AccessPolicy.POLICY_SERVER_ADMIN | LSA_AccessPolicy.POLICY_SET_AUDIT_REQUIREMENTS | LSA_AccessPolicy.POLICY_SET_DEFAULT_QUOTA_LIMITS | LSA_AccessPolicy.POLICY_TRUST_ADMIN | LSA_AccessPolicy.POLICY_VIEW_AUDIT_INFORMATION | LSA_AccessPolicy.POLICY_VIEW_LOCAL_INFORMATION ); //initialize a pointer for the policy handle IntPtr policyHandle = IntPtr.Zero; //these attributes are not used, but LsaOpenPolicy wants them to exists LSA_OBJECT_ATTRIBUTES objectAttributes = new LSA_OBJECT_ATTRIBUTES { Length = 0, RootDirectory = IntPtr.Zero, Attributes = 0, SecurityDescriptor = IntPtr.Zero, SecurityQualityOfService = IntPtr.Zero }; //get a policy handle uint resultPolicy = Advapi32.LsaOpenPolicy(ref systemName, ref objectAttributes, access, out policyHandle); winErrorCode = Advapi32.LsaNtStatusToWinError(resultPolicy); if (winErrorCode != 0) { Console.WriteLine("OpenPolicy failed: " + winErrorCode); } else { //Now that we have the SID an the policy, //we can add rights to the account. //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].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 uint res = Advapi32.LsaAddAccountRights(policyHandle, sid, userRights, 1); winErrorCode = Advapi32.LsaNtStatusToWinError(res); if (winErrorCode != 0) { Console.WriteLine("LsaAddAccountRights failed: " + winErrorCode); } Advapi32.LsaClose(policyHandle); } Advapi32.FreeSid(sid); } return(winErrorCode); }