reactos/base/system/services/rpcserver.c
Timo Kreuzer e4930be4ff [REACTOS] Use the ISO C and C++ conformant names: _wcsicmp, _stricmp
Stop using non-conforming wcsicmp, stricmp, strcasecmp
2024-10-20 14:12:25 +03:00

6820 lines
200 KiB
C

/*
* PROJECT: ReactOS Service Control Manager
* LICENSE: GPL - See COPYING in the top level directory
* FILE: base/system/services/rpcserver.c
* PURPOSE: RPC server interface for the advapi32 calls
* COPYRIGHT: Copyright 2005-2006 Eric Kohl
* Copyright 2006-2007 Hervé Poussineau <hpoussin@reactos.org>
* Copyright 2007 Ged Murphy <gedmurphy@reactos.org>
*/
/* INCLUDES ****************************************************************/
#include "services.h"
#include <winnls.h>
#include <strsafe.h>
#include <pseh/pseh2.h>
#define NDEBUG
#include <debug.h>
/* GLOBALS *****************************************************************/
#define MANAGER_TAG 0x72674D68 /* 'hMgr' */
#define SERVICE_TAG 0x63765368 /* 'hSvc' */
#define INVALID_TAG 0xAABBCCDD
typedef struct _SCMGR_HANDLE
{
DWORD Tag;
DWORD DesiredAccess;
} SCMGR_HANDLE;
typedef struct _MANAGER_HANDLE
{
SCMGR_HANDLE Handle;
WCHAR DatabaseName[1];
} MANAGER_HANDLE, *PMANAGER_HANDLE;
typedef struct _SERVICE_HANDLE
{
SCMGR_HANDLE Handle;
PSERVICE ServiceEntry;
} SERVICE_HANDLE, *PSERVICE_HANDLE;
#define SC_MANAGER_READ \
(STANDARD_RIGHTS_READ | \
SC_MANAGER_QUERY_LOCK_STATUS | \
SC_MANAGER_ENUMERATE_SERVICE)
#define SC_MANAGER_WRITE \
(STANDARD_RIGHTS_WRITE | \
SC_MANAGER_MODIFY_BOOT_CONFIG | \
SC_MANAGER_CREATE_SERVICE)
#define SC_MANAGER_EXECUTE \
(STANDARD_RIGHTS_EXECUTE | \
SC_MANAGER_LOCK | \
SC_MANAGER_ENUMERATE_SERVICE | \
SC_MANAGER_CONNECT | \
SC_MANAGER_CREATE_SERVICE)
#define SERVICE_READ \
(STANDARD_RIGHTS_READ | \
SERVICE_INTERROGATE | \
SERVICE_ENUMERATE_DEPENDENTS | \
SERVICE_QUERY_STATUS | \
SERVICE_QUERY_CONFIG)
#define SERVICE_WRITE \
(STANDARD_RIGHTS_WRITE | \
SERVICE_CHANGE_CONFIG)
#define SERVICE_EXECUTE \
(STANDARD_RIGHTS_EXECUTE | \
SERVICE_USER_DEFINED_CONTROL | \
SERVICE_PAUSE_CONTINUE | \
SERVICE_STOP | \
SERVICE_START)
#define TAG_ARRAY_SIZE 32
/* VARIABLES ***************************************************************/
static GENERIC_MAPPING
ScmManagerMapping = {SC_MANAGER_READ,
SC_MANAGER_WRITE,
SC_MANAGER_EXECUTE,
SC_MANAGER_ALL_ACCESS};
static GENERIC_MAPPING
ScmServiceMapping = {SERVICE_READ,
SERVICE_WRITE,
SERVICE_EXECUTE,
SERVICE_ALL_ACCESS};
DWORD g_dwServiceBits = 0;
/* FUNCTIONS ***************************************************************/
VOID
ScmStartRpcServer(VOID)
{
RPC_STATUS Status;
DPRINT("ScmStartRpcServer() called\n");
Status = RpcServerUseProtseqEpW(L"ncacn_np",
10,
L"\\pipe\\ntsvcs",
NULL);
if (Status != RPC_S_OK)
{
DPRINT1("RpcServerUseProtseqEpW() failed (Status %lx)\n", Status);
return;
}
Status = RpcServerRegisterIf(svcctl_v2_0_s_ifspec,
NULL,
NULL);
if (Status != RPC_S_OK)
{
DPRINT1("RpcServerRegisterIf() failed (Status %lx)\n", Status);
return;
}
Status = RpcServerListen(1, 20, TRUE);
if (Status != RPC_S_OK)
{
DPRINT1("RpcServerListen() failed (Status %lx)\n", Status);
return;
}
DPRINT("ScmStartRpcServer() done\n");
}
static DWORD
ScmCreateManagerHandle(LPWSTR lpDatabaseName,
SC_HANDLE *Handle)
{
PMANAGER_HANDLE Ptr;
if (lpDatabaseName == NULL)
lpDatabaseName = SERVICES_ACTIVE_DATABASEW;
if (_wcsicmp(lpDatabaseName, SERVICES_FAILED_DATABASEW) == 0)
{
DPRINT("Database %S, does not exist\n", lpDatabaseName);
return ERROR_DATABASE_DOES_NOT_EXIST;
}
else if (_wcsicmp(lpDatabaseName, SERVICES_ACTIVE_DATABASEW) != 0)
{
DPRINT("Invalid Database name %S\n", lpDatabaseName);
return ERROR_INVALID_NAME;
}
Ptr = HeapAlloc(GetProcessHeap(),
HEAP_ZERO_MEMORY,
FIELD_OFFSET(MANAGER_HANDLE, DatabaseName[wcslen(lpDatabaseName) + 1]));
if (Ptr == NULL)
return ERROR_NOT_ENOUGH_MEMORY;
Ptr->Handle.Tag = MANAGER_TAG;
wcscpy(Ptr->DatabaseName, lpDatabaseName);
*Handle = (SC_HANDLE)Ptr;
return ERROR_SUCCESS;
}
static DWORD
ScmCreateServiceHandle(PSERVICE lpServiceEntry,
SC_HANDLE *Handle)
{
PSERVICE_HANDLE Ptr;
Ptr = HeapAlloc(GetProcessHeap(),
HEAP_ZERO_MEMORY,
sizeof(SERVICE_HANDLE));
if (Ptr == NULL)
return ERROR_NOT_ENOUGH_MEMORY;
Ptr->Handle.Tag = SERVICE_TAG;
Ptr->ServiceEntry = lpServiceEntry;
*Handle = (SC_HANDLE)Ptr;
return ERROR_SUCCESS;
}
static PMANAGER_HANDLE
ScmGetServiceManagerFromHandle(SC_RPC_HANDLE Handle)
{
PMANAGER_HANDLE pManager = NULL;
_SEH2_TRY
{
if (((PMANAGER_HANDLE)Handle)->Handle.Tag == MANAGER_TAG)
pManager = (PMANAGER_HANDLE)Handle;
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
DPRINT1("Exception: Invalid Service Manager handle\n");
}
_SEH2_END;
return pManager;
}
static PSERVICE_HANDLE
ScmGetServiceFromHandle(SC_RPC_HANDLE Handle)
{
PSERVICE_HANDLE pService = NULL;
_SEH2_TRY
{
if (((PSERVICE_HANDLE)Handle)->Handle.Tag == SERVICE_TAG)
pService = (PSERVICE_HANDLE)Handle;
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
DPRINT1("Exception: Invalid Service handle\n");
}
_SEH2_END;
return pService;
}
static DWORD
ScmCheckAccess(SC_HANDLE Handle,
DWORD dwDesiredAccess)
{
PMANAGER_HANDLE hMgr;
hMgr = (PMANAGER_HANDLE)Handle;
if (hMgr->Handle.Tag == MANAGER_TAG)
{
RtlMapGenericMask(&dwDesiredAccess,
&ScmManagerMapping);
hMgr->Handle.DesiredAccess = dwDesiredAccess;
return ERROR_SUCCESS;
}
else if (hMgr->Handle.Tag == SERVICE_TAG)
{
RtlMapGenericMask(&dwDesiredAccess,
&ScmServiceMapping);
hMgr->Handle.DesiredAccess = dwDesiredAccess;
return ERROR_SUCCESS;
}
return ERROR_INVALID_HANDLE;
}
DWORD
ScmAssignNewTag(PSERVICE lpService)
{
HKEY hKey = NULL;
DWORD dwError;
DWORD dwGroupTagCount = 0;
PDWORD pdwGroupTags = NULL;
DWORD dwFreeTag = 0;
DWORD dwTagUsedBase = 1;
BOOLEAN TagUsed[TAG_ARRAY_SIZE];
INT nTagOffset;
DWORD i;
DWORD cbDataSize;
PLIST_ENTRY ServiceEntry;
PSERVICE CurrentService;
ASSERT(lpService != NULL);
ASSERT(lpService->lpGroup != NULL);
dwError = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
L"System\\CurrentControlSet\\Control\\GroupOrderList",
0,
KEY_READ,
&hKey);
if (dwError != ERROR_SUCCESS)
goto findFreeTag;
/* query value length */
cbDataSize = 0;
dwError = RegQueryValueExW(hKey,
lpService->lpGroup->szGroupName,
NULL,
NULL,
NULL,
&cbDataSize);
if (dwError != ERROR_SUCCESS && dwError != ERROR_MORE_DATA)
goto findFreeTag;
pdwGroupTags = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, cbDataSize);
if (!pdwGroupTags)
{
dwError = ERROR_NOT_ENOUGH_MEMORY;
goto cleanup;
}
dwError = RegQueryValueExW(hKey,
lpService->lpGroup->szGroupName,
NULL,
NULL,
(LPBYTE)pdwGroupTags,
&cbDataSize);
if (dwError != ERROR_SUCCESS)
goto findFreeTag;
if (cbDataSize < sizeof(pdwGroupTags[0]))
goto findFreeTag;
dwGroupTagCount = min(pdwGroupTags[0], cbDataSize / sizeof(pdwGroupTags[0]) - 1);
findFreeTag:
do
{
/* mark all tags as unused */
for (i = 0; i < TAG_ARRAY_SIZE; i++)
TagUsed[i] = FALSE;
/* mark tags in GroupOrderList as used */
for (i = 1; i <= dwGroupTagCount; i++)
{
nTagOffset = pdwGroupTags[i] - dwTagUsedBase;
if (nTagOffset >= 0 && nTagOffset < TAG_ARRAY_SIZE)
TagUsed[nTagOffset] = TRUE;
}
/* mark tags in service list as used */
ServiceEntry = lpService->ServiceListEntry.Flink;
while (ServiceEntry != &lpService->ServiceListEntry)
{
ASSERT(ServiceEntry != NULL);
CurrentService = CONTAINING_RECORD(ServiceEntry, SERVICE, ServiceListEntry);
if (CurrentService->lpGroup == lpService->lpGroup)
{
nTagOffset = CurrentService->dwTag - dwTagUsedBase;
if (nTagOffset >= 0 && nTagOffset < TAG_ARRAY_SIZE)
TagUsed[nTagOffset] = TRUE;
}
ServiceEntry = ServiceEntry->Flink;
}
/* find unused tag, if any */
for (i = 0; i < TAG_ARRAY_SIZE; i++)
{
if (!TagUsed[i])
{
dwFreeTag = dwTagUsedBase + i;
break;
}
}
dwTagUsedBase += TAG_ARRAY_SIZE;
} while (!dwFreeTag);
cleanup:
if (pdwGroupTags)
HeapFree(GetProcessHeap(), 0, pdwGroupTags);
if (hKey)
RegCloseKey(hKey);
if (dwFreeTag)
{
lpService->dwTag = dwFreeTag;
DPRINT("Assigning new tag %lu to service %S in group %S\n",
lpService->dwTag, lpService->lpServiceName, lpService->lpGroup->szGroupName);
dwError = ERROR_SUCCESS;
}
else
{
DPRINT1("Failed to assign new tag to service %S, error=%lu\n",
lpService->lpServiceName, dwError);
}
return dwError;
}
/* Create a path suitable for the bootloader out of the full path */
DWORD
ScmConvertToBootPathName(wchar_t *CanonName, wchar_t **RelativeName)
{
SIZE_T ServiceNameLen, ExpandedLen;
DWORD BufferSize;
WCHAR Dest;
WCHAR *Expanded;
UNICODE_STRING NtPathName, SystemRoot, LinkTarget;
OBJECT_ATTRIBUTES ObjectAttributes;
NTSTATUS Status;
HANDLE SymbolicLinkHandle;
DPRINT("ScmConvertToBootPathName %S\n", CanonName);
if (!RelativeName)
return ERROR_INVALID_PARAMETER;
*RelativeName = NULL;
ServiceNameLen = wcslen(CanonName);
/* First check, if it's already good */
if (ServiceNameLen > 12 &&
!_wcsnicmp(L"\\SystemRoot\\", CanonName, 12))
{
*RelativeName = HeapAlloc(GetProcessHeap(),
HEAP_ZERO_MEMORY,
(ServiceNameLen + 1) * sizeof(WCHAR));
if (*RelativeName == NULL)
{
DPRINT("Error allocating memory for boot driver name\n");
return ERROR_NOT_ENOUGH_MEMORY;
}
/* Copy it */
wcscpy(*RelativeName, CanonName);
DPRINT("Bootdriver name %S\n", *RelativeName);
return ERROR_SUCCESS;
}
/* If it has %SystemRoot% prefix, substitute it to \System*/
if (ServiceNameLen > 13 &&
!_wcsnicmp(L"%SystemRoot%\\", CanonName, 13))
{
/* There is no +sizeof(wchar_t) because the name is less by 1 wchar */
*RelativeName = HeapAlloc(GetProcessHeap(),
HEAP_ZERO_MEMORY,
ServiceNameLen * sizeof(WCHAR));
if (*RelativeName == NULL)
{
DPRINT("Error allocating memory for boot driver name\n");
return ERROR_NOT_ENOUGH_MEMORY;
}
/* Copy it */
wcscpy(*RelativeName, L"\\SystemRoot\\");
wcscat(*RelativeName, CanonName + 13);
DPRINT("Bootdriver name %S\n", *RelativeName);
return ERROR_SUCCESS;
}
/* Get buffer size needed for expanding env strings */
BufferSize = ExpandEnvironmentStringsW(L"%SystemRoot%\\", &Dest, 1);
if (BufferSize <= 1)
{
DPRINT("Error during a call to ExpandEnvironmentStringsW()\n");
return ERROR_INVALID_ENVIRONMENT;
}
/* Allocate memory, since the size is known now */
Expanded = HeapAlloc(GetProcessHeap(),
HEAP_ZERO_MEMORY,
(BufferSize + 1) * sizeof(WCHAR));
if (!Expanded)
{
DPRINT("Error allocating memory for boot driver name\n");
return ERROR_NOT_ENOUGH_MEMORY;
}
/* Expand it */
if (ExpandEnvironmentStringsW(L"%SystemRoot%\\", Expanded, BufferSize) >
BufferSize)
{
DPRINT("Error during a call to ExpandEnvironmentStringsW()\n");
HeapFree(GetProcessHeap(), 0, Expanded);
return ERROR_NOT_ENOUGH_MEMORY;
}
/* Convert to NT-style path */
if (!RtlDosPathNameToNtPathName_U(Expanded, &NtPathName, NULL, NULL))
{
DPRINT("Error during a call to RtlDosPathNameToNtPathName_U()\n");
return ERROR_INVALID_ENVIRONMENT;
}
DPRINT("Converted to NT-style %wZ\n", &NtPathName);
/* No need to keep the dos-path anymore */
HeapFree(GetProcessHeap(), 0, Expanded);
/* Copy it to the allocated place */
Expanded = HeapAlloc(GetProcessHeap(),
HEAP_ZERO_MEMORY,
NtPathName.Length + sizeof(UNICODE_NULL));
if (!Expanded)
{
DPRINT("Error allocating memory for boot driver name\n");
RtlFreeUnicodeString(&NtPathName);
return ERROR_NOT_ENOUGH_MEMORY;
}
ExpandedLen = NtPathName.Length / sizeof(WCHAR);
wcsncpy(Expanded, NtPathName.Buffer, ExpandedLen);
Expanded[ExpandedLen] = UNICODE_NULL;
RtlFreeUnicodeString(&NtPathName);
if (ServiceNameLen > ExpandedLen &&
!_wcsnicmp(Expanded, CanonName, ExpandedLen))
{
HeapFree(GetProcessHeap(), 0, Expanded);
/* Only \SystemRoot\ is missing */
*RelativeName = HeapAlloc(GetProcessHeap(),
HEAP_ZERO_MEMORY,
(ServiceNameLen - ExpandedLen) * sizeof(WCHAR) + 13*sizeof(WCHAR));
if (*RelativeName == NULL)
{
DPRINT("Error allocating memory for boot driver name\n");
return ERROR_NOT_ENOUGH_MEMORY;
}
wcscpy(*RelativeName, L"\\SystemRoot\\");
wcscat(*RelativeName, CanonName + ExpandedLen);
return ERROR_SUCCESS;
}
/* No longer need this */
HeapFree(GetProcessHeap(), 0, Expanded);
/* The most complex case starts here */
RtlInitUnicodeString(&SystemRoot, L"\\SystemRoot");
InitializeObjectAttributes(&ObjectAttributes,
&SystemRoot,
OBJ_CASE_INSENSITIVE,
NULL,
NULL);
/* Open this symlink */
Status = NtOpenSymbolicLinkObject(&SymbolicLinkHandle, SYMBOLIC_LINK_QUERY, &ObjectAttributes);
if (NT_SUCCESS(Status))
{
DPRINT("Opened symbolic link object\n");
RtlInitEmptyUnicodeString(&LinkTarget, NULL, 0);
Status = NtQuerySymbolicLinkObject(SymbolicLinkHandle, &LinkTarget, &BufferSize);
if (NT_SUCCESS(Status) || Status == STATUS_BUFFER_TOO_SMALL)
{
/* Check if required buffer size is sane */
if (BufferSize > UNICODE_STRING_MAX_BYTES - sizeof(UNICODE_NULL))
{
DPRINT("Too large buffer required\n");
NtClose(SymbolicLinkHandle);
return ERROR_NOT_ENOUGH_MEMORY;
}
/* Alloc the string */
LinkTarget.Length = (USHORT)BufferSize;
LinkTarget.MaximumLength = LinkTarget.Length + sizeof(UNICODE_NULL);
LinkTarget.Buffer = HeapAlloc(GetProcessHeap(),
HEAP_ZERO_MEMORY,
LinkTarget.MaximumLength);
if (!LinkTarget.Buffer)
{
DPRINT("Unable to alloc buffer\n");
NtClose(SymbolicLinkHandle);
return ERROR_NOT_ENOUGH_MEMORY;
}
/* Do a real query now */
Status = NtQuerySymbolicLinkObject(SymbolicLinkHandle, &LinkTarget, &BufferSize);
NtClose(SymbolicLinkHandle);
if (NT_SUCCESS(Status))
{
DPRINT("LinkTarget: %wZ\n", &LinkTarget);
ExpandedLen = LinkTarget.Length / sizeof(WCHAR);
if ((ServiceNameLen > ExpandedLen) &&
!_wcsnicmp(LinkTarget.Buffer, CanonName, ExpandedLen))
{
*RelativeName = HeapAlloc(GetProcessHeap(),
HEAP_ZERO_MEMORY,
(ServiceNameLen - ExpandedLen) * sizeof(WCHAR) + 13*sizeof(WCHAR));
if (*RelativeName == NULL)
{
DPRINT("Unable to alloc buffer\n");
return ERROR_NOT_ENOUGH_MEMORY;
}
/* Copy it over, substituting the first part
with SystemRoot */
wcscpy(*RelativeName, L"\\SystemRoot\\");
wcscat(*RelativeName, CanonName+ExpandedLen+1);
/* Return success */
return ERROR_SUCCESS;
}
else
{
return ERROR_INVALID_PARAMETER;
}
}
else
{
DPRINT("Error, Status = %08X\n", Status);
return ERROR_INVALID_PARAMETER;
}
}
else
{
DPRINT("Error, Status = %08X\n", Status);
NtClose(SymbolicLinkHandle);
return ERROR_INVALID_PARAMETER;
}
}
else
{
/* Failure */
DPRINT("Error, Status = %08X\n", Status);
return ERROR_INVALID_PARAMETER;
}
}
DWORD
ScmCanonDriverImagePath(DWORD dwStartType,
const wchar_t *lpServiceName,
wchar_t **lpCanonName)
{
DWORD Result;
SIZE_T ServiceNameLen;
UNICODE_STRING NtServiceName;
WCHAR *RelativeName;
const WCHAR *SourceName = lpServiceName;
/* Calculate the length of the service's name */
ServiceNameLen = wcslen(lpServiceName);
/* 12 is wcslen(L"\\SystemRoot\\") */
if (ServiceNameLen > 12 &&
!_wcsnicmp(L"\\SystemRoot\\", lpServiceName, 12))
{
/* SystemRoot prefix is already included */
*lpCanonName = HeapAlloc(GetProcessHeap(),
HEAP_ZERO_MEMORY,
(ServiceNameLen + 1) * sizeof(WCHAR));
if (*lpCanonName == NULL)
{
DPRINT("Error allocating memory for canonized service name\n");
return ERROR_NOT_ENOUGH_MEMORY;
}
/* If it's a boot-time driver, it must be systemroot relative */
if (dwStartType == SERVICE_BOOT_START)
SourceName += 12;
/* Copy it */
wcscpy(*lpCanonName, SourceName);
DPRINT("Canonicalized name %S\n", *lpCanonName);
return NO_ERROR;
}
/* Check if it has %SystemRoot% (len=13) */
if (ServiceNameLen > 13 &&
!_wcsnicmp(L"%SystemRoot%\\", lpServiceName, 13))
{
/* Substitute %SystemRoot% with \\SystemRoot\\ */
*lpCanonName = HeapAlloc(GetProcessHeap(),
HEAP_ZERO_MEMORY,
(ServiceNameLen + 1) * sizeof(WCHAR));
if (*lpCanonName == NULL)
{
DPRINT("Error allocating memory for canonized service name\n");
return ERROR_NOT_ENOUGH_MEMORY;
}
/* If it's a boot-time driver, it must be systemroot relative */
if (dwStartType == SERVICE_BOOT_START)
wcscpy(*lpCanonName, L"\\SystemRoot\\");
wcscat(*lpCanonName, lpServiceName + 13);
DPRINT("Canonicalized name %S\n", *lpCanonName);
return NO_ERROR;
}
/* Check if it's a relative path name */
if (lpServiceName[0] != L'\\' && lpServiceName[1] != L':')
{
*lpCanonName = HeapAlloc(GetProcessHeap(),
HEAP_ZERO_MEMORY,
(ServiceNameLen + 1) * sizeof(WCHAR));
if (*lpCanonName == NULL)
{
DPRINT("Error allocating memory for canonized service name\n");
return ERROR_NOT_ENOUGH_MEMORY;
}
/* Just copy it over without changing */
wcscpy(*lpCanonName, lpServiceName);
return NO_ERROR;
}
/* It seems to be a DOS path, convert it */
if (!RtlDosPathNameToNtPathName_U(lpServiceName, &NtServiceName, NULL, NULL))
{
DPRINT("RtlDosPathNameToNtPathName_U() failed\n");
return ERROR_INVALID_PARAMETER;
}
*lpCanonName = HeapAlloc(GetProcessHeap(),
HEAP_ZERO_MEMORY,
NtServiceName.Length + sizeof(WCHAR));
if (*lpCanonName == NULL)
{
DPRINT("Error allocating memory for canonized service name\n");
RtlFreeUnicodeString(&NtServiceName);
return ERROR_NOT_ENOUGH_MEMORY;
}
/* Copy the string */
wcsncpy(*lpCanonName, NtServiceName.Buffer, NtServiceName.Length / sizeof(WCHAR));
/* The unicode string is not needed anymore */
RtlFreeUnicodeString(&NtServiceName);
if (dwStartType != SERVICE_BOOT_START)
{
DPRINT("Canonicalized name %S\n", *lpCanonName);
return NO_ERROR;
}
/* The service is boot-started, so must be relative */
Result = ScmConvertToBootPathName(*lpCanonName, &RelativeName);
if (Result)
{
/* There is a problem, free name and return */
HeapFree(GetProcessHeap(), 0, *lpCanonName);
DPRINT("Error converting named\n");
return Result;
}
ASSERT(RelativeName);
/* Copy that string */
wcscpy(*lpCanonName, RelativeName + 12);
/* Free the allocated buffer */
HeapFree(GetProcessHeap(), 0, RelativeName);
DPRINT("Canonicalized name %S\n", *lpCanonName);
/* Success */
return NO_ERROR;
}
/* Recursively search for every dependency on every service */
DWORD
Int_EnumDependentServicesW(HKEY hServicesKey,
PSERVICE lpService,
DWORD dwServiceState,
PSERVICE *lpServices,
LPDWORD pcbBytesNeeded,
LPDWORD lpServicesReturned)
{
DWORD dwError = ERROR_SUCCESS;
WCHAR szNameBuf[MAX_PATH];
WCHAR szValueBuf[MAX_PATH];
WCHAR *lpszNameBuf = szNameBuf;
WCHAR *lpszValueBuf = szValueBuf;
DWORD dwSize;
DWORD dwNumSubKeys;
DWORD dwIteration;
PSERVICE lpCurrentService;
HKEY hServiceEnumKey;
DWORD dwCurrentServiceState = SERVICE_ACTIVE;
DWORD dwDependServiceStrPtr = 0;
DWORD dwRequiredSize = 0;
/* Get the number of service keys */
dwError = RegQueryInfoKeyW(hServicesKey,
NULL,
NULL,
NULL,
&dwNumSubKeys,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL);
if (dwError != ERROR_SUCCESS)
{
DPRINT("ERROR! Unable to get number of services keys\n");
return dwError;
}
/* Iterate the service keys to see if another service depends on the this service */
for (dwIteration = 0; dwIteration < dwNumSubKeys; dwIteration++)
{
dwSize = MAX_PATH;
dwError = RegEnumKeyExW(hServicesKey,
dwIteration,
lpszNameBuf,
&dwSize,
NULL,
NULL,
NULL,
NULL);
if (dwError != ERROR_SUCCESS)
return dwError;
/* Open the Service key */
dwError = RegOpenKeyExW(hServicesKey,
lpszNameBuf,
0,
KEY_READ,
&hServiceEnumKey);
if (dwError != ERROR_SUCCESS)
return dwError;
dwSize = MAX_PATH * sizeof(WCHAR);
/* Check for the DependOnService Value */
dwError = RegQueryValueExW(hServiceEnumKey,
L"DependOnService",
NULL,
NULL,
(LPBYTE)lpszValueBuf,
&dwSize);
/* FIXME: Handle load order. */
/* If the service found has a DependOnService value */
if (dwError == ERROR_SUCCESS)
{
dwDependServiceStrPtr = 0;
/* Can be more than one Dependencies in the DependOnService string */
while (wcslen(lpszValueBuf + dwDependServiceStrPtr) > 0)
{
if (_wcsicmp(lpszValueBuf + dwDependServiceStrPtr, lpService->lpServiceName) == 0)
{
/* Get the current enumed service pointer */
lpCurrentService = ScmGetServiceEntryByName(lpszNameBuf);
/* Check for valid Service */
if (!lpCurrentService)
{
/* This should never happen! */
DPRINT("This should not happen at this point, report to Developer\n");
return ERROR_NOT_FOUND;
}
/* Determine state the service is in */
if (lpCurrentService->Status.dwCurrentState == SERVICE_STOPPED)
dwCurrentServiceState = SERVICE_INACTIVE;
/* If the ServiceState matches that requested or searching for SERVICE_STATE_ALL */
if ((dwCurrentServiceState == dwServiceState) ||
(dwServiceState == SERVICE_STATE_ALL))
{
/* Calculate the required size */
dwRequiredSize += sizeof(SERVICE_STATUS);
dwRequiredSize += (DWORD)((wcslen(lpCurrentService->lpServiceName) + 1) * sizeof(WCHAR));
dwRequiredSize += (DWORD)((wcslen(lpCurrentService->lpDisplayName) + 1) * sizeof(WCHAR));
/* Add the size for service name and display name pointers */
dwRequiredSize += (2 * sizeof(PVOID));
/* increase the BytesNeeded size */
*pcbBytesNeeded = *pcbBytesNeeded + dwRequiredSize;
/* Don't fill callers buffer yet, as MSDN read that the last service with dependency
comes first */
/* Recursive call to check for its dependencies */
Int_EnumDependentServicesW(hServicesKey,
lpCurrentService,
dwServiceState,
lpServices,
pcbBytesNeeded,
lpServicesReturned);
/* If the lpServices is valid set the service pointer */
if (lpServices)
lpServices[*lpServicesReturned] = lpCurrentService;
*lpServicesReturned = *lpServicesReturned + 1;
}
}
dwDependServiceStrPtr += (DWORD)(wcslen(lpszValueBuf + dwDependServiceStrPtr) + 1);
}
}
else if (*pcbBytesNeeded)
{
dwError = ERROR_SUCCESS;
}
RegCloseKey(hServiceEnumKey);
}
return dwError;
}
/* Function 0 */
DWORD
WINAPI
RCloseServiceHandle(
LPSC_RPC_HANDLE hSCObject)
{
PMANAGER_HANDLE hManager;
PSERVICE_HANDLE hService;
PSERVICE lpService;
DPRINT("RCloseServiceHandle() called\n");
DPRINT("hSCObject = %p\n", *hSCObject);
if (*hSCObject == 0)
return ERROR_INVALID_HANDLE;
hManager = ScmGetServiceManagerFromHandle(*hSCObject);
hService = ScmGetServiceFromHandle(*hSCObject);
if (hManager != NULL)
{
DPRINT("Found manager handle\n");
/* Make sure we don't access stale memory if someone tries to use this handle again. */
hManager->Handle.Tag = INVALID_TAG;
HeapFree(GetProcessHeap(), 0, hManager);
hManager = NULL;
*hSCObject = NULL;
DPRINT("RCloseServiceHandle() done\n");
return ERROR_SUCCESS;
}
else if (hService != NULL)
{
DPRINT("Found service handle\n");
/* Lock the service database exclusively */
ScmLockDatabaseExclusive();
/* Get the pointer to the service record */
lpService = hService->ServiceEntry;
/* Make sure we don't access stale memory if someone tries to use this handle again. */
hService->Handle.Tag = INVALID_TAG;
/* Free the handle */
HeapFree(GetProcessHeap(), 0, hService);
hService = NULL;
DPRINT("Closing service %S with %d references\n", lpService->lpServiceName, lpService->RefCount);
ScmDereferenceService(lpService);
ScmUnlockDatabase();
*hSCObject = NULL;
DPRINT("RCloseServiceHandle() done\n");
return ERROR_SUCCESS;
}
DPRINT("Invalid handle tag (Tag %lx)\n", hManager->Handle.Tag);
return ERROR_INVALID_HANDLE;
}
/* Function 1 */
DWORD
WINAPI
RControlService(
SC_RPC_HANDLE hService,
DWORD dwControl,
LPSERVICE_STATUS lpServiceStatus)
{
PSERVICE_HANDLE hSvc;
PSERVICE lpService;
ACCESS_MASK DesiredAccess;
DWORD dwError = ERROR_SUCCESS;
DWORD pcbBytesNeeded = 0;
DWORD dwServicesReturned = 0;
DWORD dwControlsAccepted;
DWORD dwCurrentState;
HKEY hServicesKey = NULL;
LPCWSTR lpLogStrings[2];
WCHAR szLogBuffer[80];
UINT uID;
DPRINT("RControlService() called\n");
if (ScmShutdown)
return ERROR_SHUTDOWN_IN_PROGRESS;
/* Check the service handle */
hSvc = ScmGetServiceFromHandle(hService);
if (hSvc == NULL)
{
DPRINT1("Invalid service handle\n");
return ERROR_INVALID_HANDLE;
}
/* Check the service entry point */
lpService = hSvc->ServiceEntry;
if (lpService == NULL)
{
DPRINT1("lpService == NULL\n");
return ERROR_INVALID_HANDLE;
}
/* Check access rights */
switch (dwControl)
{
case SERVICE_CONTROL_STOP:
DesiredAccess = SERVICE_STOP;
break;
case SERVICE_CONTROL_PAUSE:
case SERVICE_CONTROL_CONTINUE:
case SERVICE_CONTROL_PARAMCHANGE:
case SERVICE_CONTROL_NETBINDADD:
case SERVICE_CONTROL_NETBINDREMOVE:
case SERVICE_CONTROL_NETBINDENABLE:
case SERVICE_CONTROL_NETBINDDISABLE:
DesiredAccess = SERVICE_PAUSE_CONTINUE;
break;
case SERVICE_CONTROL_INTERROGATE:
DesiredAccess = SERVICE_INTERROGATE;
break;
default:
if (dwControl >= 128 && dwControl <= 255)
DesiredAccess = SERVICE_USER_DEFINED_CONTROL;
else
return ERROR_INVALID_PARAMETER;
break;
}
if (!RtlAreAllAccessesGranted(hSvc->Handle.DesiredAccess,
DesiredAccess))
return ERROR_ACCESS_DENIED;
/* Return the current service status information */
RtlCopyMemory(lpServiceStatus,
&lpService->Status,
sizeof(SERVICE_STATUS));
if (dwControl == SERVICE_CONTROL_STOP)
{
/* Check if the service has dependencies running as windows
doesn't stop a service that does */
/* Open the Services Reg key */
dwError = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
L"System\\CurrentControlSet\\Services",
0,
KEY_READ,
&hServicesKey);
if (dwError != ERROR_SUCCESS)
{
DPRINT("Failed to open services key\n");
return dwError;
}
/* Call the internal function with NULL, just to get bytes we need */
Int_EnumDependentServicesW(hServicesKey,
lpService,
SERVICE_ACTIVE,
NULL,
&pcbBytesNeeded,
&dwServicesReturned);
RegCloseKey(hServicesKey);
/* If pcbBytesNeeded is not zero then there are services running that
are dependent on this service */
if (pcbBytesNeeded != 0)
{
DPRINT("Service has running dependencies. Failed to stop service\n");
return ERROR_DEPENDENT_SERVICES_RUNNING;
}
}
if (lpService->Status.dwServiceType & SERVICE_DRIVER)
{
/* Send control code to the driver */
dwError = ScmControlDriver(lpService,
dwControl,
lpServiceStatus);
}
else
{
dwControlsAccepted = lpService->Status.dwControlsAccepted;
dwCurrentState = lpService->Status.dwCurrentState;
/* Return ERROR_SERVICE_NOT_ACTIVE if the service has not been started */
if (lpService->lpImage == NULL || dwCurrentState == SERVICE_STOPPED)
return ERROR_SERVICE_NOT_ACTIVE;
/* Check the current state before sending a control request */
switch (dwCurrentState)
{
case SERVICE_STOP_PENDING:
case SERVICE_STOPPED:
return ERROR_SERVICE_CANNOT_ACCEPT_CTRL;
case SERVICE_START_PENDING:
switch (dwControl)
{
case SERVICE_CONTROL_STOP:
break;
case SERVICE_CONTROL_INTERROGATE:
RtlCopyMemory(lpServiceStatus,
&lpService->Status,
sizeof(SERVICE_STATUS));
return ERROR_SUCCESS;
default:
return ERROR_SERVICE_CANNOT_ACCEPT_CTRL;
}
break;
}
/* Check if the control code is acceptable to the service */
switch (dwControl)
{
case SERVICE_CONTROL_STOP:
if ((dwControlsAccepted & SERVICE_ACCEPT_STOP) == 0)
return ERROR_INVALID_SERVICE_CONTROL;
break;
case SERVICE_CONTROL_PAUSE:
case SERVICE_CONTROL_CONTINUE:
if ((dwControlsAccepted & SERVICE_ACCEPT_PAUSE_CONTINUE) == 0)
return ERROR_INVALID_SERVICE_CONTROL;
break;
case SERVICE_CONTROL_PARAMCHANGE:
if ((dwControlsAccepted & SERVICE_ACCEPT_PARAMCHANGE) == 0)
return ERROR_INVALID_SERVICE_CONTROL;
break;
case SERVICE_CONTROL_NETBINDADD:
case SERVICE_CONTROL_NETBINDREMOVE:
case SERVICE_CONTROL_NETBINDENABLE:
case SERVICE_CONTROL_NETBINDDISABLE:
if ((dwControlsAccepted & SERVICE_ACCEPT_NETBINDCHANGE) == 0)
return ERROR_INVALID_SERVICE_CONTROL;
break;
}
/* Send control code to the service */
dwError = ScmControlService(lpService->lpImage->hControlPipe,
lpService->lpServiceName,
dwControl,
(SERVICE_STATUS_HANDLE)lpService);
/* Return service status information */
RtlCopyMemory(lpServiceStatus,
&lpService->Status,
sizeof(SERVICE_STATUS));
}
if (dwError == ERROR_SUCCESS)
{
if (dwControl == SERVICE_CONTROL_STOP ||
dwControl == SERVICE_CONTROL_PAUSE ||
dwControl == SERVICE_CONTROL_CONTINUE)
{
/* Log a successful send control */
switch (dwControl)
{
case SERVICE_CONTROL_STOP:
uID = IDS_SERVICE_STOP;
break;
case SERVICE_CONTROL_PAUSE:
uID = IDS_SERVICE_PAUSE;
break;
case SERVICE_CONTROL_CONTINUE:
uID = IDS_SERVICE_RESUME;
break;
}
LoadStringW(GetModuleHandle(NULL), uID, szLogBuffer, ARRAYSIZE(szLogBuffer));
lpLogStrings[0] = lpService->lpDisplayName;
lpLogStrings[1] = szLogBuffer;
ScmLogEvent(EVENT_SERVICE_CONTROL_SUCCESS,
EVENTLOG_INFORMATION_TYPE,
2,
lpLogStrings);
}
}
return dwError;
}
/* Function 2 */
DWORD
WINAPI
RDeleteService(
SC_RPC_HANDLE hService)
{
PSERVICE_HANDLE hSvc;
PSERVICE lpService;
DWORD dwError;
DPRINT("RDeleteService() called\n");
if (ScmShutdown)
return ERROR_SHUTDOWN_IN_PROGRESS;
hSvc = ScmGetServiceFromHandle(hService);
if (hSvc == NULL)
{
DPRINT1("Invalid service handle\n");
return ERROR_INVALID_HANDLE;
}
if (!RtlAreAllAccessesGranted(hSvc->Handle.DesiredAccess,
DELETE))
return ERROR_ACCESS_DENIED;
lpService = hSvc->ServiceEntry;
if (lpService == NULL)
{
DPRINT("lpService == NULL\n");
return ERROR_INVALID_HANDLE;
}
/* Lock the service database exclusively */
ScmLockDatabaseExclusive();
if (lpService->bDeleted)
{
DPRINT("Service has already been marked for delete\n");
dwError = ERROR_SERVICE_MARKED_FOR_DELETE;
goto Done;
}
/* Mark service for delete */
lpService->bDeleted = TRUE;
dwError = ScmMarkServiceForDelete(lpService);
Done:
/* Unlock the service database */
ScmUnlockDatabase();
DPRINT("RDeleteService() done\n");
return dwError;
}
/* Function 3 */
DWORD
WINAPI
RLockServiceDatabase(
SC_RPC_HANDLE hSCManager,
LPSC_RPC_LOCK lpLock)
{
PMANAGER_HANDLE hMgr;
DPRINT("RLockServiceDatabase() called\n");
*lpLock = NULL;
hMgr = ScmGetServiceManagerFromHandle(hSCManager);
if (hMgr == NULL)
{
DPRINT1("Invalid service manager handle\n");
return ERROR_INVALID_HANDLE;
}
if (!RtlAreAllAccessesGranted(hMgr->Handle.DesiredAccess,
SC_MANAGER_LOCK))
return ERROR_ACCESS_DENIED;
return ScmAcquireServiceStartLock(FALSE, lpLock);
}
/* Function 4 */
DWORD
WINAPI
RQueryServiceObjectSecurity(
SC_RPC_HANDLE hService,
SECURITY_INFORMATION dwSecurityInformation,
LPBYTE lpSecurityDescriptor,
DWORD cbBufSize,
LPBOUNDED_DWORD_256K pcbBytesNeeded)
{
PSERVICE_HANDLE hSvc;
PSERVICE lpService;
ULONG DesiredAccess = 0;
NTSTATUS Status;
DWORD dwBytesNeeded;
DWORD dwError;
DPRINT("RQueryServiceObjectSecurity() called\n");
hSvc = ScmGetServiceFromHandle(hService);
if (hSvc == NULL)
{
DPRINT1("Invalid service handle\n");
return ERROR_INVALID_HANDLE;
}
if (dwSecurityInformation & (DACL_SECURITY_INFORMATION |
GROUP_SECURITY_INFORMATION |
OWNER_SECURITY_INFORMATION))
DesiredAccess |= READ_CONTROL;
if (dwSecurityInformation & SACL_SECURITY_INFORMATION)
DesiredAccess |= ACCESS_SYSTEM_SECURITY;
if (!RtlAreAllAccessesGranted(hSvc->Handle.DesiredAccess,
DesiredAccess))
{
DPRINT("Insufficient access rights! 0x%lx\n", hSvc->Handle.DesiredAccess);
return ERROR_ACCESS_DENIED;
}
lpService = hSvc->ServiceEntry;
if (lpService == NULL)
{
DPRINT("lpService == NULL\n");
return ERROR_INVALID_HANDLE;
}
/* Lock the service database */
ScmLockDatabaseShared();
/* Retrieve the security descriptor */
Status = RtlQuerySecurityObject(lpService->pSecurityDescriptor,
dwSecurityInformation,
(PSECURITY_DESCRIPTOR)lpSecurityDescriptor,
cbBufSize,
&dwBytesNeeded);
/* Unlock the service database */
ScmUnlockDatabase();
if (NT_SUCCESS(Status))
{
*pcbBytesNeeded = dwBytesNeeded;
dwError = STATUS_SUCCESS;
}
else if (Status == STATUS_BUFFER_TOO_SMALL)
{
*pcbBytesNeeded = dwBytesNeeded;
dwError = ERROR_INSUFFICIENT_BUFFER;
}
else if (Status == STATUS_BAD_DESCRIPTOR_FORMAT)
{
dwError = ERROR_GEN_FAILURE;
}
else
{
dwError = RtlNtStatusToDosError(Status);
}
return dwError;
}
/* Function 5 */
DWORD
WINAPI
RSetServiceObjectSecurity(
SC_RPC_HANDLE hService,
DWORD dwSecurityInformation,
LPBYTE lpSecurityDescriptor,
DWORD dwSecurityDescriptorSize)
{
PSERVICE_HANDLE hSvc;
PSERVICE lpService;
ACCESS_MASK DesiredAccess = 0;
HANDLE hToken = NULL;
HKEY hServiceKey = NULL;
BOOL bDatabaseLocked = FALSE;
NTSTATUS Status;
DWORD dwError;
DPRINT("RSetServiceObjectSecurity() called\n");
hSvc = ScmGetServiceFromHandle(hService);
if (hSvc == NULL)
{
DPRINT1("Invalid service handle\n");
return ERROR_INVALID_HANDLE;
}
if (dwSecurityInformation == 0 ||
dwSecurityInformation & ~(OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION
| DACL_SECURITY_INFORMATION | SACL_SECURITY_INFORMATION))
{
return ERROR_INVALID_PARAMETER;
}
if (!RtlValidSecurityDescriptor((PSECURITY_DESCRIPTOR)lpSecurityDescriptor))
return ERROR_INVALID_PARAMETER;
if (dwSecurityInformation & SACL_SECURITY_INFORMATION)
DesiredAccess |= ACCESS_SYSTEM_SECURITY;
if (dwSecurityInformation & DACL_SECURITY_INFORMATION)
DesiredAccess |= WRITE_DAC;
if (dwSecurityInformation & (OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION))
DesiredAccess |= WRITE_OWNER;
if ((dwSecurityInformation & OWNER_SECURITY_INFORMATION) &&
(((PISECURITY_DESCRIPTOR)lpSecurityDescriptor)->Owner == NULL))
{
return ERROR_INVALID_PARAMETER;
}
if ((dwSecurityInformation & GROUP_SECURITY_INFORMATION) &&
(((PISECURITY_DESCRIPTOR)lpSecurityDescriptor)->Group == NULL))
{
return ERROR_INVALID_PARAMETER;
}
if (!RtlAreAllAccessesGranted(hSvc->Handle.DesiredAccess,
DesiredAccess))
{
DPRINT1("Insufficient access rights! 0x%lx\n", hSvc->Handle.DesiredAccess);
return ERROR_ACCESS_DENIED;
}
lpService = hSvc->ServiceEntry;
if (lpService == NULL)
{
DPRINT1("lpService == NULL\n");
return ERROR_INVALID_HANDLE;
}
if (lpService->bDeleted)
return ERROR_SERVICE_MARKED_FOR_DELETE;
#if 0
RpcImpersonateClient(NULL);
Status = NtOpenThreadToken(NtCurrentThread(),
8,
TRUE,
&hToken);
if (!NT_SUCCESS(Status))
return RtlNtStatusToDosError(Status);
RpcRevertToSelf();
#endif
/* Build the new security descriptor */
Status = RtlSetSecurityObject(dwSecurityInformation,
(PSECURITY_DESCRIPTOR)lpSecurityDescriptor,
&lpService->pSecurityDescriptor,
&ScmServiceMapping,
hToken);
if (!NT_SUCCESS(Status))
{
dwError = RtlNtStatusToDosError(Status);
goto Done;
}
/* Lock the service database exclusive */
ScmLockDatabaseExclusive();
bDatabaseLocked = TRUE;
/* Open the service key */
dwError = ScmOpenServiceKey(lpService->lpServiceName,
READ_CONTROL | KEY_CREATE_SUB_KEY | KEY_SET_VALUE,
&hServiceKey);
if (dwError != ERROR_SUCCESS)
goto Done;
/* Store the new security descriptor */
dwError = ScmWriteSecurityDescriptor(hServiceKey,
lpService->pSecurityDescriptor);
RegFlushKey(hServiceKey);
Done:
if (hServiceKey != NULL)
RegCloseKey(hServiceKey);
/* Unlock service database */
if (bDatabaseLocked == TRUE)
ScmUnlockDatabase();
if (hToken != NULL)
NtClose(hToken);
DPRINT("RSetServiceObjectSecurity() done (Error %lu)\n", dwError);
return dwError;
}
/* Function 6 */
DWORD
WINAPI
RQueryServiceStatus(
SC_RPC_HANDLE hService,
LPSERVICE_STATUS lpServiceStatus)
{
PSERVICE_HANDLE hSvc;
PSERVICE lpService;
DPRINT("RQueryServiceStatus() called\n");
if (ScmShutdown)
return ERROR_SHUTDOWN_IN_PROGRESS;
hSvc = ScmGetServiceFromHandle(hService);
if (hSvc == NULL)
{
DPRINT1("Invalid service handle\n");
return ERROR_INVALID_HANDLE;
}
if (!RtlAreAllAccessesGranted(hSvc->Handle.DesiredAccess,
SERVICE_QUERY_STATUS))
{
DPRINT("Insufficient access rights! 0x%lx\n", hSvc->Handle.DesiredAccess);
return ERROR_ACCESS_DENIED;
}
lpService = hSvc->ServiceEntry;
if (lpService == NULL)
{
DPRINT("lpService == NULL\n");
return ERROR_INVALID_HANDLE;
}
/* Lock the service database shared */
ScmLockDatabaseShared();
/* Return service status information */
RtlCopyMemory(lpServiceStatus,
&lpService->Status,
sizeof(SERVICE_STATUS));
/* Unlock the service database */
ScmUnlockDatabase();
return ERROR_SUCCESS;
}
static BOOL
ScmIsValidServiceState(DWORD dwCurrentState)
{
switch (dwCurrentState)
{
case SERVICE_STOPPED:
case SERVICE_START_PENDING:
case SERVICE_STOP_PENDING:
case SERVICE_RUNNING:
case SERVICE_CONTINUE_PENDING:
case SERVICE_PAUSE_PENDING:
case SERVICE_PAUSED:
return TRUE;
default:
return FALSE;
}
}
static
DWORD
WINAPI
ScmStopThread(PVOID pParam)
{
PSERVICE lpService = (PSERVICE)pParam;
WCHAR szLogBuffer[80];
LPCWSTR lpLogStrings[2];
/* Check if we are about to stop this service */
if (lpService->lpImage->dwImageRunCount == 1)
{
/* Stop the dispatcher thread.
* We must not send a control message while holding the database lock, otherwise it can cause timeouts
* We are sure that the service won't be deleted in the meantime because we still have a reference to it. */
DPRINT("Stopping the dispatcher thread for service %S\n", lpService->lpServiceName);
ScmControlService(lpService->lpImage->hControlPipe,
L"",
SERVICE_CONTROL_STOP,
(SERVICE_STATUS_HANDLE)lpService);
}
/* Lock the service database exclusively */
ScmLockDatabaseExclusive();
DPRINT("Service %S image count:%d\n", lpService->lpServiceName, lpService->lpImage->dwImageRunCount);
/* Decrement the image run counter */
lpService->lpImage->dwImageRunCount--;
/* If we just stopped the last running service... */
if (lpService->lpImage->dwImageRunCount == 0)
{
/* Remove the service image */
DPRINT("Removing service image for %S\n", lpService->lpServiceName);
ScmRemoveServiceImage(lpService->lpImage);
lpService->lpImage = NULL;
}
/* Report the results of the status change here */
if (lpService->Status.dwWin32ExitCode != ERROR_SUCCESS)
{
/* Log a failed service stop */
StringCchPrintfW(szLogBuffer, ARRAYSIZE(szLogBuffer),
L"%lu", lpService->Status.dwWin32ExitCode);
lpLogStrings[0] = lpService->lpDisplayName;
lpLogStrings[1] = szLogBuffer;
ScmLogEvent(EVENT_SERVICE_EXIT_FAILED,
EVENTLOG_ERROR_TYPE,
ARRAYSIZE(lpLogStrings),
lpLogStrings);
}
else
{
/* Log a successful service status change */
LoadStringW(GetModuleHandle(NULL), IDS_SERVICE_STOPPED, szLogBuffer, ARRAYSIZE(szLogBuffer));
lpLogStrings[0] = lpService->lpDisplayName;
lpLogStrings[1] = szLogBuffer;
ScmLogEvent(EVENT_SERVICE_STATUS_SUCCESS,
EVENTLOG_INFORMATION_TYPE,
ARRAYSIZE(lpLogStrings),
lpLogStrings);
}
/* Remove the reference that was added when the service started */
DPRINT("Service %S has %d references while stoping\n", lpService->lpServiceName, lpService->RefCount);
ScmDereferenceService(lpService);
/* Unlock the service database */
ScmUnlockDatabase();
return 0;
}
/* Function 7 */
DWORD
WINAPI
RSetServiceStatus(
RPC_SERVICE_STATUS_HANDLE hServiceStatus,
LPSERVICE_STATUS lpServiceStatus)
{
PSERVICE lpService;
DWORD dwPreviousState;
DWORD dwPreviousType;
LPCWSTR lpLogStrings[2];
WCHAR szLogBuffer[80];
UINT uID;
DPRINT("RSetServiceStatus() called\n");
DPRINT("hServiceStatus = %lu\n", hServiceStatus);
DPRINT("dwServiceType = 0x%lx\n", lpServiceStatus->dwServiceType);
DPRINT("dwCurrentState = %lu\n", lpServiceStatus->dwCurrentState);
DPRINT("dwControlsAccepted = %lu\n", lpServiceStatus->dwControlsAccepted);
DPRINT("dwWin32ExitCode = %lu\n", lpServiceStatus->dwWin32ExitCode);
DPRINT("dwServiceSpecificExitCode = %lu\n", lpServiceStatus->dwServiceSpecificExitCode);
DPRINT("dwCheckPoint = %lu\n", lpServiceStatus->dwCheckPoint);
DPRINT("dwWaitHint = %lu\n", lpServiceStatus->dwWaitHint);
if (hServiceStatus == 0)
{
DPRINT("hServiceStatus == NULL\n");
return ERROR_INVALID_HANDLE;
}
lpService = (PSERVICE)hServiceStatus;
/* Check current state */
if (!ScmIsValidServiceState(lpServiceStatus->dwCurrentState))
{
DPRINT("Invalid service state\n");
return ERROR_INVALID_DATA;
}
/* Check service type */
if (!(lpServiceStatus->dwServiceType & SERVICE_WIN32) &&
(lpServiceStatus->dwServiceType & SERVICE_DRIVER))
{
DPRINT("Invalid service type\n");
return ERROR_INVALID_DATA;
}
/* Check accepted controls */
if (lpServiceStatus->dwControlsAccepted & ~0xFF)
{
DPRINT("Invalid controls accepted\n");
return ERROR_INVALID_DATA;
}
/* Set the wait hint and check point only if the service is in a pending state,
otherwise they should be 0 */
if (lpServiceStatus->dwCurrentState == SERVICE_STOPPED ||
lpServiceStatus->dwCurrentState == SERVICE_PAUSED ||
lpServiceStatus->dwCurrentState == SERVICE_RUNNING)
{
lpServiceStatus->dwWaitHint = 0;
lpServiceStatus->dwCheckPoint = 0;
}
/* Lock the service database exclusively */
ScmLockDatabaseExclusive();
/* Save the current service state */
dwPreviousState = lpService->Status.dwCurrentState;
/* Save the current service type */
dwPreviousType = lpService->Status.dwServiceType;
/* Update the service status */
RtlCopyMemory(&lpService->Status,
lpServiceStatus,
sizeof(SERVICE_STATUS));
/* Restore the previous service type */
lpService->Status.dwServiceType = dwPreviousType;
DPRINT("Service %S changed state %d to %d\n", lpService->lpServiceName, dwPreviousState, lpServiceStatus->dwCurrentState);
if (lpServiceStatus->dwCurrentState != SERVICE_STOPPED &&
dwPreviousState == SERVICE_STOPPED)
{
/* Keep a reference on all non stopped services */
ScmReferenceService(lpService);
DPRINT("Service %S has %d references after starting\n", lpService->lpServiceName, lpService->RefCount);
}
/* Check if the service just stopped */
if (lpServiceStatus->dwCurrentState == SERVICE_STOPPED &&
dwPreviousState != SERVICE_STOPPED)
{
HANDLE hStopThread;
DWORD dwStopThreadId;
DPRINT("Service %S, currentstate: %d, prev: %d\n", lpService->lpServiceName, lpServiceStatus->dwCurrentState, dwPreviousState);
/*
* The service just changed its status to stopped.
* Create a thread that will complete the stop sequence.
* This thread will remove the reference that was added when the service started.
* This will ensure that the service will remain valid as long as this reference is still held.
*/
hStopThread = CreateThread(NULL,
0,
ScmStopThread,
(LPVOID)lpService,
0,
&dwStopThreadId);
if (hStopThread == NULL)
{
DPRINT1("Failed to create thread to complete service stop\n");
/* We can't leave without releasing the reference.
* We also can't remove it without holding the lock. */
ScmDereferenceService(lpService);
DPRINT1("Service %S has %d references after stop\n", lpService->lpServiceName, lpService->RefCount);
}
else
{
CloseHandle(hStopThread);
}
}
/* Unlock the service database */
ScmUnlockDatabase();
/* Don't log any events here regarding a service stop as it can become invalid at any time */
if (lpServiceStatus->dwCurrentState != dwPreviousState &&
(lpServiceStatus->dwCurrentState == SERVICE_RUNNING ||
lpServiceStatus->dwCurrentState == SERVICE_PAUSED))
{
/* Log a successful service status change */
switch(lpServiceStatus->dwCurrentState)
{
case SERVICE_RUNNING:
uID = IDS_SERVICE_RUNNING;
break;
case SERVICE_PAUSED:
uID = IDS_SERVICE_PAUSED;
break;
}
LoadStringW(GetModuleHandle(NULL), uID, szLogBuffer, ARRAYSIZE(szLogBuffer));
lpLogStrings[0] = lpService->lpDisplayName;
lpLogStrings[1] = szLogBuffer;
ScmLogEvent(EVENT_SERVICE_STATUS_SUCCESS,
EVENTLOG_INFORMATION_TYPE,
2,
lpLogStrings);
}
DPRINT("Set %S to %lu\n", lpService->lpDisplayName, lpService->Status.dwCurrentState);
DPRINT("RSetServiceStatus() done\n");
return ERROR_SUCCESS;
}
/* Function 8 */
DWORD
WINAPI
RUnlockServiceDatabase(
LPSC_RPC_LOCK Lock)
{
DPRINT("RUnlockServiceDatabase(%p)\n", Lock);
return ScmReleaseServiceStartLock(Lock);
}
/* Function 9 */
DWORD
WINAPI
RNotifyBootConfigStatus(
SVCCTL_HANDLEW lpMachineName,
DWORD BootAcceptable)
{
DPRINT("RNotifyBootConfigStatus(%p %lu)\n",
lpMachineName, BootAcceptable);
if (BootAcceptable)
return ScmAcceptBoot();
return ScmRunLastKnownGood();
}
/* Function 10 */
DWORD
WINAPI
RI_ScSetServiceBitsW(
RPC_SERVICE_STATUS_HANDLE hServiceStatus,
DWORD dwServiceBits,
int bSetBitsOn,
int bUpdateImmediately,
wchar_t *lpString)
{
PSERVICE pService;
DPRINT("RI_ScSetServiceBitsW(%p %lx %d %d %S)\n",
hServiceStatus, dwServiceBits, bSetBitsOn,
bUpdateImmediately, lpString);
if (ScmShutdown)
return ERROR_SHUTDOWN_IN_PROGRESS;
if (lpString != NULL)
return ERROR_INVALID_PARAMETER;
if (hServiceStatus == 0)
{
DPRINT("hServiceStatus == NULL\n");
return ERROR_INVALID_HANDLE;
}
// FIXME: Validate the status handle
pService = (PSERVICE)hServiceStatus;
if (bSetBitsOn)
{
DPRINT("Old service bits: %08lx\n", pService->dwServiceBits);
DPRINT("Old global service bits: %08lx\n", g_dwServiceBits);
pService->dwServiceBits |= dwServiceBits;
g_dwServiceBits |= dwServiceBits;
DPRINT("New service bits: %08lx\n", pService->dwServiceBits);
DPRINT("New global service bits: %08lx\n", g_dwServiceBits);
}
else
{
DPRINT("Old service bits: %08lx\n", pService->dwServiceBits);
DPRINT("Old global service bits: %08lx\n", g_dwServiceBits);
pService->dwServiceBits &= ~dwServiceBits;
g_dwServiceBits &= ~dwServiceBits;
DPRINT("New service bits: %08lx\n", pService->dwServiceBits);
DPRINT("New global service bits: %08lx\n", g_dwServiceBits);
}
return ERROR_SUCCESS;
}
/* Function 11 */
DWORD
WINAPI
RChangeServiceConfigW(
SC_RPC_HANDLE hService,
DWORD dwServiceType,
DWORD dwStartType,
DWORD dwErrorControl,
LPWSTR lpBinaryPathName,
LPWSTR lpLoadOrderGroup,
LPDWORD lpdwTagId,
LPBYTE lpDependencies,
DWORD dwDependSize,
LPWSTR lpServiceStartName,
LPBYTE lpPassword,
DWORD dwPwSize,
LPWSTR lpDisplayName)
{
DWORD dwError = ERROR_SUCCESS;
PSERVICE_HANDLE hSvc;
PSERVICE lpService = NULL;
HKEY hServiceKey = NULL;
LPWSTR lpDisplayNameW = NULL;
LPWSTR lpImagePathW = NULL;
LPWSTR lpClearTextPassword = NULL;
DPRINT("RChangeServiceConfigW() called\n");
DPRINT("dwServiceType = 0x%lx\n", dwServiceType);
DPRINT("dwStartType = %lu\n", dwStartType);
DPRINT("dwErrorControl = %lu\n", dwErrorControl);
DPRINT("lpBinaryPathName = %S\n", lpBinaryPathName);
DPRINT("lpLoadOrderGroup = %S\n", lpLoadOrderGroup);
DPRINT("lpServiceStartName = %S\n", lpServiceStartName);
DPRINT("lpPassword = %p\n", lpPassword);
DPRINT("dwPwSite = %lu\n", dwPwSize);
DPRINT("lpDisplayName = %S\n", lpDisplayName);
if (ScmShutdown)
return ERROR_SHUTDOWN_IN_PROGRESS;
hSvc = ScmGetServiceFromHandle(hService);
if (hSvc == NULL)
{
DPRINT1("Invalid service handle\n");
return ERROR_INVALID_HANDLE;
}
if (!RtlAreAllAccessesGranted(hSvc->Handle.DesiredAccess,
SERVICE_CHANGE_CONFIG))
{
DPRINT("Insufficient access rights! 0x%lx\n", hSvc->Handle.DesiredAccess);
return ERROR_ACCESS_DENIED;
}
/* Check for invalid service type value */
if ((dwServiceType != SERVICE_NO_CHANGE) &&
(dwServiceType != SERVICE_KERNEL_DRIVER) &&
(dwServiceType != SERVICE_FILE_SYSTEM_DRIVER) &&
((dwServiceType & ~SERVICE_INTERACTIVE_PROCESS) != SERVICE_WIN32_OWN_PROCESS) &&
((dwServiceType & ~SERVICE_INTERACTIVE_PROCESS) != SERVICE_WIN32_SHARE_PROCESS))
{
return ERROR_INVALID_PARAMETER;
}
/* Check for invalid start type value */
if ((dwStartType != SERVICE_NO_CHANGE) &&
(dwStartType != SERVICE_BOOT_START) &&
(dwStartType != SERVICE_SYSTEM_START) &&
(dwStartType != SERVICE_AUTO_START) &&
(dwStartType != SERVICE_DEMAND_START) &&
(dwStartType != SERVICE_DISABLED))
{
return ERROR_INVALID_PARAMETER;
}
/* Only drivers can be boot start or system start services */
if ((dwStartType == SERVICE_BOOT_START) ||
(dwStartType == SERVICE_SYSTEM_START))
{
if ((dwServiceType != SERVICE_KERNEL_DRIVER) &&
(dwServiceType != SERVICE_FILE_SYSTEM_DRIVER))
return ERROR_INVALID_PARAMETER;
}
/* Check for invalid error control value */
if ((dwErrorControl != SERVICE_NO_CHANGE) &&
(dwErrorControl != SERVICE_ERROR_IGNORE) &&
(dwErrorControl != SERVICE_ERROR_NORMAL) &&
(dwErrorControl != SERVICE_ERROR_SEVERE) &&
(dwErrorControl != SERVICE_ERROR_CRITICAL))
{
return ERROR_INVALID_PARAMETER;
}
if (lpdwTagId && (!lpLoadOrderGroup || !*lpLoadOrderGroup))
{
return ERROR_INVALID_PARAMETER;
}
lpService = hSvc->ServiceEntry;
if (lpService == NULL)
{
DPRINT("lpService == NULL\n");
return ERROR_INVALID_HANDLE;
}
/* Lock the service database exclusively */
ScmLockDatabaseExclusive();
if (lpService->bDeleted)
{
DPRINT("Service has already been marked for delete\n");
dwError = ERROR_SERVICE_MARKED_FOR_DELETE;
goto done;
}
/* Open the service key */
dwError = ScmOpenServiceKey(lpService->szServiceName,
KEY_SET_VALUE,
&hServiceKey);
if (dwError != ERROR_SUCCESS)
goto done;
/* Write service data to the registry */
/* Set the display name */
if (lpDisplayName != NULL && *lpDisplayName != 0)
{
RegSetValueExW(hServiceKey,
L"DisplayName",
0,
REG_SZ,
(LPBYTE)lpDisplayName,
(DWORD)((wcslen(lpDisplayName) + 1) * sizeof(WCHAR)));
/* Update the display name */
lpDisplayNameW = HeapAlloc(GetProcessHeap(),
HEAP_ZERO_MEMORY,
(wcslen(lpDisplayName) + 1) * sizeof(WCHAR));
if (lpDisplayNameW == NULL)
{
dwError = ERROR_NOT_ENOUGH_MEMORY;
goto done;
}
wcscpy(lpDisplayNameW, lpDisplayName);
if (lpService->lpDisplayName != lpService->lpServiceName)
HeapFree(GetProcessHeap(), 0, lpService->lpDisplayName);
lpService->lpDisplayName = lpDisplayNameW;
}
if (dwServiceType != SERVICE_NO_CHANGE)
{
/* Set the service type */
dwError = RegSetValueExW(hServiceKey,
L"Type",
0,
REG_DWORD,
(LPBYTE)&dwServiceType,
sizeof(DWORD));
if (dwError != ERROR_SUCCESS)
goto done;
lpService->Status.dwServiceType = dwServiceType;
}
if (dwStartType != SERVICE_NO_CHANGE)
{
/* Set the start value */
dwError = RegSetValueExW(hServiceKey,
L"Start",
0,
REG_DWORD,
(LPBYTE)&dwStartType,
sizeof(DWORD));
if (dwError != ERROR_SUCCESS)
goto done;
lpService->dwStartType = dwStartType;
}
if (dwErrorControl != SERVICE_NO_CHANGE)
{
/* Set the error control value */
dwError = RegSetValueExW(hServiceKey,
L"ErrorControl",
0,
REG_DWORD,
(LPBYTE)&dwErrorControl,
sizeof(DWORD));
if (dwError != ERROR_SUCCESS)
goto done;
lpService->dwErrorControl = dwErrorControl;
}
if (lpBinaryPathName != NULL && *lpBinaryPathName != 0)
{
/* Set the image path */
lpImagePathW = lpBinaryPathName;
if (lpService->Status.dwServiceType & SERVICE_DRIVER)
{
dwError = ScmCanonDriverImagePath(lpService->dwStartType,
lpBinaryPathName,
&lpImagePathW);
if (dwError != ERROR_SUCCESS)
goto done;
}
dwError = RegSetValueExW(hServiceKey,
L"ImagePath",
0,
REG_EXPAND_SZ,
(LPBYTE)lpImagePathW,
(DWORD)((wcslen(lpImagePathW) + 1) * sizeof(WCHAR)));
if (lpImagePathW != lpBinaryPathName)
HeapFree(GetProcessHeap(), 0, lpImagePathW);
if (dwError != ERROR_SUCCESS)
goto done;
}
/* Set the group name */
if (lpLoadOrderGroup != NULL && *lpLoadOrderGroup != 0)
{
dwError = RegSetValueExW(hServiceKey,
L"Group",
0,
REG_SZ,
(LPBYTE)lpLoadOrderGroup,
(DWORD)((wcslen(lpLoadOrderGroup) + 1) * sizeof(WCHAR)));
if (dwError != ERROR_SUCCESS)
goto done;
dwError = ScmSetServiceGroup(lpService,
lpLoadOrderGroup);
if (dwError != ERROR_SUCCESS)
goto done;
}
/* Set the tag */
if (lpdwTagId != NULL)
{
dwError = ScmAssignNewTag(lpService);
if (dwError != ERROR_SUCCESS)
goto done;
dwError = RegSetValueExW(hServiceKey,
L"Tag",
0,
REG_DWORD,
(LPBYTE)&lpService->dwTag,
sizeof(DWORD));
if (dwError != ERROR_SUCCESS)
goto done;
*lpdwTagId = lpService->dwTag;
}
/* Write dependencies */
if (lpDependencies != NULL && *lpDependencies != 0)
{
dwError = ScmWriteDependencies(hServiceKey,
(LPWSTR)lpDependencies,
dwDependSize);
if (dwError != ERROR_SUCCESS)
goto done;
}
/* Start name and password are only used by Win32 services */
if (lpService->Status.dwServiceType & SERVICE_WIN32)
{
/* Write service start name */
if (lpServiceStartName != NULL && *lpServiceStartName != 0)
{
dwError = RegSetValueExW(hServiceKey,
L"ObjectName",
0,
REG_SZ,
(LPBYTE)lpServiceStartName,
(DWORD)((wcslen(lpServiceStartName) + 1) * sizeof(WCHAR)));
if (dwError != ERROR_SUCCESS)
goto done;
}
if (lpPassword != NULL)
{
if (*(LPWSTR)lpPassword != 0)
{
/* Decrypt the password */
dwError = ScmDecryptPassword(hService,
lpPassword,
dwPwSize,
&lpClearTextPassword);
if (dwError != ERROR_SUCCESS)
{
DPRINT1("ScmDecryptPassword failed (Error %lu)\n", dwError);
goto done;
}
DPRINT("Clear text password: %S\n", lpClearTextPassword);
/* Write the password */
dwError = ScmSetServicePassword(lpService->szServiceName,
lpClearTextPassword);
if (dwError != ERROR_SUCCESS)
{
DPRINT1("ScmSetServicePassword failed (Error %lu)\n", dwError);
goto done;
}
}
else
{
/* Delete the password */
dwError = ScmSetServicePassword(lpService->szServiceName,
NULL);
if (dwError == ERROR_FILE_NOT_FOUND)
dwError = ERROR_SUCCESS;
if (dwError != ERROR_SUCCESS)
{
DPRINT1("ScmSetServicePassword failed (Error %lu)\n", dwError);
goto done;
}
}
}
}
done:
if (lpClearTextPassword != NULL)
{
/* Wipe and release the password buffer */
SecureZeroMemory(lpClearTextPassword,
(wcslen(lpClearTextPassword) + 1) * sizeof(WCHAR));
HeapFree(GetProcessHeap(), 0, lpClearTextPassword);
}
if (hServiceKey != NULL)
RegCloseKey(hServiceKey);
/* Unlock the service database */
ScmUnlockDatabase();
DPRINT("RChangeServiceConfigW() done (Error %lu)\n", dwError);
return dwError;
}
/* Function 12 */
DWORD
WINAPI
RCreateServiceW(
SC_RPC_HANDLE hSCManager,
LPCWSTR lpServiceName,
LPCWSTR lpDisplayName,
DWORD dwDesiredAccess,
DWORD dwServiceType,
DWORD dwStartType,
DWORD dwErrorControl,
LPCWSTR lpBinaryPathName,
LPCWSTR lpLoadOrderGroup,
LPDWORD lpdwTagId,
LPBYTE lpDependencies,
DWORD dwDependSize,
LPCWSTR lpServiceStartName,
LPBYTE lpPassword,
DWORD dwPwSize,
LPSC_RPC_HANDLE lpServiceHandle)
{
PMANAGER_HANDLE hManager;
DWORD dwError = ERROR_SUCCESS;
PSERVICE lpService = NULL;
SC_HANDLE hServiceHandle = NULL;
LPWSTR lpImagePath = NULL;
LPWSTR lpClearTextPassword = NULL;
HKEY hServiceKey = NULL;
LPWSTR lpObjectName;
DPRINT("RCreateServiceW() called\n");
DPRINT("lpServiceName = %S\n", lpServiceName);
DPRINT("lpDisplayName = %S\n", lpDisplayName);
DPRINT("dwDesiredAccess = %lx\n", dwDesiredAccess);
DPRINT("dwServiceType = 0x%lx\n", dwServiceType);
DPRINT("dwStartType = %lu\n", dwStartType);
DPRINT("dwErrorControl = %lu\n", dwErrorControl);
DPRINT("lpBinaryPathName = %S\n", lpBinaryPathName);
DPRINT("lpLoadOrderGroup = %S\n", lpLoadOrderGroup);
DPRINT("lpdwTagId = %p\n", lpdwTagId);
if (ScmShutdown)
return ERROR_SHUTDOWN_IN_PROGRESS;
hManager = ScmGetServiceManagerFromHandle(hSCManager);
if (hManager == NULL)
{
DPRINT1("Invalid service manager handle\n");
return ERROR_INVALID_HANDLE;
}
/* Check access rights */
if (!RtlAreAllAccessesGranted(hManager->Handle.DesiredAccess,
SC_MANAGER_CREATE_SERVICE))
{
DPRINT("Insufficient access rights! 0x%lx\n",
hManager->Handle.DesiredAccess);
return ERROR_ACCESS_DENIED;
}
if (*lpServiceName == 0)
return ERROR_INVALID_NAME;
if (*lpBinaryPathName == 0)
return ERROR_INVALID_PARAMETER;
/* Check for invalid service type value */
if ((dwServiceType != SERVICE_KERNEL_DRIVER) &&
(dwServiceType != SERVICE_FILE_SYSTEM_DRIVER) &&
((dwServiceType & ~SERVICE_INTERACTIVE_PROCESS) != SERVICE_WIN32_OWN_PROCESS) &&
((dwServiceType & ~SERVICE_INTERACTIVE_PROCESS) != SERVICE_WIN32_SHARE_PROCESS))
{
return ERROR_INVALID_PARAMETER;
}
/* Check for invalid start type value */
if ((dwStartType != SERVICE_BOOT_START) &&
(dwStartType != SERVICE_SYSTEM_START) &&
(dwStartType != SERVICE_AUTO_START) &&
(dwStartType != SERVICE_DEMAND_START) &&
(dwStartType != SERVICE_DISABLED))
{
return ERROR_INVALID_PARAMETER;
}
/* Only drivers can be boot start or system start services */
if ((dwStartType == SERVICE_BOOT_START) ||
(dwStartType == SERVICE_SYSTEM_START))
{
if ((dwServiceType != SERVICE_KERNEL_DRIVER) &&
(dwServiceType != SERVICE_FILE_SYSTEM_DRIVER))
{
return ERROR_INVALID_PARAMETER;
}
}
/* Check for invalid error control value */
if ((dwErrorControl != SERVICE_ERROR_IGNORE) &&
(dwErrorControl != SERVICE_ERROR_NORMAL) &&
(dwErrorControl != SERVICE_ERROR_SEVERE) &&
(dwErrorControl != SERVICE_ERROR_CRITICAL))
{
return ERROR_INVALID_PARAMETER;
}
if ((dwServiceType == (SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS)) &&
(lpServiceStartName))
{
/* We allow LocalSystem to run interactive. */
if (_wcsicmp(lpServiceStartName, L"LocalSystem"))
{
return ERROR_INVALID_PARAMETER;
}
}
if (lpdwTagId && (!lpLoadOrderGroup || !*lpLoadOrderGroup))
{
return ERROR_INVALID_PARAMETER;
}
/* Lock the service database exclusively */
ScmLockDatabaseExclusive();
lpService = ScmGetServiceEntryByName(lpServiceName);
if (lpService)
{
/* Unlock the service database */
ScmUnlockDatabase();
/* Check if it is marked for deletion */
if (lpService->bDeleted)
return ERROR_SERVICE_MARKED_FOR_DELETE;
/* Return service-exists error */
return ERROR_SERVICE_EXISTS;
}
if (lpDisplayName != NULL &&
ScmGetServiceEntryByDisplayName(lpDisplayName) != NULL)
{
/* Unlock the service database */
ScmUnlockDatabase();
return ERROR_DUPLICATE_SERVICE_NAME;
}
if (dwServiceType & SERVICE_DRIVER)
{
dwError = ScmCanonDriverImagePath(dwStartType,
lpBinaryPathName,
&lpImagePath);
if (dwError != ERROR_SUCCESS)
goto done;
}
else
{
if (dwStartType == SERVICE_BOOT_START ||
dwStartType == SERVICE_SYSTEM_START)
{
/* Unlock the service database */
ScmUnlockDatabase();
return ERROR_INVALID_PARAMETER;
}
}
/* Allocate a new service entry */
dwError = ScmCreateNewServiceRecord(lpServiceName,
&lpService,
dwServiceType,
dwStartType);
if (dwError != ERROR_SUCCESS)
goto done;
/* Fill the new service entry */
lpService->dwErrorControl = dwErrorControl;
/* Fill the display name */
if (lpDisplayName != NULL &&
*lpDisplayName != 0 &&
_wcsicmp(lpService->lpDisplayName, lpDisplayName) != 0)
{
lpService->lpDisplayName = HeapAlloc(GetProcessHeap(),
HEAP_ZERO_MEMORY,
(wcslen(lpDisplayName) + 1) * sizeof(WCHAR));
if (lpService->lpDisplayName == NULL)
{
dwError = ERROR_NOT_ENOUGH_MEMORY;
goto done;
}
wcscpy(lpService->lpDisplayName, lpDisplayName);
}
/* Assign the service to a group */
if (lpLoadOrderGroup != NULL && *lpLoadOrderGroup != 0)
{
dwError = ScmSetServiceGroup(lpService,
lpLoadOrderGroup);
if (dwError != ERROR_SUCCESS)
goto done;
}
/* Assign a new tag */
if (lpdwTagId != NULL)
{
dwError = ScmAssignNewTag(lpService);
if (dwError != ERROR_SUCCESS)
goto done;
}
/* Assign the default security descriptor */
if (dwServiceType & SERVICE_WIN32)
{
dwError = ScmCreateDefaultServiceSD(&lpService->pSecurityDescriptor);
if (dwError != ERROR_SUCCESS)
goto done;
}
/* Write service data to the registry */
/* Create the service key */
dwError = ScmCreateServiceKey(lpServiceName,
KEY_WRITE,
&hServiceKey);
if (dwError != ERROR_SUCCESS)
goto done;
/* Set the display name */
if (lpDisplayName != NULL && *lpDisplayName != 0)
{
RegSetValueExW(hServiceKey,
L"DisplayName",
0,
REG_SZ,
(LPBYTE)lpDisplayName,
(DWORD)((wcslen(lpDisplayName) + 1) * sizeof(WCHAR)));
}
/* Set the service type */
dwError = RegSetValueExW(hServiceKey,
L"Type",
0,
REG_DWORD,
(LPBYTE)&dwServiceType,
sizeof(DWORD));
if (dwError != ERROR_SUCCESS)
goto done;
/* Set the start value */
dwError = RegSetValueExW(hServiceKey,
L"Start",
0,
REG_DWORD,
(LPBYTE)&dwStartType,
sizeof(DWORD));
if (dwError != ERROR_SUCCESS)
goto done;
/* Set the error control value */
dwError = RegSetValueExW(hServiceKey,
L"ErrorControl",
0,
REG_DWORD,
(LPBYTE)&dwErrorControl,
sizeof(DWORD));
if (dwError != ERROR_SUCCESS)
goto done;
/* Set the image path */
if (dwServiceType & SERVICE_WIN32)
{
dwError = RegSetValueExW(hServiceKey,
L"ImagePath",
0,
REG_EXPAND_SZ,
(LPBYTE)lpBinaryPathName,
(DWORD)((wcslen(lpBinaryPathName) + 1) * sizeof(WCHAR)));
if (dwError != ERROR_SUCCESS)
goto done;
}
else if (dwServiceType & SERVICE_DRIVER)
{
dwError = RegSetValueExW(hServiceKey,
L"ImagePath",
0,
REG_EXPAND_SZ,
(LPBYTE)lpImagePath,
(DWORD)((wcslen(lpImagePath) + 1) * sizeof(WCHAR)));
if (dwError != ERROR_SUCCESS)
goto done;
}
/* Set the group name */
if (lpLoadOrderGroup != NULL && *lpLoadOrderGroup != 0)
{
dwError = RegSetValueExW(hServiceKey,
L"Group",
0,
REG_SZ,
(LPBYTE)lpLoadOrderGroup,
(DWORD)((wcslen(lpLoadOrderGroup) + 1) * sizeof(WCHAR)));
if (dwError != ERROR_SUCCESS)
goto done;
}
/* Set the service tag */
if (lpdwTagId != NULL)
{
dwError = RegSetValueExW(hServiceKey,
L"Tag",
0,
REG_DWORD,
(LPBYTE)&lpService->dwTag,
sizeof(DWORD));
if (dwError != ERROR_SUCCESS)
goto done;
}
/* Write dependencies */
if (lpDependencies != NULL && *lpDependencies != 0)
{
dwError = ScmWriteDependencies(hServiceKey,
(LPCWSTR)lpDependencies,
dwDependSize);
if (dwError != ERROR_SUCCESS)
goto done;
}
/* Start name and password are only used by Win32 services */
if (dwServiceType & SERVICE_WIN32)
{
/* Write service start name */
lpObjectName = (lpServiceStartName != NULL) ? (LPWSTR)lpServiceStartName : L"LocalSystem";
dwError = RegSetValueExW(hServiceKey,
L"ObjectName",
0,
REG_SZ,
(LPBYTE)lpObjectName,
(DWORD)((wcslen(lpObjectName) + 1) * sizeof(WCHAR)));
if (dwError != ERROR_SUCCESS)
goto done;
if (lpPassword != NULL && *(LPWSTR)lpPassword != 0)
{
/* Decrypt the password */
dwError = ScmDecryptPassword(hSCManager,
lpPassword,
dwPwSize,
&lpClearTextPassword);
if (dwError != ERROR_SUCCESS)
goto done;
/* Write the password */
dwError = ScmSetServicePassword(lpServiceName,
lpClearTextPassword);
if (dwError != ERROR_SUCCESS)
goto done;
}
/* Write the security descriptor */
dwError = ScmWriteSecurityDescriptor(hServiceKey,
lpService->pSecurityDescriptor);
if (dwError != ERROR_SUCCESS)
goto done;
}
dwError = ScmCreateServiceHandle(lpService,
&hServiceHandle);
if (dwError != ERROR_SUCCESS)
goto done;
dwError = ScmCheckAccess(hServiceHandle,
dwDesiredAccess);
if (dwError != ERROR_SUCCESS)
goto done;
ScmReferenceService(lpService);
/* Get the service tag (if Win32) */
ScmGenerateServiceTag(lpService);
DPRINT("CreateService - lpService->RefCount %u\n", lpService->RefCount);
done:
/* Unlock the service database */
ScmUnlockDatabase();
if (hServiceKey != NULL)
RegCloseKey(hServiceKey);
if (lpClearTextPassword != NULL)
{
/* Wipe and release the password buffer */
SecureZeroMemory(lpClearTextPassword,
(wcslen(lpClearTextPassword) + 1) * sizeof(WCHAR));
HeapFree(GetProcessHeap(), 0, lpClearTextPassword);
}
if (dwError == ERROR_SUCCESS)
{
DPRINT("hService %p\n", hServiceHandle);
*lpServiceHandle = (SC_RPC_HANDLE)hServiceHandle;
if (lpdwTagId != NULL)
*lpdwTagId = lpService->dwTag;
}
else
{
if (lpService != NULL &&
lpService->lpServiceName != NULL)
{
/* Release the display name buffer */
HeapFree(GetProcessHeap(), 0, lpService->lpDisplayName);
}
if (hServiceHandle)
{
/* Remove the service handle */
HeapFree(GetProcessHeap(), 0, hServiceHandle);
}
if (lpService != NULL)
{
/* FIXME: remove the service entry */
}
}
if (lpImagePath != NULL)
HeapFree(GetProcessHeap(), 0, lpImagePath);
DPRINT("RCreateServiceW() done (Error %lu)\n", dwError);
return dwError;
}
/* Function 13 */
DWORD
WINAPI
REnumDependentServicesW(
SC_RPC_HANDLE hService,
DWORD dwServiceState,
LPBYTE lpServices,
DWORD cbBufSize,
LPBOUNDED_DWORD_256K pcbBytesNeeded,
LPBOUNDED_DWORD_256K lpServicesReturned)
{
DWORD dwError = ERROR_SUCCESS;
DWORD dwServicesReturned = 0;
DWORD dwServiceCount;
HKEY hServicesKey = NULL;
PSERVICE_HANDLE hSvc;
PSERVICE lpService = NULL;
PSERVICE *lpServicesArray = NULL;
LPENUM_SERVICE_STATUSW lpServicesPtr = NULL;
LPWSTR lpStr;
*pcbBytesNeeded = 0;
*lpServicesReturned = 0;
DPRINT("REnumDependentServicesW() called\n");
hSvc = ScmGetServiceFromHandle(hService);
if (hSvc == NULL)
{
DPRINT1("Invalid service handle\n");
return ERROR_INVALID_HANDLE;
}
lpService = hSvc->ServiceEntry;
/* Check access rights */
if (!RtlAreAllAccessesGranted(hSvc->Handle.DesiredAccess,
SC_MANAGER_ENUMERATE_SERVICE))
{
DPRINT("Insufficient access rights! 0x%lx\n",
hSvc->Handle.DesiredAccess);
return ERROR_ACCESS_DENIED;
}
/* Open the Services Reg key */
dwError = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
L"System\\CurrentControlSet\\Services",
0,
KEY_READ,
&hServicesKey);
if (dwError != ERROR_SUCCESS)
return dwError;
/* First determine the bytes needed and get the number of dependent services */
dwError = Int_EnumDependentServicesW(hServicesKey,
lpService,
dwServiceState,
NULL,
pcbBytesNeeded,
&dwServicesReturned);
if (dwError != ERROR_SUCCESS)
goto Done;
/* If buffer size is less than the bytes needed or pointer is null */
if ((!lpServices) || (cbBufSize < *pcbBytesNeeded))
{
dwError = ERROR_MORE_DATA;
goto Done;
}
/* Allocate memory for array of service pointers */
lpServicesArray = HeapAlloc(GetProcessHeap(),
HEAP_ZERO_MEMORY,
(dwServicesReturned + 1) * sizeof(PSERVICE));
if (!lpServicesArray)
{
DPRINT1("Could not allocate buffer\n");
dwError = ERROR_NOT_ENOUGH_MEMORY;
goto Done;
}
dwServicesReturned = 0;
*pcbBytesNeeded = 0;
dwError = Int_EnumDependentServicesW(hServicesKey,
lpService,
dwServiceState,
lpServicesArray,
pcbBytesNeeded,
&dwServicesReturned);
if (dwError != ERROR_SUCCESS)
{
goto Done;
}
lpServicesPtr = (LPENUM_SERVICE_STATUSW)lpServices;
lpStr = (LPWSTR)(lpServices + (dwServicesReturned * sizeof(ENUM_SERVICE_STATUSW)));
/* Copy EnumDepenedentService to Buffer */
for (dwServiceCount = 0; dwServiceCount < dwServicesReturned; dwServiceCount++)
{
lpService = lpServicesArray[dwServiceCount];
/* Copy status info */
memcpy(&lpServicesPtr->ServiceStatus,
&lpService->Status,
sizeof(SERVICE_STATUS));
/* Copy display name */
wcscpy(lpStr, lpService->lpDisplayName);
lpServicesPtr->lpDisplayName = (LPWSTR)((ULONG_PTR)lpStr - (ULONG_PTR)lpServices);
lpStr += (wcslen(lpService->lpDisplayName) + 1);
/* Copy service name */
wcscpy(lpStr, lpService->lpServiceName);
lpServicesPtr->lpServiceName = (LPWSTR)((ULONG_PTR)lpStr - (ULONG_PTR)lpServices);
lpStr += (wcslen(lpService->lpServiceName) + 1);
lpServicesPtr++;
}
*lpServicesReturned = dwServicesReturned;
Done:
if (lpServicesArray != NULL)
HeapFree(GetProcessHeap(), 0, lpServicesArray);
RegCloseKey(hServicesKey);
DPRINT("REnumDependentServicesW() done (Error %lu)\n", dwError);
return dwError;
}
/* Function 14 */
DWORD
WINAPI
REnumServicesStatusW(
SC_RPC_HANDLE hSCManager,
DWORD dwServiceType,
DWORD dwServiceState,
LPBYTE lpBuffer,
DWORD dwBufSize,
LPBOUNDED_DWORD_256K pcbBytesNeeded,
LPBOUNDED_DWORD_256K lpServicesReturned,
LPBOUNDED_DWORD_256K lpResumeHandle)
{
/* Enumerate all the services, not regarding of their group */
return REnumServiceGroupW(hSCManager,
dwServiceType,
dwServiceState,
lpBuffer,
dwBufSize,
pcbBytesNeeded,
lpServicesReturned,
lpResumeHandle,
NULL);
}
/* Function 15 */
DWORD
WINAPI
ROpenSCManagerW(
LPWSTR lpMachineName,
LPWSTR lpDatabaseName,
DWORD dwDesiredAccess,
LPSC_RPC_HANDLE lpScHandle)
{
DWORD dwError;
SC_HANDLE hHandle;
DPRINT("ROpenSCManagerW() called\n");
DPRINT("lpMachineName = %p\n", lpMachineName);
DPRINT("lpMachineName: %S\n", lpMachineName);
DPRINT("lpDataBaseName = %p\n", lpDatabaseName);
DPRINT("lpDataBaseName: %S\n", lpDatabaseName);
DPRINT("dwDesiredAccess = %x\n", dwDesiredAccess);
if (ScmShutdown)
return ERROR_SHUTDOWN_IN_PROGRESS;
if (!lpScHandle)
return ERROR_INVALID_PARAMETER;
dwError = ScmCreateManagerHandle(lpDatabaseName,
&hHandle);
if (dwError != ERROR_SUCCESS)
{
DPRINT("ScmCreateManagerHandle() failed (Error %lu)\n", dwError);
return dwError;
}
/* Check the desired access */
dwError = ScmCheckAccess(hHandle,
dwDesiredAccess | SC_MANAGER_CONNECT);
if (dwError != ERROR_SUCCESS)
{
DPRINT("ScmCheckAccess() failed (Error %lu)\n", dwError);
HeapFree(GetProcessHeap(), 0, hHandle);
return dwError;
}
*lpScHandle = (SC_RPC_HANDLE)hHandle;
DPRINT("*hScm = %p\n", *lpScHandle);
DPRINT("ROpenSCManagerW() done\n");
return ERROR_SUCCESS;
}
/* Function 16 */
DWORD
WINAPI
ROpenServiceW(
SC_RPC_HANDLE hSCManager,
LPWSTR lpServiceName,
DWORD dwDesiredAccess,
LPSC_RPC_HANDLE lpServiceHandle)
{
PSERVICE lpService;
PMANAGER_HANDLE hManager;
SC_HANDLE hHandle;
DWORD dwError = ERROR_SUCCESS;
DPRINT("ROpenServiceW() called\n");
DPRINT("hSCManager = %p\n", hSCManager);
DPRINT("lpServiceName = %p\n", lpServiceName);
DPRINT("lpServiceName: %S\n", lpServiceName);
DPRINT("dwDesiredAccess = %x\n", dwDesiredAccess);
if (ScmShutdown)
return ERROR_SHUTDOWN_IN_PROGRESS;
hManager = ScmGetServiceManagerFromHandle(hSCManager);
if (hManager == NULL)
{
DPRINT1("Invalid service manager handle\n");
return ERROR_INVALID_HANDLE;
}
if (!lpServiceHandle)
return ERROR_INVALID_PARAMETER;
if (!lpServiceName)
return ERROR_INVALID_ADDRESS;
/* Lock the service database exclusive */
ScmLockDatabaseExclusive();
/* Get service database entry */
lpService = ScmGetServiceEntryByName(lpServiceName);
if (lpService == NULL)
{
DPRINT("Could not find service\n");
dwError = ERROR_SERVICE_DOES_NOT_EXIST;
goto Done;
}
/* Create a service handle */
dwError = ScmCreateServiceHandle(lpService,
&hHandle);
if (dwError != ERROR_SUCCESS)
{
DPRINT("ScmCreateServiceHandle() failed (Error %lu)\n", dwError);
goto Done;
}
/* Check the desired access */
dwError = ScmCheckAccess(hHandle,
dwDesiredAccess);
if (dwError != ERROR_SUCCESS)
{
DPRINT("ScmCheckAccess() failed (Error %lu)\n", dwError);
HeapFree(GetProcessHeap(), 0, hHandle);
goto Done;
}
ScmReferenceService(lpService);
DPRINT("OpenService %S - lpService->RefCount %u\n", lpService->lpServiceName, lpService->RefCount);
*lpServiceHandle = (SC_RPC_HANDLE)hHandle;
DPRINT("*hService = %p\n", *lpServiceHandle);
Done:
/* Unlock the service database */
ScmUnlockDatabase();
DPRINT("ROpenServiceW() done\n");
return dwError;
}
/* Function 17 */
DWORD
WINAPI
RQueryServiceConfigW(
SC_RPC_HANDLE hService,
LPBYTE lpBuf, //LPQUERY_SERVICE_CONFIGW lpServiceConfig,
DWORD cbBufSize,
LPBOUNDED_DWORD_8K pcbBytesNeeded)
{
LPQUERY_SERVICE_CONFIGW lpServiceConfig = (LPQUERY_SERVICE_CONFIGW)lpBuf;
DWORD dwError = ERROR_SUCCESS;
PSERVICE_HANDLE hSvc;
PSERVICE lpService = NULL;
HKEY hServiceKey = NULL;
LPWSTR lpImagePath = NULL;
LPWSTR lpServiceStartName = NULL;
LPWSTR lpDependencies = NULL;
DWORD dwDependenciesLength = 0;
DWORD dwRequiredSize;
LPWSTR lpStr;
DPRINT("RQueryServiceConfigW() called\n");
if (ScmShutdown)
return ERROR_SHUTDOWN_IN_PROGRESS;
hSvc = ScmGetServiceFromHandle(hService);
if (hSvc == NULL)
{
DPRINT1("Invalid service handle\n");
return ERROR_INVALID_HANDLE;
}
if (!RtlAreAllAccessesGranted(hSvc->Handle.DesiredAccess,
SERVICE_QUERY_CONFIG))
{
DPRINT("Insufficient access rights! 0x%lx\n", hSvc->Handle.DesiredAccess);
return ERROR_ACCESS_DENIED;
}
lpService = hSvc->ServiceEntry;
if (lpService == NULL)
{
DPRINT("lpService == NULL\n");
return ERROR_INVALID_HANDLE;
}
/* Lock the service database shared */
ScmLockDatabaseShared();
dwError = ScmOpenServiceKey(lpService->lpServiceName,
KEY_READ,
&hServiceKey);
if (dwError != ERROR_SUCCESS)
goto Done;
/* Read the image path */
dwError = ScmReadString(hServiceKey,
L"ImagePath",
&lpImagePath);
if (dwError != ERROR_SUCCESS)
goto Done;
/* Read the service start name */
ScmReadString(hServiceKey,
L"ObjectName",
&lpServiceStartName);
/* Read the dependencies */
ScmReadDependencies(hServiceKey,
&lpDependencies,
&dwDependenciesLength);
dwRequiredSize = sizeof(QUERY_SERVICE_CONFIGW);
if (lpImagePath != NULL)
dwRequiredSize += (DWORD)((wcslen(lpImagePath) + 1) * sizeof(WCHAR));
else
dwRequiredSize += 2 * sizeof(WCHAR);
if ((lpService->lpGroup != NULL) && (lpService->lpGroup->lpGroupName != NULL))
dwRequiredSize += (DWORD)((wcslen(lpService->lpGroup->lpGroupName) + 1) * sizeof(WCHAR));
else
dwRequiredSize += 2 * sizeof(WCHAR);
if (lpDependencies != NULL)
dwRequiredSize += dwDependenciesLength * sizeof(WCHAR);
else
dwRequiredSize += 2 * sizeof(WCHAR);
if (lpServiceStartName != NULL)
dwRequiredSize += (DWORD)((wcslen(lpServiceStartName) + 1) * sizeof(WCHAR));
else
dwRequiredSize += 2 * sizeof(WCHAR);
if (lpService->lpDisplayName != NULL)
dwRequiredSize += (DWORD)((wcslen(lpService->lpDisplayName) + 1) * sizeof(WCHAR));
else
dwRequiredSize += 2 * sizeof(WCHAR);
if (lpServiceConfig == NULL || cbBufSize < dwRequiredSize)
{
dwError = ERROR_INSUFFICIENT_BUFFER;
}
else
{
lpServiceConfig->dwServiceType = lpService->Status.dwServiceType;
lpServiceConfig->dwStartType = lpService->dwStartType;
lpServiceConfig->dwErrorControl = lpService->dwErrorControl;
lpServiceConfig->dwTagId = lpService->dwTag;
lpStr = (LPWSTR)(lpServiceConfig + 1);
/* Append the image path */
if (lpImagePath != NULL)
{
wcscpy(lpStr, lpImagePath);
}
else
{
*lpStr = 0;
}
lpServiceConfig->lpBinaryPathName = (LPWSTR)((ULONG_PTR)lpStr - (ULONG_PTR)lpServiceConfig);
lpStr += (wcslen(lpStr) + 1);
/* Append the group name */
if ((lpService->lpGroup != NULL) && (lpService->lpGroup->lpGroupName != NULL))
{
wcscpy(lpStr, lpService->lpGroup->lpGroupName);
}
else
{
*lpStr = 0;
}
lpServiceConfig->lpLoadOrderGroup = (LPWSTR)((ULONG_PTR)lpStr - (ULONG_PTR)lpServiceConfig);
lpStr += (wcslen(lpStr) + 1);
/* Append Dependencies */
if (lpDependencies != NULL)
{
memcpy(lpStr,
lpDependencies,
dwDependenciesLength * sizeof(WCHAR));
}
else
{
*lpStr = 0;
}
lpServiceConfig->lpDependencies = (LPWSTR)((ULONG_PTR)lpStr - (ULONG_PTR)lpServiceConfig);
if (lpDependencies != NULL)
lpStr += dwDependenciesLength;
else
lpStr += (wcslen(lpStr) + 1);
/* Append the service start name */
if (lpServiceStartName != NULL)
{
wcscpy(lpStr, lpServiceStartName);
}
else
{
*lpStr = 0;
}
lpServiceConfig->lpServiceStartName = (LPWSTR)((ULONG_PTR)lpStr - (ULONG_PTR)lpServiceConfig);
lpStr += (wcslen(lpStr) + 1);
/* Append the display name */
if (lpService->lpDisplayName != NULL)
{
wcscpy(lpStr, lpService->lpDisplayName);
}
else
{
*lpStr = 0;
}
lpServiceConfig->lpDisplayName = (LPWSTR)((ULONG_PTR)lpStr - (ULONG_PTR)lpServiceConfig);
}
if (pcbBytesNeeded != NULL)
*pcbBytesNeeded = dwRequiredSize;
Done:
/* Unlock the service database */
ScmUnlockDatabase();
if (lpImagePath != NULL)
HeapFree(GetProcessHeap(), 0, lpImagePath);
if (lpServiceStartName != NULL)
HeapFree(GetProcessHeap(), 0, lpServiceStartName);
if (lpDependencies != NULL)
HeapFree(GetProcessHeap(), 0, lpDependencies);
if (hServiceKey != NULL)
RegCloseKey(hServiceKey);
DPRINT("RQueryServiceConfigW() done\n");
return dwError;
}
/* Function 18 */
DWORD
WINAPI
RQueryServiceLockStatusW(
SC_RPC_HANDLE hSCManager,
LPBYTE lpBuf, // LPQUERY_SERVICE_LOCK_STATUSW lpLockStatus,
DWORD cbBufSize,
LPBOUNDED_DWORD_4K pcbBytesNeeded)
{
LPQUERY_SERVICE_LOCK_STATUSW lpLockStatus = (LPQUERY_SERVICE_LOCK_STATUSW)lpBuf;
PMANAGER_HANDLE hMgr;
DWORD dwRequiredSize;
if (!lpLockStatus || !pcbBytesNeeded)
return ERROR_INVALID_PARAMETER;
hMgr = ScmGetServiceManagerFromHandle(hSCManager);
if (hMgr == NULL)
{
DPRINT1("Invalid service manager handle\n");
return ERROR_INVALID_HANDLE;
}
if (!RtlAreAllAccessesGranted(hMgr->Handle.DesiredAccess,
SC_MANAGER_QUERY_LOCK_STATUS))
{
DPRINT("Insufficient access rights! 0x%lx\n", hMgr->Handle.DesiredAccess);
return ERROR_ACCESS_DENIED;
}
/* FIXME: we need to compute instead the real length of the owner name */
dwRequiredSize = sizeof(QUERY_SERVICE_LOCK_STATUSW) + sizeof(WCHAR);
*pcbBytesNeeded = dwRequiredSize;
if (cbBufSize < dwRequiredSize)
return ERROR_INSUFFICIENT_BUFFER;
ScmQueryServiceLockStatusW(lpLockStatus);
return ERROR_SUCCESS;
}
/* Function 19 */
DWORD
WINAPI
RStartServiceW(
SC_RPC_HANDLE hService,
DWORD argc,
LPSTRING_PTRSW argv)
{
DWORD dwError = ERROR_SUCCESS;
PSERVICE_HANDLE hSvc;
PSERVICE lpService = NULL;
#ifndef NDEBUG
DWORD i;
DPRINT("RStartServiceW(%p %lu %p) called\n", hService, argc, argv);
DPRINT(" argc: %lu\n", argc);
if (argv != NULL)
{
for (i = 0; i < argc; i++)
{
DPRINT(" argv[%lu]: %S\n", i, argv[i].StringPtr);
}
}
#endif
if (ScmShutdown)
return ERROR_SHUTDOWN_IN_PROGRESS;
hSvc = ScmGetServiceFromHandle(hService);
if (hSvc == NULL)
{
DPRINT1("Invalid service handle\n");
return ERROR_INVALID_HANDLE;
}
if (!RtlAreAllAccessesGranted(hSvc->Handle.DesiredAccess,
SERVICE_START))
{
DPRINT("Insufficient access rights! 0x%lx\n", hSvc->Handle.DesiredAccess);
return ERROR_ACCESS_DENIED;
}
lpService = hSvc->ServiceEntry;
if (lpService == NULL)
{
DPRINT("lpService == NULL\n");
return ERROR_INVALID_HANDLE;
}
if (lpService->dwStartType == SERVICE_DISABLED)
return ERROR_SERVICE_DISABLED;
if (lpService->bDeleted)
return ERROR_SERVICE_MARKED_FOR_DELETE;
/* Start the service */
dwError = ScmStartService(lpService, argc, (PCWSTR*)argv);
return dwError;
}
/* Function 20 */
DWORD
WINAPI
RGetServiceDisplayNameW(
SC_RPC_HANDLE hSCManager,
LPCWSTR lpServiceName,
LPWSTR lpDisplayName,
DWORD *lpcchBuffer)
{
// PMANAGER_HANDLE hManager;
PSERVICE lpService;
LPCWSTR lpSvcDisplayName;
DWORD dwLength;
DWORD dwError;
DPRINT("RGetServiceDisplayNameW() called\n");
DPRINT("hSCManager = %p\n", hSCManager);
DPRINT("lpServiceName: %S\n", lpServiceName);
DPRINT("lpDisplayName: %p\n", lpDisplayName);
DPRINT("*lpcchBuffer: %lu\n", *lpcchBuffer);
#if 0
hManager = (PMANAGER_HANDLE)hSCManager;
if (hManager->Handle.Tag != MANAGER_TAG)
{
DPRINT("Invalid manager handle\n");
return ERROR_INVALID_HANDLE;
}
#endif
/* Get service database entry */
lpService = ScmGetServiceEntryByName(lpServiceName);
if (lpService == NULL)
{
DPRINT("Could not find service\n");
return ERROR_SERVICE_DOES_NOT_EXIST;
}
if (lpService->lpDisplayName)
lpSvcDisplayName = lpService->lpDisplayName;
else
lpSvcDisplayName = lpService->lpServiceName;
dwLength = (DWORD)wcslen(lpSvcDisplayName);
if (*lpcchBuffer > dwLength)
{
if (lpDisplayName != NULL)
wcscpy(lpDisplayName, lpSvcDisplayName);
dwError = ERROR_SUCCESS;
}
else
{
dwError = ERROR_INSUFFICIENT_BUFFER;
}
*lpcchBuffer = dwLength;
return dwError;
}
/* Function 21 */
DWORD
WINAPI
RGetServiceKeyNameW(
SC_RPC_HANDLE hSCManager,
LPCWSTR lpDisplayName,
LPWSTR lpServiceName,
DWORD *lpcchBuffer)
{
// PMANAGER_HANDLE hManager;
PSERVICE lpService;
DWORD dwLength;
DWORD dwError;
DPRINT("RGetServiceKeyNameW() called\n");
DPRINT("hSCManager = %p\n", hSCManager);
DPRINT("lpDisplayName: %S\n", lpDisplayName);
DPRINT("lpServiceName: %p\n", lpServiceName);
DPRINT("*lpcchBuffer: %lu\n", *lpcchBuffer);
#if 0
hManager = (PMANAGER_HANDLE)hSCManager;
if (hManager->Handle.Tag != MANAGER_TAG)
{
DPRINT("Invalid manager handle\n");
return ERROR_INVALID_HANDLE;
}
#endif
/* Get service database entry */
lpService = ScmGetServiceEntryByDisplayName(lpDisplayName);
if (lpService == NULL)
{
DPRINT("Could not find service\n");
return ERROR_SERVICE_DOES_NOT_EXIST;
}
dwLength = (DWORD)wcslen(lpService->lpServiceName);
if (*lpcchBuffer > dwLength)
{
if (lpServiceName != NULL)
wcscpy(lpServiceName, lpService->lpServiceName);
dwError = ERROR_SUCCESS;
}
else
{
dwError = ERROR_INSUFFICIENT_BUFFER;
}
*lpcchBuffer = dwLength;
return dwError;
}
/* Function 22 */
DWORD
WINAPI
RI_ScSetServiceBitsA(
RPC_SERVICE_STATUS_HANDLE hServiceStatus,
DWORD dwServiceBits,
int bSetBitsOn,
int bUpdateImmediately,
char *lpString)
{
if (ScmShutdown)
return ERROR_SHUTDOWN_IN_PROGRESS;
if (lpString != NULL)
return ERROR_INVALID_PARAMETER;
return RI_ScSetServiceBitsW(hServiceStatus,
dwServiceBits,
bSetBitsOn,
bUpdateImmediately,
NULL);
}
/* Function 23 */
DWORD
WINAPI
RChangeServiceConfigA(
SC_RPC_HANDLE hService,
DWORD dwServiceType,
DWORD dwStartType,
DWORD dwErrorControl,
LPSTR lpBinaryPathName,
LPSTR lpLoadOrderGroup,
LPDWORD lpdwTagId,
LPBYTE lpDependencies,
DWORD dwDependSize,
LPSTR lpServiceStartName,
LPBYTE lpPassword,
DWORD dwPwSize,
LPSTR lpDisplayName)
{
DWORD dwError = ERROR_SUCCESS;
LPWSTR lpBinaryPathNameW = NULL;
LPWSTR lpLoadOrderGroupW = NULL;
LPWSTR lpDependenciesW = NULL;
LPWSTR lpServiceStartNameW = NULL;
LPWSTR lpDisplayNameW = NULL;
DWORD dwDependenciesLength = 0;
SIZE_T cchLength;
int len;
LPCSTR lpStr;
if (lpBinaryPathName)
{
len = MultiByteToWideChar(CP_ACP, 0, lpBinaryPathName, -1, NULL, 0);
lpBinaryPathNameW = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, len * sizeof(WCHAR));
if (!lpBinaryPathNameW)
{
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
goto cleanup;
}
MultiByteToWideChar(CP_ACP, 0, lpBinaryPathName, -1, lpBinaryPathNameW, len);
}
if (lpLoadOrderGroup)
{
len = MultiByteToWideChar(CP_ACP, 0, lpLoadOrderGroup, -1, NULL, 0);
lpLoadOrderGroupW = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, len * sizeof(WCHAR));
if (!lpLoadOrderGroupW)
{
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
goto cleanup;
}
MultiByteToWideChar(CP_ACP, 0, lpLoadOrderGroup, -1, lpLoadOrderGroupW, len);
}
if (lpDependencies)
{
lpStr = (LPCSTR)lpDependencies;
while (*lpStr)
{
cchLength = strlen(lpStr) + 1;
dwDependenciesLength += (DWORD)cchLength;
lpStr = lpStr + cchLength;
}
dwDependenciesLength++;
lpDependenciesW = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dwDependenciesLength * sizeof(WCHAR));
if (!lpDependenciesW)
{
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
goto cleanup;
}
MultiByteToWideChar(CP_ACP, 0, (LPCSTR)lpDependencies, dwDependenciesLength, lpDependenciesW, dwDependenciesLength);
}
if (lpServiceStartName)
{
len = MultiByteToWideChar(CP_ACP, 0, lpServiceStartName, -1, NULL, 0);
lpServiceStartNameW = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, len * sizeof(WCHAR));
if (!lpServiceStartNameW)
{
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
goto cleanup;
}
MultiByteToWideChar(CP_ACP, 0, lpServiceStartName, -1, lpServiceStartNameW, len);
}
if (lpDisplayName)
{
len = MultiByteToWideChar(CP_ACP, 0, lpDisplayName, -1, NULL, 0);
lpDisplayNameW = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, len * sizeof(WCHAR));
if (!lpDisplayNameW)
{
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
goto cleanup;
}
MultiByteToWideChar(CP_ACP, 0, lpDisplayName, -1, lpDisplayNameW, len);
}
dwError = RChangeServiceConfigW(hService,
dwServiceType,
dwStartType,
dwErrorControl,
lpBinaryPathNameW,
lpLoadOrderGroupW,
lpdwTagId,
(LPBYTE)lpDependenciesW,
dwDependenciesLength,
lpServiceStartNameW,
lpPassword,
dwPwSize,
lpDisplayNameW);
cleanup:
if (lpBinaryPathNameW != NULL)
HeapFree(GetProcessHeap(), 0, lpBinaryPathNameW);
if (lpLoadOrderGroupW != NULL)
HeapFree(GetProcessHeap(), 0, lpLoadOrderGroupW);
if (lpDependenciesW != NULL)
HeapFree(GetProcessHeap(), 0, lpDependenciesW);
if (lpServiceStartNameW != NULL)
HeapFree(GetProcessHeap(), 0, lpServiceStartNameW);
if (lpDisplayNameW != NULL)
HeapFree(GetProcessHeap(), 0, lpDisplayNameW);
return dwError;
}
/* Function 24 */
DWORD
WINAPI
RCreateServiceA(
SC_RPC_HANDLE hSCManager,
LPSTR lpServiceName,
LPSTR lpDisplayName,
DWORD dwDesiredAccess,
DWORD dwServiceType,
DWORD dwStartType,
DWORD dwErrorControl,
LPSTR lpBinaryPathName,
LPSTR lpLoadOrderGroup,
LPDWORD lpdwTagId,
LPBYTE lpDependencies,
DWORD dwDependSize,
LPSTR lpServiceStartName,
LPBYTE lpPassword,
DWORD dwPwSize,
LPSC_RPC_HANDLE lpServiceHandle)
{
DWORD dwError = ERROR_SUCCESS;
LPWSTR lpServiceNameW = NULL;
LPWSTR lpDisplayNameW = NULL;
LPWSTR lpBinaryPathNameW = NULL;
LPWSTR lpLoadOrderGroupW = NULL;
LPWSTR lpDependenciesW = NULL;
LPWSTR lpServiceStartNameW = NULL;
DWORD dwDependenciesLength = 0;
SIZE_T cchLength;
int len;
LPCSTR lpStr;
if (lpServiceName)
{
len = MultiByteToWideChar(CP_ACP, 0, lpServiceName, -1, NULL, 0);
lpServiceNameW = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, len * sizeof(WCHAR));
if (!lpServiceNameW)
{
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
goto cleanup;
}
MultiByteToWideChar(CP_ACP, 0, lpServiceName, -1, lpServiceNameW, len);
}
if (lpDisplayName)
{
len = MultiByteToWideChar(CP_ACP, 0, lpDisplayName, -1, NULL, 0);
lpDisplayNameW = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, len * sizeof(WCHAR));
if (!lpDisplayNameW)
{
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
goto cleanup;
}
MultiByteToWideChar(CP_ACP, 0, lpDisplayName, -1, lpDisplayNameW, len);
}
if (lpBinaryPathName)
{
len = MultiByteToWideChar(CP_ACP, 0, lpBinaryPathName, -1, NULL, 0);
lpBinaryPathNameW = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, len * sizeof(WCHAR));
if (!lpBinaryPathNameW)
{
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
goto cleanup;
}
MultiByteToWideChar(CP_ACP, 0, lpBinaryPathName, -1, lpBinaryPathNameW, len);
}
if (lpLoadOrderGroup)
{
len = MultiByteToWideChar(CP_ACP, 0, lpLoadOrderGroup, -1, NULL, 0);
lpLoadOrderGroupW = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, len * sizeof(WCHAR));
if (!lpLoadOrderGroupW)
{
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
goto cleanup;
}
MultiByteToWideChar(CP_ACP, 0, lpLoadOrderGroup, -1, lpLoadOrderGroupW, len);
}
if (lpDependencies)
{
lpStr = (LPCSTR)lpDependencies;
while (*lpStr)
{
cchLength = strlen(lpStr) + 1;
dwDependenciesLength += (DWORD)cchLength;
lpStr = lpStr + cchLength;
}
dwDependenciesLength++;
lpDependenciesW = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dwDependenciesLength * sizeof(WCHAR));
if (!lpDependenciesW)
{
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
goto cleanup;
}
MultiByteToWideChar(CP_ACP, 0, (LPCSTR)lpDependencies, dwDependenciesLength, lpDependenciesW, dwDependenciesLength);
}
if (lpServiceStartName)
{
len = MultiByteToWideChar(CP_ACP, 0, lpServiceStartName, -1, NULL, 0);
lpServiceStartNameW = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, len * sizeof(WCHAR));
if (!lpServiceStartNameW)
{
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
goto cleanup;
}
MultiByteToWideChar(CP_ACP, 0, lpServiceStartName, -1, lpServiceStartNameW, len);
}
dwError = RCreateServiceW(hSCManager,
lpServiceNameW,
lpDisplayNameW,
dwDesiredAccess,
dwServiceType,
dwStartType,
dwErrorControl,
lpBinaryPathNameW,
lpLoadOrderGroupW,
lpdwTagId,
(LPBYTE)lpDependenciesW,
dwDependenciesLength,
lpServiceStartNameW,
lpPassword,
dwPwSize,
lpServiceHandle);
cleanup:
if (lpServiceNameW !=NULL)
HeapFree(GetProcessHeap(), 0, lpServiceNameW);
if (lpDisplayNameW != NULL)
HeapFree(GetProcessHeap(), 0, lpDisplayNameW);
if (lpBinaryPathNameW != NULL)
HeapFree(GetProcessHeap(), 0, lpBinaryPathNameW);
if (lpLoadOrderGroupW != NULL)
HeapFree(GetProcessHeap(), 0, lpLoadOrderGroupW);
if (lpDependenciesW != NULL)
HeapFree(GetProcessHeap(), 0, lpDependenciesW);
if (lpServiceStartNameW != NULL)
HeapFree(GetProcessHeap(), 0, lpServiceStartNameW);
return dwError;
}
/* Function 25 */
DWORD
WINAPI
REnumDependentServicesA(
SC_RPC_HANDLE hService,
DWORD dwServiceState,
LPBYTE lpServices,
DWORD cbBufSize,
LPBOUNDED_DWORD_256K pcbBytesNeeded,
LPBOUNDED_DWORD_256K lpServicesReturned)
{
DWORD dwError = ERROR_SUCCESS;
DWORD dwServicesReturned = 0;
DWORD dwServiceCount;
HKEY hServicesKey = NULL;
PSERVICE_HANDLE hSvc;
PSERVICE lpService = NULL;
PSERVICE *lpServicesArray = NULL;
LPENUM_SERVICE_STATUSA lpServicesPtr = NULL;
LPSTR lpStr;
*pcbBytesNeeded = 0;
*lpServicesReturned = 0;
DPRINT("REnumDependentServicesA() called\n");
hSvc = ScmGetServiceFromHandle(hService);
if (hSvc == NULL)
{
DPRINT1("Invalid service handle\n");
return ERROR_INVALID_HANDLE;
}
lpService = hSvc->ServiceEntry;
/* Check access rights */
if (!RtlAreAllAccessesGranted(hSvc->Handle.DesiredAccess,
SC_MANAGER_ENUMERATE_SERVICE))
{
DPRINT("Insufficient access rights! 0x%lx\n",
hSvc->Handle.DesiredAccess);
return ERROR_ACCESS_DENIED;
}
/* Open the Services Reg key */
dwError = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
L"System\\CurrentControlSet\\Services",
0,
KEY_READ,
&hServicesKey);
if (dwError != ERROR_SUCCESS)
return dwError;
/* NOTE: Windows calculates the pcbBytesNeeded based on WCHAR strings for
both EnumDependentServicesA and EnumDependentServicesW. So returned pcbBytesNeeded
are the same for both. Verified in WINXP. */
/* First determine the bytes needed and get the number of dependent services*/
dwError = Int_EnumDependentServicesW(hServicesKey,
lpService,
dwServiceState,
NULL,
pcbBytesNeeded,
&dwServicesReturned);
if (dwError != ERROR_SUCCESS)
goto Done;
/* If buffer size is less than the bytes needed or pointer is null*/
if ((!lpServices) || (cbBufSize < *pcbBytesNeeded))
{
dwError = ERROR_MORE_DATA;
goto Done;
}
/* Allocate memory for array of service pointers */
lpServicesArray = HeapAlloc(GetProcessHeap(),
HEAP_ZERO_MEMORY,
(dwServicesReturned + 1) * sizeof(PSERVICE));
if (!lpServicesArray)
{
DPRINT("Could not allocate buffer\n");
dwError = ERROR_NOT_ENOUGH_MEMORY;
goto Done;
}
dwServicesReturned = 0;
*pcbBytesNeeded = 0;
dwError = Int_EnumDependentServicesW(hServicesKey,
lpService,
dwServiceState,
lpServicesArray,
pcbBytesNeeded,
&dwServicesReturned);
if (dwError != ERROR_SUCCESS)
{
goto Done;
}
lpServicesPtr = (LPENUM_SERVICE_STATUSA)lpServices;
lpStr = (LPSTR)(lpServices + (dwServicesReturned * sizeof(ENUM_SERVICE_STATUSA)));
/* Copy EnumDepenedentService to Buffer */
for (dwServiceCount = 0; dwServiceCount < dwServicesReturned; dwServiceCount++)
{
lpService = lpServicesArray[dwServiceCount];
/* Copy the status info */
memcpy(&lpServicesPtr->ServiceStatus,
&lpService->Status,
sizeof(SERVICE_STATUS));
/* Copy display name */
WideCharToMultiByte(CP_ACP,
0,
lpService->lpDisplayName,
-1,
lpStr,
(int)wcslen(lpService->lpDisplayName),
0,
0);
lpServicesPtr->lpDisplayName = (LPSTR)((ULONG_PTR)lpStr - (ULONG_PTR)lpServices);
lpStr += strlen(lpStr) + 1;
/* Copy service name */
WideCharToMultiByte(CP_ACP,
0,
lpService->lpServiceName,
-1,
lpStr,
(int)wcslen(lpService->lpServiceName),
0,
0);
lpServicesPtr->lpServiceName = (LPSTR)((ULONG_PTR)lpStr - (ULONG_PTR)lpServices);
lpStr += strlen(lpStr) + 1;
lpServicesPtr++;
}
*lpServicesReturned = dwServicesReturned;
Done:
if (lpServicesArray)
HeapFree(GetProcessHeap(), 0, lpServicesArray);
RegCloseKey(hServicesKey);
DPRINT("REnumDependentServicesA() done (Error %lu)\n", dwError);
return dwError;
}
/* Function 26 */
DWORD
WINAPI
REnumServicesStatusA(
SC_RPC_HANDLE hSCManager,
DWORD dwServiceType,
DWORD dwServiceState,
LPBYTE lpBuffer,
DWORD dwBufSize,
LPBOUNDED_DWORD_256K pcbBytesNeeded,
LPBOUNDED_DWORD_256K lpServicesReturned,
LPBOUNDED_DWORD_256K lpResumeHandle)
{
LPENUM_SERVICE_STATUSW lpStatusPtrW = NULL;
LPENUM_SERVICE_STATUSW lpStatusPtrIncrW;
LPENUM_SERVICE_STATUSA lpStatusPtrA = NULL;
LPWSTR lpStringPtrW;
LPSTR lpStringPtrA;
DWORD dwError;
DWORD dwServiceCount;
DPRINT("REnumServicesStatusA() called\n");
if (pcbBytesNeeded == NULL || lpServicesReturned == NULL)
{
return ERROR_INVALID_ADDRESS;
}
if ((dwBufSize > 0) && (lpBuffer))
{
lpStatusPtrW = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dwBufSize);
if (!lpStatusPtrW)
{
DPRINT("Failed to allocate buffer\n");
return ERROR_NOT_ENOUGH_MEMORY;
}
}
dwError = REnumServicesStatusW(hSCManager,
dwServiceType,
dwServiceState,
(LPBYTE)lpStatusPtrW,
dwBufSize,
pcbBytesNeeded,
lpServicesReturned,
lpResumeHandle);
/* if no services were returned then we are Done */
if (*lpServicesReturned == 0)
goto Done;
lpStatusPtrIncrW = lpStatusPtrW;
lpStatusPtrA = (LPENUM_SERVICE_STATUSA)lpBuffer;
lpStringPtrA = (LPSTR)((ULONG_PTR)lpBuffer +
*lpServicesReturned * sizeof(ENUM_SERVICE_STATUSA));
lpStringPtrW = (LPWSTR)((ULONG_PTR)lpStatusPtrW +
*lpServicesReturned * sizeof(ENUM_SERVICE_STATUSW));
for (dwServiceCount = 0; dwServiceCount < *lpServicesReturned; dwServiceCount++)
{
/* Copy the service name */
WideCharToMultiByte(CP_ACP,
0,
lpStringPtrW,
-1,
lpStringPtrA,
(int)wcslen(lpStringPtrW),
0,
0);
lpStatusPtrA->lpServiceName = (LPSTR)((ULONG_PTR)lpStringPtrA - (ULONG_PTR)lpBuffer);
lpStringPtrA += wcslen(lpStringPtrW) + 1;
lpStringPtrW += wcslen(lpStringPtrW) + 1;
/* Copy the display name */
WideCharToMultiByte(CP_ACP,
0,
lpStringPtrW,
-1,
lpStringPtrA,
(int)wcslen(lpStringPtrW),
0,
0);
lpStatusPtrA->lpDisplayName = (LPSTR)((ULONG_PTR)lpStringPtrA - (ULONG_PTR)lpBuffer);
lpStringPtrA += wcslen(lpStringPtrW) + 1;
lpStringPtrW += wcslen(lpStringPtrW) + 1;
/* Copy the status information */
memcpy(&lpStatusPtrA->ServiceStatus,
&lpStatusPtrIncrW->ServiceStatus,
sizeof(SERVICE_STATUS));
lpStatusPtrIncrW++;
lpStatusPtrA++;
}
Done:
if (lpStatusPtrW)
HeapFree(GetProcessHeap(), 0, lpStatusPtrW);
DPRINT("REnumServicesStatusA() done (Error %lu)\n", dwError);
return dwError;
}
/* Function 27 */
DWORD
WINAPI
ROpenSCManagerA(
LPSTR lpMachineName,
LPSTR lpDatabaseName,
DWORD dwDesiredAccess,
LPSC_RPC_HANDLE lpScHandle)
{
UNICODE_STRING MachineName;
UNICODE_STRING DatabaseName;
DWORD dwError;
DPRINT("ROpenSCManagerA() called\n");
if (lpMachineName)
RtlCreateUnicodeStringFromAsciiz(&MachineName,
lpMachineName);
if (lpDatabaseName)
RtlCreateUnicodeStringFromAsciiz(&DatabaseName,
lpDatabaseName);
dwError = ROpenSCManagerW(lpMachineName ? MachineName.Buffer : NULL,
lpDatabaseName ? DatabaseName.Buffer : NULL,
dwDesiredAccess,
lpScHandle);
if (lpMachineName)
RtlFreeUnicodeString(&MachineName);
if (lpDatabaseName)
RtlFreeUnicodeString(&DatabaseName);
return dwError;
}
/* Function 28 */
DWORD
WINAPI
ROpenServiceA(
SC_RPC_HANDLE hSCManager,
LPSTR lpServiceName,
DWORD dwDesiredAccess,
LPSC_RPC_HANDLE lpServiceHandle)
{
UNICODE_STRING ServiceName;
DWORD dwError;
DPRINT("ROpenServiceA() called\n");
if (lpServiceName)
RtlCreateUnicodeStringFromAsciiz(&ServiceName,
lpServiceName);
dwError = ROpenServiceW(hSCManager,
lpServiceName ? ServiceName.Buffer : NULL,
dwDesiredAccess,
lpServiceHandle);
if (lpServiceName)
RtlFreeUnicodeString(&ServiceName);
return dwError;
}
/* Function 29 */
DWORD
WINAPI
RQueryServiceConfigA(
SC_RPC_HANDLE hService,
LPBYTE lpBuf, //LPQUERY_SERVICE_CONFIGA lpServiceConfig,
DWORD cbBufSize,
LPBOUNDED_DWORD_8K pcbBytesNeeded)
{
LPQUERY_SERVICE_CONFIGA lpServiceConfig = (LPQUERY_SERVICE_CONFIGA)lpBuf;
DWORD dwError = ERROR_SUCCESS;
PSERVICE_HANDLE hSvc;
PSERVICE lpService = NULL;
HKEY hServiceKey = NULL;
LPWSTR lpImagePath = NULL;
LPWSTR lpServiceStartName = NULL;
LPWSTR lpDependencies = NULL;
DWORD dwDependenciesLength = 0;
DWORD dwRequiredSize;
LPSTR lpStr;
DPRINT("RQueryServiceConfigA() called\n");
if (ScmShutdown)
return ERROR_SHUTDOWN_IN_PROGRESS;
hSvc = ScmGetServiceFromHandle(hService);
if (hSvc == NULL)
{
DPRINT1("Invalid service handle\n");
return ERROR_INVALID_HANDLE;
}
if (!RtlAreAllAccessesGranted(hSvc->Handle.DesiredAccess,
SERVICE_QUERY_CONFIG))
{
DPRINT("Insufficient access rights! 0x%lx\n", hSvc->Handle.DesiredAccess);
return ERROR_ACCESS_DENIED;
}
lpService = hSvc->ServiceEntry;
if (lpService == NULL)
{
DPRINT("lpService == NULL\n");
return ERROR_INVALID_HANDLE;
}
/* Lock the service database shared */
ScmLockDatabaseShared();
dwError = ScmOpenServiceKey(lpService->lpServiceName,
KEY_READ,
&hServiceKey);
if (dwError != ERROR_SUCCESS)
goto Done;
/* Read the image path */
dwError = ScmReadString(hServiceKey,
L"ImagePath",
&lpImagePath);
if (dwError != ERROR_SUCCESS)
goto Done;
/* Read the service start name */
ScmReadString(hServiceKey,
L"ObjectName",
&lpServiceStartName);
/* Read the dependencies */
ScmReadDependencies(hServiceKey,
&lpDependencies,
&dwDependenciesLength);
dwRequiredSize = sizeof(QUERY_SERVICE_CONFIGA);
if (lpImagePath != NULL)
dwRequiredSize += (DWORD)(wcslen(lpImagePath) + 1);
else
dwRequiredSize += 2 * sizeof(CHAR);
if ((lpService->lpGroup != NULL) && (lpService->lpGroup->lpGroupName != NULL))
dwRequiredSize += (DWORD)(wcslen(lpService->lpGroup->lpGroupName) + 1);
else
dwRequiredSize += 2 * sizeof(CHAR);
/* Add Dependencies length */
if (lpDependencies != NULL)
dwRequiredSize += dwDependenciesLength;
else
dwRequiredSize += 2 * sizeof(CHAR);
if (lpServiceStartName != NULL)
dwRequiredSize += (DWORD)(wcslen(lpServiceStartName) + 1);
else
dwRequiredSize += 2 * sizeof(CHAR);
if (lpService->lpDisplayName != NULL)
dwRequiredSize += (DWORD)(wcslen(lpService->lpDisplayName) + 1);
else
dwRequiredSize += 2 * sizeof(CHAR);
if (lpServiceConfig == NULL || cbBufSize < dwRequiredSize)
{
dwError = ERROR_INSUFFICIENT_BUFFER;
}
else
{
lpServiceConfig->dwServiceType = lpService->Status.dwServiceType;
lpServiceConfig->dwStartType = lpService->dwStartType;
lpServiceConfig->dwErrorControl = lpService->dwErrorControl;
lpServiceConfig->dwTagId = lpService->dwTag;
lpStr = (LPSTR)(lpServiceConfig + 1);
/* NOTE: Strings that are NULL for QUERY_SERVICE_CONFIG are pointers to empty strings.
Verified in WINXP */
if (lpImagePath)
{
WideCharToMultiByte(CP_ACP,
0,
lpImagePath,
-1,
lpStr,
(int)(wcslen(lpImagePath) + 1),
0,
0);
}
else
{
*lpStr = 0;
}
lpServiceConfig->lpBinaryPathName = (LPSTR)((ULONG_PTR)lpStr - (ULONG_PTR)lpServiceConfig);
lpStr += (strlen((LPSTR)lpStr) + 1);
if (lpService->lpGroup && lpService->lpGroup->lpGroupName)
{
WideCharToMultiByte(CP_ACP,
0,
lpService->lpGroup->lpGroupName,
-1,
lpStr,
(int)(wcslen(lpService->lpGroup->lpGroupName) + 1),
0,
0);
}
else
{
*lpStr = 0;
}
lpServiceConfig->lpLoadOrderGroup = (LPSTR)((ULONG_PTR)lpStr - (ULONG_PTR)lpServiceConfig);
lpStr += (strlen(lpStr) + 1);
/* Append Dependencies */
if (lpDependencies)
{
WideCharToMultiByte(CP_ACP,
0,
lpDependencies,
dwDependenciesLength,
lpStr,
dwDependenciesLength,
0,
0);
}
else
{
*lpStr = 0;
}
lpServiceConfig->lpDependencies = (LPSTR)((ULONG_PTR)lpStr - (ULONG_PTR)lpServiceConfig);
if (lpDependencies)
lpStr += dwDependenciesLength;
else
lpStr += (strlen(lpStr) + 1);
if (lpServiceStartName)
{
WideCharToMultiByte(CP_ACP,
0,
lpServiceStartName,
-1,
lpStr,
(int)(wcslen(lpServiceStartName) + 1),
0,
0);
}
else
{
*lpStr = 0;
}
lpServiceConfig->lpServiceStartName = (LPSTR)((ULONG_PTR)lpStr - (ULONG_PTR)lpServiceConfig);
lpStr += (strlen(lpStr) + 1);
if (lpService->lpDisplayName)
{
WideCharToMultiByte(CP_ACP,
0,
lpService->lpDisplayName,
-1,
lpStr,
(int)(wcslen(lpService->lpDisplayName) + 1),
0,
0);
}
else
{
*lpStr = 0;
}
lpServiceConfig->lpDisplayName = (LPSTR)((ULONG_PTR)lpStr - (ULONG_PTR)lpServiceConfig);
}
if (pcbBytesNeeded != NULL)
*pcbBytesNeeded = dwRequiredSize;
Done:
/* Unlock the service database */
ScmUnlockDatabase();
if (lpImagePath != NULL)
HeapFree(GetProcessHeap(), 0, lpImagePath);
if (lpServiceStartName != NULL)
HeapFree(GetProcessHeap(), 0, lpServiceStartName);
if (lpDependencies != NULL)
HeapFree(GetProcessHeap(), 0, lpDependencies);
if (hServiceKey != NULL)
RegCloseKey(hServiceKey);
DPRINT("RQueryServiceConfigA() done\n");
return dwError;
}
/* Function 30 */
DWORD
WINAPI
RQueryServiceLockStatusA(
SC_RPC_HANDLE hSCManager,
LPBYTE lpBuf, // LPQUERY_SERVICE_LOCK_STATUSA lpLockStatus,
DWORD cbBufSize,
LPBOUNDED_DWORD_4K pcbBytesNeeded)
{
LPQUERY_SERVICE_LOCK_STATUSA lpLockStatus = (LPQUERY_SERVICE_LOCK_STATUSA)lpBuf;
PMANAGER_HANDLE hMgr;
DWORD dwRequiredSize;
if (!lpLockStatus || !pcbBytesNeeded)
return ERROR_INVALID_PARAMETER;
hMgr = ScmGetServiceManagerFromHandle(hSCManager);
if (hMgr == NULL)
{
DPRINT1("Invalid service manager handle\n");
return ERROR_INVALID_HANDLE;
}
if (!RtlAreAllAccessesGranted(hMgr->Handle.DesiredAccess,
SC_MANAGER_QUERY_LOCK_STATUS))
{
DPRINT("Insufficient access rights! 0x%lx\n", hMgr->Handle.DesiredAccess);
return ERROR_ACCESS_DENIED;
}
/* FIXME: we need to compute instead the real length of the owner name */
dwRequiredSize = sizeof(QUERY_SERVICE_LOCK_STATUSA) + sizeof(CHAR);
*pcbBytesNeeded = dwRequiredSize;
if (cbBufSize < dwRequiredSize)
return ERROR_INSUFFICIENT_BUFFER;
ScmQueryServiceLockStatusA(lpLockStatus);
return ERROR_SUCCESS;
}
/* Function 31 */
DWORD
WINAPI
RStartServiceA(
SC_RPC_HANDLE hService,
DWORD argc,
LPSTRING_PTRSA argv)
{
DWORD dwError = ERROR_SUCCESS;
PSERVICE_HANDLE hSvc;
PSERVICE lpService = NULL;
PWSTR* pVector = NULL;
DWORD i;
DWORD dwLength;
DPRINT("RStartServiceA() called\n");
if (ScmShutdown)
return ERROR_SHUTDOWN_IN_PROGRESS;
hSvc = ScmGetServiceFromHandle(hService);
if (hSvc == NULL)
{
DPRINT1("Invalid service handle\n");
return ERROR_INVALID_HANDLE;
}
if (!RtlAreAllAccessesGranted(hSvc->Handle.DesiredAccess,
SERVICE_START))
{
DPRINT("Insufficient access rights! 0x%lx\n", hSvc->Handle.DesiredAccess);
return ERROR_ACCESS_DENIED;
}
lpService = hSvc->ServiceEntry;
if (lpService == NULL)
{
DPRINT("lpService == NULL\n");
return ERROR_INVALID_HANDLE;
}
if (lpService->dwStartType == SERVICE_DISABLED)
return ERROR_SERVICE_DISABLED;
if (lpService->bDeleted)
return ERROR_SERVICE_MARKED_FOR_DELETE;
/* Build a Unicode argument vector */
if (argc > 0)
{
pVector = HeapAlloc(GetProcessHeap(),
HEAP_ZERO_MEMORY,
argc * sizeof(PWSTR));
if (!pVector)
return ERROR_NOT_ENOUGH_MEMORY;
for (i = 0; i < argc; i++)
{
dwLength = MultiByteToWideChar(CP_ACP,
0,
argv[i].StringPtr,
-1,
NULL,
0);
pVector[i] = HeapAlloc(GetProcessHeap(),
HEAP_ZERO_MEMORY,
dwLength * sizeof(WCHAR));
if (!pVector[i])
{
dwError = ERROR_NOT_ENOUGH_MEMORY;
goto done;
}
MultiByteToWideChar(CP_ACP,
0,
argv[i].StringPtr,
-1,
pVector[i],
dwLength);
}
}
/* Start the service */
dwError = ScmStartService(lpService, argc, (PCWSTR*)pVector);
done:
/* Free the Unicode argument vector */
if (pVector)
{
for (i = 0; i < argc; i++)
{
if (pVector[i])
HeapFree(GetProcessHeap(), 0, pVector[i]);
}
HeapFree(GetProcessHeap(), 0, pVector);
}
return dwError;
}
/* Function 32 */
DWORD
WINAPI
RGetServiceDisplayNameA(
SC_RPC_HANDLE hSCManager,
LPCSTR lpServiceName,
LPSTR lpDisplayName,
LPBOUNDED_DWORD_4K lpcchBuffer)
{
// PMANAGER_HANDLE hManager;
PSERVICE lpService = NULL;
LPCWSTR lpSvcDisplayName;
LPWSTR lpServiceNameW;
DWORD dwLength;
DPRINT("RGetServiceDisplayNameA() called\n");
DPRINT("hSCManager = %p\n", hSCManager);
DPRINT("lpServiceName: %s\n", lpServiceName);
DPRINT("lpDisplayName: %p\n", lpDisplayName);
DPRINT("*lpcchBuffer: %lu\n", *lpcchBuffer);
#if 0
hManager = (PMANAGER_HANDLE)hSCManager;
if (hManager->Handle.Tag != MANAGER_TAG)
{
DPRINT("Invalid manager handle\n");
return ERROR_INVALID_HANDLE;
}
#endif
/* Get service database entry */
if (lpServiceName != NULL)
{
dwLength = (DWORD)(strlen(lpServiceName) + 1);
lpServiceNameW = HeapAlloc(GetProcessHeap(),
HEAP_ZERO_MEMORY,
dwLength * sizeof(WCHAR));
if (!lpServiceNameW)
return ERROR_NOT_ENOUGH_MEMORY;
MultiByteToWideChar(CP_ACP,
0,
lpServiceName,
-1,
lpServiceNameW,
dwLength);
lpService = ScmGetServiceEntryByName(lpServiceNameW);
HeapFree(GetProcessHeap(), 0, lpServiceNameW);
}
if (lpService == NULL)
{
DPRINT("Could not find service\n");
return ERROR_SERVICE_DOES_NOT_EXIST;
}
if (lpService->lpDisplayName)
lpSvcDisplayName = lpService->lpDisplayName;
else
lpSvcDisplayName = lpService->lpServiceName;
/*
* NOTE: On Windows the comparison on *lpcchBuffer is made against
* the number of (wide) characters of the UNICODE display name, and
* not against the number of bytes needed to store the ANSI string.
*/
dwLength = (DWORD)wcslen(lpSvcDisplayName);
if (*lpcchBuffer > dwLength)
{
if (lpDisplayName != NULL &&
WideCharToMultiByte(CP_ACP,
0,
lpSvcDisplayName,
-1,
lpDisplayName,
(int)*lpcchBuffer,
NULL,
NULL) == 0)
{
/*
* But then, if *lpcchBuffer was greater than the number of
* (wide) characters of the UNICODE display name, yet smaller
* than the number of bytes needed due to the possible presence
* of DBCS characters, the *exact* number of bytes is returned
* (without the NULL terminator).
*/
dwLength = (DWORD)WideCharToMultiByte(CP_ACP,
0,
lpSvcDisplayName,
(int)dwLength,
NULL,
0,
NULL,
NULL);
*lpDisplayName = 0;
*lpcchBuffer = dwLength;
return ERROR_INSUFFICIENT_BUFFER;
}
/*
* NOTE: On Windows, RGetServiceDisplayNameA() does not update
* *lpcchBuffer on success, contrary to RGetServiceDisplayNameW().
*/
return ERROR_SUCCESS;
}
else
{
/*
* NOTE: On Windows, if *lpcchBuffer is smaller than the number of
* (wide) characters of the UNICODE display name, only an upper
* estimation is returned by doubling the string length, to account
* for the presence of any possible DBCS characters.
*/
*lpcchBuffer = dwLength * sizeof(WCHAR);
return ERROR_INSUFFICIENT_BUFFER;
}
}
/* Function 33 */
DWORD
WINAPI
RGetServiceKeyNameA(
SC_RPC_HANDLE hSCManager,
LPCSTR lpDisplayName,
LPSTR lpServiceName,
LPBOUNDED_DWORD_4K lpcchBuffer)
{
// PMANAGER_HANDLE hManager;
PSERVICE lpService;
LPWSTR lpDisplayNameW;
DWORD dwLength;
DPRINT("RGetServiceKeyNameA() called\n");
DPRINT("hSCManager = %p\n", hSCManager);
DPRINT("lpDisplayName: %s\n", lpDisplayName);
DPRINT("lpServiceName: %p\n", lpServiceName);
DPRINT("*lpcchBuffer: %lu\n", *lpcchBuffer);
#if 0
hManager = (PMANAGER_HANDLE)hSCManager;
if (hManager->Handle.Tag != MANAGER_TAG)
{
DPRINT("Invalid manager handle\n");
return ERROR_INVALID_HANDLE;
}
#endif
/* Get service database entry */
dwLength = (DWORD)(strlen(lpDisplayName) + 1);
lpDisplayNameW = HeapAlloc(GetProcessHeap(),
HEAP_ZERO_MEMORY,
dwLength * sizeof(WCHAR));
if (!lpDisplayNameW)
return ERROR_NOT_ENOUGH_MEMORY;
MultiByteToWideChar(CP_ACP,
0,
lpDisplayName,
-1,
lpDisplayNameW,
dwLength);
lpService = ScmGetServiceEntryByDisplayName(lpDisplayNameW);
HeapFree(GetProcessHeap(), 0, lpDisplayNameW);
if (lpService == NULL)
{
DPRINT("Could not find service\n");
return ERROR_SERVICE_DOES_NOT_EXIST;
}
/*
* NOTE: On Windows the comparison on *lpcchBuffer is made against
* the number of (wide) characters of the UNICODE service name, and
* not against the number of bytes needed to store the ANSI string.
*/
dwLength = (DWORD)wcslen(lpService->lpServiceName);
if (*lpcchBuffer > dwLength)
{
if (lpServiceName != NULL &&
WideCharToMultiByte(CP_ACP,
0,
lpService->lpServiceName,
-1,
lpServiceName,
(int)*lpcchBuffer,
NULL,
NULL) == 0)
{
/*
* But then, if *lpcchBuffer was greater than the number of
* (wide) characters of the UNICODE service name, yet smaller
* than the number of bytes needed due to the possible presence
* of DBCS characters, the *exact* number of bytes is returned
* (without the NULL terminator).
*/
dwLength = (DWORD)WideCharToMultiByte(CP_ACP,
0,
lpService->lpServiceName,
(int)dwLength,
NULL,
0,
NULL,
NULL);
*lpServiceName = 0;
*lpcchBuffer = dwLength;
return ERROR_INSUFFICIENT_BUFFER;
}
/*
* NOTE: On Windows, RGetServiceKeyNameA() does not update
* *lpcchBuffer on success, contrary to RGetServiceKeyNameW().
*/
return ERROR_SUCCESS;
}
else
{
/*
* NOTE: On Windows, if *lpcchBuffer is smaller than the number of
* (wide) characters of the UNICODE service name, only an upper
* estimation is returned by doubling the string length, to account
* for the presence of any possible DBCS characters.
*/
*lpcchBuffer = dwLength * sizeof(WCHAR);
return ERROR_INSUFFICIENT_BUFFER;
}
}
/* Function 34 */
DWORD
WINAPI
RI_ScGetCurrentGroupStateW(
SC_RPC_HANDLE hSCManager,
LPWSTR lpLoadOrderGroup,
LPDWORD lpState)
{
PMANAGER_HANDLE hManager;
PSERVICE_GROUP pServiceGroup;
DWORD dwError = ERROR_SUCCESS;
DPRINT("RI_ScGetCurrentGroupStateW() called\n");
if (ScmShutdown)
return ERROR_SHUTDOWN_IN_PROGRESS;
hManager = ScmGetServiceManagerFromHandle(hSCManager);
if (hManager == NULL)
{
DPRINT1("Invalid service manager handle\n");
return ERROR_INVALID_HANDLE;
}
/* Check for SC_MANAGER_ENUMERATE_SERVICE access right */
if (!RtlAreAllAccessesGranted(hManager->Handle.DesiredAccess,
SC_MANAGER_ENUMERATE_SERVICE))
{
DPRINT("Insufficient access rights! 0x%lx\n",
hManager->Handle.DesiredAccess);
return ERROR_ACCESS_DENIED;
}
/* Lock the service database shared */
ScmLockDatabaseShared();
/* Get the group list entry */
pServiceGroup = ScmGetServiceGroupByName(lpLoadOrderGroup);
if (pServiceGroup == NULL)
{
dwError = ERROR_SERVICE_DOES_NOT_EXIST;
goto done;
}
/* FIXME: Return the group state */
*lpState = 0;
done:
/* Unlock the service database */
ScmUnlockDatabase();
DPRINT("RI_ScGetCurrentGroupStateW() done (Error %lu)\n", dwError);
return dwError;
}
/* Function 35 */
DWORD
WINAPI
REnumServiceGroupW(
SC_RPC_HANDLE hSCManager,
DWORD dwServiceType,
DWORD dwServiceState,
LPBYTE lpBuffer,
DWORD cbBufSize,
LPBOUNDED_DWORD_256K pcbBytesNeeded,
LPBOUNDED_DWORD_256K lpServicesReturned,
LPBOUNDED_DWORD_256K lpResumeIndex,
LPCWSTR pszGroupName)
{
PMANAGER_HANDLE hManager;
PSERVICE lpService;
DWORD dwError = ERROR_SUCCESS;
PLIST_ENTRY ServiceEntry;
PSERVICE CurrentService;
DWORD dwState;
DWORD dwRequiredSize;
DWORD dwServiceCount;
DWORD dwSize;
DWORD dwLastResumeCount = 0;
LPENUM_SERVICE_STATUSW lpStatusPtr;
LPWSTR lpStringPtr;
DPRINT("REnumServiceGroupW() called\n");
if (ScmShutdown)
return ERROR_SHUTDOWN_IN_PROGRESS;
hManager = ScmGetServiceManagerFromHandle(hSCManager);
if (hManager == NULL)
{
DPRINT1("Invalid service manager handle\n");
return ERROR_INVALID_HANDLE;
}
if (pcbBytesNeeded == NULL || lpServicesReturned == NULL)
{
return ERROR_INVALID_ADDRESS;
}
*pcbBytesNeeded = 0;
*lpServicesReturned = 0;
if ((dwServiceType == 0) ||
((dwServiceType & ~SERVICE_TYPE_ALL) != 0))
{
DPRINT("Invalid Service Type\n");
return ERROR_INVALID_PARAMETER;
}
if ((dwServiceState == 0) ||
((dwServiceState & ~SERVICE_STATE_ALL) != 0))
{
DPRINT("Invalid Service State\n");
return ERROR_INVALID_PARAMETER;
}
/* Check access rights */
if (!RtlAreAllAccessesGranted(hManager->Handle.DesiredAccess,
SC_MANAGER_ENUMERATE_SERVICE))
{
DPRINT("Insufficient access rights! 0x%lx\n",
hManager->Handle.DesiredAccess);
return ERROR_ACCESS_DENIED;
}
if (lpResumeIndex)
dwLastResumeCount = *lpResumeIndex;
/* Lock the service database shared */
ScmLockDatabaseShared();
lpService = ScmGetServiceEntryByResumeCount(dwLastResumeCount);
if (lpService == NULL)
{
dwError = ERROR_SUCCESS;
goto Done;
}
dwRequiredSize = 0;
dwServiceCount = 0;
for (ServiceEntry = &lpService->ServiceListEntry;
ServiceEntry != &ServiceListHead;
ServiceEntry = ServiceEntry->Flink)
{
CurrentService = CONTAINING_RECORD(ServiceEntry,
SERVICE,
ServiceListEntry);
if ((CurrentService->Status.dwServiceType & dwServiceType) == 0)
continue;
dwState = SERVICE_ACTIVE;
if (CurrentService->Status.dwCurrentState == SERVICE_STOPPED)
dwState = SERVICE_INACTIVE;
if ((dwState & dwServiceState) == 0)
continue;
if (pszGroupName)
{
if (*pszGroupName == 0)
{
if (CurrentService->lpGroup != NULL)
continue;
}
else
{
if ((CurrentService->lpGroup == NULL) ||
_wcsicmp(pszGroupName, CurrentService->lpGroup->lpGroupName) != 0)
continue;
}
}
dwSize = sizeof(ENUM_SERVICE_STATUSW) +
(DWORD)((wcslen(CurrentService->lpServiceName) + 1) * sizeof(WCHAR)) +
(DWORD)((wcslen(CurrentService->lpDisplayName) + 1) * sizeof(WCHAR));
if (dwRequiredSize + dwSize > cbBufSize)
{
DPRINT("Service name: %S no fit\n", CurrentService->lpServiceName);
break;
}
DPRINT("Service name: %S fit\n", CurrentService->lpServiceName);
dwRequiredSize += dwSize;
dwServiceCount++;
dwLastResumeCount = CurrentService->dwResumeCount;
}
DPRINT("dwRequiredSize: %lu\n", dwRequiredSize);
DPRINT("dwServiceCount: %lu\n", dwServiceCount);
for (;
ServiceEntry != &ServiceListHead;
ServiceEntry = ServiceEntry->Flink)
{
CurrentService = CONTAINING_RECORD(ServiceEntry,
SERVICE,
ServiceListEntry);
if ((CurrentService->Status.dwServiceType & dwServiceType) == 0)
continue;
dwState = SERVICE_ACTIVE;
if (CurrentService->Status.dwCurrentState == SERVICE_STOPPED)
dwState = SERVICE_INACTIVE;
if ((dwState & dwServiceState) == 0)
continue;
if (pszGroupName)
{
if (*pszGroupName == 0)
{
if (CurrentService->lpGroup != NULL)
continue;
}
else
{
if ((CurrentService->lpGroup == NULL) ||
_wcsicmp(pszGroupName, CurrentService->lpGroup->lpGroupName) != 0)
continue;
}
}
dwRequiredSize += (sizeof(ENUM_SERVICE_STATUSW) +
(DWORD)((wcslen(CurrentService->lpServiceName) + 1) * sizeof(WCHAR)) +
(DWORD)((wcslen(CurrentService->lpDisplayName) + 1) * sizeof(WCHAR)));
dwError = ERROR_MORE_DATA;
}
DPRINT("*pcbBytesNeeded: %lu\n", dwRequiredSize);
if (lpResumeIndex)
*lpResumeIndex = dwLastResumeCount;
*lpServicesReturned = dwServiceCount;
*pcbBytesNeeded = dwRequiredSize;
lpStatusPtr = (LPENUM_SERVICE_STATUSW)lpBuffer;
lpStringPtr = (LPWSTR)((ULONG_PTR)lpBuffer +
dwServiceCount * sizeof(ENUM_SERVICE_STATUSW));
dwRequiredSize = 0;
for (ServiceEntry = &lpService->ServiceListEntry;
ServiceEntry != &ServiceListHead;
ServiceEntry = ServiceEntry->Flink)
{
CurrentService = CONTAINING_RECORD(ServiceEntry,
SERVICE,
ServiceListEntry);
if ((CurrentService->Status.dwServiceType & dwServiceType) == 0)
continue;
dwState = SERVICE_ACTIVE;
if (CurrentService->Status.dwCurrentState == SERVICE_STOPPED)
dwState = SERVICE_INACTIVE;
if ((dwState & dwServiceState) == 0)
continue;
if (pszGroupName)
{
if (*pszGroupName == 0)
{
if (CurrentService->lpGroup != NULL)
continue;
}
else
{
if ((CurrentService->lpGroup == NULL) ||
_wcsicmp(pszGroupName, CurrentService->lpGroup->lpGroupName) != 0)
continue;
}
}
dwSize = sizeof(ENUM_SERVICE_STATUSW) +
(DWORD)((wcslen(CurrentService->lpServiceName) + 1) * sizeof(WCHAR)) +
(DWORD)((wcslen(CurrentService->lpDisplayName) + 1) * sizeof(WCHAR));
if (dwRequiredSize + dwSize > cbBufSize)
break;
/* Copy the service name */
wcscpy(lpStringPtr, CurrentService->lpServiceName);
lpStatusPtr->lpServiceName = (LPWSTR)((ULONG_PTR)lpStringPtr - (ULONG_PTR)lpBuffer);
lpStringPtr += (wcslen(CurrentService->lpServiceName) + 1);
/* Copy the display name */
wcscpy(lpStringPtr, CurrentService->lpDisplayName);
lpStatusPtr->lpDisplayName = (LPWSTR)((ULONG_PTR)lpStringPtr - (ULONG_PTR)lpBuffer);
lpStringPtr += (wcslen(CurrentService->lpDisplayName) + 1);
/* Copy the status information */
memcpy(&lpStatusPtr->ServiceStatus,
&CurrentService->Status,
sizeof(SERVICE_STATUS));
lpStatusPtr++;
dwRequiredSize += dwSize;
}
if (dwError == ERROR_SUCCESS)
{
*pcbBytesNeeded = 0;
if (lpResumeIndex) *lpResumeIndex = 0;
}
Done:
/* Unlock the service database */
ScmUnlockDatabase();
DPRINT("REnumServiceGroupW() done (Error %lu)\n", dwError);
return dwError;
}
/* Function 36 */
DWORD
WINAPI
RChangeServiceConfig2A(
SC_RPC_HANDLE hService,
SC_RPC_CONFIG_INFOA Info)
{
SC_RPC_CONFIG_INFOW InfoW = { 0 };
DWORD dwRet, dwLength;
PVOID ptr = NULL;
DPRINT("RChangeServiceConfig2A() called\n");
DPRINT("dwInfoLevel = %lu\n", Info.dwInfoLevel);
if ((Info.dwInfoLevel < SERVICE_CONFIG_DESCRIPTION) ||
(Info.dwInfoLevel > SERVICE_CONFIG_FAILURE_ACTIONS))
{
return ERROR_INVALID_LEVEL;
}
InfoW.dwInfoLevel = Info.dwInfoLevel;
if (InfoW.dwInfoLevel == SERVICE_CONFIG_DESCRIPTION)
{
LPSERVICE_DESCRIPTIONW lpServiceDescriptionW;
LPSERVICE_DESCRIPTIONA lpServiceDescriptionA;
lpServiceDescriptionA = Info.psd;
if (lpServiceDescriptionA &&
lpServiceDescriptionA->lpDescription)
{
dwLength = (DWORD)((strlen(lpServiceDescriptionA->lpDescription) + 1) * sizeof(WCHAR));
lpServiceDescriptionW = HeapAlloc(GetProcessHeap(),
HEAP_ZERO_MEMORY,
dwLength + sizeof(SERVICE_DESCRIPTIONW));
if (!lpServiceDescriptionW)
{
return ERROR_NOT_ENOUGH_MEMORY;
}
lpServiceDescriptionW->lpDescription = (LPWSTR)(lpServiceDescriptionW + 1);
MultiByteToWideChar(CP_ACP,
0,
lpServiceDescriptionA->lpDescription,
-1,
lpServiceDescriptionW->lpDescription,
dwLength);
ptr = lpServiceDescriptionW;
InfoW.psd = lpServiceDescriptionW;
}
}
else if (Info.dwInfoLevel == SERVICE_CONFIG_FAILURE_ACTIONS)
{
LPSERVICE_FAILURE_ACTIONSW lpServiceFailureActionsW;
LPSERVICE_FAILURE_ACTIONSA lpServiceFailureActionsA;
DWORD dwRebootLen = 0;
DWORD dwCommandLen = 0;
DWORD dwActionArrayLen = 0;
LPWSTR lpStr = NULL;
lpServiceFailureActionsA = Info.psfa;
if (lpServiceFailureActionsA)
{
/*
* The following code is inspired by the
* SERVICE_CONFIG_FAILURE_ACTIONS case of
* the RQueryServiceConfig2W function.
*/
/* Retrieve the needed length for the two data strings */
if (lpServiceFailureActionsA->lpRebootMsg)
{
dwRebootLen = (DWORD)((strlen(lpServiceFailureActionsA->lpRebootMsg) + 1) * sizeof(WCHAR));
}
if (lpServiceFailureActionsA->lpCommand)
{
dwCommandLen = (DWORD)((strlen(lpServiceFailureActionsA->lpCommand) + 1) * sizeof(WCHAR));
}
/*
* Retrieve the size of the lpsaActions array if needed.
* We will copy the lpsaActions array only if there is at
* least one action AND that the original array is valid.
*/
if (lpServiceFailureActionsA->cActions > 0 && lpServiceFailureActionsA->lpsaActions)
{
dwActionArrayLen = lpServiceFailureActionsA->cActions * sizeof(SC_ACTION);
}
/* Compute the total length for the UNICODE structure, including data */
dwLength = sizeof(SERVICE_FAILURE_ACTIONSW) +
dwActionArrayLen + dwRebootLen + dwCommandLen;
/* Allocate the structure */
lpServiceFailureActionsW = HeapAlloc(GetProcessHeap(),
HEAP_ZERO_MEMORY,
dwLength);
if (!lpServiceFailureActionsW)
{
return ERROR_NOT_ENOUGH_MEMORY;
}
/* Copy the members */
lpServiceFailureActionsW->dwResetPeriod = lpServiceFailureActionsA->dwResetPeriod;
lpServiceFailureActionsW->cActions = lpServiceFailureActionsA->cActions;
/* Copy the lpsaActions array if needed */
if (dwActionArrayLen > 0)
{
/* The storage zone is just after the end of the SERVICE_FAILURE_ACTIONSW structure */
lpServiceFailureActionsW->lpsaActions = (LPSC_ACTION)((ULONG_PTR)(lpServiceFailureActionsW + 1));
/* dwActionArrayLen == lpServiceFailureActionsW->cActions * sizeof(SC_ACTION) */
RtlCopyMemory(lpServiceFailureActionsW->lpsaActions,
lpServiceFailureActionsA->lpsaActions,
dwActionArrayLen);
}
else
{
/* No lpsaActions array */
lpServiceFailureActionsW->lpsaActions = NULL;
}
/* The data strings are stored just after the lpsaActions array */
lpStr = (LPWSTR)((ULONG_PTR)(lpServiceFailureActionsW + 1) + dwActionArrayLen);
/*
* Convert the data strings to UNICODE
*/
lpServiceFailureActionsW->lpRebootMsg = NULL;
lpServiceFailureActionsW->lpCommand = NULL;
if (dwRebootLen)
{
/* lpRebootMsg points just after the lpsaActions array */
lpServiceFailureActionsW->lpRebootMsg = lpStr;
MultiByteToWideChar(CP_ACP,
0,
lpServiceFailureActionsA->lpRebootMsg,
-1,
lpServiceFailureActionsW->lpRebootMsg,
dwRebootLen);
lpStr += dwRebootLen / sizeof(WCHAR);
}
if (dwCommandLen)
{
/* lpRebootMsg points just after the lpRebootMsg data string */
lpServiceFailureActionsW->lpCommand = lpStr;
MultiByteToWideChar(CP_ACP,
0,
lpServiceFailureActionsA->lpCommand,
-1,
lpServiceFailureActionsW->lpCommand,
dwCommandLen);
}
/* Set the pointers */
ptr = lpServiceFailureActionsW;
InfoW.psfa = lpServiceFailureActionsW;
}
}
dwRet = RChangeServiceConfig2W(hService, InfoW);
HeapFree(GetProcessHeap(), 0, ptr);
return dwRet;
}
static DWORD
ScmSetFailureActions(HKEY hServiceKey,
LPSERVICE_FAILURE_ACTIONSW lpFailureActions)
{
LPSERVICE_FAILURE_ACTIONSW lpReadBuffer = NULL;
LPSERVICE_FAILURE_ACTIONSW lpWriteBuffer = NULL;
DWORD dwRequiredSize = 0;
DWORD dwType = 0;
DWORD dwError;
/* There is nothing to be done if we have no failure actions */
if (lpFailureActions == NULL)
return ERROR_SUCCESS;
/*
* 1- Retrieve the original value of FailureActions.
*/
/* Query value length */
dwError = RegQueryValueExW(hServiceKey,
L"FailureActions",
NULL,
&dwType,
NULL,
&dwRequiredSize);
if (dwError != ERROR_SUCCESS &&
dwError != ERROR_MORE_DATA &&
dwError != ERROR_FILE_NOT_FOUND)
{
return dwError;
}
dwRequiredSize = (dwType == REG_BINARY) ? max(sizeof(SERVICE_FAILURE_ACTIONSW), dwRequiredSize)
: sizeof(SERVICE_FAILURE_ACTIONSW);
/* Initialize the read buffer */
lpReadBuffer = HeapAlloc(GetProcessHeap(),
HEAP_ZERO_MEMORY,
dwRequiredSize);
if (lpReadBuffer == NULL)
return ERROR_NOT_ENOUGH_MEMORY;
/* Now we can fill the read buffer */
if (dwError != ERROR_FILE_NOT_FOUND &&
dwType == REG_BINARY)
{
dwError = RegQueryValueExW(hServiceKey,
L"FailureActions",
NULL,
NULL,
(LPBYTE)lpReadBuffer,
&dwRequiredSize);
if (dwError != ERROR_SUCCESS &&
dwError != ERROR_FILE_NOT_FOUND)
goto done;
if (dwRequiredSize < sizeof(SERVICE_FAILURE_ACTIONSW))
dwRequiredSize = sizeof(SERVICE_FAILURE_ACTIONSW);
}
else
{
/*
* The value of the error doesn't really matter, the only
* important thing is that it must be != ERROR_SUCCESS.
*/
dwError = ERROR_INVALID_DATA;
}
if (dwError == ERROR_SUCCESS)
{
lpReadBuffer->cActions = min(lpReadBuffer->cActions, (dwRequiredSize - sizeof(SERVICE_FAILURE_ACTIONSW)) / sizeof(SC_ACTION));
lpReadBuffer->lpsaActions = (lpReadBuffer->cActions > 0 ? (LPSC_ACTION)(lpReadBuffer + 1) : NULL);
}
else
{
lpReadBuffer->dwResetPeriod = 0;
lpReadBuffer->cActions = 0;
lpReadBuffer->lpsaActions = NULL;
}
lpReadBuffer->lpRebootMsg = NULL;
lpReadBuffer->lpCommand = NULL;
/*
* 2- Initialize the new value to set.
*/
dwRequiredSize = sizeof(SERVICE_FAILURE_ACTIONSW);
if (lpFailureActions->lpsaActions == NULL)
{
/*
* lpFailureActions->cActions is ignored.
* Therefore we use the original values
* of cActions and lpsaActions.
*/
dwRequiredSize += lpReadBuffer->cActions * sizeof(SC_ACTION);
}
else
{
/*
* The reset period and array of failure actions
* are deleted if lpFailureActions->cActions == 0 .
*/
dwRequiredSize += lpFailureActions->cActions * sizeof(SC_ACTION);
}
lpWriteBuffer = HeapAlloc(GetProcessHeap(),
HEAP_ZERO_MEMORY,
dwRequiredSize);
if (lpWriteBuffer == NULL)
{
dwError = ERROR_NOT_ENOUGH_MEMORY;
goto done;
}
/* Clean the pointers as they have no meaning when the structure is stored in the registry */
lpWriteBuffer->lpRebootMsg = NULL;
lpWriteBuffer->lpCommand = NULL;
lpWriteBuffer->lpsaActions = NULL;
/* Set the members */
if (lpFailureActions->lpsaActions == NULL)
{
/*
* lpFailureActions->dwResetPeriod and lpFailureActions->cActions are ignored.
* Therefore we use the original values of dwResetPeriod, cActions and lpsaActions.
*/
lpWriteBuffer->dwResetPeriod = lpReadBuffer->dwResetPeriod;
lpWriteBuffer->cActions = lpReadBuffer->cActions;
if (lpReadBuffer->lpsaActions != NULL)
{
memmove(lpWriteBuffer + 1,
lpReadBuffer->lpsaActions,
lpReadBuffer->cActions * sizeof(SC_ACTION));
}
}
else
{
if (lpFailureActions->cActions > 0)
{
lpWriteBuffer->dwResetPeriod = lpFailureActions->dwResetPeriod;
lpWriteBuffer->cActions = lpFailureActions->cActions;
memmove(lpWriteBuffer + 1,
lpFailureActions->lpsaActions,
lpFailureActions->cActions * sizeof(SC_ACTION));
}
else
{
/* The reset period and array of failure actions are deleted */
lpWriteBuffer->dwResetPeriod = 0;
lpWriteBuffer->cActions = 0;
}
}
/* Save the new failure actions into the registry */
dwError = RegSetValueExW(hServiceKey,
L"FailureActions",
0,
REG_BINARY,
(LPBYTE)lpWriteBuffer,
dwRequiredSize);
/* We modify the strings only in case of success.*/
if (dwError == ERROR_SUCCESS)
{
/* Modify the Reboot Message value, if specified */
if (lpFailureActions->lpRebootMsg != NULL)
{
/* If the Reboot Message is "" then we delete it */
if (*lpFailureActions->lpRebootMsg == 0)
{
DPRINT("Delete Reboot Message value\n");
RegDeleteValueW(hServiceKey, L"RebootMessage");
}
else
{
DPRINT("Setting Reboot Message value %S\n", lpFailureActions->lpRebootMsg);
RegSetValueExW(hServiceKey,
L"RebootMessage",
0,
REG_SZ,
(LPBYTE)lpFailureActions->lpRebootMsg,
(DWORD)((wcslen(lpFailureActions->lpRebootMsg) + 1) * sizeof(WCHAR)));
}
}
/* Modify the Failure Command value, if specified */
if (lpFailureActions->lpCommand != NULL)
{
/* If the FailureCommand string is an empty string, delete the value */
if (*lpFailureActions->lpCommand == 0)
{
DPRINT("Delete Failure Command value\n");
RegDeleteValueW(hServiceKey, L"FailureCommand");
}
else
{
DPRINT("Setting Failure Command value %S\n", lpFailureActions->lpCommand);
RegSetValueExW(hServiceKey,
L"FailureCommand",
0,
REG_SZ,
(LPBYTE)lpFailureActions->lpCommand,
(DWORD)((wcslen(lpFailureActions->lpCommand) + 1) * sizeof(WCHAR)));
}
}
}
done:
if (lpWriteBuffer != NULL)
HeapFree(GetProcessHeap(), 0, lpWriteBuffer);
if (lpReadBuffer != NULL)
HeapFree(GetProcessHeap(), 0, lpReadBuffer);
return dwError;
}
/* Function 37 */
DWORD
WINAPI
RChangeServiceConfig2W(
SC_RPC_HANDLE hService,
SC_RPC_CONFIG_INFOW Info)
{
DWORD dwError = ERROR_SUCCESS;
PSERVICE_HANDLE hSvc;
PSERVICE lpService = NULL;
HKEY hServiceKey = NULL;
ACCESS_MASK RequiredAccess = SERVICE_CHANGE_CONFIG;
DPRINT("RChangeServiceConfig2W() called\n");
DPRINT("dwInfoLevel = %lu\n", Info.dwInfoLevel);
if (ScmShutdown)
return ERROR_SHUTDOWN_IN_PROGRESS;
if ((Info.dwInfoLevel < SERVICE_CONFIG_DESCRIPTION) ||
(Info.dwInfoLevel > SERVICE_CONFIG_FAILURE_ACTIONS))
{
return ERROR_INVALID_LEVEL;
}
hSvc = ScmGetServiceFromHandle(hService);
if (hSvc == NULL)
{
DPRINT("Invalid service handle\n");
return ERROR_INVALID_HANDLE;
}
if (Info.dwInfoLevel == SERVICE_CONFIG_FAILURE_ACTIONS)
RequiredAccess |= SERVICE_START;
/* Check the access rights */
if (!RtlAreAllAccessesGranted(hSvc->Handle.DesiredAccess,
RequiredAccess))
{
DPRINT("Insufficient access rights! 0x%lx\n", hSvc->Handle.DesiredAccess);
return ERROR_ACCESS_DENIED;
}
if (Info.dwInfoLevel == SERVICE_CONFIG_FAILURE_ACTIONS)
{
/* FIXME: Check if the caller has the SE_SHUTDOWN_NAME privilege */
}
lpService = hSvc->ServiceEntry;
if (lpService == NULL)
{
DPRINT("lpService == NULL\n");
return ERROR_INVALID_HANDLE;
}
/* Failure actions can only be set for Win32 services, not for drivers */
if (Info.dwInfoLevel == SERVICE_CONFIG_FAILURE_ACTIONS)
{
if (lpService->Status.dwServiceType & SERVICE_DRIVER)
return ERROR_CANNOT_DETECT_DRIVER_FAILURE;
}
/* Lock the service database exclusively */
ScmLockDatabaseExclusive();
if (lpService->bDeleted)
{
DPRINT("Service has already been marked for delete\n");
dwError = ERROR_SERVICE_MARKED_FOR_DELETE;
goto done;
}
/* Open the service key */
dwError = ScmOpenServiceKey(lpService->szServiceName,
KEY_READ | KEY_SET_VALUE,
&hServiceKey);
if (dwError != ERROR_SUCCESS)
goto done;
if (Info.dwInfoLevel == SERVICE_CONFIG_DESCRIPTION)
{
LPSERVICE_DESCRIPTIONW lpServiceDescription = (LPSERVICE_DESCRIPTIONW)Info.psd;
/* Modify the service description, if specified */
if (lpServiceDescription != NULL &&
lpServiceDescription->lpDescription != NULL)
{
/* If the description is "" then we delete it */
if (*lpServiceDescription->lpDescription == 0)
{
DPRINT("Delete service description\n");
dwError = RegDeleteValueW(hServiceKey, L"Description");
if (dwError == ERROR_FILE_NOT_FOUND)
dwError = ERROR_SUCCESS;
}
else
{
DPRINT("Setting service description value %S\n", lpServiceDescription->lpDescription);
dwError = RegSetValueExW(hServiceKey,
L"Description",
0,
REG_SZ,
(LPBYTE)lpServiceDescription->lpDescription,
(DWORD)((wcslen(lpServiceDescription->lpDescription) + 1) * sizeof(WCHAR)));
}
}
else
{
dwError = ERROR_SUCCESS;
}
}
else if (Info.dwInfoLevel == SERVICE_CONFIG_FAILURE_ACTIONS)
{
dwError = ScmSetFailureActions(hServiceKey,
(LPSERVICE_FAILURE_ACTIONSW)Info.psfa);
}
done:
if (hServiceKey != NULL)
RegCloseKey(hServiceKey);
/* Unlock the service database */
ScmUnlockDatabase();
DPRINT("RChangeServiceConfig2W() done (Error %lu)\n", dwError);
return dwError;
}
/* Function 38 */
DWORD
WINAPI
RQueryServiceConfig2A(
SC_RPC_HANDLE hService,
DWORD dwInfoLevel,
LPBYTE lpBuffer,
DWORD cbBufSize,
LPBOUNDED_DWORD_8K pcbBytesNeeded)
{
DWORD dwError = ERROR_SUCCESS;
PSERVICE_HANDLE hSvc;
PSERVICE lpService = NULL;
HKEY hServiceKey = NULL;
DWORD dwRequiredSize = 0;
DWORD dwType = 0;
LPWSTR lpDescriptionW = NULL;
LPWSTR lpRebootMessageW = NULL;
LPWSTR lpFailureCommandW = NULL;
DPRINT("RQueryServiceConfig2A() called hService %p dwInfoLevel %u, lpBuffer %p cbBufSize %u pcbBytesNeeded %p\n",
hService, dwInfoLevel, lpBuffer, cbBufSize, pcbBytesNeeded);
if (!lpBuffer)
return ERROR_INVALID_ADDRESS;
if (ScmShutdown)
return ERROR_SHUTDOWN_IN_PROGRESS;
if ((dwInfoLevel < SERVICE_CONFIG_DESCRIPTION) ||
(dwInfoLevel > SERVICE_CONFIG_FAILURE_ACTIONS))
{
return ERROR_INVALID_LEVEL;
}
hSvc = ScmGetServiceFromHandle(hService);
if (hSvc == NULL)
{
DPRINT1("Invalid service handle\n");
return ERROR_INVALID_HANDLE;
}
if (!RtlAreAllAccessesGranted(hSvc->Handle.DesiredAccess,
SERVICE_QUERY_CONFIG))
{
DPRINT("Insufficient access rights! 0x%lx\n", hSvc->Handle.DesiredAccess);
return ERROR_ACCESS_DENIED;
}
lpService = hSvc->ServiceEntry;
if (lpService == NULL)
{
DPRINT("lpService == NULL\n");
return ERROR_INVALID_HANDLE;
}
/* Lock the service database shared */
ScmLockDatabaseShared();
dwError = ScmOpenServiceKey(lpService->lpServiceName,
KEY_READ,
&hServiceKey);
if (dwError != ERROR_SUCCESS)
goto done;
if (dwInfoLevel == SERVICE_CONFIG_DESCRIPTION)
{
LPSERVICE_DESCRIPTIONA lpServiceDescription = (LPSERVICE_DESCRIPTIONA)lpBuffer;
LPSTR lpStr;
dwError = ScmReadString(hServiceKey,
L"Description",
&lpDescriptionW);
if (dwError != ERROR_SUCCESS && dwError != ERROR_FILE_NOT_FOUND)
goto done;
*pcbBytesNeeded = sizeof(SERVICE_DESCRIPTIONA);
if (dwError == ERROR_SUCCESS)
*pcbBytesNeeded += (DWORD)((wcslen(lpDescriptionW) + 1) * sizeof(WCHAR));
if (cbBufSize < *pcbBytesNeeded)
{
dwError = ERROR_INSUFFICIENT_BUFFER;
goto done;
}
if (dwError == ERROR_SUCCESS)
{
lpStr = (LPSTR)(lpServiceDescription + 1);
WideCharToMultiByte(CP_ACP,
0,
lpDescriptionW,
-1,
lpStr,
(int)wcslen(lpDescriptionW),
NULL,
NULL);
lpServiceDescription->lpDescription = (LPSTR)((ULONG_PTR)lpStr - (ULONG_PTR)lpServiceDescription);
}
else
{
lpServiceDescription->lpDescription = NULL;
dwError = ERROR_SUCCESS;
}
}
else if (dwInfoLevel == SERVICE_CONFIG_FAILURE_ACTIONS)
{
LPSERVICE_FAILURE_ACTIONSA lpFailureActions = (LPSERVICE_FAILURE_ACTIONSA)lpBuffer;
LPSTR lpStr = NULL;
/* Query value length */
dwError = RegQueryValueExW(hServiceKey,
L"FailureActions",
NULL,
&dwType,
NULL,
&dwRequiredSize);
if (dwError != ERROR_SUCCESS &&
dwError != ERROR_MORE_DATA &&
dwError != ERROR_FILE_NOT_FOUND)
{
goto done;
}
dwRequiredSize = (dwType == REG_BINARY) ? max(sizeof(SERVICE_FAILURE_ACTIONSA), dwRequiredSize)
: sizeof(SERVICE_FAILURE_ACTIONSA);
/* Get the strings */
ScmReadString(hServiceKey,
L"FailureCommand",
&lpFailureCommandW);
ScmReadString(hServiceKey,
L"RebootMessage",
&lpRebootMessageW);
if (lpRebootMessageW)
dwRequiredSize += (DWORD)((wcslen(lpRebootMessageW) + 1) * sizeof(WCHAR));
if (lpFailureCommandW)
dwRequiredSize += (DWORD)((wcslen(lpFailureCommandW) + 1) * sizeof(WCHAR));
if (cbBufSize < dwRequiredSize)
{
*pcbBytesNeeded = dwRequiredSize;
dwError = ERROR_INSUFFICIENT_BUFFER;
goto done;
}
/* Now we can fill the buffer */
if (dwError != ERROR_FILE_NOT_FOUND && dwType == REG_BINARY)
{
dwError = RegQueryValueExW(hServiceKey,
L"FailureActions",
NULL,
NULL,
(LPBYTE)lpFailureActions,
&dwRequiredSize);
if (dwError != ERROR_SUCCESS && dwError != ERROR_FILE_NOT_FOUND)
goto done;
if (dwRequiredSize < sizeof(SERVICE_FAILURE_ACTIONSA))
dwRequiredSize = sizeof(SERVICE_FAILURE_ACTIONSA);
}
else
{
/*
* The value of the error doesn't really matter, the only
* important thing is that it must be != ERROR_SUCCESS .
*/
dwError = ERROR_INVALID_DATA;
}
if (dwError == ERROR_SUCCESS)
{
lpFailureActions->cActions = min(lpFailureActions->cActions, (dwRequiredSize - sizeof(SERVICE_FAILURE_ACTIONSA)) / sizeof(SC_ACTION));
/* Here lpFailureActions->lpsaActions contains an offset. The conversion is done by the caller. */
lpFailureActions->lpsaActions = (lpFailureActions->cActions > 0 ? (LPSC_ACTION)(ULONG_PTR)sizeof(SERVICE_FAILURE_ACTIONSA) : NULL);
lpStr = (LPSTR)((ULONG_PTR)(lpFailureActions + 1) + lpFailureActions->cActions * sizeof(SC_ACTION));
}
else
{
lpFailureActions->dwResetPeriod = 0;
lpFailureActions->cActions = 0;
lpFailureActions->lpsaActions = NULL;
lpStr = (LPSTR)(lpFailureActions + 1);
}
lpFailureActions->lpRebootMsg = NULL;
lpFailureActions->lpCommand = NULL;
if (lpRebootMessageW)
{
WideCharToMultiByte(CP_ACP,
0,
lpRebootMessageW,
-1,
lpStr,
(int)wcslen(lpRebootMessageW),
NULL,
NULL);
lpFailureActions->lpRebootMsg = (LPSTR)((ULONG_PTR)lpStr - (ULONG_PTR)lpFailureActions);
lpStr += strlen(lpStr) + 1;
}
if (lpFailureCommandW)
{
WideCharToMultiByte(CP_ACP,
0,
lpFailureCommandW,
-1,
lpStr,
(int)wcslen(lpFailureCommandW),
NULL,
NULL);
lpFailureActions->lpCommand = (LPSTR)((ULONG_PTR)lpStr - (ULONG_PTR)lpFailureActions);
/* lpStr += strlen(lpStr) + 1; */
}
dwError = ERROR_SUCCESS;
}
done:
/* Unlock the service database */
ScmUnlockDatabase();
if (lpDescriptionW != NULL)
HeapFree(GetProcessHeap(), 0, lpDescriptionW);
if (lpRebootMessageW != NULL)
HeapFree(GetProcessHeap(), 0, lpRebootMessageW);
if (lpFailureCommandW != NULL)
HeapFree(GetProcessHeap(), 0, lpFailureCommandW);
if (hServiceKey != NULL)
RegCloseKey(hServiceKey);
DPRINT("RQueryServiceConfig2A() done (Error %lu)\n", dwError);
return dwError;
}
/* Function 39 */
DWORD
WINAPI
RQueryServiceConfig2W(
SC_RPC_HANDLE hService,
DWORD dwInfoLevel,
LPBYTE lpBuffer,
DWORD cbBufSize,
LPBOUNDED_DWORD_8K pcbBytesNeeded)
{
DWORD dwError = ERROR_SUCCESS;
PSERVICE_HANDLE hSvc;
PSERVICE lpService = NULL;
HKEY hServiceKey = NULL;
DWORD dwRequiredSize = 0;
DWORD dwType = 0;
LPWSTR lpDescription = NULL;
LPWSTR lpRebootMessage = NULL;
LPWSTR lpFailureCommand = NULL;
DPRINT("RQueryServiceConfig2W() called\n");
if (!lpBuffer)
return ERROR_INVALID_ADDRESS;
if (ScmShutdown)
return ERROR_SHUTDOWN_IN_PROGRESS;
if ((dwInfoLevel < SERVICE_CONFIG_DESCRIPTION) ||
(dwInfoLevel > SERVICE_CONFIG_FAILURE_ACTIONS))
{
return ERROR_INVALID_LEVEL;
}
hSvc = ScmGetServiceFromHandle(hService);
if (hSvc == NULL)
{
DPRINT1("Invalid service handle\n");
return ERROR_INVALID_HANDLE;
}
if (!RtlAreAllAccessesGranted(hSvc->Handle.DesiredAccess,
SERVICE_QUERY_CONFIG))
{
DPRINT("Insufficient access rights! 0x%lx\n", hSvc->Handle.DesiredAccess);
return ERROR_ACCESS_DENIED;
}
lpService = hSvc->ServiceEntry;
if (lpService == NULL)
{
DPRINT("lpService == NULL\n");
return ERROR_INVALID_HANDLE;
}
/* Lock the service database shared */
ScmLockDatabaseShared();
dwError = ScmOpenServiceKey(lpService->lpServiceName,
KEY_READ,
&hServiceKey);
if (dwError != ERROR_SUCCESS)
goto done;
if (dwInfoLevel == SERVICE_CONFIG_DESCRIPTION)
{
LPSERVICE_DESCRIPTIONW lpServiceDescription = (LPSERVICE_DESCRIPTIONW)lpBuffer;
LPWSTR lpStr;
dwError = ScmReadString(hServiceKey,
L"Description",
&lpDescription);
if (dwError != ERROR_SUCCESS && dwError != ERROR_FILE_NOT_FOUND)
goto done;
*pcbBytesNeeded = sizeof(SERVICE_DESCRIPTIONW);
if (dwError == ERROR_SUCCESS)
*pcbBytesNeeded += (DWORD)((wcslen(lpDescription) + 1) * sizeof(WCHAR));
if (cbBufSize < *pcbBytesNeeded)
{
dwError = ERROR_INSUFFICIENT_BUFFER;
goto done;
}
if (dwError == ERROR_SUCCESS)
{
lpStr = (LPWSTR)(lpServiceDescription + 1);
wcscpy(lpStr, lpDescription);
lpServiceDescription->lpDescription = (LPWSTR)((ULONG_PTR)lpStr - (ULONG_PTR)lpServiceDescription);
}
else
{
lpServiceDescription->lpDescription = NULL;
dwError = ERROR_SUCCESS;
}
}
else if (dwInfoLevel == SERVICE_CONFIG_FAILURE_ACTIONS)
{
LPSERVICE_FAILURE_ACTIONSW lpFailureActions = (LPSERVICE_FAILURE_ACTIONSW)lpBuffer;
LPWSTR lpStr = NULL;
/* Query value length */
dwError = RegQueryValueExW(hServiceKey,
L"FailureActions",
NULL,
&dwType,
NULL,
&dwRequiredSize);
if (dwError != ERROR_SUCCESS &&
dwError != ERROR_MORE_DATA &&
dwError != ERROR_FILE_NOT_FOUND)
{
goto done;
}
dwRequiredSize = (dwType == REG_BINARY) ? max(sizeof(SERVICE_FAILURE_ACTIONSW), dwRequiredSize)
: sizeof(SERVICE_FAILURE_ACTIONSW);
/* Get the strings */
ScmReadString(hServiceKey,
L"FailureCommand",
&lpFailureCommand);
ScmReadString(hServiceKey,
L"RebootMessage",
&lpRebootMessage);
if (lpRebootMessage)
dwRequiredSize += (DWORD)((wcslen(lpRebootMessage) + 1) * sizeof(WCHAR));
if (lpFailureCommand)
dwRequiredSize += (DWORD)((wcslen(lpFailureCommand) + 1) * sizeof(WCHAR));
if (cbBufSize < dwRequiredSize)
{
*pcbBytesNeeded = dwRequiredSize;
dwError = ERROR_INSUFFICIENT_BUFFER;
goto done;
}
/* Now we can fill the buffer */
if (dwError != ERROR_FILE_NOT_FOUND && dwType == REG_BINARY)
{
dwError = RegQueryValueExW(hServiceKey,
L"FailureActions",
NULL,
NULL,
(LPBYTE)lpFailureActions,
&dwRequiredSize);
if (dwError != ERROR_SUCCESS && dwError != ERROR_FILE_NOT_FOUND)
goto done;
if (dwRequiredSize < sizeof(SERVICE_FAILURE_ACTIONSW))
dwRequiredSize = sizeof(SERVICE_FAILURE_ACTIONSW);
}
else
{
/*
* The value of the error doesn't really matter, the only
* important thing is that it must be != ERROR_SUCCESS .
*/
dwError = ERROR_INVALID_DATA;
}
if (dwError == ERROR_SUCCESS)
{
lpFailureActions->cActions = min(lpFailureActions->cActions, (dwRequiredSize - sizeof(SERVICE_FAILURE_ACTIONSW)) / sizeof(SC_ACTION));
/* Here lpFailureActions->lpsaActions contains an offset. The conversion is done by the caller. */
lpFailureActions->lpsaActions = (lpFailureActions->cActions > 0 ? (LPSC_ACTION)(ULONG_PTR)sizeof(SERVICE_FAILURE_ACTIONSW) : NULL);
lpStr = (LPWSTR)((ULONG_PTR)(lpFailureActions + 1) + lpFailureActions->cActions * sizeof(SC_ACTION));
}
else
{
lpFailureActions->dwResetPeriod = 0;
lpFailureActions->cActions = 0;
lpFailureActions->lpsaActions = NULL;
lpStr = (LPWSTR)(lpFailureActions + 1);
}
lpFailureActions->lpRebootMsg = NULL;
lpFailureActions->lpCommand = NULL;
if (lpRebootMessage)
{
wcscpy(lpStr, lpRebootMessage);
lpFailureActions->lpRebootMsg = (LPWSTR)((ULONG_PTR)lpStr - (ULONG_PTR)lpFailureActions);
lpStr += wcslen(lpStr) + 1;
}
if (lpFailureCommand)
{
wcscpy(lpStr, lpFailureCommand);
lpFailureActions->lpCommand = (LPWSTR)((ULONG_PTR)lpStr - (ULONG_PTR)lpFailureActions);
/* lpStr += wcslen(lpStr) + 1; */
}
dwError = ERROR_SUCCESS;
}
done:
/* Unlock the service database */
ScmUnlockDatabase();
if (lpDescription != NULL)
HeapFree(GetProcessHeap(), 0, lpDescription);
if (lpRebootMessage != NULL)
HeapFree(GetProcessHeap(), 0, lpRebootMessage);
if (lpFailureCommand != NULL)
HeapFree(GetProcessHeap(), 0, lpFailureCommand);
if (hServiceKey != NULL)
RegCloseKey(hServiceKey);
DPRINT("RQueryServiceConfig2W() done (Error %lu)\n", dwError);
return dwError;
}
/* Function 40 */
DWORD
WINAPI
RQueryServiceStatusEx(
SC_RPC_HANDLE hService,
SC_STATUS_TYPE InfoLevel,
LPBYTE lpBuffer,
DWORD cbBufSize,
LPBOUNDED_DWORD_8K pcbBytesNeeded)
{
LPSERVICE_STATUS_PROCESS lpStatus;
PSERVICE_HANDLE hSvc;
PSERVICE lpService;
DPRINT("RQueryServiceStatusEx() called\n");
if (ScmShutdown)
return ERROR_SHUTDOWN_IN_PROGRESS;
if (InfoLevel != SC_STATUS_PROCESS_INFO)
return ERROR_INVALID_LEVEL;
*pcbBytesNeeded = sizeof(SERVICE_STATUS_PROCESS);
if (cbBufSize < sizeof(SERVICE_STATUS_PROCESS))
return ERROR_INSUFFICIENT_BUFFER;
hSvc = ScmGetServiceFromHandle(hService);
if (hSvc == NULL)
{
DPRINT1("Invalid service handle\n");
return ERROR_INVALID_HANDLE;
}
if (!RtlAreAllAccessesGranted(hSvc->Handle.DesiredAccess,
SERVICE_QUERY_STATUS))
{
DPRINT("Insufficient access rights! 0x%lx\n", hSvc->Handle.DesiredAccess);
return ERROR_ACCESS_DENIED;
}
lpService = hSvc->ServiceEntry;
if (lpService == NULL)
{
DPRINT("lpService == NULL\n");
return ERROR_INVALID_HANDLE;
}
/* Lock the service database shared */
ScmLockDatabaseShared();
lpStatus = (LPSERVICE_STATUS_PROCESS)lpBuffer;
/* Return service status information */
RtlCopyMemory(lpStatus,
&lpService->Status,
sizeof(SERVICE_STATUS));
/* Copy the service process ID */
if ((lpService->Status.dwCurrentState == SERVICE_STOPPED) || (lpService->lpImage == NULL))
lpStatus->dwProcessId = 0;
else
lpStatus->dwProcessId = lpService->lpImage->dwProcessId;
lpStatus->dwServiceFlags = 0; /* FIXME */
/* Unlock the service database */
ScmUnlockDatabase();
return ERROR_SUCCESS;
}
/* Function 41 */
DWORD
WINAPI
REnumServicesStatusExA(
SC_RPC_HANDLE hSCManager,
SC_ENUM_TYPE InfoLevel,
DWORD dwServiceType,
DWORD dwServiceState,
LPBYTE lpBuffer,
DWORD cbBufSize,
LPBOUNDED_DWORD_256K pcbBytesNeeded,
LPBOUNDED_DWORD_256K lpServicesReturned,
LPBOUNDED_DWORD_256K lpResumeIndex,
LPCSTR pszGroupName)
{
LPENUM_SERVICE_STATUS_PROCESSW lpStatusPtrW = NULL;
LPENUM_SERVICE_STATUS_PROCESSW lpStatusPtrIncrW;
LPENUM_SERVICE_STATUS_PROCESSA lpStatusPtrA = NULL;
LPWSTR lpStringPtrW;
LPSTR lpStringPtrA;
LPWSTR pszGroupNameW = NULL;
DWORD dwError;
DWORD dwServiceCount;
DPRINT("REnumServicesStatusExA() called\n");
if (pcbBytesNeeded == NULL || lpServicesReturned == NULL)
{
return ERROR_INVALID_ADDRESS;
}
if (pszGroupName)
{
pszGroupNameW = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, (strlen(pszGroupName) + 1) * sizeof(WCHAR));
if (!pszGroupNameW)
{
DPRINT("Failed to allocate buffer\n");
dwError = ERROR_NOT_ENOUGH_MEMORY;
goto Done;
}
MultiByteToWideChar(CP_ACP,
0,
pszGroupName,
-1,
pszGroupNameW,
(int)(strlen(pszGroupName) + 1));
}
if ((cbBufSize > 0) && (lpBuffer))
{
lpStatusPtrW = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, cbBufSize);
if (!lpStatusPtrW)
{
DPRINT("Failed to allocate buffer\n");
dwError = ERROR_NOT_ENOUGH_MEMORY;
goto Done;
}
}
dwError = REnumServicesStatusExW(hSCManager,
InfoLevel,
dwServiceType,
dwServiceState,
(LPBYTE)lpStatusPtrW,
cbBufSize,
pcbBytesNeeded,
lpServicesReturned,
lpResumeIndex,
pszGroupNameW);
/* if no services were returned then we are Done */
if (*lpServicesReturned == 0)
goto Done;
lpStatusPtrIncrW = lpStatusPtrW;
lpStatusPtrA = (LPENUM_SERVICE_STATUS_PROCESSA)lpBuffer;
lpStringPtrA = (LPSTR)((ULONG_PTR)lpBuffer +
*lpServicesReturned * sizeof(ENUM_SERVICE_STATUS_PROCESSA));
lpStringPtrW = (LPWSTR)((ULONG_PTR)lpStatusPtrW +
*lpServicesReturned * sizeof(ENUM_SERVICE_STATUS_PROCESSW));
for (dwServiceCount = 0; dwServiceCount < *lpServicesReturned; dwServiceCount++)
{
/* Copy the service name */
WideCharToMultiByte(CP_ACP,
0,
lpStringPtrW,
-1,
lpStringPtrA,
(int)wcslen(lpStringPtrW),
0,
0);
lpStatusPtrA->lpServiceName = (LPSTR)((ULONG_PTR)lpStringPtrA - (ULONG_PTR)lpBuffer);
lpStringPtrA += wcslen(lpStringPtrW) + 1;
lpStringPtrW += wcslen(lpStringPtrW) + 1;
/* Copy the display name */
WideCharToMultiByte(CP_ACP,
0,
lpStringPtrW,
-1,
lpStringPtrA,
(int)wcslen(lpStringPtrW),
0,
0);
lpStatusPtrA->lpDisplayName = (LPSTR)((ULONG_PTR)lpStringPtrA - (ULONG_PTR)lpBuffer);
lpStringPtrA += wcslen(lpStringPtrW) + 1;
lpStringPtrW += wcslen(lpStringPtrW) + 1;
/* Copy the status information */
memcpy(&lpStatusPtrA->ServiceStatusProcess,
&lpStatusPtrIncrW->ServiceStatusProcess,
sizeof(SERVICE_STATUS));
/* Copy the service process ID */
lpStatusPtrA->ServiceStatusProcess.dwProcessId = lpStatusPtrIncrW->ServiceStatusProcess.dwProcessId;
lpStatusPtrA->ServiceStatusProcess.dwServiceFlags = 0; /* FIXME */
lpStatusPtrIncrW++;
lpStatusPtrA++;
}
Done:
if (pszGroupNameW)
HeapFree(GetProcessHeap(), 0, pszGroupNameW);
if (lpStatusPtrW)
HeapFree(GetProcessHeap(), 0, lpStatusPtrW);
DPRINT("REnumServicesStatusExA() done (Error %lu)\n", dwError);
return dwError;
}
/* Function 42 */
DWORD
WINAPI
REnumServicesStatusExW(
SC_RPC_HANDLE hSCManager,
SC_ENUM_TYPE InfoLevel,
DWORD dwServiceType,
DWORD dwServiceState,
LPBYTE lpBuffer,
DWORD cbBufSize,
LPBOUNDED_DWORD_256K pcbBytesNeeded,
LPBOUNDED_DWORD_256K lpServicesReturned,
LPBOUNDED_DWORD_256K lpResumeIndex,
LPCWSTR pszGroupName)
{
PMANAGER_HANDLE hManager;
PSERVICE lpService;
DWORD dwError = ERROR_SUCCESS;
PLIST_ENTRY ServiceEntry;
PSERVICE CurrentService;
DWORD dwState;
DWORD dwRequiredSize;
DWORD dwServiceCount;
DWORD dwSize;
DWORD dwLastResumeCount = 0;
LPENUM_SERVICE_STATUS_PROCESSW lpStatusPtr;
LPWSTR lpStringPtr;
DPRINT("REnumServicesStatusExW() called\n");
if (ScmShutdown)
return ERROR_SHUTDOWN_IN_PROGRESS;
if (InfoLevel != SC_ENUM_PROCESS_INFO)
return ERROR_INVALID_LEVEL;
hManager = ScmGetServiceManagerFromHandle(hSCManager);
if (hManager == NULL)
{
DPRINT1("Invalid service manager handle\n");
return ERROR_INVALID_HANDLE;
}
if (pcbBytesNeeded == NULL || lpServicesReturned == NULL)
{
return ERROR_INVALID_ADDRESS;
}
*pcbBytesNeeded = 0;
*lpServicesReturned = 0;
if ((dwServiceType == 0) ||
((dwServiceType & ~SERVICE_TYPE_ALL) != 0))
{
DPRINT("Invalid Service Type\n");
return ERROR_INVALID_PARAMETER;
}
if ((dwServiceState == 0) ||
((dwServiceState & ~SERVICE_STATE_ALL) != 0))
{
DPRINT("Invalid Service State\n");
return ERROR_INVALID_PARAMETER;
}
/* Check access rights */
if (!RtlAreAllAccessesGranted(hManager->Handle.DesiredAccess,
SC_MANAGER_ENUMERATE_SERVICE))
{
DPRINT("Insufficient access rights 0x%lx\n",
hManager->Handle.DesiredAccess);
return ERROR_ACCESS_DENIED;
}
if (lpResumeIndex)
dwLastResumeCount = *lpResumeIndex;
/* Lock the service database shared */
ScmLockDatabaseShared();
lpService = ScmGetServiceEntryByResumeCount(dwLastResumeCount);
if (lpService == NULL)
{
dwError = ERROR_SUCCESS;
goto Done;
}
dwRequiredSize = 0;
dwServiceCount = 0;
for (ServiceEntry = &lpService->ServiceListEntry;
ServiceEntry != &ServiceListHead;
ServiceEntry = ServiceEntry->Flink)
{
CurrentService = CONTAINING_RECORD(ServiceEntry,
SERVICE,
ServiceListEntry);
if ((CurrentService->Status.dwServiceType & dwServiceType) == 0)
continue;
dwState = SERVICE_ACTIVE;
if (CurrentService->Status.dwCurrentState == SERVICE_STOPPED)
dwState = SERVICE_INACTIVE;
if ((dwState & dwServiceState) == 0)
continue;
if (pszGroupName)
{
if (*pszGroupName == 0)
{
if (CurrentService->lpGroup != NULL)
continue;
}
else
{
if ((CurrentService->lpGroup == NULL) ||
_wcsicmp(pszGroupName, CurrentService->lpGroup->lpGroupName) != 0)
continue;
}
}
dwSize = sizeof(ENUM_SERVICE_STATUS_PROCESSW) +
(DWORD)((wcslen(CurrentService->lpServiceName) + 1) * sizeof(WCHAR)) +
(DWORD)((wcslen(CurrentService->lpDisplayName) + 1) * sizeof(WCHAR));
if (dwRequiredSize + dwSize <= cbBufSize)
{
DPRINT("Service name: %S fit\n", CurrentService->lpServiceName);
dwRequiredSize += dwSize;
dwServiceCount++;
dwLastResumeCount = CurrentService->dwResumeCount;
}
else
{
DPRINT("Service name: %S no fit\n", CurrentService->lpServiceName);
break;
}
}
DPRINT("dwRequiredSize: %lu\n", dwRequiredSize);
DPRINT("dwServiceCount: %lu\n", dwServiceCount);
for (;
ServiceEntry != &ServiceListHead;
ServiceEntry = ServiceEntry->Flink)
{
CurrentService = CONTAINING_RECORD(ServiceEntry,
SERVICE,
ServiceListEntry);
if ((CurrentService->Status.dwServiceType & dwServiceType) == 0)
continue;
dwState = SERVICE_ACTIVE;
if (CurrentService->Status.dwCurrentState == SERVICE_STOPPED)
dwState = SERVICE_INACTIVE;
if ((dwState & dwServiceState) == 0)
continue;
if (pszGroupName)
{
if (*pszGroupName == 0)
{
if (CurrentService->lpGroup != NULL)
continue;
}
else
{
if ((CurrentService->lpGroup == NULL) ||
_wcsicmp(pszGroupName, CurrentService->lpGroup->lpGroupName) != 0)
continue;
}
}
dwRequiredSize += (sizeof(ENUM_SERVICE_STATUS_PROCESSW) +
(DWORD)((wcslen(CurrentService->lpServiceName) + 1) * sizeof(WCHAR)) +
(DWORD)((wcslen(CurrentService->lpDisplayName) + 1) * sizeof(WCHAR)));
dwError = ERROR_MORE_DATA;
}
DPRINT("*pcbBytesNeeded: %lu\n", dwRequiredSize);
if (lpResumeIndex)
*lpResumeIndex = dwLastResumeCount;
*lpServicesReturned = dwServiceCount;
*pcbBytesNeeded = dwRequiredSize;
/* If there was no services that matched */
if ((!dwServiceCount) && (dwError != ERROR_MORE_DATA))
{
dwError = ERROR_SERVICE_DOES_NOT_EXIST;
goto Done;
}
lpStatusPtr = (LPENUM_SERVICE_STATUS_PROCESSW)lpBuffer;
lpStringPtr = (LPWSTR)((ULONG_PTR)lpBuffer +
dwServiceCount * sizeof(ENUM_SERVICE_STATUS_PROCESSW));
dwRequiredSize = 0;
for (ServiceEntry = &lpService->ServiceListEntry;
ServiceEntry != &ServiceListHead;
ServiceEntry = ServiceEntry->Flink)
{
CurrentService = CONTAINING_RECORD(ServiceEntry,
SERVICE,
ServiceListEntry);
if ((CurrentService->Status.dwServiceType & dwServiceType) == 0)
continue;
dwState = SERVICE_ACTIVE;
if (CurrentService->Status.dwCurrentState == SERVICE_STOPPED)
dwState = SERVICE_INACTIVE;
if ((dwState & dwServiceState) == 0)
continue;
if (pszGroupName)
{
if (*pszGroupName == 0)
{
if (CurrentService->lpGroup != NULL)
continue;
}
else
{
if ((CurrentService->lpGroup == NULL) ||
_wcsicmp(pszGroupName, CurrentService->lpGroup->lpGroupName) != 0)
continue;
}
}
dwSize = sizeof(ENUM_SERVICE_STATUS_PROCESSW) +
(DWORD)((wcslen(CurrentService->lpServiceName) + 1) * sizeof(WCHAR)) +
(DWORD)((wcslen(CurrentService->lpDisplayName) + 1) * sizeof(WCHAR));
if (dwRequiredSize + dwSize <= cbBufSize)
{
/* Copy the service name */
wcscpy(lpStringPtr,
CurrentService->lpServiceName);
lpStatusPtr->lpServiceName = (LPWSTR)((ULONG_PTR)lpStringPtr - (ULONG_PTR)lpBuffer);
lpStringPtr += (wcslen(CurrentService->lpServiceName) + 1);
/* Copy the display name */
wcscpy(lpStringPtr,
CurrentService->lpDisplayName);
lpStatusPtr->lpDisplayName = (LPWSTR)((ULONG_PTR)lpStringPtr - (ULONG_PTR)lpBuffer);
lpStringPtr += (wcslen(CurrentService->lpDisplayName) + 1);
/* Copy the status information */
memcpy(&lpStatusPtr->ServiceStatusProcess,
&CurrentService->Status,
sizeof(SERVICE_STATUS));
/* Copy the service process ID */
if ((CurrentService->Status.dwCurrentState == SERVICE_STOPPED) || (CurrentService->lpImage == NULL))
lpStatusPtr->ServiceStatusProcess.dwProcessId = 0;
else
lpStatusPtr->ServiceStatusProcess.dwProcessId = CurrentService->lpImage->dwProcessId;
lpStatusPtr->ServiceStatusProcess.dwServiceFlags = 0; /* FIXME */
lpStatusPtr++;
dwRequiredSize += dwSize;
}
else
{
break;
}
}
if (dwError == 0)
{
*pcbBytesNeeded = 0;
if (lpResumeIndex)
*lpResumeIndex = 0;
}
Done:
/* Unlock the service database */
ScmUnlockDatabase();
DPRINT("REnumServicesStatusExW() done (Error %lu)\n", dwError);
return dwError;
}
/* Function 43 */
DWORD
WINAPI
RSendTSMessage(
handle_t BindingHandle) /* FIXME */
{
UNIMPLEMENTED;
return ERROR_CALL_NOT_IMPLEMENTED;
}
/* Function 44 */
DWORD
WINAPI
RCreateServiceWOW64A(
handle_t BindingHandle,
LPSTR lpServiceName,
LPSTR lpDisplayName,
DWORD dwDesiredAccess,
DWORD dwServiceType,
DWORD dwStartType,
DWORD dwErrorControl,
LPSTR lpBinaryPathName,
LPSTR lpLoadOrderGroup,
LPDWORD lpdwTagId,
LPBYTE lpDependencies,
DWORD dwDependSize,
LPSTR lpServiceStartName,
LPBYTE lpPassword,
DWORD dwPwSize,
LPSC_RPC_HANDLE lpServiceHandle)
{
UNIMPLEMENTED;
return ERROR_CALL_NOT_IMPLEMENTED;
}
/* Function 45 */
DWORD
WINAPI
RCreateServiceWOW64W(
handle_t BindingHandle,
LPWSTR lpServiceName,
LPWSTR lpDisplayName,
DWORD dwDesiredAccess,
DWORD dwServiceType,
DWORD dwStartType,
DWORD dwErrorControl,
LPWSTR lpBinaryPathName,
LPWSTR lpLoadOrderGroup,
LPDWORD lpdwTagId,
LPBYTE lpDependencies,
DWORD dwDependSize,
LPWSTR lpServiceStartName,
LPBYTE lpPassword,
DWORD dwPwSize,
LPSC_RPC_HANDLE lpServiceHandle)
{
UNIMPLEMENTED;
return ERROR_CALL_NOT_IMPLEMENTED;
}
/* Function 46 */
DWORD
WINAPI
RI_ScQueryServiceTagInfo(
SC_RPC_HANDLE hSCManager,
TAG_INFO_LEVEL dwInfoLevel,
PTAG_INFO_NAME_FROM_TAG_IN_PARAMS * lpInParams,
PTAG_INFO_NAME_FROM_TAG_OUT_PARAMS * lpOutParams)
{
PMANAGER_HANDLE hManager;
/* Validate handle */
hManager = ScmGetServiceManagerFromHandle(hSCManager);
if (hManager == NULL)
{
return ERROR_INVALID_HANDLE;
}
/* FIXME: should check whether client is local */
/* Check access rights */
if (!RtlAreAllAccessesGranted(hManager->Handle.DesiredAccess,
SC_MANAGER_ENUMERATE_SERVICE))
{
return ERROR_ACCESS_DENIED;
}
/* Check parameters */
if (lpInParams == NULL || lpOutParams == NULL)
{
return ERROR_INVALID_PARAMETER;
}
/* Check info level */
if (dwInfoLevel != TagInfoLevelNameFromTag)
{
return ERROR_RETRY;
}
/* Call internal helper */
return ScmGetServiceNameFromTag(*lpInParams, lpOutParams);
}
/* Function 47 */
DWORD
WINAPI
RNotifyServiceStatusChange(
SC_RPC_HANDLE hService,
SC_RPC_NOTIFY_PARAMS NotifyParams,
GUID *pClientProcessGuid,
GUID *pSCMProcessGuid,
PBOOL pfCreateRemoteQueue,
LPSC_NOTIFY_RPC_HANDLE phNotify)
{
UNIMPLEMENTED;
return ERROR_CALL_NOT_IMPLEMENTED;
}
/* Function 48 */
DWORD
WINAPI
RGetNotifyResults(
SC_NOTIFY_RPC_HANDLE hNotify,
PSC_RPC_NOTIFY_PARAMS_LIST *ppNotifyParams)
{
UNIMPLEMENTED;
return ERROR_CALL_NOT_IMPLEMENTED;
}
/* Function 49 */
DWORD
WINAPI
RCloseNotifyHandle(
LPSC_NOTIFY_RPC_HANDLE phNotify,
PBOOL pfApcFired)
{
UNIMPLEMENTED;
return ERROR_CALL_NOT_IMPLEMENTED;
}
/* Function 50 */
DWORD
WINAPI
RControlServiceExA(
SC_RPC_HANDLE hService,
DWORD dwControl,
DWORD dwInfoLevel)
{
UNIMPLEMENTED;
return ERROR_CALL_NOT_IMPLEMENTED;
}
/* Function 51 */
DWORD
WINAPI
RControlServiceExW(
SC_RPC_HANDLE hService,
DWORD dwControl,
DWORD dwInfoLevel)
{
UNIMPLEMENTED;
return ERROR_CALL_NOT_IMPLEMENTED;
}
/* Function 52 */
DWORD
WINAPI
RSendPnPMessage(
handle_t BindingHandle) /* FIXME */
{
UNIMPLEMENTED;
return ERROR_CALL_NOT_IMPLEMENTED;
}
/* Function 53 */
DWORD
WINAPI
RI_ScValidatePnPService(
_In_ SC_RPC_HANDLE hSCManager,
_In_ LPWSTR pszServiceName,
_Out_ RPC_SERVICE_STATUS_HANDLE *phServiceStatus)
{
PMANAGER_HANDLE hManager;
PSERVICE pService;
DPRINT("RI_ScValidatePnPService(%p %S %p)\n", hSCManager, pszServiceName, phServiceStatus);
/* Validate handle */
hManager = ScmGetServiceManagerFromHandle(hSCManager);
if (hManager == NULL)
{
DPRINT1("Invalid handle\n");
return ERROR_INVALID_HANDLE;
}
/* FIXME: should check whether client is local */
/* Check access rights */
if (!RtlAreAllAccessesGranted(hManager->Handle.DesiredAccess,
SC_MANAGER_CONNECT))
{
DPRINT1("No SC_MANAGER_CONNECT access\n");
return ERROR_ACCESS_DENIED;
}
pService = ScmGetServiceEntryByName(pszServiceName);
DPRINT("pService: %p\n", pService);
if (pService == NULL)
return ERROR_SERVICE_DOES_NOT_EXIST;
*phServiceStatus = (RPC_SERVICE_STATUS_HANDLE)pService;
return ERROR_SUCCESS;
}
/* Function 54 */
DWORD
WINAPI
ROpenServiceStatusHandle(
handle_t BindingHandle) /* FIXME */
{
UNIMPLEMENTED;
return ERROR_CALL_NOT_IMPLEMENTED;
}
/* Function 55 */
DWORD
WINAPI
RFunction55(
handle_t BindingHandle) /* FIXME */
{
UNIMPLEMENTED;
return ERROR_CALL_NOT_IMPLEMENTED;
}
void __RPC_FAR * __RPC_USER midl_user_allocate(SIZE_T len)
{
return HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, len);
}
void __RPC_USER midl_user_free(void __RPC_FAR * ptr)
{
HeapFree(GetProcessHeap(), 0, ptr);
}
void __RPC_USER SC_RPC_HANDLE_rundown(SC_RPC_HANDLE hSCObject)
{
/* Close the handle */
RCloseServiceHandle(&hSCObject);
}
void __RPC_USER SC_RPC_LOCK_rundown(SC_RPC_LOCK Lock)
{
/* Unlock the database */
RUnlockServiceDatabase(&Lock);
}
void __RPC_USER SC_NOTIFY_RPC_HANDLE_rundown(SC_NOTIFY_RPC_HANDLE hNotify)
{
}
/* EOF */