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
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 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;
|
|
}
|
|
|