Windows NT 4.0 source code leak
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.
 
 
 
 
 
 

2680 lines
78 KiB

/*++
Copyright (c) 1992 Microsoft Corporation
Module Name:
depend.c
Abstract:
This module contains routines which handle service start and
stop dependencies:
ScInitAutoStart
ScEndAutoStart
ScAutoStartServices
ScStartServiceAndDependencies
ScSetServiceStartRequest
ScMarkGroupStartNow
RI_ScGetCurrentGroupStateW
ScStartMarkedServices
ScHandleServiceFailure
ScDependenciesStarted
ScLookForHungServices
ScHandleBadDependencies
ScServiceToStartDependOn
ScNotifyChangeState
ScDependentsStopped
ScEnumDependents
ScFoundDuplicateDependent
ScInHardwareProfile
Author:
Rita Wong (ritaw) 03-Apr-1992
Environment:
Win32
Revision History:
11-Jun-1996 AnirudhS
During setup/upgrade, don't event-log failure of a service to start
due to a dependent service not starting. (The most common case of
this is a service that runs in a domain account, and hence has an
implicit dependency on netlogon, which is disabled during setup.)
03-Nov-1995 AnirudhS
Don't try to start a service that isn't in the current hardware
profile.
30-Oct-1995 AnirudhS
ScStartMarkedServices: If ScStartService says that a service is
already running, treat this as a success.
15-Aug-1995 AnirudhS
Added I_ScGetCurrentGroupStateW.
Changed while loops to for loops and if stmts to switch stmts for
improved readability.
16-Aug-1994 Danl
ScLookForHungServices: If a long waitHint was passed in, the sleep
time would be set to a huge number (like 4.9 days). This was
changed so that if the waitHint is over 100 seconds, then the
sleep time is limited to 10 seconds, but the number of iterations
for the polling goes up.
09-Jun-1994 Danl
Begin working on making sure NetLogon is started if we are
going to start a service that runs in an account. This requires
making a dependency on NetLogon.
21-Apr-1992 JohnRo
Use SC_LOG0(), FORMAT_ equates, etc
03-Apr-1992 ritaw
created
--*/
#include <string.h> // memcpy
#include <stdlib.h> // wcslen
#include <nt.h>
#include <ntrtl.h>
#include <nturtl.h>
#include <windef.h>
#include <winerror.h>
#include <winbase.h> // CRITICAL_SECTION
#include <winsvc.h> // SERVICE_STATUS needed by dataman.h
#include <lmcons.h> // NET_API_STATUS
#include <srvann.h> // I_ScGetCurrentGroupStateW and related definitions
#include <winreg.h> // REGSAM needed by cfgmgr32.h
#include <cfgmgr32.h> // PNP manager functions
#include <pnp.h> // PNP manager functions, server side
#include <cfgmgrp.h> // PNP manager functions, server side, internal (PNP_GET_HWPROFFLAGS)
#include <regstr.h> // CSCONFIGFLAG_ constants
#include <rpc.h> // RpcImpersonateClient
#include <tstr.h> // WCSSIZE
#include <scdebug.h> // SC_LOG1(), etc.
#include <svcctl.h> // MIDL generated header file. (SC_RPC_HANDLE)
#include <sclib.h> // ScCopyStringToBufferW
#include "dataman.h" // LPSERVICE_RECORD needed by scopen.h
#include "scopen.h" // Handle data types
#include "start.h" // ScStartService
#include "depend.h"
#include "info.h" // ScQueryServiceStatus
#include "bootcfg.h" // ScRevertToLastKnownGood
#include "driver.h" // ScGetDriverStatus
#include "lockapi.h" // ScLockDatabase
#include "svcctrl.h" // ScLogEvent
#include "account.h" // SC_LOCAL_SYSTEM_USER_NAME
#include "scconfig.h" // ScRegCloseKey
#define SERVICE_START_TIMEOUT 80000 // 80 seconds
#define LENGTH(array) (sizeof(array)/sizeof((array)[0]))
//
// TDI GROUP SPECIAL: The PNP_TDI group is treated as a subgroup of
// the TDI group for dependency purposes (though not for group start
// ordering purposes). This is implemented via the following macros.
// A service BELONGS_TO a group either if it is a member of that group,
// or if the group is the TDI group and the service is a member of the
// PNP_TDI group.
// IS_SUBGROUP returns true if Group1 is equal to or a subgroup of Group2.
//
#define IS_SUBGROUP(Group1, Group2) \
((Group1) == (Group2) || \
(Group2) == ScGlobalTDIGroup && (Group1) == ScGlobalPNP_TDIGroup)
#define BELONGS_TO(Service, Group) \
IS_SUBGROUP((Service)->MemberOfGroup, (Group))
//-------------------------------------------------------------------//
// //
// Static global variables //
// //
//-------------------------------------------------------------------//
//
// For notifying us that a service has gone from start-pending state
// to running or stopped state.
//
/* static */ HANDLE ScServiceChangeStateEvent = NULL;
//
// For serializing start requests
//
/* static */ CRITICAL_SECTION ScServiceStartCriticalSection;
//-------------------------------------------------------------------//
// //
// Local function prototypes //
// //
//-------------------------------------------------------------------//
VOID
ScSetServiceStartRequest(
IN LPSERVICE_RECORD ServiceRecord,
IN BOOL DemandStarting
);
BOOL
ScMarkGroupStartNow(
IN LPLOAD_ORDER_GROUP Group
);
DWORD
ScGetCurrentGroupState(
LPLOAD_ORDER_GROUP Group
);
DWORD
ScStartMarkedServices(
IN LPSERVICE_RECORD ServiceToStart OPTIONAL,
IN DWORD NumArgs,
IN LPSTRING_PTRSW CmdArgs,
IN BOOL WaitForAll
);
VOID
ScHandleServiceFailure(
IN LPSERVICE_RECORD Service
);
BOOL
ScDependenciesStarted(
IN LPSERVICE_RECORD Service,
OUT BOOL *IsBadDependencies,
OUT BOOL *AllStarted,
OUT BOOL *ExistsBlockedService
);
BOOL
IsDependOnLaterGroup(
IN LPLOAD_ORDER_GROUP ServiceGroup,
IN LPLOAD_ORDER_GROUP DependOnGroup,
IN DEPEND_TYPE DependType
);
VOID
ScCleanupStartFailure(
LPSERVICE_RECORD Service,
DWORD StartError
);
VOID
ScLookForHungServices(
VOID
);
VOID
ScHandleBadDependencies(
VOID
);
BOOL
ScServiceToStartDependOn(
LPSERVICE_RECORD ServiceToStart OPTIONAL,
LPSERVICE_RECORD StartPendingService
);
BOOL
ScFoundDuplicateDependent(
IN LPWSTR ServiceName,
IN LPENUM_SERVICE_STATUSW EnumBuffer,
IN LPENUM_SERVICE_STATUSW BufferEnd
);
#ifndef _CAIRO_
VOID
ScCheckNetLogonDepend(
LPSERVICE_RECORD ServiceRecord,
BOOL DemandStarting
);
#endif // _CAIRO_
BOOL
ScInitAutoStart(
VOID
)
/*++
Routine Description:
This function creates the event for notifying the service controller
that one of the automatically started service is running and creates
the mutex for serializing start requests.
Arguments:
None.
Return Value:
TRUE - Event and mutex were created successfully. FALSE otherwise.
--*/
{
//
// Create event which indicates that some service that has been
// automatically started is now running or stopped.
//
if ((ScServiceChangeStateEvent =
CreateEvent(
NULL, // Event attributes
TRUE, // Event must be manually reset
FALSE, // Initial state not signalled
NULL
)) == (HANDLE) NULL) {
return FALSE;
}
//
// Create critical section which is used to serialize start requests:
// if auto-starting services, and a user tries to demand start
// a service, auto-starting has to complete before we process the
// demand start.
//
InitializeCriticalSection(&ScServiceStartCriticalSection);
return TRUE;
}
VOID
ScEndAutoStart(
VOID
)
{
if (ScServiceChangeStateEvent != (HANDLE) NULL) {
(void) CloseHandle(ScServiceChangeStateEvent);
}
DeleteCriticalSection(&ScServiceStartCriticalSection);
}
VOID
ScAutoStartServices(
VOID
)
/*++
Routine Description:
This function automatically starts all services that must be
auto-started, in group order.
This routine may not return if because we may instigate a reboot to
revert to last-known-good.
Arguments:
None.
Return Value:
None.
--*/
{
//
// Set current request flag of all auto-start services as well as
// their dependencies
//
//
// Start services with start request flag set in group order
//
(void) ScStartServiceAndDependencies(NULL, 0, NULL);
}
DWORD
ScStartServiceAndDependencies(
IN LPSERVICE_RECORD ServiceToStart OPTIONAL,
IN DWORD NumArgs,
IN LPSTRING_PTRSW CmdArgs
)
/*++
Routine Description:
This function marks a group or a service to be started now before
calling ScStartMarkedServices to start them.
Arguments:
ServiceToStart - Service to be started; or, NULL if autostarting
services.
NumArgs, CmdArgs - Arguments for the service to be started.
Return Value:
NO_ERROR if successful; otherwise, the return value from the first
unsuccessful call to ScStartMarkedServices.
--*/
{
DWORD status;
DWORD ApiStatus = NO_ERROR;
LPSERVICE_RECORD Service;
PLOAD_ORDER_GROUP Group;
SC_RPC_LOCK Lock;
BOOL databaseLocked = FALSE;
//
// Serialize start requests by allowing auto-starting of groups or
// demand start of a service one at a time.
//
EnterCriticalSection(&ScServiceStartCriticalSection);
//
// Grab the SC Manager database lock.
//
if (!ScStillInitializing) {
if ((status = ScLockDatabase(
TRUE, // called internally
SERVICES_ACTIVE_DATABASEW,
&Lock
)) != NO_ERROR) {
LeaveCriticalSection(&ScServiceStartCriticalSection);
return status;
}
databaseLocked = TRUE;
}
//
// Since we aren't going to modify the group information in this
// operation, we only need a shared grouplist lock. This allows
// RI_ScGetCurrentGroupStateW to read the status of a group while
// autostart is in progress. To prevent deadlocks, the grouplist
// lock is always acquired before the service database lock, if
// both are needed.
//
ScGroupListLock(SC_GET_SHARED);
//
// Get the exclusive database lock so that we can increment the
// use count of the service being started as well as their dependencies
// because otherwise they could go away if deleted.
//
ScDatabaseLock(SC_GET_EXCLUSIVE, "StartServiceAndDependencies1");
if (ARGUMENT_PRESENT(ServiceToStart)) {
//
// Demand starting a service.
//
//
// We can never start a disabled service
//
if (ServiceToStart->StartType == SERVICE_DISABLED ||
! ScInHardwareProfile(ServiceToStart, 0)) {
ApiStatus = ERROR_SERVICE_DISABLED;
goto ReleaseLocks;
}
//
// Cannot start a deleted service.
//
if (DELETE_FLAG_IS_SET(ServiceToStart)) {
ApiStatus = ERROR_SERVICE_MARKED_FOR_DELETE;
goto ReleaseLocks;
}
//
// Get the current state of the service
//
if (ServiceToStart->ServiceStatus.dwServiceType & SERVICE_DRIVER) {
ScDatabaseLock(SC_MAKE_SHARED, "StartServiceAndDependencies0");
(void) ScGetDriverStatus(ServiceToStart, NULL);
ScDatabaseLock(SC_MAKE_EXCLUSIVE, "StartServiceAndDependencies0");
}
if (ServiceToStart->ServiceStatus.dwCurrentState != SERVICE_STOPPED) {
ApiStatus = ERROR_SERVICE_ALREADY_RUNNING;
goto ReleaseLocks;
}
ScSetServiceStartRequest(ServiceToStart, TRUE);
}
else {
//
// Auto-starting services.
//
// Set the CurrentStartRequest flag to TRUE for all services
// of type AUTO_START that are enabled in this hardware profile,
// and their dependencies.
//
FOR_SERVICES_THAT(Service, Service->StartType == SERVICE_AUTO_START &&
ScInHardwareProfile(Service, 0))
{
ScSetServiceStartRequest(Service, FALSE);
}
}
ScDatabaseLock(SC_RELEASE, "StartServiceAndDependencies1");
//
// Always start services in group order.
//
for (Group = ScGetOrderGroupList();
Group != NULL;
Group = Group->Next)
{
//
// Start each group in load group order
//
if (ScMarkGroupStartNow(Group)) {
BOOL WaitForGroup;
if (ARGUMENT_PRESENT(ServiceToStart) &&
ServiceToStart->MemberOfGroup == Group) {
//
// Don't have to wait for all marked members of the group
// to finish starting because the service which is demand
// started is polled by the UI.
//
WaitForGroup = FALSE;
}
else {
//
// Auto-starting (ServiceToStart == NULL) or demand-starting
// a service that is not within this group. Wait for group
// all marked members finish starting.
//
WaitForGroup = TRUE;
}
status = ScStartMarkedServices(
ServiceToStart,
NumArgs,
CmdArgs,
WaitForGroup
);
if (status != NO_ERROR && ApiStatus == NO_ERROR) {
//
// Save first error to be returned
//
ApiStatus = status;
}
}
}
//
// Services that does not belong in any group are considered
// in a group that starts last.
//
if (ScMarkGroupStartNow(NULL)) {
status = ScStartMarkedServices(
ServiceToStart,
NumArgs,
CmdArgs,
! ARGUMENT_PRESENT(ServiceToStart) // Wait only if
); // auto-start
if (status != NO_ERROR && ApiStatus == NO_ERROR) {
//
// Save first error to be returned
//
ApiStatus = status;
}
}
//
// Clear the CurrentStartRequest flags when done starting service(s).
//
ScDatabaseLock(SC_GET_EXCLUSIVE, "StartServiceAndDependencies2");
FOR_SERVICES_THAT(Service, CURRENTSTART_FLAG_IS_SET(Service))
{
CLEAR_CURRENTSTART_FLAG(Service);
ScDecrementUseCountAndDelete(Service);
}
ReleaseLocks:
ScDatabaseLock(SC_RELEASE, "StartServiceAndDependencies2");
ScGroupListLock(SC_RELEASE);
//
// Release the SC Manager database lock.
//
if (databaseLocked) {
ScUnlockDatabase(&Lock);
}
LeaveCriticalSection(&ScServiceStartCriticalSection);
return ApiStatus;
}
VOID
ScSetServiceStartRequest(
IN LPSERVICE_RECORD ServiceRecord,
IN BOOL DemandStarting
)
/*++
Routine Description:
This function sets the CurrentStartRequest flag of the specified service
to TRUE and recursively sets the flag of all the services this
service depends on. It also initializes the StartState and StartError
of the services that are about to be started.
Arguments:
ServiceRecord - Supplies a pointer to the service record of service
to be started.
DemandStarting - Supplies a flag that is set to TRUE if we are demand-
starting a service, FALSE if we are auto-starting services.
Return Value:
None.
Note:
This function expects the caller to have held the exclusive service
database lock. This function is called by ScStartServiceAndDependencies.
--*/
{
LPDEPEND_RECORD Depend;
if (CURRENTSTART_FLAG_IS_SET(ServiceRecord)) {
return;
}
//
// Set the CurrentStartRequest to TRUE
//
SET_CURRENTSTART_FLAG(ServiceRecord);
//
// Update the StartState and StartError
//
if (ServiceRecord->StartType == SERVICE_DISABLED ||
! ScInHardwareProfile(ServiceRecord, 0)) {
ServiceRecord->StartState = SC_START_FAIL;
ServiceRecord->StartError = ERROR_SERVICE_DISABLED;
}
else if (DELETE_FLAG_IS_SET(ServiceRecord)) {
ServiceRecord->StartState = SC_START_FAIL;
ServiceRecord->StartError = ERROR_SERVICE_MARKED_FOR_DELETE;
}
else {
if (ServiceRecord->ServiceStatus.dwServiceType & SERVICE_DRIVER) {
ScDatabaseLock(SC_MAKE_SHARED, "ScSetServiceStartRequest");
(void) ScGetDriverStatus(ServiceRecord, NULL);
ScDatabaseLock(SC_MAKE_EXCLUSIVE, "ScSetServiceStartRequest");
}
switch (ServiceRecord->ServiceStatus.dwCurrentState) {
case SERVICE_STOPPED:
if (DemandStarting) {
//
// Demand starting a service. We want to retry
// eventhough we have failed once before.
//
ServiceRecord->StartState = SC_NEVER_STARTED;
}
else {
//
// Auto-starting bunch of services at boot. If
// the service was ever started before and failed,
// we don't want to start it again.
//
if (ServiceRecord->ServiceStatus.dwWin32ExitCode !=
ERROR_SERVICE_NEVER_STARTED) {
ServiceRecord->StartState = SC_START_FAIL;
}
else {
ServiceRecord->StartState = SC_NEVER_STARTED;
}
}
break;
case SERVICE_START_PENDING:
ServiceRecord->StartState = SC_START_PENDING;
break;
case SERVICE_STOP_PENDING:
case SERVICE_PAUSED:
case SERVICE_CONTINUE_PENDING:
case SERVICE_PAUSE_PENDING:
case SERVICE_RUNNING:
ServiceRecord->StartState = SC_START_SUCCESS;
break;
default:
SC_LOG1(
ERROR,
"ScSetServiceStartRequest: Unexpected dwCurrentState %0lx\n",
ServiceRecord->ServiceStatus.dwCurrentState
);
SC_ASSERT(FALSE);
ServiceRecord->StartState = SC_START_FAIL;
break;
}
}
//
// Increment the reference count so that the dependency service
// never goes away while we are in the process of starting them.
//
ServiceRecord->UseCount++;
SC_LOG2(USECOUNT, "ScSetServiceStartRequest: " FORMAT_LPWSTR
" increment USECOUNT=%lu\n", ServiceRecord->ServiceName, ServiceRecord->UseCount);
SC_LOG2(DEPEND_DUMP, "CSR=TRUE for "
FORMAT_LPWSTR " USECOUNT=%lu\n", ServiceRecord->ServiceName,
ServiceRecord->UseCount);
//
// For each of this service's dependencies
//
for (Depend = ServiceRecord->StartDepend;
Depend != NULL;
Depend = Depend->Next)
{
if (Depend->DependType == TypeDependOnService) {
if (CURRENTSTART_FLAG_IS_SET(Depend->DependService)) {
//
// CurrentStartRequest of a dependency service is already
// set to TRUE. Just go on to next dependency.
//
SC_LOG2(DEPEND_DUMP, "DependService " FORMAT_LPWSTR
" CSR=TRUE already, USECOUNT=%lu\n",
Depend->DependService->ServiceName,
Depend->DependService->UseCount);
}
else {
ScSetServiceStartRequest(Depend->DependService, DemandStarting);
}
}
else if (Depend->DependType == TypeDependOnGroup) {
//
// This service has a dependency on a group.
// For each service in that group
//
LPSERVICE_RECORD Service;
FOR_SERVICES_THAT(Service, BELONGS_TO(Service, Depend->DependGroup))
{
if (CURRENTSTART_FLAG_IS_SET(Service)) {
//
// CurrentStartRequest of a dependency service is
// already set to TRUE. Just go on to next dependency.
//
SC_LOG3(DEPEND_DUMP, "DependGroup " FORMAT_LPWSTR
", Service " FORMAT_LPWSTR
" CSR=TRUE already, USECOUNT=%lu\n",
Depend->DependGroup->GroupName,
Service->ServiceName, Service->UseCount);
}
else {
ScSetServiceStartRequest(
Service,
DemandStarting
);
}
}
}
}
#ifndef _CAIRO_
//
// We have now gone through all the dependencies that are listed. Now
// Determine if this service needs to depend on NetLogon. If the service
// runs in an account, it may require NetLogon.
//
ScCheckNetLogonDepend(ServiceRecord,DemandStarting);
#endif // _CAIRO_
}
BOOL
ScMarkGroupStartNow(
IN LPLOAD_ORDER_GROUP Group
)
/*++
Routine Description:
This function go through all services that belong in the specified
group and mark the services that have the CurrentStartRequest flag
set to be started immediately.
Arguments:
Group - Supplies a pointer to the load order group to mark for
start.
Return Value:
Returns TRUE if at least one member of the group is marked
START_NOW or is START_PENDING. FALSE otherwise. This flag
is to indicate whether ScStartMarkedServices should be called
to handle starting a group.
--*/
{
LPSERVICE_RECORD Service;
BOOL ReturnFlag = FALSE;
//
// Mark all the CurrentStartRequest (which includes all auto-start)
// services to be started now
//
// A service is marked START_NOW if it is a member of the specified
// group. If the specified group is NULL, mark all services that
// do not belong to any group as well as services that belong to
// standalone groups.
//
FOR_SERVICES_THAT(Service,
((Service->MemberOfGroup == Group) ||
(Group == NULL && (Service->MemberOfGroup != NULL) &&
(Service->MemberOfGroup->RefCount != MAXULONG) ))
&&
CURRENTSTART_FLAG_IS_SET(Service) )
{
if (Service->StartState == SC_NEVER_STARTED) {
Service->StartState = SC_START_NOW;
Service->StartError = NO_ERROR;
}
if (Service->StartState == SC_START_NOW ||
Service->StartState == SC_START_PENDING) {
ReturnFlag = TRUE;
}
}
return ReturnFlag;
}
DWORD
RI_ScGetCurrentGroupStateW(
IN SC_RPC_HANDLE hSCManager,
IN LPWSTR pszGroupName,
OUT LPDWORD pdwCurrentState
)
/*++
Routine Description:
This is an internal routine that checks the startup status of a
specified service group. It is used by the LSA to speed up logon,
by proceeding with logon after the transports have been started,
rather than waiting for the workstation service to start.
NOTE: This routine acquires a shared lock on both the group list and
the services database.
Arguments:
hSCManager - Handle to the service controller. This must have been
opened with SC_MANAGER_ENUMERATE_SERVICE access.
pszGroupName - Name of the group whose state is to be determined.
pdwCurrentState - One of the following is returned here:
GROUP_START_FAIL - all service(s) in the group failed to start,
or there are no services in the group.
GROUP_NOT_STARTED - there are some services in the group that
have neither started nor failed to start.
GROUP_ONE_STARTED - there is at least one service in the group,
and all services in the group have started.
Group members that have not been started, and are disabled or
marked for deletion, are ignored (i.e. treated as though they
didn't exist).
Return Value:
NO_ERROR - The operation was completely successful.
ERROR_SERVICE_DOES_NOT_EXIST - The group named does not exist.
ERROR_SHUTDOWN_IN_PROGRESS - Service controller is shutting down.
ERROR_INVALID_HANDLE - hSCManager is invalid.
--*/
{
DWORD status;
LPLOAD_ORDER_GROUP Group;
if (ScShutdownInProgress) {
return(ERROR_SHUTDOWN_IN_PROGRESS);
}
//
// Check the signature on the handle.
//
if (((LPSC_HANDLE_STRUCT)hSCManager)->Signature != SC_SIGNATURE) {
return(ERROR_INVALID_HANDLE);
}
//
// Was the handle opened with SC_MANAGER_ENUMERATE_SERVICE access?
//
if (! RtlAreAllAccessesGranted(
((LPSC_HANDLE_STRUCT)hSCManager)->AccessGranted,
SC_MANAGER_ENUMERATE_SERVICE
)) {
return(ERROR_ACCESS_DENIED);
}
ScGroupListLock(SC_GET_SHARED);
//
// Search both group lists for the named group
//
Group = ScGetNamedGroupRecord(pszGroupName);
if (Group == NULL)
{
//
// Group not found
//
status = ERROR_SERVICE_DOES_NOT_EXIST;
}
else
{
//
// Read the group's state
//
ScDatabaseLock(SC_GET_SHARED, "RI_ScGetCurrentGroupStateW1");
*pdwCurrentState = ScGetCurrentGroupState(Group);
ScDatabaseLock(SC_RELEASE, "RI_ScGetCurrentGroupStateW2");
status = NO_ERROR;
}
ScGroupListLock(SC_RELEASE);
return(status);
}
DWORD
ScGetCurrentGroupState(
LPLOAD_ORDER_GROUP Group
)
{
LPSERVICE_RECORD Service;
BOOL OneStarted = FALSE;
FOR_SERVICES_THAT(Service, BELONGS_TO(Service, Group))
{
switch (Service->StartState)
{
case SC_NEVER_STARTED:
//
// Ignore services that are disabled or marked for
// deletion.
// (This check is really needed only when this function is
// called from RI_ScGetCurrentGroupState. When called
// from ScDependenciesStarted, such services will already
// have had their StartState set to SC_START_FAIL.)
//
if (Service->StartType == SERVICE_DISABLED ||
DELETE_FLAG_IS_SET(Service) ||
! ScInHardwareProfile(Service, 0))
{
continue;
}
//
// else fall through
//
case SC_START_NOW:
case SC_START_PENDING:
SC_LOG2(DEPEND, "Group " FORMAT_LPWSTR " NOT started "
"because of Service " FORMAT_LPWSTR "\n",
Group->GroupName, Service->ServiceName);
return GROUP_NOT_STARTED;
case SC_START_SUCCESS:
OneStarted = TRUE;
break; // out of switch, not out of loop
}
}
if (OneStarted)
{
SC_LOG1(DEPEND, "Group " FORMAT_LPWSTR " ONE started\n",
Group->GroupName);
return GROUP_ONE_STARTED;
}
else
{
SC_LOG1(DEPEND, "Group " FORMAT_LPWSTR " FAILED to start\n",
Group->GroupName);
return GROUP_START_FAIL;
}
}
DWORD
ScStartMarkedServices(
IN LPSERVICE_RECORD ServiceToStart OPTIONAL,
IN DWORD NumArgs,
IN LPSTRING_PTRSW CmdArgs,
IN BOOL WaitForAll
)
/*++
Routine Description:
This function starts the services that are marked as SERVICE_START_NOW
in the service record list. Once the service is running, or if the
service failed to start, the SERVICE_START_NOW bit is removed.
If a service marked as SERVICE_START_NOW depends on a service that is
not marked, the dependency service will also be marked SERVICE_START_NOW.
Arguments:
ServiceToStart - Supplies a pointer to the service which is to be demand
started via the StartService API. If this parameter is NULL, this
routine is called by the service controller to auto-start services
at boot.
NumArgs - Supplies the number of command-line arguments for the demand
started service. If ServiceToStart is NULL, this parameter is ignored.
CmdArgs - Supplies an array of command arguments to the demand started
service. If ServiceToStart is NULL, this parameter is ignored.
WaitForAll - Supplies a flag which if TRUE tells this function to
wait until all start-pending services that were marked START_NOW
to get done.
Return Value:
Returns error if failure to reset the ScServiceChangeStateEvent.
--*/
{
DWORD Error;
BOOL AllStarted;
BOOL ExistsBlockedService;
BOOL IsBadDependencies;
BOOL IsStartPending;
DWORD ServiceCurrentState;
LPSERVICE_RECORD Service;
#if DBG
DWORD LoopCount = 0;
#endif
LPWSTR ScSubStrings[2];
WCHAR ScErrorCodeString[25];
//
// Reset ScServiceChangeStateEvent to non-signalled state
//
if (! ResetEvent(ScServiceChangeStateEvent)) {
Error = GetLastError();
//
// This is a serious error--we cannot proceed.
//
SC_LOG1(ERROR, "Error reseting ScServiceChangeStateEvent " FORMAT_DWORD
"\n", Error);
ScSubStrings[0] = SC_RESET_EVENT;
wcscpy(ScErrorCodeString,L"%%");
ultow(Error, ScErrorCodeString+2, 10);
ScSubStrings[1] = ScErrorCodeString;
ScLogEvent(
EVENT_CALL_TO_FUNCTION_FAILED,
2,
ScSubStrings
);
return Error;
}
//
// Start all services that are marked
//
do { // while (! AllStarted)
AllStarted = TRUE;
IsStartPending = FALSE;
ExistsBlockedService = FALSE;
IsBadDependencies = FALSE;
SC_LOG1(DEPEND, "BIG LOOP COUNT " FORMAT_DWORD "\n", LoopCount++);
//
// Loop through every service which is currently in the database
//
FOR_ALL_SERVICES(Service)
{
//
// Check if the current service failed to start, and if we have
// to revert to last-known-good. Don't revert if demand start.
//
if (! ARGUMENT_PRESENT(ServiceToStart)) {
ScHandleServiceFailure(Service);
}
if (Service->StartState == SC_START_NOW) {
SC_LOG1(DEPEND, FORMAT_LPWSTR " is marked START NOW\n",
Service->ServiceName);
//
// Start the current service only if all its dependencies
// have started successfully.
//
if (ScDependenciesStarted(
Service,
&IsBadDependencies,
&AllStarted,
&ExistsBlockedService
)) {
//
// Start the service and save the start error code
//
SC_LOG1(DEPEND, "ScStartMarkedServices: Starting "
FORMAT_LPWSTR "\n", Service->ServiceName);
if (Service == ServiceToStart) {
Service->StartError = ScStartService(
Service,
NumArgs,
CmdArgs
);
}
else {
Service->StartError = ScStartService(
Service,
0,
NULL
);
//
// We are starting a new service so remember to loop
// through again to process any service which are
// dependent on it. Don't have to set AllStarted
// to FALSE if this service is ServiceToStart because
// nothing is dependent on it since it is demand
// started.
//
AllStarted = FALSE;
}
if (Service->StartError == NO_ERROR ||
Service->StartError == ERROR_SERVICE_ALREADY_RUNNING) {
//
// Get the state of the just started service
//
ScDatabaseLock(SC_GET_SHARED, "StartMarkedService1");
ServiceCurrentState = Service->ServiceStatus.dwCurrentState;
ScDatabaseLock(SC_RELEASE, "StartMarkedServices2");
switch (ServiceCurrentState) {
case SERVICE_START_PENDING:
IsStartPending = TRUE;
Service->StartState = SC_START_PENDING;
break;
case SERVICE_STOP_PENDING:
case SERVICE_PAUSED:
case SERVICE_CONTINUE_PENDING:
case SERVICE_PAUSE_PENDING:
case SERVICE_RUNNING:
Service->StartState = SC_START_SUCCESS;
break;
case SERVICE_STOPPED:
Service->StartState = SC_START_FAIL;
break;
default:
SC_LOG1(ERROR, "Unexpected service state "
FORMAT_HEX_DWORD "\n",
ServiceCurrentState);
SC_ASSERT(FALSE);
Service->StartState = SC_START_FAIL;
}
}
else {
//
// Clear ERROR_SERVICE_NEVER_STARTED in the Win32ExitCode
// field if service failed to start.
//
ScDatabaseLock(SC_GET_EXCLUSIVE, "StartMarkedService3");
if (Service->ServiceStatus.dwWin32ExitCode ==
ERROR_SERVICE_NEVER_STARTED) {
Service->ServiceStatus.dwWin32ExitCode = Service->StartError;
}
ScDatabaseLock(SC_RELEASE, "StartMarkedServices4");
Service->StartState = SC_START_FAIL;
//
// For popup after user has logged on to indicate that some
// service started at boot has failed.
// We don't log the error if it is ERROR_IGNORE.
//
if (Service->ErrorControl != SERVICE_ERROR_IGNORE) {
ScSubStrings[0] = Service->DisplayName;
wcscpy(ScErrorCodeString,L"%%");
ultow(Service->StartError, ScErrorCodeString+2, 10);
ScSubStrings[1] = ScErrorCodeString;
ScLogEvent(
EVENT_SERVICE_START_FAILED,
2,
ScSubStrings
);
ScPopupStartFail = TRUE;
}
}
}
}
else if (Service->StartState == SC_START_PENDING) {
//
// We need to wait for this pending service to be completely
// started if:
// 1) We are auto-starting services in sequence;
// ServiceToStart == NULL
// 2) We are demand starting ServiceToStart and
// it depends on services that are currently
// start-pending
//
// We don't wait if the pending service is started by demand
// and is unrelated in the start sequence of ServiceToStart,
// or it is ServiceToStart itself.
//
if ((Service != ServiceToStart) &&
ScServiceToStartDependOn(ServiceToStart, Service)) {
SC_LOG1(DEPEND, FORMAT_LPWSTR " is still PENDING\n",
Service->ServiceName);
IsStartPending = TRUE;
AllStarted = FALSE;
}
}
} // for every service
//
// Only wait for services to finish starting if:
// the services are auto-started at boot
// the services are required to be running before a service that
// is demand-started can run.
//
if (IsStartPending && (ExistsBlockedService || WaitForAll)) {
SC_LOG0(DEPEND, "About to wait on ScServiceChangeEvent\n");
//
// ScServiceChangeStateEvent is signalled by RSetServiceStatus whenever
// a service changes its state from SERVICE_START_PENDING to
// SERVICE_RUNNING or SERVICE_STOPPED.
//
Error = WaitForSingleObject(
ScServiceChangeStateEvent,
SERVICE_START_TIMEOUT
);
if (Error == WAIT_TIMEOUT) {
//
// Go through all services and see if any one has hung
// while starting.
//
ScLookForHungServices();
}
else if (Error == 0) {
//
// Reset ScServiceChangeStateEvent to non-signalled state
//
if (! ResetEvent(ScServiceChangeStateEvent)) {
Error = GetLastError();
//
// This is a serious error--we cannot proceed.
//
SC_LOG1(ERROR, "Error reseting ScServiceChangeStateEvent "
FORMAT_DWORD "\n", Error);
ScSubStrings[0] = SC_RESET_EVENT;
wcscpy(ScErrorCodeString,L"%%");
ultow(Error, ScErrorCodeString+2, 10);
ScSubStrings[1] = ScErrorCodeString;
ScLogEvent(
EVENT_CALL_TO_FUNCTION_FAILED,
2,
ScSubStrings
);
return Error;
}
}
else if (Error == 0xffffffff) {
//
// An error has occurred
//
SC_LOG1(ERROR, "Wait for ScServiceChangeStateEvent returned "
FORMAT_DWORD "\n", GetLastError());
SC_ASSERT(FALSE);
}
}
else if ((AllStarted && ExistsBlockedService) || IsBadDependencies) {
//
// Circular dependencies!
//
SC_LOG0(ERROR, "Detected circular dependencies!!\n");
SC_LOG3(ERROR,
"AllStarted=" FORMAT_DWORD
", ExistsBlockedService=" FORMAT_DWORD
", IsBadDependencies=" FORMAT_DWORD "\n",
(DWORD) AllStarted, (DWORD) ExistsBlockedService,
(DWORD) IsBadDependencies);
if (ARGUMENT_PRESENT(ServiceToStart)) {
SC_LOG1(ERROR, " Demand starting " FORMAT_LPWSTR "\n",
ServiceToStart->DisplayName);
ScSubStrings[0] = ServiceToStart->DisplayName;
ScLogEvent(
EVENT_CIRCULAR_DEPENDENCY_DEMAND,
1,
ScSubStrings
);
}
else {
SC_LOG0(ERROR, " Auto-starting services\n");
ScLogEvent(
EVENT_CIRCULAR_DEPENDENCY_AUTO,
0,
ScSubStrings
);
ScHandleBadDependencies();
}
return ERROR_CIRCULAR_DEPENDENCY;
}
} while (! AllStarted);
return NO_ERROR;
}
VOID
ScHandleServiceFailure(
IN LPSERVICE_RECORD Service
)
/*++
Routine Description:
This function checks to see if the specified service failed to start.
If so, it clears the SERVICE_START_NOW flag, and determine if we
have to revert to last-known-good.
Arguments:
Service - Supplies a pointer to the service record to examine if
the service failed to start.
Return Value:
None.
--*/
{
if (Service->StartState == SC_START_FAIL) {
//
// Revert to last-known-good only if service is auto-start and
// fail to start due to reasons other than failure to logon.
//
if ((Service->ErrorControl == SERVICE_ERROR_SEVERE ||
Service->ErrorControl == SERVICE_ERROR_CRITICAL) &&
CURRENTSTART_FLAG_IS_SET(Service) &&
Service->StartError != ERROR_SERVICE_LOGON_FAILED) {
SC_LOG1(DEPEND,
"ScHandleServiceFailure: "
"About to call ScRevertToLastKnownGood for " FORMAT_LPWSTR
"\n", Service->ServiceName);
(void) ScRevertToLastKnownGood();
}
}
}
BOOL
ScDependenciesStarted(
IN LPSERVICE_RECORD Service,
OUT BOOL *IsBadDependencies,
OUT BOOL *AllStarted,
OUT BOOL *ExistsBlockedService
)
/*++
Routine Description:
This function checks to see if the dependencies of the specified
service are all started. If any of the dependencies has failed to
start, the specified service will be marked as failed to start.
If any of the dependencies is not marked as starting now (because
they are demand-start services), they are marked to be started now.
Arguments:
Service - Supplies a pointer to the service which we want to check
the start dependencies.
IsBadDependencies - Receives the value of TRUE if the service we
depend on belongs in a group that starts after the group we
are in. Otherwise, FALSE is returned.
AllStarted - Receives the value of FALSE if we have marked
a service as failed to be started because its dependent
didn't start. This means that our job of starting all services is
not done and we have to loop through an additional time to resolve
the state of any service that is dependent on it.
ExistsBlockedService - Receives the value of TRUE if a dependent
is not started or not failed to start. This indicates that
the specified service is still blocked from starting.
Return Value:
TRUE - if all dependencies have already been started successfully.
FALSE - if there exists one dependency that has not started or failed
to start.
--*/
{
BOOL AllDependenciesStarted = TRUE;
LPDEPEND_RECORD DependEntry;
LPSERVICE_RECORD DependService;
LPLOAD_ORDER_GROUP DependGroup;
DWORD GroupState;
LPWSTR ScSubStrings[3];
WCHAR ScErrorCodeString[25];
for (DependEntry = Service->StartDepend;
DependEntry != NULL;
DependEntry = DependEntry->Next)
{
switch (DependEntry->DependType)
{
case TypeDependOnUnresolved:
//
// Error with service setup because it depends on a group or
// service which does not exists
//
SC_LOG2(ERROR, FORMAT_LPWSTR " depends on non-existing " FORMAT_LPWSTR
"\n", Service->DisplayName,
DependEntry->DependUnresolved->Name);
if (Service->ErrorControl != SERVICE_ERROR_IGNORE) {
ScSubStrings[0] = Service->DisplayName;
ScSubStrings[1] = DependEntry->DependUnresolved->Name;
ScLogEvent(
EVENT_SERVICE_START_FAILED_NONE,
2,
ScSubStrings
);
}
ScCleanupStartFailure(Service, ERROR_SERVICE_DEPENDENCY_DELETED);
*AllStarted = FALSE;
return FALSE;
case TypeDependOnService:
//
// Depend on a service
//
DependService = DependEntry->DependService;
//
// If dependency service already failed to start, the current service
// is set as failed to start.
//
if (DependService->StartState == SC_START_FAIL) {
SC_LOG3(ERROR, FORMAT_LPWSTR " depends on " FORMAT_LPWSTR
" which failed to start because " FORMAT_DWORD "\n",
Service->DisplayName, DependService->DisplayName,
(DependService->StartError != NO_ERROR) ?
DependService->StartError :
DependService->ServiceStatus.dwWin32ExitCode);
if (Service->ErrorControl != SERVICE_ERROR_IGNORE &&
! SetupInProgress(NULL)) {
ScSubStrings[0] = Service->DisplayName;
ScSubStrings[1] = DependService->DisplayName;
wcscpy(ScErrorCodeString,L"%%");
ScSubStrings[2] = ScErrorCodeString;
if (DependService->StartError != NO_ERROR) {
ultow(DependService->StartError, ScErrorCodeString+2, 10);
}
else {
ultow(DependService->ServiceStatus.dwWin32ExitCode,
ScErrorCodeString+2,
10
);
}
ScLogEvent(
EVENT_SERVICE_START_FAILED_II,
3,
ScSubStrings
);
}
ScCleanupStartFailure(Service, ERROR_SERVICE_DEPENDENCY_FAIL);
*AllStarted = FALSE;
return FALSE;
}
if (DependService->StartState == SC_NEVER_STARTED) {
*IsBadDependencies = IsDependOnLaterGroup(
Service->MemberOfGroup,
DependService->MemberOfGroup,
TypeDependOnService
);
if (*IsBadDependencies) {
//
// Circular dependency!
//
SC_LOG1(ERROR, "Circular dependency! " FORMAT_LPWSTR
" depends on service in a group which starts later\n",
Service->DisplayName);
if (Service->ErrorControl != SERVICE_ERROR_IGNORE) {
ScSubStrings[0] = Service->DisplayName;
ScLogEvent(
EVENT_DEPEND_ON_LATER_SERVICE,
1,
ScSubStrings
);
}
ScCleanupStartFailure(Service, ERROR_CIRCULAR_DEPENDENCY);
return FALSE;
}
//
// No circular dependency. Mark the dependency service
// as START_NOW.
//
DependService->StartState = SC_START_NOW;
DependService->StartError = NO_ERROR;
*AllStarted = FALSE;
}
//
// Get the current state of the dependency service
//
if (DependService->StartState != SC_START_SUCCESS) {
AllDependenciesStarted = FALSE;
if (DependService->StartState != SC_START_FAIL) {
//
// The current service is still blocked.
//
*ExistsBlockedService = TRUE;
}
}
break;
case TypeDependOnGroup:
//
// Depend on a group
//
DependGroup = DependEntry->DependGroup;
GroupState = ScGetCurrentGroupState(DependGroup);
switch (GroupState)
{
case GROUP_START_FAIL:
SC_LOG2(ERROR, FORMAT_LPWSTR " depends on failed group "
FORMAT_LPWSTR "\n", Service->DisplayName,
DependGroup->GroupName);
if (Service->ErrorControl != SERVICE_ERROR_IGNORE &&
! SetupInProgress(NULL)) {
ScSubStrings[0] = Service->DisplayName;
ScSubStrings[1] = DependGroup->GroupName;
ScLogEvent(
EVENT_SERVICE_START_FAILED_GROUP,
2,
ScSubStrings
);
}
ScCleanupStartFailure(Service, ERROR_SERVICE_DEPENDENCY_FAIL);
*AllStarted = FALSE;
return FALSE;
case GROUP_NOT_STARTED:
*IsBadDependencies = IsDependOnLaterGroup(
Service->MemberOfGroup,
DependGroup,
TypeDependOnGroup
);
if (*IsBadDependencies) {
//
// Circular dependency!
//
SC_LOG1(ERROR, "Circular dependency! " FORMAT_LPWSTR
" depends on a group which starts later\n",
Service->DisplayName);
if (Service->ErrorControl != SERVICE_ERROR_IGNORE) {
ScSubStrings[0] = Service->DisplayName;
ScLogEvent(
EVENT_DEPEND_ON_LATER_GROUP,
1,
ScSubStrings
);
}
ScCleanupStartFailure(Service, ERROR_CIRCULAR_DEPENDENCY);
return FALSE;
}
//
// No circular dependency. Mark the services in the
// dependency group to START_NOW.
//
{
LPSERVICE_RECORD Svc;
FOR_SERVICES_THAT(Svc,
BELONGS_TO(Svc, DependGroup) &&
Svc->StartState == SC_NEVER_STARTED)
{
Svc->StartState = SC_START_NOW;
Svc->StartError = NO_ERROR;
}
}
AllDependenciesStarted = FALSE;
*ExistsBlockedService = TRUE;
break;
default:
//
// Otherwise group must be started. Nothing to do.
//
SC_ASSERT(GroupState == GROUP_ONE_STARTED);
break;
}
break;
}
}
return AllDependenciesStarted;
}
BOOL
IsDependOnLaterGroup(
IN LPLOAD_ORDER_GROUP ServiceGroup,
IN LPLOAD_ORDER_GROUP DependOnGroup,
IN DEPEND_TYPE DependType
)
{
LPLOAD_ORDER_GROUP Group;
switch (DependType)
{
case TypeDependOnService:
if (ServiceGroup == DependOnGroup) {
//
// It is OK for a service to depend on another service
// in the same group.
//
return FALSE;
}
break;
case TypeDependOnGroup:
if (IS_SUBGROUP(ServiceGroup, DependOnGroup)) {
//
// It is circular dependency if a service depends on the
// group it itself belongs to
//
return TRUE;
}
break;
default:
SC_LOG(ERROR, "IsDependOnLaterGroup: got invalid DependType %lu\n",
DependType);
SC_ASSERT(FALSE);
return FALSE;
}
if (ServiceGroup == NULL ||
ServiceGroup->RefCount != MAXULONG ||
DependOnGroup == NULL ||
DependOnGroup->RefCount != MAXULONG) {
//
// Service we are starting belongs to a standalone group,
// or service or group we depend on is standalone.
//
return FALSE;
}
//
// Both the service's group and the depended on group are in the
// load order group list.
// The depended on group must not occur after the service's group.
// TDI GROUP SPECIAL: Also, if the depended on group is the TDI
// group, then there is an implicit dependency on the PNP_TDI group,
// so that must not occur after the service's group either.
//
for (Group = ServiceGroup->Next;
Group != NULL;
Group = Group->Next)
{
if (IS_SUBGROUP(Group, DependOnGroup)) {
return TRUE;
}
}
return FALSE;
}
VOID
ScCleanupStartFailure(
LPSERVICE_RECORD Service,
DWORD StartError
)
{
Service->StartState = SC_START_FAIL;
Service->StartError = StartError;
//
// Clear ERROR_SERVICE_NEVER_STARTED in the Win32ExitCode field if
// service failed to start.
//
ScDatabaseLock(SC_GET_EXCLUSIVE, "ScCleanupStartFailure");
if (Service->ServiceStatus.dwWin32ExitCode ==
ERROR_SERVICE_NEVER_STARTED) {
Service->ServiceStatus.dwWin32ExitCode = StartError;
}
ScDatabaseLock(SC_RELEASE, "ScCleanupStartFailure");
//
// For popup after user has logged on to indicate that some
// service started at boot has failed.
//
if (Service->ErrorControl != SERVICE_ERROR_IGNORE) {
ScPopupStartFail = TRUE;
}
}
VOID
ScLookForHungServices(
VOID
)
/*++
Routine Description:
This function loops through all services and queries the status
of each service that is start pending. It waits for the service to
show signs of progress in starting by waiting for the wait-hint
amount of time (in millisecs), and if the service is still start
pending and checkpoint has not incremented, the service's exitcode
is set to ERROR_SERVICE_START_HANG.
Arguments:
None.
Return Value:
None.
--*/
{
DWORD status;
LPSERVICE_RECORD Service;
SERVICE_STATUS CurrentServiceStatus;
DWORD OldCheckPoint;
LPWSTR ScSubStrings[1];
FOR_SERVICES_THAT(Service, Service->StartState == SC_START_PENDING)
{
status = ScQueryServiceStatus(
Service,
&CurrentServiceStatus
);
if ((status == NO_ERROR) &&
(CurrentServiceStatus.dwCurrentState == SERVICE_START_PENDING)) {
#define SC_POLL_FACTOR 10
#define SC_MAX_SLEEP_TIME 10000
DWORD SleepTime = 1;
DWORD i;
DWORD NumIterations;
OldCheckPoint = CurrentServiceStatus.dwCheckPoint;
//
// Set up for the loop where we will poll the service status.
// The maximum sleep time during this polling operation will
// be 10 seconds.
//
//
// If the wait hint is greater than 100 seconds, then
// we want to modify the number of iterations through the
// loop so that we only sleep for the MAX_SLEEP_TIME.
//
// If the wait hint is less than that, then we change the
// sleep time to be less than 10 seconds, so that we go
// through the loop a max of 10 times.
//
if (CurrentServiceStatus.dwWaitHint > 100000) {
NumIterations = CurrentServiceStatus.dwWaitHint / SC_MAX_SLEEP_TIME;
SleepTime = SC_MAX_SLEEP_TIME;
}
else {
NumIterations = SC_POLL_FACTOR;
if (CurrentServiceStatus.dwWaitHint > SC_POLL_FACTOR) {
SleepTime = CurrentServiceStatus.dwWaitHint / SC_POLL_FACTOR;
}
}
for (i = 0; i < NumIterations; i++) {
//
// Wait a while for the checkpoint to increment, or
// service to be out of start-pending state.
//
Sleep(SleepTime);
status = ScQueryServiceStatus(
Service,
&CurrentServiceStatus
);
if (status == NO_ERROR) {
if (CurrentServiceStatus.dwCurrentState != SERVICE_START_PENDING ||
(CurrentServiceStatus.dwCurrentState == SERVICE_START_PENDING &&
OldCheckPoint < CurrentServiceStatus.dwCheckPoint)) {
goto NextService;
}
}
SC_LOG2(DEPEND, " Wait %ld on %ws for response\n", i + 1,
Service->ServiceName);
}
if ((status == NO_ERROR) &&
(CurrentServiceStatus.dwCurrentState == SERVICE_START_PENDING) &&
(OldCheckPoint == CurrentServiceStatus.dwCheckPoint)) {
SC_LOG2(ERROR, "%ws hung on starting (wait hint %lu ms)\n",
Service->DisplayName,
CurrentServiceStatus.dwWaitHint);
if (Service->ErrorControl != SERVICE_ERROR_IGNORE) {
ScSubStrings[0] = Service->DisplayName;
ScLogEvent(
EVENT_SERVICE_START_HUNG,
1,
ScSubStrings
);
}
ScCleanupStartFailure(Service, ERROR_SERVICE_START_HANG);
}
}
if (status != NO_ERROR) {
SC_LOG2(ERROR, "ScLookForHungService: ScQueryServiceStatus "
FORMAT_LPWSTR " failed " FORMAT_DWORD "\n",
Service->ServiceName, status);
Service->StartState = SC_START_FAIL;
Service->StartError = ERROR_GEN_FAILURE;
}
NextService:
;
}
}
VOID
ScHandleBadDependencies(
VOID
)
/*++
Routine Description:
This function is called when a circular dependency is detected.
Arguments:
None.
Return Value:
None.
--*/
{
LPWSTR ScSubStrings[1];
LPSERVICE_RECORD Service;
FOR_SERVICES_THAT(Service,
(Service->StartState == SC_START_NOW) &&
(Service->ErrorControl == SERVICE_ERROR_SEVERE ||
Service->ErrorControl == SERVICE_ERROR_CRITICAL) )
{
SC_LOG1(ERROR, "ScHandleBadDependencies: "
"About to call ScRevertToLastKnownGood for "
FORMAT_LPWSTR "\n", Service->DisplayName);
ScSubStrings[0] = Service->DisplayName;
ScLogEvent(
EVENT_SEVERE_SERVICE_FAILED,
1,
ScSubStrings
);
(void) ScRevertToLastKnownGood();
}
}
BOOL
ScServiceToStartDependOn(
LPSERVICE_RECORD ServiceToStart OPTIONAL,
LPSERVICE_RECORD StartPendingService
)
/*++
Routine Description:
This function is called by ScStartMarkedServices (BIG LOOP) to determine
if we have to wait for a pending service to complete.
If ServiceToStart == NULL, we are auto-starting service and we always
want to wait.
If ServiceToStart is not NULL, we are demand starting a service.
We have to wait if ServiceToStart depends on the StartPendingService.
Arguments:
ServiceToStart - Supplies the service record pointer of the service
being demand started.
StartPendingService - Supplies the service record pointer of the
service that is currently start pending.
Return Value:
TRUE - If ServiceToStart depends on StartPendingService or
ServiceToStart == NULL.
FALSE - Otherwise.
--*/
{
if (! ARGUMENT_PRESENT(ServiceToStart)) {
return TRUE;
}
if (ServiceToStart->StartState == SC_START_FAIL) {
return FALSE;
}
if (CURRENTSTART_FLAG_IS_SET(StartPendingService)) {
SC_LOG2(DEPEND_DUMP, "Service %ws directly/indirectly depends on pending service %ws\n",
ServiceToStart->ServiceName,
StartPendingService->ServiceName);
return TRUE;
}
SC_LOG(DEPEND_DUMP, "ScServiceToStartDependOn: Won't wait for pending "
FORMAT_LPWSTR "\n", StartPendingService->ServiceName);
return FALSE;
}
VOID
ScNotifyChangeState(
VOID
)
/*++
Routine Description:
This function is called by RSetServiceStatus which a service state
changes from start-pending to running or stopped. This will
notify the thread processing start dependencies that we can
proceed with starting up services that depend on the one that
called RSetServiceStatus.
Arguments:
None.
Return Value:
None.
--*/
{
if (! SetEvent(ScServiceChangeStateEvent)) {
SC_LOG1(ERROR, "ScNotifyChangeState: SetEvent error " FORMAT_DWORD "\n",
GetLastError());
SC_ASSERT(FALSE);
}
}
BOOL
ScDependentsStopped(
IN LPSERVICE_RECORD ServiceToStop
)
/*++
Routine Description:
This function checks to see if any service which depends on the
specified service is active. If so, it returns FALSE, otherwise
if no service depends on the specified service, or all services
which depend on the specified service is stopped, it returns
TRUE.
A service which is not in SERVICE_STOPPED is considered active.
Arguments:
ServiceToStop - Supplies a pointer to the service to see if other
active services depend on it.
Return Value:
TRUE - if all services which depend on ServiceToStop are stopped,
or there are no services which depend on ServiceToStop.
FALSE - if one or more of the services which depend on ServiceToStop
is active.
Note:
The database lock must be acquired with share access before calling
this routine.
--*/
{
LPDEPEND_RECORD StopDepend;
for (StopDepend = ServiceToStop->StopDepend;
StopDepend != NULL;
StopDepend = StopDepend->Next)
{
if (StopDepend->DependService->ServiceStatus.dwCurrentState
!= SERVICE_STOPPED) {
SC_LOG1(DEPEND, FORMAT_LPWSTR " is still ACTIVE\n",
StopDepend->DependService->ServiceName);
return FALSE;
}
SC_LOG1(DEPEND, FORMAT_LPWSTR " is STOPPED\n",
StopDepend->DependService->ServiceName);
}
return TRUE;
}
VOID
ScEnumDependents(
IN LPSERVICE_RECORD ServiceRecord,
IN LPENUM_SERVICE_STATUSW EnumBuffer,
IN DWORD RequestedState,
IN OUT LPDWORD EntriesRead,
IN OUT LPDWORD BytesNeeded,
IN OUT LPENUM_SERVICE_STATUSW *EnumRecord,
IN OUT LPWSTR *EndOfVariableData,
IN OUT LPDWORD Status
)
/*++
Routine Description:
This function enumerates the stop depend list of the specified
service in the order which the dependents should be stopped.
Arguments:
ServiceRecord - Supplies a pointer to the service whose dependents
are to be enumerated.
EnumBuffer - Supplies a pointer to the first byte of the enum
buffer we are writing to. This is for duplicate entry checking.
RequestedState - Supplies one or the bitwise or of SERVICE_ACTIVE
and SERVICE_INACTIVE.
BytesNeeded - Supplies a pointer to a variable to receive the
running sum of bytes needed to enumerate all the entries.
EnumRecord - Supplies a pointer into the next location in the
output buffer to receive the next entry. The pointer is
updated on return.
EndOfVariableData - Supplies a pointer past the last available
byte in the output buffer so that variable length data
can be written from the end of the buffer. This pointer is
updated on return.
Status - Receives ERROR_MORE_DATA if dependent services does not
entirely fit in the output buffer. It should be initialized
to NO_ERROR this function is called.
Return Value:
None.
Note:
The database lock must be acquired with share access before calling
this routine.
--*/
{
LPDEPEND_RECORD StopDepend;
for (StopDepend = ServiceRecord->StopDepend;
StopDepend != NULL;
StopDepend = StopDepend->Next)
{
if (StopDepend->DependService->StopDepend != NULL) {
//
// Stop dependent also have other services that depends on
// it. Recursively call this routine to enumerate its
// dependents.
//
ScEnumDependents(
StopDepend->DependService,
EnumBuffer,
RequestedState,
EntriesRead,
BytesNeeded,
EnumRecord,
EndOfVariableData,
Status
);
}
if (
((StopDepend->DependService->ServiceStatus.dwCurrentState
!= SERVICE_STOPPED) &&
(RequestedState & SERVICE_ACTIVE))
||
((StopDepend->DependService->ServiceStatus.dwCurrentState
== SERVICE_STOPPED) &&
(RequestedState & SERVICE_INACTIVE))
) {
SC_LOG1(DEPEND, "Enumerating dependent " FORMAT_LPWSTR "\n",
StopDepend->DependService->ServiceName);
if (! ScFoundDuplicateDependent(
StopDepend->DependService->ServiceName,
EnumBuffer,
*EnumRecord
)) {
*BytesNeeded += (sizeof(ENUM_SERVICE_STATUSW) +
WCSSIZE(StopDepend->DependService->ServiceName) +
WCSSIZE(StopDepend->DependService->DisplayName));
if (*Status == NO_ERROR) {
if (((DWORD) *EnumRecord + sizeof(ENUM_SERVICE_STATUSW)) >=
(DWORD) *EndOfVariableData) {
*Status = ERROR_MORE_DATA;
}
else {
//
// Write the entry into output buffer
//
memcpy(
(PVOID) &((*EnumRecord)->ServiceStatus),
(PVOID) &(StopDepend->DependService->ServiceStatus),
sizeof(SERVICE_STATUS)
);
//
// Copy the ServiceName string data
//
if (! ScCopyStringToBufferW(
StopDepend->DependService->ServiceName,
wcslen(StopDepend->DependService->ServiceName),
(LPWSTR) ((LPENUM_SERVICE_STATUSW) (*EnumRecord) + 1),
EndOfVariableData,
(LPWSTR *) &((*EnumRecord)->lpServiceName)
)) {
*Status = ERROR_MORE_DATA;
}
//
// Copy the DisplayName string data
//
if (! ScCopyStringToBufferW(
StopDepend->DependService->DisplayName,
wcslen(StopDepend->DependService->DisplayName),
(LPWSTR) ((LPENUM_SERVICE_STATUSW) (*EnumRecord) + 1),
EndOfVariableData,
(LPWSTR *) &((*EnumRecord)->lpDisplayName)
)) {
*Status = ERROR_MORE_DATA;
}
}
if (*Status == NO_ERROR) {
(*EnumRecord)++;
(*EntriesRead)++;
SC_LOG0(DEPEND, " Written into buffer successfully\n");
}
else {
SC_LOG0(DEPEND, " Failed to fit into buffer\n");
}
} // *Status is still NO_ERROR
} // non-duplicate entry
}
}
}
BOOL
ScFoundDuplicateDependent(
IN LPWSTR ServiceName,
IN LPENUM_SERVICE_STATUSW EnumBuffer,
IN LPENUM_SERVICE_STATUSW BufferEnd
)
/*++
Routine Description:
This function looks at service entries written to EnumBuffer for
any service names that matches the specified ServiceName.
Arguments:
ServiceName - Supplies the name of the service to look for.
EnumBuffer - Supplies a pointer to the buffer to look for matching
service name.
BufferEnd - Supplies a pointer to the end of buffer.
Return Value:
TRUE - if found a matching service name.
FALSE - no matching service name found.
--*/
{
LPENUM_SERVICE_STATUSW EnumEntry;
for (EnumEntry = EnumBuffer;
EnumEntry < BufferEnd;
EnumEntry++)
{
if (_wcsicmp(EnumEntry->lpServiceName, ServiceName) == 0) {
return TRUE;
}
}
return FALSE;
}
#ifndef _CAIRO_
VOID
ScCheckNetLogonDepend(
LPSERVICE_RECORD ServiceRecord,
BOOL DemandStarting
)
/*++
Routine Description:
If the current service is running in a remote account of if we are
running on an Advanced Server (NtProductLanManNt), then this routine
makes a (soft) dependency on Netlogon. This dependency is not stored
in the registry.
Arguments:
ServiceRecord - Pointer to the service record that is to be checked.
DemandStarting - boolean that indicates if we are demand starting or
auto starting.
Return Value:
none - If something fails within this function, we will just press on
since there isn't much we can do about it.
--*/
{
DWORD status;
HKEY ServiceNameKey;
LPWSTR DomainName;
BOOL bRemoteAccount=TRUE;
LPSERVICE_RECORD pNetLogonSR;
//
// Open the service name key.
//
status = ScOpenServiceConfigKey(
ServiceRecord->ServiceName,
KEY_READ,
FALSE, // Create if missing
&ServiceNameKey
);
if (status != NO_ERROR) {
return;
}
//
// Read the account name from the registry.
//
status = ScReadStartName(
ServiceNameKey,
&DomainName
);
if (status != NO_ERROR) {
ScRegCloseKey(ServiceNameKey);
return;
}
ScRegCloseKey(ServiceNameKey);
if (_wcsicmp(DomainName, SC_LOCAL_SYSTEM_USER_NAME) == 0) {
//
// LocalSystem account, we don't need netlogon.
//
SC_LOG1(TRACE,"ScCheckNetLogonDepend: %ws Service is LocalSystem!\n",
ServiceRecord->ServiceName);
LocalFree(DomainName);
return;
}
else if (wcsncmp(DomainName, L".\\", 2) == 0) {
bRemoteAccount = FALSE;
SC_LOG1(TRACE,"ScCheckNetLogonDepend: %ws Service has a local domain name\n",
ServiceRecord->ServiceName);
}
LocalFree(DomainName);
//
// We know if it runs in a remote account or not.
// Now we should check the product type. If it is an
// advanced server, or runs in an remote account, then
// we need to start NetLogon.
//
if ((ScGlobalProductType == NtProductLanManNt) || (bRemoteAccount)) {
//
// Get the service record for NetLogon.
//
status = ScGetNamedServiceRecord(L"NetLogon", &pNetLogonSR);
if (status != NO_ERROR) {
return;
}
//
// If it is already marked to start, then we don't
// have to do anything right now. If it isn't then
// we should SetServiceStartRequest and create a
// dependency.
//
if (CURRENTSTART_FLAG_IS_SET(pNetLogonSR)) {
//
// CurrentStartRequest of a dependency service is already
// set to TRUE. Just go on to next dependency.
//
SC_LOG2(DEPEND_DUMP, "DependService " FORMAT_LPWSTR
" CSR=TRUE already, USECOUNT=%lu\n",
pNetLogonSR->ServiceName,
pNetLogonSR->UseCount);
}
else {
LPDEPEND_RECORD pDependRecord;
ScSetServiceStartRequest(pNetLogonSR,DemandStarting);
//
// Add the dependency to the service record and mark it as
// temporary.
//
status = ScCreateDependRecord(TRUE,ServiceRecord,&pDependRecord);
if (status != NO_ERROR) {
return;
}
pDependRecord->DependType = TypeDependOnService;
pDependRecord->DependService = pNetLogonSR;
}
}
return;
}
#endif // _CAIRO_
BOOL
ScInHardwareProfile(
IN LPSERVICE_RECORD Service,
IN ULONG GetDeviceListFlags
)
/*++
Routine Description:
This function checks whether a specified service is enabled in the
current hardware profile.
Arguments:
Service - Specifies the service of interest.
GetDeviceListFlags - Specifies any special flags to be passed to
PNP_GetDeviceList. The CM_GETIDLIST_DONOTGENERATE
flag indicates that a legacy device instance should
not be generated for the service.
Return Value:
TRUE - if the service is enabled in the current hardware profile, or
if this cannot be determined.
FALSE - if the service is disabled in the current hardware profile.
--*/
{
CONFIGRET Status;
BOOL RetStatus;
WCHAR Buffer[50]; // default buffer on stack
WCHAR * pBuffer = Buffer;
ULONG cchLen;
LPCWSTR pDeviceID;
//
// Allocate a buffer for the list of device instances associated with
// this service
//
Status = PNP_GetDeviceListSize(
NULL, // hBinding
Service->ServiceName, // pszFilter
&cchLen, // list length in wchars
CM_GETIDLIST_FILTER_SERVICE); // filter is a service name
if (Status != CR_SUCCESS)
{
SC_LOG2(ERROR, "PNP_GetDeviceListSize failed %#lx for service %ws\n",
Status, Service->ServiceName);
return TRUE;
}
if (cchLen > LENGTH(Buffer))
{
SC_LOG2(DEPEND, "PNP_GetDeviceListSize wants a %lu-character buffer for service %ws\n",
cchLen, Service->ServiceName);
pBuffer = (WCHAR *) LocalAlloc(0, cchLen * sizeof(WCHAR));
if (pBuffer == NULL)
{
SC_LOG(ERROR, "Couldn't allocate buffer for device list, error %lu\n",
GetLastError());
return TRUE;
}
}
else
{
cchLen = LENGTH(Buffer);
}
//
// Initialize parameters for PNP_GetDeviceList, the same way as is
// normally done in the client side of the API
//
pBuffer[0] = L'\0';
//
// Get the list of device instances that are associated with this service
//
// (For legacy services, the PNP manager makes up an artificial device
// instance; but for PNP-aware services, we could get an empty device list.)
//
Status = PNP_GetDeviceList(
NULL, // binding handle
Service->ServiceName, // pszFilter
pBuffer, // buffer for device list
&cchLen, // buffer length in wchars
CM_GETIDLIST_FILTER_SERVICE | // filter is a service name
GetDeviceListFlags // OR with passed in flag
);
if (Status != CR_SUCCESS)
{
SC_LOG2(ERROR, "PNP_GetDeviceList failed %#lx for service %ws\n",
Status, Service->ServiceName);
RetStatus = TRUE;
goto CleanExit;
}
//
// Get each device instance's config flags. The service is enabled in
// the current hardware profile if at least one of its devices is enabled.
//
for (pDeviceID = pBuffer;
pDeviceID[0] != L'\0';
pDeviceID += wcslen(pDeviceID) + 1)
{
ULONG ConfigFlags;
Status = PNP_HwProfFlags(
NULL, // binding handle
PNP_GET_HWPROFFLAGS, // action: get, not set
pDeviceID,
0, // which profile: current one
&ConfigFlags,
0 // flags, MBZ
);
if (Status == CR_SUCCESS)
{
if (!(ConfigFlags & (CSCONFIGFLAG_DISABLED |
CSCONFIGFLAG_DO_NOT_CREATE)))
{
//
// The device is enabled, so the service is enabled
//
RetStatus = TRUE;
goto CleanExit;
}
}
else
{
SC_LOG2(ERROR, "PNP_HwProfFlags failed %#lx for device %ws\n",
Status, pDeviceID);
}
}
RetStatus = FALSE;
SC_LOG(DEPEND, "The %ws service is disabled in this hardware profile\n",
Service->ServiceName);
CleanExit:
if (pBuffer != Buffer)
{
LocalFree(pBuffer);
}
return RetStatus;
}