mirror of https://github.com/tongzx/nt5src
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.
2958 lines
90 KiB
2958 lines
90 KiB
/*++
|
|
|
|
Copyright (c) 1992 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
depend.cxx
|
|
|
|
Abstract:
|
|
|
|
This module contains routines which handle service start and
|
|
stop dependencies:
|
|
ScInitAutoStart
|
|
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-2000 JSchwart
|
|
Add no-reboot support for machine name change during OOBE
|
|
setup and initial boot
|
|
08-Jan-1997 AnirudhS
|
|
Remove I_ScGetCurrentGroupStateW as it is no longer used. In
|
|
ScStartServiceAndDependencies, get an exclusive grouplist lock
|
|
once more, instead of a shared one.
|
|
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 "precomp.hxx"
|
|
#include <string.h> // memcpy
|
|
#include <stdlib.h> // wcslen
|
|
#include <winerror.h>
|
|
#include <lmcons.h> // NET_API_STATUS
|
|
#include <srvann.h> // I_ScGetCurrentGroupStateW and related definitions
|
|
#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 <tstr.h> // WCSSIZE
|
|
#include <sclib.h> // ScCopyStringToBufferW
|
|
#include <svcslib.h> // SetupInProgress
|
|
#include <winsvcp.h> // OOBE setup event names
|
|
|
|
#include "start.h" // ScStartService
|
|
#include "depend.h"
|
|
#include "info.h" // ScQueryServiceStatus
|
|
#include "bootcfg.h" // ScRevertToLastKnownGood
|
|
#include "driver.h" // ScGetDriverStatus
|
|
#include "lockapi.h" // ScLockDatabase
|
|
#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]))
|
|
|
|
#define GROUP_NOT_STARTED 0x00000000
|
|
#define GROUP_ONE_STARTED 0x00000001
|
|
#define GROUP_START_FAIL 0x00000002
|
|
|
|
#define REG_KEY_SETUP_ALLOW_START L"System\\Setup\\AllowStart"
|
|
|
|
//
|
|
// 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;
|
|
|
|
//
|
|
// For telling internal routines when auto-start is in progress
|
|
//
|
|
BOOL ScAutoStartInProgress;
|
|
|
|
//-------------------------------------------------------------------//
|
|
// //
|
|
// 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_STATUS_WOW64 EnumBuffer,
|
|
IN LPENUM_SERVICE_STATUS_WOW64 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;
|
|
}
|
|
|
|
|
|
DWORD
|
|
ScAutoStartServices(
|
|
IN OUT LPSC_RPC_LOCK lpLock
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function automatically starts all services that must be
|
|
auto-started, in group order.
|
|
|
|
This routine may not return because we may instigate a reboot to
|
|
revert to last-known-good.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
BOOL fIsOOBE;
|
|
|
|
ScAutoStartInProgress = TRUE;
|
|
|
|
//
|
|
// Set current request flag of all auto-start services as well as
|
|
// their dependencies
|
|
//
|
|
|
|
SetupInProgress(NULL, &fIsOOBE);
|
|
|
|
if (fIsOOBE)
|
|
{
|
|
|
|
#define NUM_OOBE_SERVICES 2
|
|
|
|
DWORD dwError;
|
|
DWORD dwTries = 0;
|
|
LPSERVICE_RECORD ServiceRecord = NULL;
|
|
HANDLE hPnPDone;
|
|
HANDLE hMachineNameDone;
|
|
LPWSTR lpServicesForOobe[NUM_OOBE_SERVICES] = { L"PlugPlay", L"AudioSrv" };
|
|
UINT i;
|
|
|
|
SC_LOG0(TRACE,
|
|
"ScAutoStartServices: Is OOBE -- creating events\n");
|
|
|
|
//
|
|
// OOBE setup will wait on this event first. We set it
|
|
// once PnP is done starting to wake up OOBE setup (which
|
|
// will then prompt the user for the new machine name)
|
|
//
|
|
hPnPDone = CreateEvent(
|
|
NULL, // Event Attributes
|
|
TRUE, // ManualReset
|
|
FALSE, // Initial State (not-signaled)
|
|
SC_OOBE_PNP_DONE); // Name
|
|
|
|
if (!hPnPDone)
|
|
{
|
|
dwError = GetLastError();
|
|
|
|
SC_LOG1(ERROR,
|
|
"ScAutoStartServices: Creation of SC_OOBE_PNP_DONE event failed %d\n",
|
|
dwError);
|
|
|
|
return dwError;
|
|
}
|
|
|
|
//
|
|
// Once the machine name is set, OOBE setup will set this
|
|
// event to tell us to finish auto-starting the services
|
|
//
|
|
hMachineNameDone = CreateEvent(
|
|
NULL, // Event Attributes
|
|
TRUE, // ManualReset
|
|
FALSE, // Initial State (not-signaled)
|
|
SC_OOBE_MACHINE_NAME_DONE); // Name
|
|
|
|
|
|
if (!hMachineNameDone)
|
|
{
|
|
dwError = GetLastError();
|
|
|
|
SC_LOG1(ERROR,
|
|
"ScAutoStartServices: Creation of SC_OOBE_PNP_DONE event failed %d\n",
|
|
dwError);
|
|
|
|
CloseHandle(hPnPDone);
|
|
return dwError;
|
|
}
|
|
|
|
|
|
SC_LOG0(TRACE,
|
|
"ScAutoStartServices: Starting services used by OOBE\n");
|
|
|
|
for (i = 0; i < NUM_OOBE_SERVICES; i++)
|
|
{
|
|
//
|
|
// Since a computer name change may require PnP to be running,
|
|
// start it and only it and wait until signalled to start the rest.
|
|
// Grab the service list lock since ScGetNamedServiceRecord uses
|
|
// the FOR_ALL_SERVICES macro, which expects it. Also start
|
|
// AudioSrv, which is needed for audio for OOBE.
|
|
//
|
|
|
|
{
|
|
CServiceListSharedLock LLock;
|
|
|
|
dwError = ScGetNamedServiceRecord(lpServicesForOobe[i], &ServiceRecord);
|
|
}
|
|
|
|
if (dwError != NO_ERROR)
|
|
{
|
|
//
|
|
// Ignore failure since ServiceRecord will now be NULL so
|
|
// the call below will start all the auto-start services
|
|
// instead of just PnP. The machine name will be wrong in
|
|
// some of the services until the next reboot, but there's
|
|
// nothing we can do about it at this point.
|
|
//
|
|
SC_LOG2(ERROR,
|
|
"ScAutoStartServices: OOBE lookup of service %ws failed %d\n",
|
|
lpServicesForOobe[i],
|
|
dwError);
|
|
}
|
|
|
|
dwError = ScStartServiceAndDependencies(ServiceRecord, 0, NULL, TRUE);
|
|
|
|
if (dwError != NO_ERROR)
|
|
{
|
|
//
|
|
// Ignore failure -- it's possible PnP won't need to be
|
|
// up and running (it's only necessary if the OEM setup
|
|
// for the machine was done without a keyboard and PnP
|
|
// is now needed to detect the hardware). Audio failure
|
|
// is definitely non-fatal.
|
|
//
|
|
|
|
SC_LOG2(ERROR,
|
|
"ScAutoStartServices: OOBE start of %ws failed %d\n",
|
|
lpServicesForOobe[i],
|
|
dwError);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Drop the database lock since OOBE may invoke server-side PnP
|
|
// installations and setupapi.dll will try to lock the database.
|
|
// In addition, it may invoke 3rd-party class installers and we
|
|
// have no idea what they may try to do with the lock.
|
|
//
|
|
|
|
ScUnlockDatabase(lpLock);
|
|
|
|
//
|
|
// Let setup proceed
|
|
//
|
|
|
|
if (! SetEvent(hPnPDone))
|
|
{
|
|
dwError = GetLastError();
|
|
SC_LOG1(ERROR,
|
|
"ScAutoStartServices: Failed to set event (hPnPDone) %d\n",
|
|
dwError);
|
|
}
|
|
|
|
|
|
//
|
|
// Wait for setup to let us proceed.
|
|
//
|
|
|
|
WaitForSingleObject(hMachineNameDone, INFINITE);
|
|
|
|
dwError = NO_ERROR;
|
|
|
|
//
|
|
// Reacquire the database lock. Wait 10 minutes at most.
|
|
//
|
|
|
|
do
|
|
{
|
|
if (dwError != NO_ERROR)
|
|
{
|
|
Sleep(2000);
|
|
}
|
|
|
|
dwError = ScLockDatabase(TRUE, SERVICES_ACTIVE_DATABASEW, lpLock);
|
|
|
|
dwTries++;
|
|
}
|
|
while (dwError != NO_ERROR && dwTries != 300);
|
|
|
|
CloseHandle(hPnPDone);
|
|
CloseHandle(hMachineNameDone);
|
|
|
|
if (dwError != NO_ERROR)
|
|
{
|
|
SC_LOG1(ERROR,
|
|
"ScAutoStartServices: Failed to reacquire database lock %d\n",
|
|
dwError);
|
|
|
|
return dwError;
|
|
}
|
|
|
|
#undef NUM_OOBE_SERVICES
|
|
|
|
}
|
|
|
|
|
|
//
|
|
// Start services with start request flag set in group order
|
|
//
|
|
ScStartServiceAndDependencies(NULL, 0, NULL, FALSE);
|
|
|
|
ScAutoStartInProgress = FALSE;
|
|
|
|
return NO_ERROR;
|
|
}
|
|
|
|
|
|
DWORD
|
|
ScStartServiceAndDependencies(
|
|
IN LPSERVICE_RECORD ServiceToStart OPTIONAL,
|
|
IN DWORD NumArgs,
|
|
IN LPSTRING_PTRSW CmdArgs,
|
|
IN BOOL fIsOOBE
|
|
)
|
|
/*++
|
|
|
|
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;
|
|
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;
|
|
}
|
|
|
|
//
|
|
// There is one (highly unlikely) condition in which we might need to
|
|
// modify the group information here: if we call ScStartService and it
|
|
// calls ScDeactivateSR on a service whose update flag is set (its
|
|
// config was changed while it was running).
|
|
// To prevent deadlocks, the grouplist lock is always acquired before
|
|
// the service database lock, if both are needed. So we get an
|
|
// exclusive lock on the grouplist here.
|
|
//
|
|
{
|
|
CGroupListExclusiveLock GLock;
|
|
|
|
//
|
|
// Get a shared lock on the service list as we shall walk it several
|
|
// times.
|
|
//
|
|
CServiceListSharedLock LLock;
|
|
|
|
//
|
|
// 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.
|
|
//
|
|
{
|
|
CServiceRecordExclusiveLock RLock;
|
|
|
|
if (ARGUMENT_PRESENT(ServiceToStart)) {
|
|
|
|
//
|
|
// Demand starting a service.
|
|
//
|
|
|
|
//
|
|
// We can never start a disabled service
|
|
//
|
|
if (ServiceToStart->StartType == SERVICE_DISABLED ||
|
|
! ScInHardwareProfile(ServiceToStart->ServiceName, 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) {
|
|
(void) ScGetDriverStatus(ServiceToStart, NULL);
|
|
}
|
|
|
|
if (ServiceToStart->ServiceStatus.dwCurrentState != SERVICE_STOPPED) {
|
|
ApiStatus = ERROR_SERVICE_ALREADY_RUNNING;
|
|
goto ReleaseLocks;
|
|
}
|
|
|
|
//
|
|
// Set the current-start flag on the service and all of
|
|
// its dependencies. Also increment their use counts so
|
|
// that we know they won't go away, even when we release
|
|
// the locks temporarily.
|
|
//
|
|
SC_LOG(DEPEND,"Setting start request for %ws and dependencies\n",
|
|
ServiceToStart->ServiceName);
|
|
SC_LOG(WHY, " Will start %ws because of an explicit start request\n",
|
|
ServiceToStart->ServiceName);
|
|
ScSetServiceStartRequest(ServiceToStart, TRUE);
|
|
}
|
|
else {
|
|
|
|
//
|
|
// Auto-starting services.
|
|
//
|
|
|
|
// If we are in GUI mode setup, we only start those auto-start
|
|
// services that are allowed. Allowed services are listed
|
|
// under HKLM\System\Setup\AllowStart as registry keys.
|
|
// If the subkey with the same name as the service exits,
|
|
// it means we allow it to be autostarted during GUI mode.
|
|
//
|
|
HKEY hkeyAllowStart = NULL;
|
|
BOOL InSetup = SetupInProgress(NULL, NULL);
|
|
|
|
if (InSetup)
|
|
{
|
|
status = ScRegOpenKeyExW(
|
|
HKEY_LOCAL_MACHINE,
|
|
REG_KEY_SETUP_ALLOW_START,
|
|
REG_OPTION_NON_VOLATILE,
|
|
KEY_READ,
|
|
&hkeyAllowStart);
|
|
|
|
if (NO_ERROR != status)
|
|
{
|
|
// This is not expected as the registry key is created
|
|
// by hivesys.inf. We'll skip checking services in
|
|
// this case.
|
|
//
|
|
hkeyAllowStart = NULL;
|
|
}
|
|
}
|
|
|
|
// Set the CurrentStartRequest flag to TRUE for all services
|
|
// of type AUTO_START that are enabled in this hardware profile,
|
|
// and their dependencies.
|
|
//
|
|
SC_LOG0(DEPEND,"Setting start request for auto-started services and dependencies\n");
|
|
FOR_SERVICES_THAT(Service, Service->StartType == SERVICE_AUTO_START &&
|
|
ScInHardwareProfile(Service->ServiceName, 0))
|
|
{
|
|
BOOL AllowStart = TRUE;
|
|
|
|
if (hkeyAllowStart)
|
|
{
|
|
HKEY hkey;
|
|
|
|
ASSERT(InSetup);
|
|
|
|
status = ScRegOpenKeyExW(
|
|
hkeyAllowStart,
|
|
Service->ServiceName,
|
|
REG_OPTION_NON_VOLATILE,
|
|
KEY_READ,
|
|
&hkey);
|
|
if (NO_ERROR == status)
|
|
{
|
|
// The service name exists and is therefore
|
|
// allowed to start. Don't forget to close the
|
|
// key.
|
|
ScRegCloseKey(hkey);
|
|
|
|
ASSERT(AllowStart);
|
|
}
|
|
else
|
|
{
|
|
AllowStart = FALSE;
|
|
|
|
SC_LOG(WHY, " Will NOT start %ws because it's not "
|
|
"allowed to run during system setup\n",
|
|
Service->ServiceName);
|
|
}
|
|
}
|
|
|
|
if (AllowStart)
|
|
{
|
|
SC_LOG(WHY, " Will start %ws because it's configured to autostart\n",
|
|
Service->ServiceName);
|
|
ScSetServiceStartRequest(Service, FALSE);
|
|
}
|
|
}
|
|
|
|
if (hkeyAllowStart)
|
|
{
|
|
ScRegCloseKey(hkeyAllowStart);
|
|
}
|
|
}
|
|
} // Release RLock
|
|
|
|
//
|
|
// Always start services in group order.
|
|
//
|
|
for (Group = ScGetOrderGroupList();
|
|
Group != NULL;
|
|
Group = Group->Next)
|
|
{
|
|
//
|
|
// Start each group in load group order
|
|
//
|
|
if (ScMarkGroupStartNow(Group)) {
|
|
|
|
SC_LOG(DEPEND,"------ Starting services in group \"%ws\" -----\n", Group->GroupName);
|
|
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 || fIsOOBE)
|
|
);
|
|
|
|
if (status != NO_ERROR && ApiStatus == NO_ERROR) {
|
|
//
|
|
// Save first error to be returned
|
|
//
|
|
ApiStatus = status;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Services that do not belong in any group are considered
|
|
// in a group that starts last.
|
|
//
|
|
if (ScMarkGroupStartNow(NULL)) {
|
|
|
|
SC_LOG0(DEPEND,"------ Starting services that aren't in ServiceGroupOrder -----\n");
|
|
|
|
//
|
|
// Wait only for auto-start services or for the service(s)
|
|
// we explicitly demand-start during OOBE setup
|
|
//
|
|
status = ScStartMarkedServices(
|
|
ServiceToStart,
|
|
NumArgs,
|
|
CmdArgs,
|
|
(! ARGUMENT_PRESENT(ServiceToStart) || fIsOOBE)
|
|
);
|
|
|
|
if (status != NO_ERROR && ApiStatus == NO_ERROR) {
|
|
//
|
|
// Save first error to be returned
|
|
//
|
|
ApiStatus = status;
|
|
}
|
|
}
|
|
|
|
SC_LOG(DEPEND,"------ Done %s-starting services -----\n",
|
|
ARGUMENT_PRESENT(ServiceToStart) ? "demand" : "auto");
|
|
|
|
//
|
|
// Clear the CurrentStartRequest flags when done starting service(s).
|
|
//
|
|
{
|
|
CServiceRecordExclusiveLock RLock;
|
|
|
|
FOR_SERVICES_THAT(Service, CURRENTSTART_FLAG_IS_SET(Service))
|
|
{
|
|
CLEAR_CURRENTSTART_FLAG(Service);
|
|
|
|
ScDecrementUseCountAndDelete(Service);
|
|
}
|
|
}
|
|
} // Release LLock and GLock
|
|
|
|
ReleaseLocks:
|
|
|
|
//
|
|
// 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.
|
|
|
|
--*/
|
|
{
|
|
SC_ASSERT(ScServiceListLock.Have());
|
|
SC_ASSERT(ScServiceRecordLock.HaveExclusive());
|
|
|
|
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->ServiceName, 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) {
|
|
(void) ScGetDriverStatus(ServiceRecord, NULL);
|
|
}
|
|
|
|
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 (LPDEPEND_RECORD 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(WHY, " (Will start %ws because %ws depends on it too)\n",
|
|
Depend->DependService->ServiceName, ServiceRecord->ServiceName);
|
|
SC_LOG2(DEPEND_DUMP, "DependService " FORMAT_LPWSTR
|
|
" CSR=TRUE already, USECOUNT=%lu\n",
|
|
Depend->DependService->ServiceName,
|
|
Depend->DependService->UseCount);
|
|
}
|
|
else {
|
|
|
|
SC_LOG2(WHY, " Will start %ws because %ws depends on it\n",
|
|
Depend->DependService->ServiceName, ServiceRecord->ServiceName);
|
|
ScSetServiceStartRequest(Depend->DependService, DemandStarting);
|
|
}
|
|
|
|
}
|
|
else if (Depend->DependType == TypeDependOnGroup) {
|
|
|
|
//
|
|
// This service has a dependency on a group.
|
|
// For each service in that group
|
|
//
|
|
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(WHY, " (Will start %ws because %ws depends on its group %ws too)\n",
|
|
Service->ServiceName, ServiceRecord->ServiceName,
|
|
Depend->DependGroup->GroupName);
|
|
SC_LOG3(DEPEND_DUMP, "DependGroup " FORMAT_LPWSTR
|
|
", Service " FORMAT_LPWSTR
|
|
" CSR=TRUE already, USECOUNT=%lu\n",
|
|
Depend->DependGroup->GroupName,
|
|
Service->ServiceName, Service->UseCount);
|
|
}
|
|
else {
|
|
|
|
SC_LOG3(WHY, " Will start %ws because %ws depends on its group %ws\n",
|
|
Service->ServiceName, ServiceRecord->ServiceName,
|
|
Depend->DependGroup->GroupName);
|
|
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.
|
|
|
|
--*/
|
|
{
|
|
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 obsolete, but the RPC entry point still exists.
|
|
|
|
--*/
|
|
{
|
|
return ERROR_NOT_SUPPORTED;
|
|
}
|
|
|
|
|
|
DWORD
|
|
ScGetCurrentGroupState(
|
|
LPLOAD_ORDER_GROUP Group
|
|
)
|
|
{
|
|
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->ServiceName, 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;
|
|
|
|
#if DBG
|
|
DWORD LoopCount = 0;
|
|
#endif
|
|
|
|
//
|
|
// 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);
|
|
|
|
ScLogEvent(
|
|
NEVENT_CALL_TO_FUNCTION_FAILED,
|
|
SC_RESET_EVENT,
|
|
Error
|
|
);
|
|
|
|
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
|
|
//
|
|
{
|
|
CServiceRecordSharedLock RLock;
|
|
|
|
ServiceCurrentState =
|
|
Service->ServiceStatus.dwCurrentState;
|
|
}
|
|
|
|
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.
|
|
//
|
|
{
|
|
CServiceRecordExclusiveLock RLock;
|
|
|
|
if (Service->ServiceStatus.dwWin32ExitCode ==
|
|
ERROR_SERVICE_NEVER_STARTED) {
|
|
Service->ServiceStatus.dwWin32ExitCode =
|
|
Service->StartError;
|
|
}
|
|
}
|
|
|
|
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 or if
|
|
// we tried to start a non-Safeboot service in Safeboot.
|
|
//
|
|
if (Service->ErrorControl != SERVICE_ERROR_IGNORE
|
|
&&
|
|
Service->StartError != ERROR_NOT_SAFEBOOT_SERVICE)
|
|
{
|
|
ScLogEvent(
|
|
NEVENT_SERVICE_START_FAILED,
|
|
Service->DisplayName,
|
|
Service->StartError
|
|
);
|
|
|
|
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_LOG3(DEPEND, FORMAT_LPWSTR " is still PENDING "
|
|
"(chkpt %lu, wait hint %lu ms)\n",
|
|
Service->ServiceName,
|
|
Service->ServiceStatus.dwCheckPoint,
|
|
Service->ServiceStatus.dwWaitHint);
|
|
|
|
//
|
|
// If a service makes some hokey state changes, it will be
|
|
// stuck in the SC_START_PENDING state, which means that we'll
|
|
// loop/wait forever. Correct things if the service is in a
|
|
// "confused" state. Note that this can happen for a service
|
|
// that goes from SERVICE_START_PENDING to anything other
|
|
// than SERVICE_RUNNING, SERVICE_STOPPED, or SERVICE_STOP_PENDING
|
|
// (and can then change to one of those three states without
|
|
// causing the state-change event to be set)
|
|
//
|
|
|
|
//
|
|
// Get the state of the pending service
|
|
//
|
|
{
|
|
CServiceRecordSharedLock RLock;
|
|
|
|
ServiceCurrentState = Service->ServiceStatus.dwCurrentState;
|
|
}
|
|
|
|
switch (ServiceCurrentState)
|
|
{
|
|
case SERVICE_START_PENDING:
|
|
IsStartPending = TRUE;
|
|
ExistsBlockedService = TRUE;
|
|
AllStarted = FALSE;
|
|
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 %x\n",
|
|
ServiceCurrentState);
|
|
SC_ASSERT(FALSE);
|
|
Service->StartState = SC_START_FAIL;
|
|
}
|
|
}
|
|
}
|
|
} // 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);
|
|
|
|
ScLogEvent(
|
|
NEVENT_CALL_TO_FUNCTION_FAILED,
|
|
SC_RESET_EVENT,
|
|
Error
|
|
);
|
|
|
|
return Error;
|
|
}
|
|
}
|
|
else if (Error == 0xffffffff)
|
|
{
|
|
//
|
|
// An error has occurred
|
|
//
|
|
SC_LOG1(ERROR,
|
|
"Wait for ScServiceChangeStateEvent returned %d\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);
|
|
|
|
ScLogEvent(
|
|
NEVENT_CIRCULAR_DEPENDENCY_DEMAND,
|
|
ServiceToStart->DisplayName
|
|
);
|
|
}
|
|
else
|
|
{
|
|
SC_LOG0(ERROR, " Auto-starting services\n");
|
|
|
|
ScLogEvent(NEVENT_CIRCULAR_DEPENDENCY_AUTO);
|
|
|
|
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;
|
|
|
|
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)
|
|
{
|
|
ScLogEvent(
|
|
NEVENT_SERVICE_START_FAILED_NONE,
|
|
Service->DisplayName,
|
|
DependEntry->DependUnresolved->Name
|
|
);
|
|
}
|
|
|
|
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)
|
|
{
|
|
DWORD dwError;
|
|
|
|
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
|
|
&&
|
|
DependService->StartError != ERROR_NOT_SAFEBOOT_SERVICE
|
|
&&
|
|
!SetupInProgress(NULL, NULL))
|
|
{
|
|
ScLogEvent(
|
|
NEVENT_SERVICE_START_FAILED_II,
|
|
Service->DisplayName,
|
|
DependService->DisplayName,
|
|
DependService->StartError != NO_ERROR ?
|
|
DependService->StartError : DependService->ServiceStatus.dwWin32ExitCode
|
|
);
|
|
}
|
|
|
|
dwError = ERROR_NOT_SAFEBOOT_SERVICE;
|
|
|
|
//
|
|
// Walk through the list of service dependencies and pick
|
|
// the more appropriate error code. Do this to avoid
|
|
// reporting ERROR_SERVICE_DEPENDENCY_FAIL on auto-start
|
|
// and ERROR_NOT_SAFEBOOT_SERVICE on demand-start for the
|
|
// same service.
|
|
//
|
|
for (DependEntry = Service->StartDepend;
|
|
DependEntry != NULL;
|
|
DependEntry = DependEntry->Next)
|
|
{
|
|
if (DependEntry->DependType == TypeDependOnService
|
|
&&
|
|
DependEntry->DependService->StartError
|
|
!= ERROR_NOT_SAFEBOOT_SERVICE)
|
|
{
|
|
//
|
|
// There was a "real" start failure
|
|
//
|
|
dwError = ERROR_SERVICE_DEPENDENCY_FAIL;
|
|
break;
|
|
}
|
|
}
|
|
|
|
ScCleanupStartFailure(Service, dwError);
|
|
*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)
|
|
{
|
|
ScLogEvent(
|
|
NEVENT_DEPEND_ON_LATER_SERVICE,
|
|
Service->DisplayName);
|
|
}
|
|
|
|
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
|
|
&&
|
|
!g_SafeBootEnabled
|
|
&&
|
|
!SetupInProgress(NULL, NULL))
|
|
{
|
|
ScLogEvent(
|
|
NEVENT_SERVICE_START_FAILED_GROUP,
|
|
Service->DisplayName,
|
|
DependGroup->GroupName
|
|
);
|
|
}
|
|
|
|
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)
|
|
{
|
|
ScLogEvent(
|
|
NEVENT_DEPEND_ON_LATER_GROUP,
|
|
Service->DisplayName
|
|
);
|
|
}
|
|
ScCleanupStartFailure(Service, ERROR_CIRCULAR_DEPENDENCY);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
//
|
|
// No circular dependency. Mark the services in the
|
|
// dependency group to START_NOW.
|
|
//
|
|
{
|
|
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.
|
|
//
|
|
CServiceRecordExclusiveLock RLock;
|
|
|
|
if (Service->ServiceStatus.dwWin32ExitCode ==
|
|
ERROR_SERVICE_NEVER_STARTED) {
|
|
Service->ServiceStatus.dwWin32ExitCode = StartError;
|
|
}
|
|
|
|
//
|
|
// 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;
|
|
SERVICE_STATUS CurrentServiceStatus;
|
|
STATUS_UNION ServiceStatus;
|
|
DWORD OldCheckPoint;
|
|
|
|
|
|
FOR_SERVICES_THAT(Service, Service->StartState == SC_START_PENDING)
|
|
{
|
|
ServiceStatus.Regular = &CurrentServiceStatus;
|
|
|
|
status = ScQueryServiceStatus(
|
|
Service,
|
|
ServiceStatus,
|
|
FALSE
|
|
);
|
|
|
|
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,
|
|
ServiceStatus,
|
|
FALSE
|
|
);
|
|
|
|
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->ServiceName,
|
|
CurrentServiceStatus.dwWaitHint);
|
|
|
|
if (Service->ErrorControl != SERVICE_ERROR_IGNORE)
|
|
{
|
|
ScLogEvent(
|
|
NEVENT_SERVICE_START_HUNG,
|
|
Service->DisplayName
|
|
);
|
|
}
|
|
|
|
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.
|
|
|
|
--*/
|
|
{
|
|
|
|
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);
|
|
|
|
ScLogEvent(
|
|
NEVENT_SEVERE_SERVICE_FAILED,
|
|
Service->DisplayName
|
|
);
|
|
|
|
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.
|
|
|
|
--*/
|
|
{
|
|
SC_ASSERT(ScServiceRecordLock.Have());
|
|
|
|
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_STATUS_WOW64 EnumBuffer,
|
|
IN DWORD RequestedState,
|
|
IN OUT LPDWORD EntriesRead,
|
|
IN OUT LPDWORD BytesNeeded,
|
|
IN OUT LPENUM_SERVICE_STATUS_WOW64 *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.
|
|
|
|
--*/
|
|
{
|
|
SC_ASSERT(ScServiceRecordLock.Have());
|
|
|
|
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_STATUS_WOW64) +
|
|
(DWORD) WCSSIZE(StopDepend->DependService->ServiceName) +
|
|
(DWORD) WCSSIZE(StopDepend->DependService->DisplayName));
|
|
|
|
if (*Status == NO_ERROR) {
|
|
|
|
if (((DWORD_PTR) *EnumRecord + sizeof(ENUM_SERVICE_STATUS_WOW64)) >=
|
|
(DWORD_PTR) *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,
|
|
(DWORD) wcslen(StopDepend->DependService->ServiceName),
|
|
(LPWSTR) (*EnumRecord + 1),
|
|
EndOfVariableData,
|
|
(LPWSTR *) &((*EnumRecord)->dwServiceNameOffset),
|
|
(LPBYTE) EnumBuffer))
|
|
{
|
|
*Status = ERROR_MORE_DATA;
|
|
}
|
|
|
|
//
|
|
// Copy the DisplayName string data
|
|
//
|
|
if (! ScCopyStringToBufferW(
|
|
StopDepend->DependService->DisplayName,
|
|
(DWORD) wcslen(StopDepend->DependService->DisplayName),
|
|
(LPWSTR) ((*EnumRecord) + 1),
|
|
EndOfVariableData,
|
|
(LPWSTR *) &((*EnumRecord)->dwDisplayNameOffset),
|
|
(LPBYTE) EnumBuffer))
|
|
{
|
|
*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_STATUS_WOW64 EnumBuffer,
|
|
IN LPENUM_SERVICE_STATUS_WOW64 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_STATUS_WOW64 EnumEntry;
|
|
|
|
for (EnumEntry = EnumBuffer;
|
|
EnumEntry < BufferEnd;
|
|
EnumEntry++)
|
|
{
|
|
if (_wcsicmp((LPWSTR) ((LPBYTE) EnumEntry + EnumEntry->dwServiceNameOffset),
|
|
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 or 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.
|
|
|
|
Note that all services with account names that are UPNs will require
|
|
the dependency on Netlogon.
|
|
|
|
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 || DomainName == NULL)
|
|
{
|
|
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);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Check for local-only accounts (domain name of NT AUTHORITY). Note
|
|
// that the domain name may be quoted.
|
|
//
|
|
LPWSTR lpTempDomain = DomainName;
|
|
|
|
if (*lpTempDomain == '"')
|
|
{
|
|
lpTempDomain++;
|
|
}
|
|
|
|
if (_wcsnicmp(lpTempDomain,
|
|
SC_LOCAL_NTAUTH_NAME,
|
|
sizeof(SC_LOCAL_NTAUTH_NAME) / sizeof (WCHAR) - 1) == 0)
|
|
{
|
|
SC_LOG1(TRACE,
|
|
"ScCheckNetLogonDepend: %ws service is LocalService/NetworkService\n",
|
|
ServiceRecord->ServiceName);
|
|
LocalFree(DomainName);
|
|
return;
|
|
}
|
|
}
|
|
|
|
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;
|
|
|
|
SC_LOG(WHY, " Will start NetLogon because %ws runs in a remote account, or this is a server\n",
|
|
ServiceRecord->ServiceName);
|
|
|
|
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 LPCWSTR ServiceName,
|
|
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[200]; // 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
|
|
ServiceName, // pszFilter
|
|
&cchLen, // list length in wchars
|
|
CM_GETIDLIST_FILTER_SERVICE); // filter is a service name
|
|
|
|
if (Status != CR_SUCCESS)
|
|
{
|
|
SC_LOG2(WARNING, "PNP_GetDeviceListSize failed %#lx for service %ws\n",
|
|
Status, ServiceName);
|
|
return TRUE;
|
|
}
|
|
|
|
if (cchLen > LENGTH(Buffer))
|
|
{
|
|
SC_LOG2(DEPEND, "PNP_GetDeviceListSize wants a %lu-character buffer for service %ws\n",
|
|
cchLen, 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
|
|
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, 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,
|
|
NULL,
|
|
NULL,
|
|
0,
|
|
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 has no enabled device instances\n"
|
|
" in this hardware profile. Attempts to start\n"
|
|
" it will result in ERROR_SERVICE_DISABLED.\n",
|
|
ServiceName);
|
|
|
|
CleanExit:
|
|
|
|
if (pBuffer != Buffer)
|
|
{
|
|
LocalFree(pBuffer);
|
|
}
|
|
|
|
return RetStatus;
|
|
}
|