Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

444 lines
12 KiB

/*++
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 <ntreppch.h>
#pragma hdrstop
#include <frs.h>
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 ServiceStatus;
//
// Open the service.
//
ServiceHandle = FrsOpenServiceHandle(MachineName, ServiceName);
if (!HANDLE_IS_VALID(ServiceHandle)) {
return 0;
}
//
// Get the service's status
//
if (!QueryServiceStatus(ServiceHandle, &ServiceStatus)) {
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 ServiceStatus.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;
}