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.
1266 lines
39 KiB
1266 lines
39 KiB
/*++
|
|
|
|
Copyright (c) 1991-92 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
info.cxx
|
|
|
|
Abstract:
|
|
|
|
Contains entry points for REnumServicesStatusW and RQueryServiceStatus as
|
|
well as support routines. This file contains the following external
|
|
functions:
|
|
REnumServicesStatusW
|
|
REnumServicesStatusExW
|
|
REnumServiceGroupW
|
|
RQueryServiceStatus
|
|
RQueryServiceStatusEx
|
|
REnumDependentServicesW
|
|
|
|
Author:
|
|
|
|
Dan Lafferty (danl) 25-Jan-1992
|
|
|
|
Environment:
|
|
|
|
User Mode -Win32
|
|
|
|
Revision History:
|
|
25-Apr-1996 AnirudhS
|
|
Don't popup messages or log events for boot start and system start
|
|
drivers that are disabled in the current hardware profile.
|
|
14-Feb-1996 AnirudhS
|
|
Add REnumServiceGroupW.
|
|
10-Feb-1993 Danl
|
|
Use ROUND_DOWN_COUNT to properly align the enumeration buffer.
|
|
10-Apr-1992 JohnRo
|
|
Use ScImagePathsMatch() to allow mixed-case image names.
|
|
Changed names of some <valid.h> macros.
|
|
25-Jan-1992 danl
|
|
Created.
|
|
|
|
--*/
|
|
|
|
//
|
|
// INCLUDES
|
|
//
|
|
|
|
#include "precomp.hxx"
|
|
#include <stdlib.h> // wide character c runtimes.
|
|
#include <tstr.h> // Unicode string macros
|
|
#include <align.h> // ROUND_DOWN_COUNT
|
|
#include <sclib.h> // ScCopyStringToBufferW(), etc.
|
|
#include <valid.h> // ENUM_STATE_INVALID
|
|
#include "info.h" // ScQueryServiceStatus
|
|
#include "depend.h" // ScEnumDependents, ScInHardwareProfile
|
|
#include "driver.h" // ScGetDriverStatus
|
|
#include <cfgmgr32.h> // PNP manager functions
|
|
#include <scwow.h> // 32/64-bit interop structures
|
|
|
|
|
|
//
|
|
// DEFINITIONS
|
|
//
|
|
|
|
#define IS_NOGROUP_STRING(string) (string[0] == L'\0')
|
|
|
|
|
|
//
|
|
// Local function declarations
|
|
//
|
|
|
|
DWORD
|
|
REnumServiceGroupHelp (
|
|
IN SC_RPC_HANDLE hSCManager,
|
|
IN DWORD dwServiceType,
|
|
IN DWORD dwServiceState,
|
|
OUT PBYTE lpBuffer,
|
|
IN DWORD cbBufSize,
|
|
OUT LPDWORD pcbBytesNeeded,
|
|
OUT LPDWORD lpServicesReturned,
|
|
IN OUT LPDWORD lpResumeIndex OPTIONAL,
|
|
IN LPCWSTR lpLoadOrderGroup OPTIONAL,
|
|
IN BOOL fExtended
|
|
);
|
|
|
|
|
|
//
|
|
// Function implementations
|
|
//
|
|
|
|
DWORD
|
|
REnumServicesStatusW (
|
|
IN SC_RPC_HANDLE hSCManager,
|
|
IN DWORD dwServiceType,
|
|
IN DWORD dwServiceState,
|
|
OUT PBYTE lpBuffer,
|
|
IN DWORD cbBufSize,
|
|
OUT LPDWORD pcbBytesNeeded,
|
|
OUT LPDWORD lpServicesReturned,
|
|
IN OUT LPDWORD lpResumeIndex OPTIONAL
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function lists the services installed in the Service Controller's
|
|
database. The status of each service is returned with the name of
|
|
the service.
|
|
|
|
Arguments:
|
|
|
|
hSCManager - This is a handle to the service controller. It must
|
|
have been opened with SC_MANAGER_ENUMERATE_SERVICE access.
|
|
|
|
dwServiceType - Value to select the type of services to enumerate.
|
|
It must be one of the bitwise OR of the following values:
|
|
SERVICE_WIN32 - enumerate Win32 services only.
|
|
SERVICE_DRIVER - enumerate Driver services only.
|
|
|
|
dwServiceState - Value so select the services to enumerate based on the
|
|
running state. It must be one or the bitwise OR of the following
|
|
values:
|
|
SERVICE_ACTIVE - enumerate services that have started.
|
|
SERVICE_INACTIVE - enumerate services that are stopped.
|
|
|
|
lpBuffer - A pointer to a buffer to receive an array of enum status
|
|
(or service) entries.
|
|
|
|
cbBufSize - Size of the buffer in bytes pointed to by lpBuffer.
|
|
|
|
pcbBytesNeeded - A pointer to a location where the number of bytes
|
|
left (to be enumerated) is to be placed. This indicates to the
|
|
caller how large the buffer must be in order to complete the
|
|
enumeration with the next call.
|
|
|
|
lpServicesReturned - A pointer to a variable to receive the number of
|
|
of service entries returned.
|
|
|
|
lpResumeIndex - A pointer to a variable which on input specifies the
|
|
index of a service entry to begin enumeration. An index of 0
|
|
indicates to start at the beginning. On output, if this function
|
|
returns ERROR_MORE_DATA, the index returned is the next service
|
|
entry to resume the enumeration. The returned index is 0 if this
|
|
function returns a NO_ERROR.
|
|
|
|
Return Value:
|
|
|
|
NO_ERROR - The operation was successful.
|
|
|
|
ERROR_MORE_DATA - Not all of the data in the active database could be
|
|
returned due to the size of the user's buffer. pcbBytesNeeded
|
|
contains the number of bytes required to get the remaining
|
|
entries.
|
|
|
|
ERROR_INVALID_PARAMETER - An illegal parameter value was passed in.
|
|
(such as dwServiceType).
|
|
|
|
ERROR_INVALID_HANDLE - The specified handle was invalid.
|
|
|
|
Note:
|
|
|
|
It is expected that the RPC Stub functions will find the following
|
|
parameter problems:
|
|
|
|
Bad pointers for lpBuffer, pcbReturned, pcbBytesNeeded,
|
|
lpBuffer, ReturnedServerName, and lpResumeIndex.
|
|
|
|
|
|
--*/
|
|
{
|
|
return REnumServiceGroupHelp(hSCManager,
|
|
dwServiceType,
|
|
dwServiceState,
|
|
lpBuffer,
|
|
cbBufSize,
|
|
pcbBytesNeeded,
|
|
lpServicesReturned,
|
|
lpResumeIndex,
|
|
NULL, // Enumerate everything
|
|
FALSE); // Regular (non-Ex) enumeration
|
|
}
|
|
|
|
|
|
DWORD
|
|
REnumServicesStatusExW (
|
|
IN SC_RPC_HANDLE hSCManager,
|
|
IN SC_ENUM_TYPE InfoLevel,
|
|
IN DWORD dwServiceType,
|
|
IN DWORD dwServiceState,
|
|
OUT PBYTE lpBuffer,
|
|
IN DWORD cbBufSize,
|
|
OUT LPDWORD pcbBytesNeeded,
|
|
OUT LPDWORD lpServicesReturned,
|
|
IN OUT LPDWORD lpResumeIndex OPTIONAL,
|
|
IN LPCWSTR lpLoadOrderGroup
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function is analogous to REnumServicesStatusW, with the data
|
|
being enumerated being dependent upon the InfoLevel parameter
|
|
|
|
Arguments:
|
|
|
|
InfoLevel - An enumerated type that determines what service attributes
|
|
are enumerated:
|
|
|
|
SC_ENUM_PROCESS_INFO - Enumerates all the service information from
|
|
REnumServicesStatusW plus the service's PID and flags
|
|
|
|
lpLoadOrderGroup - Only enumerate services belonging to the given group.
|
|
If this parameter is the empty string, services not belonging to
|
|
a group are enumerated. If this parameter is NULL, no attention
|
|
is paid to group.
|
|
|
|
Return Value:
|
|
|
|
NO_ERROR - The operation was successful.
|
|
|
|
ERROR_MORE_DATA - Not all of the data in the active database could be
|
|
returned due to the size of the user's buffer. pcbBytesNeeded
|
|
contains the number of bytes required to get the remaining
|
|
entries.
|
|
|
|
ERROR_INVALID_PARAMETER - An illegal parameter value was passed in.
|
|
(such as dwServiceType).
|
|
|
|
ERROR_INVALID_HANDLE - The specified handle was invalid.
|
|
|
|
ERROR_INVALID_LEVEL - The specified InfoLevel is invalid
|
|
|
|
Note:
|
|
|
|
It is expected that the RPC Stub functions will find the following
|
|
parameter problems:
|
|
|
|
Bad pointers for lpBuffer, pcbReturned, pcbBytesNeeded,
|
|
lpBuffer, ReturnedServerName, and lpResumeIndex.
|
|
|
|
|
|
--*/
|
|
{
|
|
switch (InfoLevel) {
|
|
|
|
case SC_ENUM_PROCESS_INFO:
|
|
|
|
return REnumServiceGroupHelp(hSCManager,
|
|
dwServiceType,
|
|
dwServiceState,
|
|
lpBuffer,
|
|
cbBufSize,
|
|
pcbBytesNeeded,
|
|
lpServicesReturned,
|
|
lpResumeIndex,
|
|
lpLoadOrderGroup,
|
|
TRUE); // Extended enumeration
|
|
|
|
default:
|
|
|
|
return ERROR_INVALID_LEVEL;
|
|
}
|
|
}
|
|
|
|
|
|
DWORD
|
|
REnumServiceGroupW (
|
|
IN SC_RPC_HANDLE hSCManager,
|
|
IN DWORD dwServiceType,
|
|
IN DWORD dwServiceState,
|
|
OUT PBYTE lpBuffer,
|
|
IN DWORD cbBufSize,
|
|
OUT LPDWORD pcbBytesNeeded,
|
|
OUT LPDWORD lpServicesReturned,
|
|
IN OUT LPDWORD lpResumeIndex OPTIONAL,
|
|
IN LPCWSTR lpLoadOrderGroup OPTIONAL
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function lists the services installed in the Service Controllers
|
|
database that belong to a specified group. The status of each service
|
|
is returned with the name of the service.
|
|
|
|
Arguments:
|
|
|
|
Same as REnumServicesStatusW and one additional argument:
|
|
|
|
lpLoadOrderGroup - Only services belonging to this group are included in
|
|
the enumeration. If this is NULL services are enumerated
|
|
regardless of their group membership.
|
|
|
|
Return Value:
|
|
|
|
Same as REnumServicesStatusW plus one more:
|
|
|
|
ERROR_SERVICE_DOES_NOT_EXIST - the group specified by lpLoadOrderGroup
|
|
does not exist.
|
|
|
|
--*/
|
|
{
|
|
return REnumServiceGroupHelp(hSCManager,
|
|
dwServiceType,
|
|
dwServiceState,
|
|
lpBuffer,
|
|
cbBufSize,
|
|
pcbBytesNeeded,
|
|
lpServicesReturned,
|
|
lpResumeIndex,
|
|
lpLoadOrderGroup,
|
|
FALSE); // Regular (non-Ex) enumeration
|
|
}
|
|
|
|
|
|
DWORD
|
|
REnumServiceGroupHelp (
|
|
IN SC_RPC_HANDLE hSCManager,
|
|
IN DWORD dwServiceType,
|
|
IN DWORD dwServiceState,
|
|
OUT PBYTE lpBuffer,
|
|
IN DWORD cbBufSize,
|
|
OUT LPDWORD pcbBytesNeeded,
|
|
OUT LPDWORD lpServicesReturned,
|
|
IN OUT LPDWORD lpResumeIndex OPTIONAL,
|
|
IN LPCWSTR lpLoadOrderGroup OPTIONAL,
|
|
IN BOOL fExtended
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Helper function that does the work for REnumServiceGroup,
|
|
REnumServicesStatusW, and REnumServicesStatusExW
|
|
|
|
Arguments:
|
|
|
|
Same as REnumServiceGroup and one additional argument
|
|
|
|
fExtended -- TRUE if this function was called from the extended version
|
|
of REnumServicesStatus, FALSE if not
|
|
|
|
Return Value:
|
|
|
|
Same as REnumServiceGroupW
|
|
|
|
--*/
|
|
{
|
|
DWORD status = NO_ERROR;
|
|
BOOL copyStatus;
|
|
LPSERVICE_RECORD serviceRecord;
|
|
LPLOAD_ORDER_GROUP Group = NULL; // group being enumerated, if any
|
|
DWORD resumeIndex = 0; // resume handle value
|
|
LPENUM_SERVICE_STATUS_WOW64 pNextEnumRec; // next enum record
|
|
LPENUM_SERVICE_STATUS_WOW64 pEnumRec; // current regular enum record
|
|
LPWSTR pStringBuf; // works backwards in enum buf
|
|
DWORD serviceState; // temp state holder
|
|
BOOL exitEarly = FALSE; // buffer is full - enum not done.
|
|
BOOL fNoGroup = FALSE; // enumerate services not in a group
|
|
|
|
#ifdef TIMING_TEST
|
|
DWORD TickCount1;
|
|
DWORD TickCount2;
|
|
|
|
TickCount1 = GetTickCount();
|
|
#endif // TIMING_TEST
|
|
|
|
SC_LOG(TRACE," Inside REnumServicesStatusW\n",0);
|
|
|
|
if (ScShutdownInProgress) {
|
|
return(ERROR_SHUTDOWN_IN_PROGRESS);
|
|
}
|
|
|
|
//
|
|
// Check the handle.
|
|
//
|
|
|
|
if (!ScIsValidScManagerHandle(hSCManager))
|
|
{
|
|
return ERROR_INVALID_HANDLE;
|
|
}
|
|
|
|
//
|
|
// Check for invalid parameters. The ServiceType and Service State are
|
|
// invalid if neither of the bit masks are set, or if any bit outside
|
|
// of the bitmask range is set.
|
|
//
|
|
if (SERVICE_TYPE_MASK_INVALID(dwServiceType)) {
|
|
return (ERROR_INVALID_PARAMETER);
|
|
}
|
|
|
|
if (ENUM_STATE_MASK_INVALID(dwServiceState)) {
|
|
return (ERROR_INVALID_PARAMETER);
|
|
}
|
|
|
|
//
|
|
// 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);
|
|
}
|
|
|
|
//
|
|
// Initialize some of the return parameters.
|
|
//
|
|
*lpServicesReturned = 0;
|
|
*pcbBytesNeeded = 0;
|
|
|
|
if (ARGUMENT_PRESENT(lpResumeIndex)) {
|
|
resumeIndex = *lpResumeIndex;
|
|
}
|
|
|
|
|
|
//
|
|
// If a group name was specified, find the group record.
|
|
//
|
|
|
|
if (ARGUMENT_PRESENT(lpLoadOrderGroup)) {
|
|
|
|
if (!IS_NOGROUP_STRING(lpLoadOrderGroup)) {
|
|
|
|
ScGroupListLock.GetShared();
|
|
Group = ScGetNamedGroupRecord(lpLoadOrderGroup);
|
|
if (Group == NULL) {
|
|
ScGroupListLock.Release();
|
|
return(ERROR_SERVICE_DOES_NOT_EXIST);
|
|
}
|
|
}
|
|
else {
|
|
|
|
//
|
|
// Enumerate services not in a group
|
|
//
|
|
fNoGroup = TRUE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Get a shared (read) lock on the database so that it cannot be changed
|
|
// while we're gathering up data.
|
|
//
|
|
{
|
|
CServiceListSharedLock LLock;
|
|
CServiceRecordSharedLock RLock;
|
|
|
|
//
|
|
// Point to the start of the database.
|
|
//
|
|
|
|
if (!ScFindEnumStart(resumeIndex, &serviceRecord))
|
|
{
|
|
//
|
|
// There are no service records beyond the resume index.
|
|
//
|
|
goto CleanExit;
|
|
}
|
|
|
|
//
|
|
// Set up a pointer for EnumStatus Structures at the top of the
|
|
// buffer, and Strings at the bottom of the buffer.
|
|
//
|
|
cbBufSize = ROUND_DOWN_COUNT(cbBufSize, ALIGN_WCHAR);
|
|
pEnumRec = (LPENUM_SERVICE_STATUS_WOW64) lpBuffer;
|
|
pStringBuf = (LPWSTR)((LPBYTE)lpBuffer + cbBufSize);
|
|
|
|
//
|
|
// Loop through, gathering Enum Status into the return buffer.
|
|
//
|
|
|
|
do
|
|
{
|
|
//
|
|
// Examine the data in the service record to see if it meets the
|
|
// criteria of the passed in keys.
|
|
//
|
|
|
|
//
|
|
// Since driver state can be modified through other means than the
|
|
// SCM, make sure we've got the most recent state information if we're
|
|
// enumerating drivers.
|
|
//
|
|
|
|
if (serviceRecord->ServiceStatus.dwServiceType & SERVICE_DRIVER)
|
|
{
|
|
//
|
|
// It's OK to use CServiceRecordTEMPORARYEXCLUSIVELOCK
|
|
// because the only field of the service record that we
|
|
// are relying on here is the SERVICE_DRIVER bits, and
|
|
// those are never changed.
|
|
//
|
|
|
|
CServiceRecordTEMPORARYEXCLUSIVELOCK Lock;
|
|
|
|
//
|
|
// Ignore the error and don't ask for the updated info since we'll
|
|
// use the fields in the serviceRecord below anyhow for comparisons
|
|
// and copying. If the call succeeded, they'll be updated already.
|
|
// If not, we'll end up using the most recent state info we have.
|
|
//
|
|
|
|
ScGetDriverStatus(serviceRecord, NULL);
|
|
}
|
|
|
|
|
|
serviceState = SERVICE_INACTIVE;
|
|
if (serviceRecord->ServiceStatus.dwCurrentState != SERVICE_STOPPED)
|
|
{
|
|
serviceState = SERVICE_ACTIVE;
|
|
}
|
|
|
|
//
|
|
// If the service record meets the criteria of the passed in key,
|
|
// put its information into the return buffer. The three checks
|
|
// below are:
|
|
//
|
|
// 1. Enumerate everything
|
|
// 2. Enumerate members of a certain group only, so check to see
|
|
// if the current service is a member of that group
|
|
// 3. Enumerate only services that aren't in a group, so check to
|
|
// see if the current service qualifies (NULL MemberOfGroup
|
|
// field)
|
|
//
|
|
if ((Group == NULL && !fNoGroup
|
|
|| Group && Group == serviceRecord->MemberOfGroup
|
|
|| fNoGroup && !serviceRecord->MemberOfGroup)
|
|
&&
|
|
((dwServiceType & serviceRecord->ServiceStatus.dwServiceType) != 0)
|
|
&&
|
|
((dwServiceState & serviceState) != 0))
|
|
{
|
|
//
|
|
// Determine if there is room for any string data in the buffer.
|
|
//
|
|
|
|
if (fExtended)
|
|
{
|
|
pNextEnumRec =
|
|
(LPENUM_SERVICE_STATUS_WOW64)
|
|
((LPENUM_SERVICE_STATUS_PROCESS_WOW64) pEnumRec + 1);
|
|
}
|
|
else
|
|
{
|
|
pNextEnumRec = pEnumRec + 1;
|
|
}
|
|
|
|
if ((LPWSTR) pNextEnumRec >= pStringBuf)
|
|
{
|
|
exitEarly = TRUE;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Copy the ServiceName string data.
|
|
//
|
|
copyStatus = ScCopyStringToBufferW(
|
|
serviceRecord->ServiceName,
|
|
(DWORD) wcslen(serviceRecord->ServiceName),
|
|
(LPWSTR) pNextEnumRec,
|
|
&pStringBuf,
|
|
(LPWSTR *) &(pEnumRec->dwServiceNameOffset),
|
|
lpBuffer);
|
|
|
|
if (copyStatus == FALSE)
|
|
{
|
|
SC_LOG(TRACE,
|
|
"REnumServicesStatusW:NetpCopyStringToBuf not enough room\n",0);
|
|
exitEarly = TRUE;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Copy the DisplayName string data.
|
|
//
|
|
copyStatus = ScCopyStringToBufferW(
|
|
serviceRecord->DisplayName,
|
|
(DWORD) wcslen(serviceRecord->DisplayName),
|
|
(LPWSTR) pNextEnumRec,
|
|
&pStringBuf,
|
|
(LPWSTR *) &(pEnumRec->dwDisplayNameOffset),
|
|
lpBuffer);
|
|
|
|
if (copyStatus == FALSE)
|
|
{
|
|
SC_LOG(TRACE,
|
|
"REnumServicesStatusW:NetpCopyStringToBuf not enough room\n",0);
|
|
exitEarly = TRUE;
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Copy the rest of the status information.
|
|
//
|
|
|
|
LPSERVICE_STATUS_PROCESS lpStatusEx = NULL;
|
|
|
|
//
|
|
// If we're enumerating the extended status, assign the
|
|
// "helper variable" and initialize the extra fields
|
|
//
|
|
|
|
if (fExtended)
|
|
{
|
|
lpStatusEx =
|
|
&((LPENUM_SERVICE_STATUS_PROCESS_WOW64)pEnumRec)->ServiceStatusProcess;
|
|
|
|
lpStatusEx->dwProcessId = 0;
|
|
lpStatusEx->dwServiceFlags = 0;
|
|
}
|
|
|
|
if (serviceRecord->ServiceStatus.dwServiceType & SERVICE_DRIVER)
|
|
{
|
|
RtlCopyMemory(&(pEnumRec->ServiceStatus),
|
|
&(serviceRecord->ServiceStatus),
|
|
sizeof(SERVICE_STATUS));
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Otherwise, just copy what is already in the service
|
|
// record.
|
|
//
|
|
|
|
RtlCopyMemory(
|
|
&(pEnumRec->ServiceStatus),
|
|
&(serviceRecord->ServiceStatus),
|
|
sizeof(SERVICE_STATUS));
|
|
|
|
if (fExtended)
|
|
{
|
|
//
|
|
// Only assign the PID and flags if there's an
|
|
// image record for the service. If there's no
|
|
// image record, the service isn't running.
|
|
//
|
|
if (serviceRecord->ImageRecord)
|
|
{
|
|
lpStatusEx->dwProcessId = serviceRecord->ImageRecord->Pid;
|
|
|
|
if (serviceRecord->ImageRecord->ImageFlags
|
|
&
|
|
IS_SYSTEM_SERVICE)
|
|
{
|
|
lpStatusEx->dwServiceFlags |=
|
|
SERVICE_RUNS_IN_SYSTEM_PROCESS;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
(*lpServicesReturned)++;
|
|
resumeIndex = serviceRecord->ResumeNum;
|
|
|
|
//
|
|
// Get Location for next Enum Record in the return buffer.
|
|
// Note that since we dealt with the pointer addition on
|
|
// pNextEnumRec above, it doesn't matter that this is
|
|
// being cast to an LPENUM_SERVICE_STATUS_WOW64, even if
|
|
// we're returning the extended status.
|
|
//
|
|
pEnumRec = (LPENUM_SERVICE_STATUS_WOW64) pNextEnumRec;
|
|
|
|
//
|
|
// TODO: Determine how many bytes are being marshalled.
|
|
// This is only worthwhile if RPC will pack the
|
|
// buffer tighter than this code does.
|
|
// Since packstr loads strings from the end of
|
|
// the buffer, we end up using the whole width of
|
|
// it - even if the middle is basically empty.
|
|
//
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// Go to the next service record.
|
|
//
|
|
serviceRecord = serviceRecord->Next;
|
|
}
|
|
while (serviceRecord != NULL);
|
|
|
|
//
|
|
// If we did not enum the whole database, then
|
|
// determine how large a buffer is needed to complete the database on
|
|
// the next call.
|
|
//
|
|
if (exitEarly) {
|
|
|
|
do {
|
|
//
|
|
// Examine the data in the service record to see if it meets the
|
|
// criteria of the passed in keys.
|
|
//
|
|
|
|
serviceState = SERVICE_INACTIVE;
|
|
if (serviceRecord->ServiceStatus.dwCurrentState != SERVICE_STOPPED) {
|
|
serviceState = SERVICE_ACTIVE;
|
|
}
|
|
|
|
//
|
|
// If the service record meets the criteria of the passed in key,
|
|
// add the number of bytes to the running sum. Note that this
|
|
// check (and the associated rules) are the same as the one above.
|
|
//
|
|
if ((Group == NULL && !fNoGroup
|
|
|| Group && Group == serviceRecord->MemberOfGroup
|
|
|| fNoGroup && !serviceRecord->MemberOfGroup)
|
|
&&
|
|
((dwServiceType & serviceRecord->ServiceStatus.dwServiceType) != 0)
|
|
&&
|
|
((dwServiceState & serviceState) != 0))
|
|
{
|
|
*pcbBytesNeeded += (DWORD)((fExtended ? sizeof(ENUM_SERVICE_STATUS_PROCESS_WOW64) :
|
|
sizeof(ENUM_SERVICE_STATUS_WOW64)) +
|
|
WCSSIZE(serviceRecord->ServiceName) +
|
|
WCSSIZE(serviceRecord->DisplayName));
|
|
|
|
}
|
|
|
|
//
|
|
// Go to the next service record.
|
|
//
|
|
serviceRecord = serviceRecord->Next;
|
|
}
|
|
while (serviceRecord != NULL);
|
|
|
|
} // exitEarly
|
|
|
|
else {
|
|
|
|
//
|
|
// exitEarly == FALSE (we went through the whole database)
|
|
//
|
|
// If no records were read, return a successful status.
|
|
//
|
|
if (*lpServicesReturned == 0) {
|
|
goto CleanExit;
|
|
}
|
|
}
|
|
} // Release RLock and LLock
|
|
|
|
|
|
//
|
|
// Determine the proper return status. Indicate if there is more data
|
|
// to enumerate than would fit in the buffer.
|
|
//
|
|
if(*pcbBytesNeeded != 0) {
|
|
status = ERROR_MORE_DATA;
|
|
}
|
|
|
|
//
|
|
// update the ResumeHandle
|
|
//
|
|
if (ARGUMENT_PRESENT(lpResumeIndex)) {
|
|
if (status == NO_ERROR) {
|
|
*lpResumeIndex = 0;
|
|
}
|
|
else {
|
|
*lpResumeIndex = resumeIndex;
|
|
}
|
|
}
|
|
|
|
CleanExit:
|
|
|
|
if (ARGUMENT_PRESENT(lpLoadOrderGroup) && !IS_NOGROUP_STRING(lpLoadOrderGroup)) {
|
|
ScGroupListLock.Release();
|
|
}
|
|
|
|
#ifdef TIMING_TEST
|
|
TickCount2 = GetTickCount();
|
|
DbgPrint("\n[SC_TIMING] Time for Enum = %d\n",TickCount2-TickCount1);
|
|
#endif // TIMING_TEST
|
|
|
|
return (status);
|
|
}
|
|
|
|
|
|
DWORD
|
|
RQueryServiceStatus(
|
|
IN SC_RPC_HANDLE hService,
|
|
OUT LPSERVICE_STATUS lpServiceStatus
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function returns the service status information maintained by
|
|
the Service Controller. The status information will be the last status
|
|
information that the service reported to the Service Controller.
|
|
The service may have just changed its status and may not have updated
|
|
the Service Controller yet.
|
|
|
|
Arguments:
|
|
|
|
hService - Handle obtained from a previous CreateService or OpenService
|
|
call.
|
|
|
|
lpServiceStatus - A pointer to a buffer to receive a SERVICE_STATUS
|
|
information structure.
|
|
|
|
Return Value:
|
|
|
|
NO_ERROR - The operation was successful.
|
|
|
|
ERROR_INVALID_HANDLE - The specified handle was invalid.
|
|
|
|
ERROR_ACCESS_DENIED - The specified handle was not opened with
|
|
SERVICE_QUERY_STATUS access.
|
|
|
|
ERROR_INSUFFICIENT_BUFFER - The supplied output buffer is too small
|
|
for the SERVICE_STATUS information structure. Nothing is written
|
|
to the supplied output buffer.
|
|
|
|
--*/
|
|
{
|
|
LPSERVICE_RECORD serviceRecord;
|
|
STATUS_UNION ServiceStatus;
|
|
|
|
if (ScShutdownInProgress)
|
|
{
|
|
return ERROR_SHUTDOWN_IN_PROGRESS;
|
|
}
|
|
|
|
//
|
|
// Check the handle.
|
|
//
|
|
if (!ScIsValidServiceHandle(hService))
|
|
{
|
|
return ERROR_INVALID_HANDLE;
|
|
}
|
|
|
|
//
|
|
// Was the handle opened with SERVICE_QUERY_STATUS access?
|
|
//
|
|
if (! RtlAreAllAccessesGranted(
|
|
((LPSC_HANDLE_STRUCT)hService)->AccessGranted,
|
|
SERVICE_QUERY_STATUS
|
|
)) {
|
|
return ERROR_ACCESS_DENIED;
|
|
}
|
|
|
|
//
|
|
// Get the Service Status from the database.
|
|
//
|
|
serviceRecord = ((LPSC_HANDLE_STRUCT)hService)->Type.ScServiceObject.ServiceRecord;
|
|
|
|
ServiceStatus.Regular = lpServiceStatus;
|
|
|
|
return ScQueryServiceStatus(serviceRecord, ServiceStatus, FALSE);
|
|
}
|
|
|
|
|
|
DWORD
|
|
RQueryServiceStatusEx(
|
|
IN SC_RPC_HANDLE hService,
|
|
IN SC_STATUS_TYPE InfoLevel,
|
|
OUT LPBYTE lpBuffer,
|
|
IN DWORD cbBufSize,
|
|
OUT LPDWORD pcbBytesNeeded
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function is analogous to RQueryServiceStatus, but may return
|
|
different status information about the service based on the InfoLevel
|
|
|
|
Arguments:
|
|
|
|
hService - Handle obtained from a previous CreateService or OpenService
|
|
call.
|
|
|
|
InfoLevel - An enumerated type that determines what service attributes
|
|
are returned:
|
|
|
|
SC_STATUS_PROCESS_INFO - Returns all the service information
|
|
from RQueryServiceStatus plus the service's PID and flags
|
|
|
|
lpBuffer - Buffer in which to put the status information.
|
|
|
|
cbBufSize - Size of the buffer, in bytes
|
|
|
|
pcbBytesNeeded - Number of bytes needed to hold all the status information.
|
|
Filled in on both success and failure.
|
|
|
|
Return Value:
|
|
|
|
NO_ERROR - The operation was successful.
|
|
|
|
ERROR_INVALID_HANDLE - The specified handle was invalid.
|
|
|
|
ERROR_ACCESS_DENIED - The specified handle was not opened with
|
|
SERVICE_QUERY_STATUS access.
|
|
|
|
ERROR_INSUFFICIENT_BUFFER - The supplied output buffer is too small
|
|
for the SERVICE_STATUS information structure. Nothing is written
|
|
to the supplied output buffer.
|
|
|
|
ERROR_INVALID_PARAMETER - The cbSize field in the lpServiceStatusEx
|
|
structure is not valid
|
|
|
|
ERROR_INVALID_LEVEL - InfoLevel contains an unsupported value. Note that
|
|
this can only happen if there is a bug in RPC
|
|
|
|
--*/
|
|
{
|
|
LPSERVICE_RECORD serviceRecord;
|
|
|
|
if (ScShutdownInProgress)
|
|
{
|
|
return ERROR_SHUTDOWN_IN_PROGRESS;
|
|
}
|
|
|
|
//
|
|
// Check the handle.
|
|
//
|
|
if (!ScIsValidServiceHandle(hService))
|
|
{
|
|
return ERROR_INVALID_HANDLE;
|
|
}
|
|
|
|
//
|
|
// Was the handle opened with SERVICE_QUERY_STATUS access?
|
|
//
|
|
if (! RtlAreAllAccessesGranted(
|
|
((LPSC_HANDLE_STRUCT)hService)->AccessGranted,
|
|
SERVICE_QUERY_STATUS
|
|
)) {
|
|
|
|
return ERROR_ACCESS_DENIED;
|
|
}
|
|
|
|
//
|
|
// Get the Service Status from the database.
|
|
//
|
|
serviceRecord = ((LPSC_HANDLE_STRUCT)hService)->Type.ScServiceObject.ServiceRecord;
|
|
|
|
switch (InfoLevel) {
|
|
|
|
case SC_STATUS_PROCESS_INFO:
|
|
{
|
|
STATUS_UNION ServiceStatus;
|
|
|
|
*pcbBytesNeeded = sizeof(SERVICE_STATUS_PROCESS);
|
|
|
|
if (cbBufSize < sizeof(SERVICE_STATUS_PROCESS)) {
|
|
return ERROR_INSUFFICIENT_BUFFER;
|
|
}
|
|
|
|
ServiceStatus.Ex = (LPSERVICE_STATUS_PROCESS) lpBuffer;
|
|
|
|
return ScQueryServiceStatus(serviceRecord, ServiceStatus, TRUE);
|
|
}
|
|
|
|
default:
|
|
return ERROR_INVALID_LEVEL;
|
|
}
|
|
}
|
|
|
|
|
|
DWORD
|
|
ScQueryServiceStatus(
|
|
IN LPSERVICE_RECORD ServiceRecord,
|
|
OUT STATUS_UNION ServiceStatus,
|
|
IN BOOL fExtendedStatus
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function copies the service status structure to the output
|
|
pointer after having acquired a shared lock.
|
|
|
|
Arguments:
|
|
|
|
ServiceRecord - Supplies a pointer to the service record.
|
|
|
|
ServiceStatus - Receives the service status structure.
|
|
|
|
fExtendedStatus - TRUE if the function was called from
|
|
RQueryServiceStatusEx, FALSE otherwise
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
SC_ASSERT(! ScServiceRecordLock.Have());
|
|
|
|
//
|
|
// Sanity check that the contents of the union are stored
|
|
// in the same set of 4 bytes since they're both pointers
|
|
//
|
|
SC_ASSERT((LPBYTE)ServiceStatus.Ex == (LPBYTE)ServiceStatus.Regular);
|
|
|
|
if (fExtendedStatus) {
|
|
|
|
ServiceStatus.Ex->dwProcessId = 0;
|
|
ServiceStatus.Ex->dwServiceFlags = 0;
|
|
}
|
|
|
|
//
|
|
// If this request is for a driver, call ScGetDriverStatus and return.
|
|
//
|
|
if (ServiceRecord->ServiceStatus.dwServiceType & SERVICE_DRIVER) {
|
|
|
|
CServiceRecordExclusiveLock RLock;
|
|
|
|
return ScGetDriverStatus(ServiceRecord,
|
|
ServiceStatus.Regular);
|
|
}
|
|
else {
|
|
|
|
CServiceRecordSharedLock RLock;
|
|
|
|
//
|
|
// Copy the latest status into the return buffer.
|
|
//
|
|
RtlCopyMemory(
|
|
ServiceStatus.Regular,
|
|
&(ServiceRecord->ServiceStatus),
|
|
sizeof(SERVICE_STATUS));
|
|
|
|
if (fExtendedStatus) {
|
|
|
|
//
|
|
// Copy the PID and flags into the structure -- if the service
|
|
// hasn't started yet (i.e., there's no ImageRecord), the the PID
|
|
// and the flags will be 0
|
|
//
|
|
if (ServiceRecord->ImageRecord) {
|
|
|
|
ServiceStatus.Ex->dwProcessId = ServiceRecord->ImageRecord->Pid;
|
|
|
|
if (ServiceRecord->ImageRecord->ImageFlags & IS_SYSTEM_SERVICE) {
|
|
|
|
ServiceStatus.Ex->dwServiceFlags |= SERVICE_RUNS_IN_SYSTEM_PROCESS;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return NO_ERROR;
|
|
}
|
|
|
|
|
|
DWORD
|
|
REnumDependentServicesW(
|
|
IN SC_RPC_HANDLE hService,
|
|
IN DWORD dwServiceState,
|
|
OUT LPBYTE lpServices,
|
|
IN DWORD cbBufSize,
|
|
OUT LPDWORD pcbBytesNeeded,
|
|
OUT LPDWORD lpServicesReturned
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function enumerates the services which are dependent on the
|
|
specified service. The list returned is an ordered list of services
|
|
to be stopped before the specified service can be stopped. This
|
|
list has to be ordered because there may be dependencies between
|
|
the services that depend on the specified service.
|
|
|
|
Arguments:
|
|
|
|
dwServiceState - Value so select the services to enumerate based on the
|
|
running state. It must be one or the bitwise OR of the following
|
|
values:
|
|
SERVICE_ACTIVE - enumerate services that have started.
|
|
SERVICE_INACTIVE - enumerate services that are stopped.
|
|
|
|
lpServices - A pointer to a buffer to receive an array of enum status
|
|
(or service) entries.
|
|
|
|
cbBufSize - Size of the buffer in bytes pointed to by lpBuffer.
|
|
|
|
pcbBytesNeeded - A pointer to a location where the number of bytes
|
|
left (to be enumerated) is to be placed. This indicates to the
|
|
caller how large the buffer must be in order to complete the
|
|
enumeration with the next call.
|
|
|
|
lpServicesReturned - A pointer to a variable to receive the number of
|
|
of service entries returned.
|
|
|
|
Return Value:
|
|
|
|
NO_ERROR - The operation was successful.
|
|
|
|
ERROR_MORE_DATA - Not all of the data in the active database could be
|
|
returned due to the size of the user's buffer. pcbBytesNeeded
|
|
contains the number of bytes required to get the remaining
|
|
entries.
|
|
|
|
ERROR_INVALID_PARAMETER - An illegal parameter value was passed in
|
|
for the service state.
|
|
|
|
ERROR_INVALID_HANDLE - The specified handle was invalid.
|
|
|
|
Note:
|
|
|
|
It is expected that the RPC Stub functions will find the following
|
|
parameter problems:
|
|
|
|
Bad pointers for lpServices, pcbServicesReturned, and
|
|
pcbBytesNeeded.
|
|
|
|
--*/
|
|
{
|
|
DWORD status;
|
|
LPSERVICE_RECORD Service;
|
|
|
|
LPENUM_SERVICE_STATUS_WOW64 EnumRecord = (LPENUM_SERVICE_STATUS_WOW64) lpServices;
|
|
LPWSTR EndOfVariableData;
|
|
|
|
cbBufSize = ROUND_DOWN_COUNT(cbBufSize, ALIGN_WCHAR);
|
|
EndOfVariableData = (LPWSTR) ((DWORD_PTR) EnumRecord + cbBufSize);
|
|
|
|
SC_LOG(TRACE," Inside REnumDependentServicesW\n",0);
|
|
|
|
if (ScShutdownInProgress)
|
|
{
|
|
return(ERROR_SHUTDOWN_IN_PROGRESS);
|
|
}
|
|
|
|
//
|
|
// Check the handle.
|
|
//
|
|
if (!ScIsValidServiceHandle(hService))
|
|
{
|
|
return ERROR_INVALID_HANDLE;
|
|
}
|
|
|
|
Service = ((LPSC_HANDLE_STRUCT)hService)->Type.ScServiceObject.ServiceRecord;
|
|
|
|
//
|
|
// Service State is invalid if neither of the bit masks is set, or if any bit
|
|
// outside of the bitmask range is set.
|
|
//
|
|
if (ENUM_STATE_MASK_INVALID(dwServiceState)) {
|
|
return ERROR_INVALID_PARAMETER;
|
|
}
|
|
|
|
//
|
|
// Was the handle opened with SERVICE_ENUMERATE_DEPENDENTS access?
|
|
//
|
|
if (! RtlAreAllAccessesGranted(
|
|
((LPSC_HANDLE_STRUCT)hService)->AccessGranted,
|
|
SERVICE_ENUMERATE_DEPENDENTS
|
|
)) {
|
|
return ERROR_ACCESS_DENIED;
|
|
}
|
|
|
|
//
|
|
// Initialize returned values
|
|
//
|
|
*lpServicesReturned = 0;
|
|
*pcbBytesNeeded = 0;
|
|
|
|
status = NO_ERROR;
|
|
|
|
//
|
|
// Get a shared (read) lock on the database so that it cannot be changed
|
|
// while we're gathering up data.
|
|
//
|
|
{
|
|
CServiceRecordSharedLock RLock;
|
|
|
|
ScEnumDependents(
|
|
Service,
|
|
EnumRecord,
|
|
dwServiceState,
|
|
lpServicesReturned,
|
|
pcbBytesNeeded,
|
|
&EnumRecord,
|
|
&EndOfVariableData,
|
|
&status
|
|
);
|
|
}
|
|
|
|
if (status == NO_ERROR)
|
|
{
|
|
*pcbBytesNeeded = 0;
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
VOID
|
|
ScGetBootAndSystemDriverState(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function is called once at service controller init time to get
|
|
the latest state of boot and system drivers.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
DWORD status;
|
|
CServiceListSharedLock LLock; // to avoid assertions
|
|
|
|
//
|
|
// ScGetDriverStatus assumes that the exclusive database lock is claimed.
|
|
//
|
|
CServiceRecordExclusiveLock RLock;
|
|
|
|
FOR_ALL_SERVICES(Service)
|
|
{
|
|
if ((Service->StartType == SERVICE_BOOT_START ||
|
|
Service->StartType == SERVICE_SYSTEM_START)
|
|
|
|
&&
|
|
|
|
(Service->ServiceStatus.dwServiceType == SERVICE_KERNEL_DRIVER ||
|
|
Service->ServiceStatus.dwServiceType == SERVICE_FILE_SYSTEM_DRIVER))
|
|
{
|
|
status = ScGetDriverStatus(
|
|
Service,
|
|
NULL
|
|
);
|
|
|
|
if (status == NO_ERROR)
|
|
{
|
|
if (Service->ServiceStatus.dwCurrentState == SERVICE_STOPPED
|
|
&&
|
|
ScInHardwareProfile(Service->ServiceName, CM_GETIDLIST_DONOTGENERATE))
|
|
{
|
|
Service->ServiceStatus.dwControlsAccepted = 0;
|
|
Service->ServiceStatus.dwWin32ExitCode = ERROR_GEN_FAILURE;
|
|
|
|
//
|
|
// For popup after user has logged on to indicate that some
|
|
// service started at boot has failed.
|
|
//
|
|
if (Service->ErrorControl == SERVICE_ERROR_NORMAL ||
|
|
Service->ErrorControl == SERVICE_ERROR_SEVERE ||
|
|
Service->ErrorControl == SERVICE_ERROR_CRITICAL)
|
|
{
|
|
(void) ScAddFailedDriver(Service->ServiceName);
|
|
ScPopupStartFail = TRUE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|