/*++ Copyright (c) 1997 Microsoft Corporation Module Name: service.c Abstract: Routines that talk to the service controller. Author: Billy J. Fuller 11-Apr-1997 Environment User mode winnt --*/ #include #pragma hdrstop #include extern SERVICE_STATUS ServiceStatus; extern CRITICAL_SECTION ServiceLock; extern SERVICE_STATUS_HANDLE ServiceStatusHandle; // // This is a lookup table of legal/illegal service state transitions. FrsSetServiceStatus API // uses this table to validate the input transition requested and takes appropriate action. // DWORD StateTransitionLookup[FRS_SVC_TRANSITION_TABLE_SIZE][FRS_SVC_TRANSITION_TABLE_SIZE] = { {0, SERVICE_STOPPED, SERVICE_START_PENDING, SERVICE_STOP_PENDING, SERVICE_RUNNING }, {SERVICE_STOPPED, FRS_SVC_TRANSITION_NOOP, FRS_SVC_TRANSITION_NOOP, FRS_SVC_TRANSITION_ILLEGAL,FRS_SVC_TRANSITION_ILLEGAL}, {SERVICE_START_PENDING,FRS_SVC_TRANSITION_LEGAL,FRS_SVC_TRANSITION_LEGAL, FRS_SVC_TRANSITION_LEGAL, FRS_SVC_TRANSITION_LEGAL }, {SERVICE_STOP_PENDING, FRS_SVC_TRANSITION_LEGAL,FRS_SVC_TRANSITION_ILLEGAL,FRS_SVC_TRANSITION_LEGAL, FRS_SVC_TRANSITION_ILLEGAL}, {SERVICE_RUNNING, FRS_SVC_TRANSITION_LEGAL,FRS_SVC_TRANSITION_ILLEGAL,FRS_SVC_TRANSITION_LEGAL, FRS_SVC_TRANSITION_NOOP } }; SC_HANDLE FrsOpenServiceHandle( IN PTCHAR MachineName, IN PTCHAR ServiceName ) /*++ Routine Description: Open a service on a machine. Arguments: MachineName - the name of the machine to contact ServiceName - the service to open Return Value: The service's handle or NULL. --*/ { #undef DEBSUB #define DEBSUB "FrsOpenServiceHandle:" SC_HANDLE SCMHandle; SC_HANDLE ServiceHandle; ULONG WStatus; // // Attempt to contact the SC manager. // SCMHandle = OpenSCManager(MachineName, NULL, SC_MANAGER_CONNECT); if (!HANDLE_IS_VALID(SCMHandle)) { WStatus = GetLastError(); DPRINT1_WS(0, ":SC: Couldn't open service control manager on machine %ws;", MachineName, WStatus); return NULL; } // // Contact the service. // ServiceHandle = OpenService(SCMHandle, ServiceName, SERVICE_ALL_ACCESS); if (!HANDLE_IS_VALID(ServiceHandle)) { WStatus = GetLastError(); DPRINT2_WS(0, ":SC: Couldn't open service control manager for service (%ws) on machine %ws;", ServiceName, MachineName, WStatus); ServiceHandle = NULL; } CloseServiceHandle(SCMHandle); return ServiceHandle; } DWORD FrsGetServiceState( IN PWCHAR MachineName, IN PWCHAR ServiceName ) /*++ Routine Description: Return the service's state Arguments: MachineName - the name of the machine to contact ServiceName - the service to check Return Value: The service's state or 0 if the state could not be obtained. --*/ { #undef DEBSUB #define DEBSUB "FrsGetServiceState:" SC_HANDLE ServiceHandle; SERVICE_STATUS LocalServiceStatus; // // Open the service. // ServiceHandle = FrsOpenServiceHandle(MachineName, ServiceName); if (!HANDLE_IS_VALID(ServiceHandle)) { return 0; } // // Get the service's status // if (!QueryServiceStatus(ServiceHandle, &LocalServiceStatus)) { DPRINT3(0, ":SC: WARN - QueryServiceStatus(%ws, %ws) returned %d\n", MachineName, ServiceName, GetLastError()); CloseServiceHandle(ServiceHandle); return 0; } CloseServiceHandle(ServiceHandle); // // Successfully retrieved service status; check state // return LocalServiceStatus.dwCurrentState; } BOOL FrsIsServiceRunning( IN PWCHAR MachineName, IN PWCHAR ServiceName ) /*++ Routine Description: Is a service running on a machine. Arguments: MachineName - the name of the machine to contact ServiceName - the service to check Return Value: TRUE - Service is running. FALSE - Service is not running. --*/ { #undef DEBSUB #define DEBSUB "FrsIsServiceRunning:" DWORD State; State = FrsGetServiceState(MachineName, ServiceName); return (State == SERVICE_RUNNING); } DWORD FrsSetServiceFailureAction( VOID ) /*++ Routine Description: If unset, initialize the service's failure actions. Arguments: Return Value: WIN32 STATUS --*/ { #undef DEBSUB #define DEBSUB "FrsSetServiceFailureAction:" #define NUM_ACTIONS (3) #define SERVICE_RESTART_MILLISECONDS (30 * 60 * 1000) SC_HANDLE ServiceHandle; DWORD BufSize, BytesNeeded; SC_ACTION *Actions; SERVICE_FAILURE_ACTIONS *FailureActions; ULONG WStatus = ERROR_SUCCESS, i; if (!RunningAsAService || !HANDLE_IS_VALID(ServiceStatusHandle)) { return ERROR_SUCCESS; } BufSize = sizeof(SERVICE_FAILURE_ACTIONS) + sizeof(SC_ACTION) * NUM_ACTIONS; FailureActions = FrsAlloc(BufSize); EnterCriticalSection(&ServiceLock); // // Retrieve the current failure actions for the service NtFrs // ServiceHandle = FrsOpenServiceHandle(NULL, SERVICE_NAME); if (!HANDLE_IS_VALID(ServiceHandle)) { LeaveCriticalSection(&ServiceLock); FailureActions = FrsFree(FailureActions); DPRINT(0, ":SC: Failed to open service handle.\n"); return ERROR_OPEN_FAILED; } if (!QueryServiceConfig2(ServiceHandle, SERVICE_CONFIG_FAILURE_ACTIONS, (PVOID)FailureActions, BufSize, &BytesNeeded)) { WStatus = GetLastError(); CloseServiceHandle(ServiceHandle); LeaveCriticalSection(&ServiceLock); if (WIN_BUF_TOO_SMALL(WStatus)) { DPRINT(0, ":SC: Restart actions for service are already set.\n"); WStatus = ERROR_SUCCESS; } else { DPRINT_WS(0, ":SC: Could not query service for restart action;", WStatus); } FailureActions = FrsFree(FailureActions); return WStatus; } // // Check if failure action already set. E.g. by the User. // if (FailureActions->cActions) { CloseServiceHandle(ServiceHandle); LeaveCriticalSection(&ServiceLock); DPRINT(0, ":SC: Restart actions for service are already set.\n"); FailureActions = FrsFree(FailureActions); return ERROR_SUCCESS; } // // Service failure actions are unset; initialize them // WStatus = ERROR_SUCCESS; Actions = (SC_ACTION *)(((PUCHAR)FailureActions) + sizeof(SERVICE_FAILURE_ACTIONS)); for (i = 0; i < NUM_ACTIONS; ++i) { Actions[i].Type = SC_ACTION_RESTART; Actions[i].Delay = SERVICE_RESTART_MILLISECONDS; } FailureActions->cActions = NUM_ACTIONS; FailureActions->lpsaActions = Actions; if (!ChangeServiceConfig2(ServiceHandle, SERVICE_CONFIG_FAILURE_ACTIONS, (PVOID)FailureActions)) { WStatus = GetLastError(); } CloseServiceHandle(ServiceHandle); LeaveCriticalSection(&ServiceLock); if (!WIN_SUCCESS(WStatus)) { DPRINT_WS(0, ":SC: Could not set restart actions;", WStatus); } else { DPRINT(4, ":SC: Success setting restart actions for service.\n"); } FailureActions = FrsFree(FailureActions); return WStatus; } BOOL FrsWaitService( IN PWCHAR MachineName, IN PWCHAR ServiceName, IN INT IntervalMS, IN INT TotalMS ) /*++ Routine Description: This routine determines if the specified NT service is in a running state or not. This function will sleep and retry once if the service is not yet running. Arguments: MachineName - machine to contact ServiceName - Name of the NT service to interrogate. IntervalMS - Check every IntervalMS milliseconds. TotalMS - Stop checking after this long. Return Value: TRUE - Service is running. FALSE - Service state cannot be determined. --*/ { #undef DEBSUB #define DEBSUB "FrsWaitService:" do { if (FrsIsServiceRunning(MachineName, ServiceName)) { return TRUE; } if (FrsIsShuttingDown) { break; } Sleep(IntervalMS); } while ((TotalMS -= IntervalMS) > 0); DPRINT2(0, ":SC: %ws is not running on %ws\n", ServiceName, ComputerName); return FALSE; } DWORD FrsSetServiceStatus( IN DWORD State, IN DWORD CheckPoint, IN DWORD Hint, IN DWORD ExitCode ) /*++ Routine Description: Acquire the service lock, ServiceLock, and set the service's state using the global service handle and service status set in main.c. Check if this is a valid state transition using the lookup table. This will prevent the service from making any invalid state transitions. Arguments: Status - Set the state to this value Hint - Suggested timeout for the service controller ExitCode - For SERVICE_STOPPED; Return Value: WIN32 STATUS --*/ { #undef DEBSUB #define DEBSUB "FrsSetServiceStatus:" DWORD WStatus = ERROR_SUCCESS; BOOL Ret; DWORD FromState,ToState; DWORD TransitionCheck = FRS_SVC_TRANSITION_ILLEGAL; // // Set the service's status after acquiring the lock // if (RunningAsAService && HANDLE_IS_VALID(ServiceStatusHandle)) { EnterCriticalSection(&ServiceLock); // // Check if this is a valid service state transition. // for (FromState = 0 ; FromState < FRS_SVC_TRANSITION_TABLE_SIZE ; ++FromState) { for (ToState = 0 ; ToState < FRS_SVC_TRANSITION_TABLE_SIZE ; ++ToState) { if (StateTransitionLookup[FromState][0] == ServiceStatus.dwCurrentState && StateTransitionLookup[0][ToState] == State) { TransitionCheck = StateTransitionLookup[FromState][ToState]; break; } } } if (TransitionCheck == FRS_SVC_TRANSITION_LEGAL) { DPRINT2(4,":SC: Current State = %d, Moving to %d\n", ServiceStatus.dwCurrentState, State); ServiceStatus.dwCurrentState = State; ServiceStatus.dwCheckPoint = CheckPoint; ServiceStatus.dwWaitHint = Hint; ServiceStatus.dwWin32ExitCode = ExitCode; // // Do not accept stop control unless the service is in SERVICE_RUNNING state. // This prevents the service from getting confused when a stop is called // while the service is starting. // if (ServiceStatus.dwCurrentState == SERVICE_RUNNING) { ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN; } else { ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_SHUTDOWN; } Ret = SetServiceStatus(ServiceStatusHandle, &ServiceStatus); if (!Ret) { WStatus = GetLastError(); DPRINT1_WS(0, ":SC: ERROR - SetServiceStatus(%d);", ServiceStatus, WStatus); } else { DPRINT4(0, ":SC: SetServiceStatus(State %d, CheckPoint %d, Hint %d, ExitCode %d) succeeded\n", State, CheckPoint, Hint, ExitCode); } } else if (TransitionCheck == FRS_SVC_TRANSITION_ILLEGAL) { DPRINT2(0,":SC: Error - Illegal service state transition request. From State = %d, To %d\n", ServiceStatus.dwCurrentState, State); WStatus = ERROR_INVALID_PARAMETER; } LeaveCriticalSection(&ServiceLock); } return WStatus; }