public bool ConfigureRestartActions(string serviceName) { /* Note: For now, this function is hard-coded to set recovery actions * that will restart the service, waiting 60s between restart * attempts. It could be enhanced in the future, to take these * actions as parameters, and configure accordingly. */ IntPtr scManagerHandle = IntPtr.Zero; IntPtr actionsBuffer = IntPtr.Zero; IntPtr scManagerLockHandle = IntPtr.Zero; IntPtr serviceHandle = IntPtr.Zero; try { if (ServiceExists(serviceName) == false) { _logger.Log($"ERROR: Service does not exist [{serviceName}]."); return(false); } scManagerHandle = NativeMethods.OpenSCManagerA( null, null, NativeMethods.ServiceControlManagerType.SC_MANAGER_ALL_ACCESS); if (scManagerHandle == IntPtr.Zero) { _logger.Log("ERROR: Unable to open service control manager."); return(false); } scManagerLockHandle = NativeMethods.LockServiceDatabase(scManagerHandle); if (scManagerLockHandle == IntPtr.Zero) { _logger.Log("ERROR: Unable to lock service control manager database."); return(false); } serviceHandle = NativeMethods.OpenServiceA( scManagerHandle, serviceName, NativeMethods.ACCESS_TYPE.SERVICE_ALL_ACCESS); if (serviceHandle == IntPtr.Zero) { _logger.Log("ERROR: Unable to open specified service [" + serviceName + "]."); return(false); } NativeMethods.SC_ACTION[] scActions = new NativeMethods.SC_ACTION[3]; NativeMethods.SERVICE_FAILURE_ACTIONS serviceFailureActions; // Reference: https://docs.microsoft.com/en-us/windows/win32/api/winsvc/ns-winsvc-service_failure_actionsa serviceFailureActions.dwResetPeriod = 24 * 3600; // The time after which to reset the failure count to zero if there are no failures, in seconds. serviceFailureActions.lpRebootMsg = ""; // No broadcast message. serviceFailureActions.lpCommand = null; // If this value is NULL, the command is unchanged. serviceFailureActions.cActions = scActions.Length; // (3) failure actions. scActions[0].Delay = 60000; scActions[0].SCActionType = NativeMethods.SC_ACTION_TYPE.SC_ACTION_RESTART; scActions[1].Delay = 60000; scActions[1].SCActionType = NativeMethods.SC_ACTION_TYPE.SC_ACTION_RESTART; scActions[2].Delay = 60000; scActions[2].SCActionType = NativeMethods.SC_ACTION_TYPE.SC_ACTION_RESTART; actionsBuffer = Marshal.AllocHGlobal(Marshal.SizeOf(new NativeMethods.SC_ACTION()) * 3); NativeMethods.CopyMemory(actionsBuffer, scActions, Marshal.SizeOf(new NativeMethods.SC_ACTION()) * 3); serviceFailureActions.lpsaActions = actionsBuffer; bool configSuccess = NativeMethods.ChangeServiceConfig2A( serviceHandle, NativeMethods.InfoLevel.SERVICE_CONFIG_FAILURE_ACTIONS, ref serviceFailureActions); if (!configSuccess) { _logger.Log("ERROR: Unable to configure service failure actions [ChangeServiceConfig2A=" + Marshal.GetLastWin32Error().ToString() + "]."); return(false); } } catch (Exception e) { _logger.Log(e, "Failed to configure service failure actions."); } finally { if (actionsBuffer != IntPtr.Zero) { Marshal.FreeHGlobal(actionsBuffer); } if (serviceHandle != IntPtr.Zero) { NativeMethods.CloseServiceHandle(serviceHandle); } if (scManagerLockHandle != IntPtr.Zero) { NativeMethods.UnlockServiceDatabase(scManagerLockHandle); } if (scManagerHandle != IntPtr.Zero) { NativeMethods.CloseServiceHandle(scManagerHandle); } } return(true); }
/// <summary> /// Create the engine service and start it /// </summary> public static void ConfigureCMPWorkerService(ServiceConfigurationHandler serviceConfigurationHandler) { AppAssert.AssertNotNull(serviceConfigurationHandler, "serviceConfigurationHandler"); string installPath = SetupConstants.GetServerInstallPath(); //attempt to remove services first. //this will ignore errors of service does not exist and service marked for deletion //If service is marked for deletion, then exception will be thrown at create time as //we will be unable to create the service. //reason: //1. Keeps the code path of Install and Repair same //2. Handles corner case of service already existing in Install mode //3. Repairs the configuration of the service if broken IntPtr hSCManager = NativeMethods.NullIntPtr; IntPtr password = NativeMethods.NullIntPtr; try { hSCManager = ServiceConfigurationHandler.GetSCMHandle(); //TODO: Handle rollback if exception is thrown ServiceConfigurationHandler.StopAndRemoveService(hSCManager, SetupConstants.EngineServiceName); //construct paths to service binaries string servicePathEngine = PathHelper.QuoteString(installPath + @"MSIT\CmpWorkerService\" + SetupConstants.EngineServiceBinary); SetupLogger.LogInfo("BackEnd.Configure: Engine Service path is : {0}", servicePathEngine); //Get account string userAccountName = null; bool runasLocalAccount = SetupInputs.Instance.FindItem(SetupInputTags.CmpServiceLocalAccountTag); if (!runasLocalAccount) { userAccountName = UserAccountHelper.GetVmmServiceDomainAccount(); password = Marshal.SecureStringToGlobalAllocUnicode(SetupInputs.Instance.FindItem(SetupInputTags.CmpServiceUserPasswordTag)); } //create engine service ServiceConfigurationHandler.CreateService( hSCManager, SetupConstants.EngineServiceName, Resources.EngineServiceDisplayName, Resources.EngineServiceDescription, servicePathEngine, null, // dependent services userAccountName, password: password, autoStart: true, interactive: false); //set failure actions for VMMService NativeMethods.SERVICE_FAILURE_ACTIONS sfa = new NativeMethods.SERVICE_FAILURE_ACTIONS(); NativeMethods.SC_ACTION[] sca = new NativeMethods.SC_ACTION[SetupConstants.ServiceActionsCount + 1]; for (int i = 0; i < SetupConstants.ServiceActionsCount; i++) { sca[i].Delay = SetupConstants.ServiceRestartDelay; sca[i].Type = NativeMethods.SC_ACTION_TYPE.SC_ACTION_RESTART; } sca[SetupConstants.ServiceActionsCount].Delay = 0; sca[SetupConstants.ServiceActionsCount].Type = NativeMethods.SC_ACTION_TYPE.SC_ACTION_NONE; IntPtr unmanagedStructArray = NativeMethods.NullIntPtr; try { unmanagedStructArray = GetUnmanagedStructArray(sca); sfa.sc_Action = unmanagedStructArray; sfa.cActions = SetupConstants.ServiceActionsCount + 1; sfa.dwResetPeriod = SetupConstants.ServiceResetPeriod; sfa.lpCommand = null; sfa.lpRebootMsg = null; //set service failure actions for engine service ServiceConfigurationHandler.SetFailureActions(SetupConstants.EngineServiceName, ref sfa); //ConfigurationProgressEvent(this, new EventArgs()); } finally { if (NativeMethods.NullIntPtr != unmanagedStructArray) { Marshal.FreeHGlobal(unmanagedStructArray); } } } finally { if (!NativeMethods.NullIntPtr.Equals(hSCManager)) { NativeMethods.CloseServiceHandle(hSCManager); } if (!NativeMethods.NullIntPtr.Equals(password)) { Marshal.ZeroFreeGlobalAllocUnicode(password); } } }