445 lines
12 KiB
C
445 lines
12 KiB
C
/*++
|
||
|
||
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;
|
||
}
|
||
|