reactos/reactos/base/system/services/database.c

1995 lines
58 KiB
C
Raw Normal View History

/*
* PROJECT: ReactOS Service Control Manager
* LICENSE: GPL - See COPYING in the top level directory
* FILE: base/system/services/database.c
* PURPOSE: Database control interface
* COPYRIGHT: Copyright 2002-2006 Eric Kohl
* Copyright 2006 Herv<EFBFBD> Poussineau <hpoussin@reactos.org>
* Copyright 2007 Ged Murphy <gedmurphy@reactos.org>
* Gregor Brunmar <gregor.brunmar@home.se>
*
*/
/* INCLUDES *****************************************************************/
#include "services.h"
#include <winuser.h>
#define NDEBUG
#include <debug.h>
/*
* Uncomment the line below to start services
* using the SERVICE_START_PENDING state.
*/
#define USE_SERVICE_START_PENDING
/*
* Uncomment the line below to use asynchronous IO operations
* on the service control pipes.
*/
#define USE_ASYNCHRONOUS_IO
/* GLOBALS *******************************************************************/
LIST_ENTRY ImageListHead;
LIST_ENTRY ServiceListHead;
static RTL_RESOURCE DatabaseLock;
static DWORD ResumeCount = 1;
/* The critical section synchronizes service control requests */
static CRITICAL_SECTION ControlServiceCriticalSection;
static DWORD PipeTimeout = 30000; /* 30 Seconds */
/* FUNCTIONS *****************************************************************/
static DWORD
ScmCreateNewControlPipe(PSERVICE_IMAGE pServiceImage)
{
WCHAR szControlPipeName[MAX_PATH + 1];
HKEY hServiceCurrentKey = INVALID_HANDLE_VALUE;
DWORD ServiceCurrent = 0;
DWORD KeyDisposition;
DWORD dwKeySize;
DWORD dwError;
/* Get the service number */
/* TODO: Create registry entry with correct write access */
dwError = RegCreateKeyExW(HKEY_LOCAL_MACHINE,
L"SYSTEM\\CurrentControlSet\\Control\\ServiceCurrent", 0, NULL,
REG_OPTION_VOLATILE,
KEY_WRITE | KEY_READ,
NULL,
&hServiceCurrentKey,
&KeyDisposition);
if (dwError != ERROR_SUCCESS)
{
DPRINT1("RegCreateKeyEx() failed with error %lu\n", dwError);
return dwError;
}
if (KeyDisposition == REG_OPENED_EXISTING_KEY)
{
dwKeySize = sizeof(DWORD);
dwError = RegQueryValueExW(hServiceCurrentKey,
L"", 0, NULL, (BYTE*)&ServiceCurrent, &dwKeySize);
if (dwError != ERROR_SUCCESS)
{
RegCloseKey(hServiceCurrentKey);
DPRINT1("RegQueryValueEx() failed with error %lu\n", dwError);
return dwError;
}
ServiceCurrent++;
}
dwError = RegSetValueExW(hServiceCurrentKey, L"", 0, REG_DWORD, (BYTE*)&ServiceCurrent, sizeof(ServiceCurrent));
RegCloseKey(hServiceCurrentKey);
if (dwError != ERROR_SUCCESS)
{
DPRINT1("RegSetValueExW() failed (Error %lu)\n", dwError);
return dwError;
}
/* Create '\\.\pipe\net\NtControlPipeXXX' instance */
swprintf(szControlPipeName, L"\\\\.\\pipe\\net\\NtControlPipe%lu", ServiceCurrent);
DPRINT("PipeName: %S\n", szControlPipeName);
pServiceImage->hControlPipe = CreateNamedPipeW(szControlPipeName,
#ifdef USE_ASYNCHRONOUS_IO
PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
#else
PIPE_ACCESS_DUPLEX,
#endif
PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT,
100,
8000,
4,
PipeTimeout,
NULL);
DPRINT("CreateNamedPipeW(%S) done\n", szControlPipeName);
if (pServiceImage->hControlPipe == INVALID_HANDLE_VALUE)
{
DPRINT1("Failed to create control pipe!\n");
return GetLastError();
}
return ERROR_SUCCESS;
}
static PSERVICE_IMAGE
ScmGetServiceImageByImagePath(LPWSTR lpImagePath)
{
PLIST_ENTRY ImageEntry;
PSERVICE_IMAGE CurrentImage;
DPRINT("ScmGetServiceImageByImagePath(%S) called\n", lpImagePath);
ImageEntry = ImageListHead.Flink;
while (ImageEntry != &ImageListHead)
{
CurrentImage = CONTAINING_RECORD(ImageEntry,
SERVICE_IMAGE,
ImageListEntry);
if (_wcsicmp(CurrentImage->szImagePath, lpImagePath) == 0)
{
DPRINT("Found image: '%S'\n", CurrentImage->szImagePath);
return CurrentImage;
}
ImageEntry = ImageEntry->Flink;
}
DPRINT("Couldn't find a matching image\n");
return NULL;
}
static DWORD
ScmCreateOrReferenceServiceImage(PSERVICE pService)
{
RTL_QUERY_REGISTRY_TABLE QueryTable[2];
UNICODE_STRING ImagePath;
PSERVICE_IMAGE pServiceImage = NULL;
NTSTATUS Status;
DWORD dwError = ERROR_SUCCESS;
DPRINT("ScmCreateOrReferenceServiceImage(%p)\n", pService);
RtlInitUnicodeString(&ImagePath, NULL);
/* Get service data */
RtlZeroMemory(&QueryTable,
sizeof(QueryTable));
QueryTable[0].Name = L"ImagePath";
QueryTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_REQUIRED;
QueryTable[0].EntryContext = &ImagePath;
Status = RtlQueryRegistryValues(RTL_REGISTRY_SERVICES,
pService->lpServiceName,
QueryTable,
NULL,
NULL);
if (!NT_SUCCESS(Status))
{
DPRINT1("RtlQueryRegistryValues() failed (Status %lx)\n", Status);
return RtlNtStatusToDosError(Status);
}
DPRINT("ImagePath: '%wZ'\n", &ImagePath);
pServiceImage = ScmGetServiceImageByImagePath(ImagePath.Buffer);
if (pServiceImage == NULL)
{
/* Create a new service image */
pServiceImage = HeapAlloc(GetProcessHeap(),
HEAP_ZERO_MEMORY,
FIELD_OFFSET(SERVICE_IMAGE, szImagePath[ImagePath.Length / sizeof(WCHAR) + 1]));
if (pServiceImage == NULL)
{
dwError = ERROR_NOT_ENOUGH_MEMORY;
goto done;
}
pServiceImage->dwImageRunCount = 1;
pServiceImage->hControlPipe = INVALID_HANDLE_VALUE;
/* Set the image path */
wcscpy(pServiceImage->szImagePath,
ImagePath.Buffer);
RtlFreeUnicodeString(&ImagePath);
/* Create the control pipe */
dwError = ScmCreateNewControlPipe(pServiceImage);
if (dwError != ERROR_SUCCESS)
{
HeapFree(GetProcessHeap(), 0, pServiceImage);
goto done;
}
/* FIXME: Add more initialization code here */
/* Append service record */
InsertTailList(&ImageListHead,
&pServiceImage->ImageListEntry);
}
else
{
/* Increment the run counter */
pServiceImage->dwImageRunCount++;
}
DPRINT("pServiceImage->dwImageRunCount: %lu\n", pServiceImage->dwImageRunCount);
/* Link the service image to the service */
pService->lpImage = pServiceImage;
done:;
RtlFreeUnicodeString(&ImagePath);
DPRINT("ScmCreateOrReferenceServiceImage() done (Error: %lu)\n", dwError);
return dwError;
}
static VOID
ScmDereferenceServiceImage(PSERVICE_IMAGE pServiceImage)
{
DPRINT1("ScmDereferenceServiceImage() called\n");
pServiceImage->dwImageRunCount--;
if (pServiceImage->dwImageRunCount == 0)
{
DPRINT1("dwImageRunCount == 0\n");
/* FIXME: Terminate the process */
/* Remove the service image from the list */
RemoveEntryList(&pServiceImage->ImageListEntry);
/* Close the control pipe */
if (pServiceImage->hControlPipe != INVALID_HANDLE_VALUE)
CloseHandle(pServiceImage->hControlPipe);
/* Release the service image */
HeapFree(GetProcessHeap(), 0, pServiceImage);
}
}
PSERVICE
ScmGetServiceEntryByName(LPCWSTR lpServiceName)
{
PLIST_ENTRY ServiceEntry;
PSERVICE CurrentService;
DPRINT("ScmGetServiceEntryByName() called\n");
ServiceEntry = ServiceListHead.Flink;
while (ServiceEntry != &ServiceListHead)
{
CurrentService = CONTAINING_RECORD(ServiceEntry,
SERVICE,
ServiceListEntry);
if (_wcsicmp(CurrentService->lpServiceName, lpServiceName) == 0)
{
DPRINT("Found service: '%S'\n", CurrentService->lpServiceName);
return CurrentService;
}
ServiceEntry = ServiceEntry->Flink;
}
DPRINT("Couldn't find a matching service\n");
return NULL;
}
PSERVICE
ScmGetServiceEntryByDisplayName(LPCWSTR lpDisplayName)
{
PLIST_ENTRY ServiceEntry;
PSERVICE CurrentService;
DPRINT("ScmGetServiceEntryByDisplayName() called\n");
ServiceEntry = ServiceListHead.Flink;
while (ServiceEntry != &ServiceListHead)
{
CurrentService = CONTAINING_RECORD(ServiceEntry,
SERVICE,
ServiceListEntry);
if (_wcsicmp(CurrentService->lpDisplayName, lpDisplayName) == 0)
{
DPRINT("Found service: '%S'\n", CurrentService->lpDisplayName);
return CurrentService;
}
ServiceEntry = ServiceEntry->Flink;
}
DPRINT("Couldn't find a matching service\n");
return NULL;
}
PSERVICE
ScmGetServiceEntryByResumeCount(DWORD dwResumeCount)
{
PLIST_ENTRY ServiceEntry;
PSERVICE CurrentService;
DPRINT("ScmGetServiceEntryByResumeCount() called\n");
ServiceEntry = ServiceListHead.Flink;
while (ServiceEntry != &ServiceListHead)
{
CurrentService = CONTAINING_RECORD(ServiceEntry,
SERVICE,
ServiceListEntry);
if (CurrentService->dwResumeCount > dwResumeCount)
{
DPRINT("Found service: '%S'\n", CurrentService->lpDisplayName);
return CurrentService;
}
ServiceEntry = ServiceEntry->Flink;
}
DPRINT("Couldn't find a matching service\n");
return NULL;
}
DWORD
ScmCreateNewServiceRecord(LPCWSTR lpServiceName,
PSERVICE* lpServiceRecord)
{
PSERVICE lpService = NULL;
DPRINT("Service: '%S'\n", lpServiceName);
/* Allocate service entry */
lpService = HeapAlloc(GetProcessHeap(),
HEAP_ZERO_MEMORY,
FIELD_OFFSET(SERVICE, szServiceName[wcslen(lpServiceName) + 1]));
if (lpService == NULL)
return ERROR_NOT_ENOUGH_MEMORY;
*lpServiceRecord = lpService;
/* Copy service name */
wcscpy(lpService->szServiceName, lpServiceName);
lpService->lpServiceName = lpService->szServiceName;
lpService->lpDisplayName = lpService->lpServiceName;
/* Set the resume count */
lpService->dwResumeCount = ResumeCount++;
/* Append service record */
InsertTailList(&ServiceListHead,
&lpService->ServiceListEntry);
/* Initialize the service status */
lpService->Status.dwCurrentState = SERVICE_STOPPED;
lpService->Status.dwControlsAccepted = 0;
lpService->Status.dwWin32ExitCode = ERROR_SERVICE_NEVER_STARTED;
lpService->Status.dwServiceSpecificExitCode = 0;
lpService->Status.dwCheckPoint = 0;
lpService->Status.dwWaitHint = 2000; /* 2 seconds */
return ERROR_SUCCESS;
}
VOID
ScmDeleteServiceRecord(PSERVICE lpService)
{
DPRINT("Deleting Service %S\n", lpService->lpServiceName);
/* Delete the display name */
if (lpService->lpDisplayName != NULL &&
lpService->lpDisplayName != lpService->lpServiceName)
HeapFree(GetProcessHeap(), 0, lpService->lpDisplayName);
/* Dereference the service image */
if (lpService->lpImage)
ScmDereferenceServiceImage(lpService->lpImage);
/* Decrement the group reference counter */
if (lpService->lpGroup)
lpService->lpGroup->dwRefCount--;
/* FIXME: SecurityDescriptor */
/* Remove the Service from the List */
RemoveEntryList(&lpService->ServiceListEntry);
DPRINT("Deleted Service %S\n", lpService->lpServiceName);
/* Delete the service record */
HeapFree(GetProcessHeap(), 0, lpService);
DPRINT("Done\n");
}
static DWORD
CreateServiceListEntry(LPCWSTR lpServiceName,
HKEY hServiceKey)
{
PSERVICE lpService = NULL;
LPWSTR lpDisplayName = NULL;
LPWSTR lpGroup = NULL;
DWORD dwSize;
DWORD dwError;
DWORD dwServiceType;
DWORD dwStartType;
DWORD dwErrorControl;
DWORD dwTagId;
DPRINT("Service: '%S'\n", lpServiceName);
if (*lpServiceName == L'{')
return ERROR_SUCCESS;
dwSize = sizeof(DWORD);
dwError = RegQueryValueExW(hServiceKey,
L"Type",
NULL,
NULL,
(LPBYTE)&dwServiceType,
&dwSize);
if (dwError != ERROR_SUCCESS)
return ERROR_SUCCESS;
if (((dwServiceType & ~SERVICE_INTERACTIVE_PROCESS) != SERVICE_WIN32_OWN_PROCESS) &&
((dwServiceType & ~SERVICE_INTERACTIVE_PROCESS) != SERVICE_WIN32_SHARE_PROCESS) &&
(dwServiceType != SERVICE_KERNEL_DRIVER) &&
(dwServiceType != SERVICE_FILE_SYSTEM_DRIVER))
return ERROR_SUCCESS;
DPRINT("Service type: %lx\n", dwServiceType);
dwSize = sizeof(DWORD);
dwError = RegQueryValueExW(hServiceKey,
L"Start",
NULL,
NULL,
(LPBYTE)&dwStartType,
&dwSize);
if (dwError != ERROR_SUCCESS)
return ERROR_SUCCESS;
DPRINT("Start type: %lx\n", dwStartType);
dwSize = sizeof(DWORD);
dwError = RegQueryValueExW(hServiceKey,
L"ErrorControl",
NULL,
NULL,
(LPBYTE)&dwErrorControl,
&dwSize);
if (dwError != ERROR_SUCCESS)
return ERROR_SUCCESS;
DPRINT("Error control: %lx\n", dwErrorControl);
dwError = RegQueryValueExW(hServiceKey,
L"Tag",
NULL,
NULL,
(LPBYTE)&dwTagId,
&dwSize);
if (dwError != ERROR_SUCCESS)
dwTagId = 0;
DPRINT("Tag: %lx\n", dwTagId);
dwError = ScmReadString(hServiceKey,
L"Group",
&lpGroup);
if (dwError != ERROR_SUCCESS)
lpGroup = NULL;
DPRINT("Group: %S\n", lpGroup);
dwError = ScmReadString(hServiceKey,
L"DisplayName",
&lpDisplayName);
if (dwError != ERROR_SUCCESS)
lpDisplayName = NULL;
DPRINT("Display name: %S\n", lpDisplayName);
dwError = ScmCreateNewServiceRecord(lpServiceName,
&lpService);
if (dwError != ERROR_SUCCESS)
goto done;
lpService->Status.dwServiceType = dwServiceType;
lpService->dwStartType = dwStartType;
lpService->dwErrorControl = dwErrorControl;
lpService->dwTag = dwTagId;
if (lpGroup != NULL)
{
dwError = ScmSetServiceGroup(lpService, lpGroup);
if (dwError != ERROR_SUCCESS)
goto done;
}
if (lpDisplayName != NULL)
{
lpService->lpDisplayName = lpDisplayName;
lpDisplayName = NULL;
}
DPRINT("ServiceName: '%S'\n", lpService->lpServiceName);
if (lpService->lpGroup != NULL)
{
DPRINT("Group: '%S'\n", lpService->lpGroup->lpGroupName);
}
DPRINT("Start %lx Type %lx Tag %lx ErrorControl %lx\n",
lpService->dwStartType,
lpService->Status.dwServiceType,
lpService->dwTag,
lpService->dwErrorControl);
if (ScmIsDeleteFlagSet(hServiceKey))
lpService->bDeleted = TRUE;
done:;
if (lpGroup != NULL)
HeapFree(GetProcessHeap(), 0, lpGroup);
if (lpDisplayName != NULL)
HeapFree(GetProcessHeap(), 0, lpDisplayName);
if (lpService != NULL)
{
if (lpService->lpImage != NULL)
ScmDereferenceServiceImage(lpService->lpImage);
}
return dwError;
}
DWORD
ScmDeleteRegKey(HKEY hKey, LPCWSTR lpszSubKey)
{
DWORD dwRet, dwMaxSubkeyLen = 0, dwSize;
WCHAR szNameBuf[MAX_PATH], *lpszName = szNameBuf;
HKEY hSubKey = 0;
dwRet = RegOpenKeyExW(hKey, lpszSubKey, 0, KEY_READ, &hSubKey);
if (!dwRet)
{
/* Find the maximum subkey length so that we can allocate a buffer */
dwRet = RegQueryInfoKeyW(hSubKey, NULL, NULL, NULL, NULL,
&dwMaxSubkeyLen, NULL, NULL, NULL, NULL, NULL, NULL);
if (!dwRet)
{
dwMaxSubkeyLen++;
if (dwMaxSubkeyLen > sizeof(szNameBuf) / sizeof(WCHAR))
{
/* Name too big: alloc a buffer for it */
lpszName = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dwMaxSubkeyLen * sizeof(WCHAR));
}
if (!lpszName)
dwRet = ERROR_NOT_ENOUGH_MEMORY;
else
{
while (dwRet == ERROR_SUCCESS)
{
dwSize = dwMaxSubkeyLen;
dwRet = RegEnumKeyExW(hSubKey, 0, lpszName, &dwSize, NULL, NULL, NULL, NULL);
if (dwRet == ERROR_SUCCESS || dwRet == ERROR_MORE_DATA)
dwRet = ScmDeleteRegKey(hSubKey, lpszName);
}
if (dwRet == ERROR_NO_MORE_ITEMS)
dwRet = ERROR_SUCCESS;
if (lpszName != szNameBuf)
HeapFree(GetProcessHeap(), 0, lpszName); /* Free buffer if allocated */
}
}
RegCloseKey(hSubKey);
if (!dwRet)
dwRet = RegDeleteKeyW(hKey, lpszSubKey);
}
return dwRet;
}
VOID
ScmDeleteMarkedServices(VOID)
{
PLIST_ENTRY ServiceEntry;
PSERVICE CurrentService;
HKEY hServicesKey;
DWORD dwError;
ServiceEntry = ServiceListHead.Flink;
while (ServiceEntry != &ServiceListHead)
{
CurrentService = CONTAINING_RECORD(ServiceEntry, SERVICE, ServiceListEntry);
ServiceEntry = ServiceEntry->Flink;
if (CurrentService->bDeleted == TRUE)
{
dwError = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
L"System\\CurrentControlSet\\Services",
0,
DELETE,
&hServicesKey);
if (dwError == ERROR_SUCCESS)
{
dwError = ScmDeleteRegKey(hServicesKey, CurrentService->lpServiceName);
RegCloseKey(hServicesKey);
if (dwError == ERROR_SUCCESS)
{
RemoveEntryList(&CurrentService->ServiceListEntry);
HeapFree(GetProcessHeap(), 0, CurrentService);
}
}
if (dwError != ERROR_SUCCESS)
DPRINT1("Delete service failed: %S\n", CurrentService->lpServiceName);
}
}
}
DWORD
ScmCreateServiceDatabase(VOID)
{
WCHAR szSubKey[MAX_PATH];
HKEY hServicesKey;
HKEY hServiceKey;
DWORD dwSubKey;
DWORD dwSubKeyLength;
FILETIME ftLastChanged;
DWORD dwError;
DPRINT("ScmCreateServiceDatabase() called\n");
dwError = ScmCreateGroupList();
if (dwError != ERROR_SUCCESS)
return dwError;
/* Initialize basic variables */
InitializeListHead(&ImageListHead);
InitializeListHead(&ServiceListHead);
/* Initialize the database lock */
RtlInitializeResource(&DatabaseLock);
dwError = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
L"System\\CurrentControlSet\\Services",
0,
KEY_READ,
&hServicesKey);
if (dwError != ERROR_SUCCESS)
return dwError;
dwSubKey = 0;
for (;;)
{
dwSubKeyLength = MAX_PATH;
dwError = RegEnumKeyExW(hServicesKey,
dwSubKey,
szSubKey,
&dwSubKeyLength,
NULL,
NULL,
NULL,
&ftLastChanged);
if (dwError == ERROR_SUCCESS &&
szSubKey[0] != L'{')
{
DPRINT("SubKeyName: '%S'\n", szSubKey);
dwError = RegOpenKeyExW(hServicesKey,
szSubKey,
0,
KEY_READ,
&hServiceKey);
if (dwError == ERROR_SUCCESS)
{
dwError = CreateServiceListEntry(szSubKey,
hServiceKey);
RegCloseKey(hServiceKey);
}
}
if (dwError != ERROR_SUCCESS)
break;
dwSubKey++;
}
RegCloseKey(hServicesKey);
/* Wait for the LSA server */
ScmWaitForLsa();
/* Delete services that are marked for delete */
ScmDeleteMarkedServices();
DPRINT("ScmCreateServiceDatabase() done\n");
return ERROR_SUCCESS;
}
VOID
ScmShutdownServiceDatabase(VOID)
{
DPRINT("ScmShutdownServiceDatabase() called\n");
ScmDeleteMarkedServices();
RtlDeleteResource(&DatabaseLock);
DPRINT("ScmShutdownServiceDatabase() done\n");
}
static NTSTATUS
ScmCheckDriver(PSERVICE Service)
{
OBJECT_ATTRIBUTES ObjectAttributes;
UNICODE_STRING DirName;
HANDLE DirHandle;
NTSTATUS Status;
POBJECT_DIRECTORY_INFORMATION DirInfo;
ULONG BufferLength;
ULONG DataLength;
ULONG Index;
DPRINT("ScmCheckDriver(%S) called\n", Service->lpServiceName);
if (Service->Status.dwServiceType == SERVICE_KERNEL_DRIVER)
{
RtlInitUnicodeString(&DirName, L"\\Driver");
}
else // if (Service->Status.dwServiceType == SERVICE_FILE_SYSTEM_DRIVER)
{
ASSERT(Service->Status.dwServiceType == SERVICE_FILE_SYSTEM_DRIVER);
RtlInitUnicodeString(&DirName, L"\\FileSystem");
}
InitializeObjectAttributes(&ObjectAttributes,
&DirName,
0,
NULL,
NULL);
Status = NtOpenDirectoryObject(&DirHandle,
DIRECTORY_QUERY | DIRECTORY_TRAVERSE,
&ObjectAttributes);
if (!NT_SUCCESS(Status))
{
return Status;
}
BufferLength = sizeof(OBJECT_DIRECTORY_INFORMATION) +
2 * MAX_PATH * sizeof(WCHAR);
DirInfo = HeapAlloc(GetProcessHeap(),
HEAP_ZERO_MEMORY,
BufferLength);
Index = 0;
while (TRUE)
{
Status = NtQueryDirectoryObject(DirHandle,
DirInfo,
BufferLength,
TRUE,
FALSE,
&Index,
&DataLength);
if (Status == STATUS_NO_MORE_ENTRIES)
{
/* FIXME: Add current service to 'failed service' list */
DPRINT("Service '%S' failed\n", Service->lpServiceName);
break;
}
if (!NT_SUCCESS(Status))
break;
DPRINT("Comparing: '%S' '%wZ'\n", Service->lpServiceName, &DirInfo->Name);
if (_wcsicmp(Service->lpServiceName, DirInfo->Name.Buffer) == 0)
{
DPRINT("Found: '%S' '%wZ'\n",
Service->lpServiceName, &DirInfo->Name);
/* Mark service as 'running' */
Service->Status.dwCurrentState = SERVICE_RUNNING;
/* Mark the service group as 'running' */
if (Service->lpGroup != NULL)
{
Service->lpGroup->ServicesRunning = TRUE;
}
break;
}
}
HeapFree(GetProcessHeap(),
0,
DirInfo);
NtClose(DirHandle);
return STATUS_SUCCESS;
}
VOID
ScmGetBootAndSystemDriverState(VOID)
{
PLIST_ENTRY ServiceEntry;
PSERVICE CurrentService;
DPRINT("ScmGetBootAndSystemDriverState() called\n");
ServiceEntry = ServiceListHead.Flink;
while (ServiceEntry != &ServiceListHead)
{
CurrentService = CONTAINING_RECORD(ServiceEntry, SERVICE, ServiceListEntry);
if (CurrentService->dwStartType == SERVICE_BOOT_START ||
CurrentService->dwStartType == SERVICE_SYSTEM_START)
{
/* Check driver */
DPRINT(" Checking service: %S\n", CurrentService->lpServiceName);
ScmCheckDriver(CurrentService);
}
ServiceEntry = ServiceEntry->Flink;
}
DPRINT("ScmGetBootAndSystemDriverState() done\n");
}
DWORD
ScmControlService(PSERVICE Service,
DWORD dwControl)
{
PSCM_CONTROL_PACKET ControlPacket;
SCM_REPLY_PACKET ReplyPacket;
DWORD dwWriteCount = 0;
DWORD dwReadCount = 0;
DWORD PacketSize;
PWSTR Ptr;
DWORD dwError = ERROR_SUCCESS;
BOOL bResult;
#ifdef USE_ASYNCHRONOUS_IO
OVERLAPPED Overlapped = {0};
#endif
DPRINT("ScmControlService() called\n");
/* Acquire the service control critical section, to synchronize requests */
EnterCriticalSection(&ControlServiceCriticalSection);
/* Calculate the total length of the start command line */
PacketSize = sizeof(SCM_CONTROL_PACKET);
PacketSize += (DWORD)((wcslen(Service->lpServiceName) + 1) * sizeof(WCHAR));
ControlPacket = HeapAlloc(GetProcessHeap(),
HEAP_ZERO_MEMORY,
PacketSize);
if (ControlPacket == NULL)
{
LeaveCriticalSection(&ControlServiceCriticalSection);
return ERROR_NOT_ENOUGH_MEMORY;
}
ControlPacket->dwSize = PacketSize;
ControlPacket->dwControl = dwControl;
ControlPacket->hServiceStatus = (SERVICE_STATUS_HANDLE)Service;
ControlPacket->dwServiceNameOffset = sizeof(SCM_CONTROL_PACKET);
Ptr = (PWSTR)((PBYTE)ControlPacket + ControlPacket->dwServiceNameOffset);
wcscpy(Ptr, Service->lpServiceName);
ControlPacket->dwArgumentsCount = 0;
ControlPacket->dwArgumentsOffset = 0;
#ifdef USE_ASYNCHRONOUS_IO
bResult = WriteFile(Service->lpImage->hControlPipe,
ControlPacket,
PacketSize,
&dwWriteCount,
&Overlapped);
if (bResult == FALSE)
{
DPRINT1("WriteFile() returned FALSE\n");
dwError = GetLastError();
if (dwError == ERROR_IO_PENDING)
{
DPRINT1("dwError: ERROR_IO_PENDING\n");
dwError = WaitForSingleObject(Service->lpImage->hControlPipe,
PipeTimeout);
DPRINT1("WaitForSingleObject() returned %lu\n", dwError);
if (dwError == WAIT_TIMEOUT)
{
bResult = CancelIo(Service->lpImage->hControlPipe);
if (bResult == FALSE)
{
DPRINT1("CancelIo() failed (Error: %lu)\n", GetLastError());
}
dwError = ERROR_SERVICE_REQUEST_TIMEOUT;
goto Done;
}
else if (dwError == WAIT_OBJECT_0)
{
bResult = GetOverlappedResult(Service->lpImage->hControlPipe,
&Overlapped,
&dwWriteCount,
TRUE);
if (bResult == FALSE)
{
dwError = GetLastError();
DPRINT1("GetOverlappedResult() failed (Error %lu)\n", dwError);
goto Done;
}
}
}
else
{
DPRINT1("WriteFile() failed (Error %lu)\n", dwError);
goto Done;
}
}
/* Read the reply */
Overlapped.hEvent = (HANDLE) NULL;
bResult = ReadFile(Service->lpImage->hControlPipe,
&ReplyPacket,
sizeof(SCM_REPLY_PACKET),
&dwReadCount,
&Overlapped);
if (bResult == FALSE)
{
DPRINT1("ReadFile() returned FALSE\n");
dwError = GetLastError();
if (dwError == ERROR_IO_PENDING)
{
DPRINT1("dwError: ERROR_IO_PENDING\n");
dwError = WaitForSingleObject(Service->lpImage->hControlPipe,
PipeTimeout);
DPRINT1("WaitForSingleObject() returned %lu\n", dwError);
if (dwError == WAIT_TIMEOUT)
{
bResult = CancelIo(Service->lpImage->hControlPipe);
if (bResult == FALSE)
{
DPRINT1("CancelIo() failed (Error: %lu)\n", GetLastError());
}
dwError = ERROR_SERVICE_REQUEST_TIMEOUT;
goto Done;
}
else if (dwError == WAIT_OBJECT_0)
{
bResult = GetOverlappedResult(Service->lpImage->hControlPipe,
&Overlapped,
&dwReadCount,
TRUE);
if (bResult == FALSE)
{
dwError = GetLastError();
DPRINT1("GetOverlappedResult() failed (Error %lu)\n", dwError);
goto Done;
}
}
}
else
{
DPRINT1("ReadFile() failed (Error %lu)\n", dwError);
goto Done;
}
}
#else
/* Send the control packet */
bResult = WriteFile(Service->lpImage->hControlPipe,
ControlPacket,
PacketSize,
&dwWriteCount,
NULL);
if (bResult == FALSE)
{
dwError = GetLastError();
DPRINT("WriteFile() failed (Error %lu)\n", dwError);
if ((dwError == ERROR_GEN_FAILURE) &&
(dwControl == SERVICE_CONTROL_STOP))
{
/* Service is already terminated */
Service->Status.dwCurrentState = SERVICE_STOPPED;
Service->Status.dwControlsAccepted = 0;
Service->Status.dwWin32ExitCode = ERROR_SERVICE_NOT_ACTIVE;
dwError = ERROR_SUCCESS;
}
goto Done;
}
/* Read the reply */
bResult = ReadFile(Service->lpImage->hControlPipe,
&ReplyPacket,
sizeof(SCM_REPLY_PACKET),
&dwReadCount,
NULL);
if (bResult == FALSE)
{
dwError = GetLastError();
DPRINT("ReadFile() failed (Error %lu)\n", dwError);
}
#endif
Done:
/* Release the contol packet */
HeapFree(GetProcessHeap(),
0,
ControlPacket);
if (dwReadCount == sizeof(SCM_REPLY_PACKET))
{
dwError = ReplyPacket.dwError;
}
if (dwError == ERROR_SUCCESS &&
dwControl == SERVICE_CONTROL_STOP)
{
ScmDereferenceServiceImage(Service->lpImage);
}
LeaveCriticalSection(&ControlServiceCriticalSection);
DPRINT("ScmControlService() done\n");
return dwError;
}
static DWORD
ScmSendStartCommand(PSERVICE Service,
DWORD argc,
LPWSTR* argv)
{
PSCM_CONTROL_PACKET ControlPacket;
SCM_REPLY_PACKET ReplyPacket;
DWORD PacketSize;
PWSTR Ptr;
DWORD dwWriteCount = 0;
DWORD dwReadCount = 0;
DWORD dwError = ERROR_SUCCESS;
DWORD i;
PWSTR *pOffPtr;
PWSTR pArgPtr;
BOOL bResult;
#ifdef USE_ASYNCHRONOUS_IO
OVERLAPPED Overlapped = {0};
#endif
DPRINT("ScmSendStartCommand() called\n");
/* Calculate the total length of the start command line */
PacketSize = sizeof(SCM_CONTROL_PACKET) +
(DWORD)((wcslen(Service->lpServiceName) + 1) * sizeof(WCHAR));
/* Calculate the required packet size for the start arguments */
if (argc > 0 && argv != NULL)
{
PacketSize = ALIGN_UP(PacketSize, LPWSTR);
DPRINT("Argc: %lu\n", argc);
for (i = 0; i < argc; i++)
{
DPRINT("Argv[%lu]: %S\n", i, argv[i]);
PacketSize += (DWORD)((wcslen(argv[i]) + 1) * sizeof(WCHAR) + sizeof(PWSTR));
}
}
/* Allocate a control packet */
ControlPacket = HeapAlloc(GetProcessHeap(),
HEAP_ZERO_MEMORY,
PacketSize);
if (ControlPacket == NULL)
return ERROR_NOT_ENOUGH_MEMORY;
ControlPacket->dwSize = PacketSize;
ControlPacket->dwControl = (Service->Status.dwServiceType & SERVICE_WIN32_OWN_PROCESS)
? SERVICE_CONTROL_START_OWN
: SERVICE_CONTROL_START_SHARE;
ControlPacket->hServiceStatus = (SERVICE_STATUS_HANDLE)Service;
ControlPacket->dwServiceNameOffset = sizeof(SCM_CONTROL_PACKET);
Ptr = (PWSTR)((PBYTE)ControlPacket + ControlPacket->dwServiceNameOffset);
wcscpy(Ptr, Service->lpServiceName);
ControlPacket->dwArgumentsCount = 0;
ControlPacket->dwArgumentsOffset = 0;
/* Copy argument list */
if (argc > 0 && argv != NULL)
{
Ptr += wcslen(Service->lpServiceName) + 1;
pOffPtr = (PWSTR*)ALIGN_UP_POINTER(Ptr, PWSTR);
pArgPtr = (PWSTR)((ULONG_PTR)pOffPtr + argc * sizeof(PWSTR));
ControlPacket->dwArgumentsCount = argc;
ControlPacket->dwArgumentsOffset = (DWORD)((ULONG_PTR)pOffPtr - (ULONG_PTR)ControlPacket);
DPRINT("dwArgumentsCount: %lu\n", ControlPacket->dwArgumentsCount);
DPRINT("dwArgumentsOffset: %lu\n", ControlPacket->dwArgumentsOffset);
for (i = 0; i < argc; i++)
{
wcscpy(pArgPtr, argv[i]);
*pOffPtr = (PWSTR)((ULONG_PTR)pArgPtr - (ULONG_PTR)pOffPtr);
DPRINT("offset: %p\n", *pOffPtr);
pArgPtr += wcslen(argv[i]) + 1;
pOffPtr++;
}
}
#ifdef USE_ASYNCHRONOUS_IO
bResult = WriteFile(Service->lpImage->hControlPipe,
ControlPacket,
PacketSize,
&dwWriteCount,
&Overlapped);
if (bResult == FALSE)
{
DPRINT1("WriteFile() returned FALSE\n");
dwError = GetLastError();
if (dwError == ERROR_IO_PENDING)
{
DPRINT1("dwError: ERROR_IO_PENDING\n");
dwError = WaitForSingleObject(Service->lpImage->hControlPipe,
PipeTimeout);
DPRINT1("WaitForSingleObject() returned %lu\n", dwError);
if (dwError == WAIT_TIMEOUT)
{
bResult = CancelIo(Service->lpImage->hControlPipe);
if (bResult == FALSE)
{
DPRINT1("CancelIo() failed (Error: %lu)\n", GetLastError());
}
dwError = ERROR_SERVICE_REQUEST_TIMEOUT;
goto Done;
}
else if (dwError == WAIT_OBJECT_0)
{
bResult = GetOverlappedResult(Service->lpImage->hControlPipe,
&Overlapped,
&dwWriteCount,
TRUE);
if (bResult == FALSE)
{
dwError = GetLastError();
DPRINT1("GetOverlappedResult() failed (Error %lu)\n", dwError);
goto Done;
}
}
}
else
{
DPRINT1("WriteFile() failed (Error %lu)\n", dwError);
goto Done;
}
}
/* Read the reply */
Overlapped.hEvent = (HANDLE) NULL;
bResult = ReadFile(Service->lpImage->hControlPipe,
&ReplyPacket,
sizeof(SCM_REPLY_PACKET),
&dwReadCount,
&Overlapped);
if (bResult == FALSE)
{
DPRINT1("ReadFile() returned FALSE\n");
dwError = GetLastError();
if (dwError == ERROR_IO_PENDING)
{
DPRINT1("dwError: ERROR_IO_PENDING\n");
dwError = WaitForSingleObject(Service->lpImage->hControlPipe,
PipeTimeout);
DPRINT1("WaitForSingleObject() returned %lu\n", dwError);
if (dwError == WAIT_TIMEOUT)
{
bResult = CancelIo(Service->lpImage->hControlPipe);
if (bResult == FALSE)
{
DPRINT1("CancelIo() failed (Error: %lu)\n", GetLastError());
}
dwError = ERROR_SERVICE_REQUEST_TIMEOUT;
goto Done;
}
else if (dwError == WAIT_OBJECT_0)
{
bResult = GetOverlappedResult(Service->lpImage->hControlPipe,
&Overlapped,
&dwReadCount,
TRUE);
if (bResult == FALSE)
{
dwError = GetLastError();
DPRINT1("GetOverlappedResult() failed (Error %lu)\n", dwError);
goto Done;
}
}
}
else
{
DPRINT1("ReadFile() failed (Error %lu)\n", dwError);
goto Done;
}
}
#else
/* Send the start command */
bResult = WriteFile(Service->lpImage->hControlPipe,
ControlPacket,
PacketSize,
&dwWriteCount,
NULL);
if (bResult == FALSE)
{
dwError = GetLastError();
DPRINT("WriteFile() failed (Error %lu)\n", dwError);
goto Done;
}
/* Read the reply */
bResult = ReadFile(Service->lpImage->hControlPipe,
&ReplyPacket,
sizeof(SCM_REPLY_PACKET),
&dwReadCount,
NULL);
if (bResult == FALSE)
{
dwError = GetLastError();
DPRINT("ReadFile() failed (Error %lu)\n", dwError);
}
#endif
Done:
/* Release the contol packet */
HeapFree(GetProcessHeap(),
0,
ControlPacket);
if (dwReadCount == sizeof(SCM_REPLY_PACKET))
{
dwError = ReplyPacket.dwError;
}
DPRINT("ScmSendStartCommand() done\n");
return dwError;
}
static DWORD
ScmWaitForServiceConnect(PSERVICE Service)
{
DWORD dwRead = 0;
DWORD dwProcessId = 0;
DWORD dwError = ERROR_SUCCESS;
BOOL bResult;
#ifdef USE_ASYNCHRONOUS_IO
OVERLAPPED Overlapped = {0};
#endif
DPRINT("ScmWaitForServiceConnect()\n");
#ifdef USE_ASYNCHRONOUS_IO
Overlapped.hEvent = (HANDLE)NULL;
bResult = ConnectNamedPipe(Service->lpImage->hControlPipe,
&Overlapped);
if (bResult == FALSE)
{
DPRINT("ConnectNamedPipe() returned FALSE\n");
dwError = GetLastError();
if (dwError == ERROR_IO_PENDING)
{
DPRINT("dwError: ERROR_IO_PENDING\n");
dwError = WaitForSingleObject(Service->lpImage->hControlPipe,
PipeTimeout);
DPRINT("WaitForSingleObject() returned %lu\n", dwError);
if (dwError == WAIT_TIMEOUT)
{
DPRINT("WaitForSingleObject() returned WAIT_TIMEOUT\n");
bResult = CancelIo(Service->lpImage->hControlPipe);
if (bResult == FALSE)
{
DPRINT1("CancelIo() failed (Error: %lu)\n", GetLastError());
}
return ERROR_SERVICE_REQUEST_TIMEOUT;
}
else if (dwError == WAIT_OBJECT_0)
{
bResult = GetOverlappedResult(Service->lpImage->hControlPipe,
&Overlapped,
&dwRead,
TRUE);
if (bResult == FALSE)
{
dwError = GetLastError();
DPRINT1("GetOverlappedResult failed (Error %lu)\n", dwError);
return dwError;
}
}
}
else if (dwError != ERROR_PIPE_CONNECTED)
{
DPRINT1("ConnectNamedPipe failed (Error %lu)\n", dwError);
return dwError;
}
}
DPRINT("Control pipe connected!\n");
Overlapped.hEvent = (HANDLE) NULL;
/* Read the process id from pipe */
bResult = ReadFile(Service->lpImage->hControlPipe,
(LPVOID)&dwProcessId,
sizeof(DWORD),
&dwRead,
&Overlapped);
if (bResult == FALSE)
{
DPRINT("ReadFile() returned FALSE\n");
dwError = GetLastError();
if (dwError == ERROR_IO_PENDING)
{
DPRINT("dwError: ERROR_IO_PENDING\n");
dwError = WaitForSingleObject(Service->lpImage->hControlPipe,
PipeTimeout);
if (dwError == WAIT_TIMEOUT)
{
DPRINT("WaitForSingleObject() returned WAIT_TIMEOUT\n");
bResult = CancelIo(Service->lpImage->hControlPipe);
if (bResult == FALSE)
{
DPRINT1("CancelIo() failed (Error: %lu)\n", GetLastError());
}
return ERROR_SERVICE_REQUEST_TIMEOUT;
}
else if (dwError == WAIT_OBJECT_0)
{
DPRINT("WaitForSingleObject() returned WAIT_OBJECT_0\n");
DPRINT("Process Id: %lu\n", dwProcessId);
bResult = GetOverlappedResult(Service->lpImage->hControlPipe,
&Overlapped,
&dwRead,
TRUE);
if (bResult == FALSE)
{
dwError = GetLastError();
DPRINT1("GetOverlappedResult() failed (Error %lu)\n", dwError);
return dwError;
}
}
else
{
DPRINT1("WaitForSingleObject() returned %lu\n", dwError);
}
}
else
{
DPRINT1("ReadFile() failed (Error %lu)\n", dwError);
return dwError;
}
}
DPRINT1("ScmWaitForServiceConnect() done\n");
return ERROR_SUCCESS;
#else
/* Connect control pipe */
if (ConnectNamedPipe(Service->lpImage->hControlPipe, NULL) ?
TRUE : (dwError = GetLastError()) == ERROR_PIPE_CONNECTED)
{
DPRINT("Control pipe connected!\n");
/* Read SERVICE_STATUS_HANDLE from pipe */
bResult = ReadFile(Service->lpImage->hControlPipe,
(LPVOID)&dwProcessId,
sizeof(DWORD),
&dwRead,
NULL);
if (bResult == FALSE)
{
dwError = GetLastError();
DPRINT1("Reading the service control pipe failed (Error %lu)\n",
dwError);
}
else
{
dwError = ERROR_SUCCESS;
DPRINT("Read control pipe successfully\n");
}
}
else
{
DPRINT1("Connecting control pipe failed! (Error %lu)\n", dwError);
}
return dwError;
#endif
}
static DWORD
ScmStartUserModeService(PSERVICE Service,
DWORD argc,
LPWSTR* argv)
{
PROCESS_INFORMATION ProcessInformation;
STARTUPINFOW StartupInfo;
BOOL Result;
DWORD dwError = ERROR_SUCCESS;
DPRINT("ScmStartUserModeService(%p)\n", Service);
/* If the image is already running ... */
if (Service->lpImage->dwImageRunCount > 1)
{
/* ... just send a start command */
return ScmSendStartCommand(Service, argc, argv);
}
/* Otherwise start its process */
ZeroMemory(&StartupInfo, sizeof(StartupInfo));
StartupInfo.cb = sizeof(StartupInfo);
ZeroMemory(&ProcessInformation, sizeof(ProcessInformation));
Result = CreateProcessW(NULL,
Service->lpImage->szImagePath,
NULL,
NULL,
FALSE,
DETACHED_PROCESS | CREATE_SUSPENDED,
NULL,
NULL,
&StartupInfo,
&ProcessInformation);
if (!Result)
{
dwError = GetLastError();
DPRINT1("Starting '%S' failed!\n", Service->lpServiceName);
return dwError;
}
DPRINT("Process Id: %lu Handle %p\n",
ProcessInformation.dwProcessId,
ProcessInformation.hProcess);
DPRINT("Thread Id: %lu Handle %p\n",
ProcessInformation.dwThreadId,
ProcessInformation.hThread);
/* Get process handle and id */
Service->lpImage->dwProcessId = ProcessInformation.dwProcessId;
/* Resume Thread */
ResumeThread(ProcessInformation.hThread);
/* Connect control pipe */
dwError = ScmWaitForServiceConnect(Service);
if (dwError == ERROR_SUCCESS)
{
/* Send start command */
dwError = ScmSendStartCommand(Service, argc, argv);
}
else
{
DPRINT1("Connecting control pipe failed! (Error %lu)\n", dwError);
Service->lpImage->dwProcessId = 0;
}
/* Close thread and process handle */
CloseHandle(ProcessInformation.hThread);
CloseHandle(ProcessInformation.hProcess);
return dwError;
}
static DWORD
ScmLoadService(PSERVICE Service,
DWORD argc,
LPWSTR* argv)
{
PSERVICE_GROUP Group = Service->lpGroup;
DWORD dwError = ERROR_SUCCESS;
LPCWSTR ErrorLogStrings[2];
WCHAR szErrorBuffer[32];
DPRINT("ScmLoadService() called\n");
DPRINT("Start Service %p (%S)\n", Service, Service->lpServiceName);
if (Service->Status.dwCurrentState != SERVICE_STOPPED)
{
DPRINT("Service %S is already running!\n", Service->lpServiceName);
return ERROR_SERVICE_ALREADY_RUNNING;
}
DPRINT("Service->Type: %lu\n", Service->Status.dwServiceType);
if (Service->Status.dwServiceType & SERVICE_DRIVER)
{
/* Load driver */
dwError = ScmLoadDriver(Service);
if (dwError == ERROR_SUCCESS)
{
Service->Status.dwControlsAccepted = SERVICE_ACCEPT_STOP;
Service->Status.dwCurrentState = SERVICE_RUNNING;
}
}
else // if (Service->Status.dwServiceType & (SERVICE_WIN32 | SERVICE_INTERACTIVE_PROCESS))
{
/* Start user-mode service */
dwError = ScmCreateOrReferenceServiceImage(Service);
if (dwError == ERROR_SUCCESS)
{
dwError = ScmStartUserModeService(Service, argc, argv);
if (dwError == ERROR_SUCCESS)
{
#ifdef USE_SERVICE_START_PENDING
Service->Status.dwCurrentState = SERVICE_START_PENDING;
#else
Service->Status.dwCurrentState = SERVICE_RUNNING;
#endif
}
else
{
ScmDereferenceServiceImage(Service->lpImage);
Service->lpImage = NULL;
}
}
}
DPRINT("ScmLoadService() done (Error %lu)\n", dwError);
if (dwError == ERROR_SUCCESS)
{
if (Group != NULL)
{
Group->ServicesRunning = TRUE;
}
}
else
{
if (Service->dwErrorControl != SERVICE_ERROR_IGNORE)
{
/* Log a failed service start */
swprintf(szErrorBuffer, L"%lu", dwError);
ErrorLogStrings[0] = Service->lpServiceName;
ErrorLogStrings[1] = szErrorBuffer;
ScmLogError(EVENT_SERVICE_START_FAILED,
2,
ErrorLogStrings);
}
#if 0
switch (Service->dwErrorControl)
{
case SERVICE_ERROR_SEVERE:
if (IsLastKnownGood == FALSE)
{
/* FIXME: Boot last known good configuration */
}
break;
case SERVICE_ERROR_CRITICAL:
if (IsLastKnownGood == FALSE)
{
/* FIXME: Boot last known good configuration */
}
else
{
/* FIXME: BSOD! */
}
break;
}
#endif
}
return dwError;
}
DWORD
ScmStartService(PSERVICE Service,
DWORD argc,
LPWSTR* argv)
{
DWORD dwError = ERROR_SUCCESS;
SC_RPC_LOCK Lock = NULL;
DPRINT("ScmStartService() called\n");
DPRINT("Start Service %p (%S)\n", Service, Service->lpServiceName);
/* Acquire the service control critical section, to synchronize starts */
EnterCriticalSection(&ControlServiceCriticalSection);
/*
* Acquire the user service start lock while the service is starting, if
* needed (i.e. if we are not starting it during the initialization phase).
* If we don't success, bail out.
*/
if (!ScmInitialize)
{
dwError = ScmAcquireServiceStartLock(TRUE, &Lock);
if (dwError != ERROR_SUCCESS) goto done;
}
/* Really start the service */
dwError = ScmLoadService(Service, argc, argv);
/* Release the service start lock, if needed, and the critical section */
if (Lock) ScmReleaseServiceStartLock(&Lock);
done:
LeaveCriticalSection(&ControlServiceCriticalSection);
DPRINT("ScmStartService() done (Error %lu)\n", dwError);
return dwError;
}
VOID
ScmAutoStartServices(VOID)
{
DWORD dwError = ERROR_SUCCESS;
PLIST_ENTRY GroupEntry;
PLIST_ENTRY ServiceEntry;
PSERVICE_GROUP CurrentGroup;
PSERVICE CurrentService;
WCHAR szSafeBootServicePath[MAX_PATH];
HKEY hKey;
ULONG i;
/*
* This function MUST be called ONLY at initialization time.
* Therefore, no need to acquire the user service start lock.
*/
ASSERT(ScmInitialize);
/* Acquire the service control critical section, to synchronize starts */
EnterCriticalSection(&ControlServiceCriticalSection);
/* Clear 'ServiceVisited' flag (or set if not to start in Safe Mode) */
ServiceEntry = ServiceListHead.Flink;
while (ServiceEntry != &ServiceListHead)
{
CurrentService = CONTAINING_RECORD(ServiceEntry, SERVICE, ServiceListEntry);
/* Build the safe boot path */
wcscpy(szSafeBootServicePath,
L"SYSTEM\\CurrentControlSet\\Control\\SafeBoot");
switch (GetSystemMetrics(SM_CLEANBOOT))
{
/* NOTE: Assumes MINIMAL (1) and DSREPAIR (3) load same items */
case 1:
case 3:
wcscat(szSafeBootServicePath, L"\\Minimal\\");
break;
case 2:
wcscat(szSafeBootServicePath, L"\\Network\\");
break;
}
if (GetSystemMetrics(SM_CLEANBOOT))
{
/* If key does not exist then do not assume safe mode */
dwError = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
szSafeBootServicePath,
0,
KEY_READ,
&hKey);
if (dwError == ERROR_SUCCESS)
{
RegCloseKey(hKey);
/* Finish Safe Boot path off */
wcsncat(szSafeBootServicePath,
CurrentService->lpServiceName,
MAX_PATH - wcslen(szSafeBootServicePath));
/* Check that the key is in the Safe Boot path */
dwError = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
szSafeBootServicePath,
0,
KEY_READ,
&hKey);
if (dwError != ERROR_SUCCESS)
{
/* Mark service as visited so it is not auto-started */
CurrentService->ServiceVisited = TRUE;
}
else
{
/* Must be auto-started in safe mode - mark as unvisited */
RegCloseKey(hKey);
CurrentService->ServiceVisited = FALSE;
}
}
else
{
DPRINT1("WARNING: Could not open the associated Safe Boot key!");
CurrentService->ServiceVisited = FALSE;
}
}
ServiceEntry = ServiceEntry->Flink;
}
/* Start all services which are members of an existing group */
GroupEntry = GroupListHead.Flink;
while (GroupEntry != &GroupListHead)
{
CurrentGroup = CONTAINING_RECORD(GroupEntry, SERVICE_GROUP, GroupListEntry);
DPRINT("Group '%S'\n", CurrentGroup->lpGroupName);
/* Start all services witch have a valid tag */
for (i = 0; i < CurrentGroup->TagCount; i++)
{
ServiceEntry = ServiceListHead.Flink;
while (ServiceEntry != &ServiceListHead)
{
CurrentService = CONTAINING_RECORD(ServiceEntry, SERVICE, ServiceListEntry);
if ((CurrentService->lpGroup == CurrentGroup) &&
(CurrentService->dwStartType == SERVICE_AUTO_START) &&
(CurrentService->ServiceVisited == FALSE) &&
(CurrentService->dwTag == CurrentGroup->TagArray[i]))
{
CurrentService->ServiceVisited = TRUE;
ScmLoadService(CurrentService, 0, NULL);
}
ServiceEntry = ServiceEntry->Flink;
}
}
/* Start all services which have an invalid tag or which do not have a tag */
ServiceEntry = ServiceListHead.Flink;
while (ServiceEntry != &ServiceListHead)
{
CurrentService = CONTAINING_RECORD(ServiceEntry, SERVICE, ServiceListEntry);
if ((CurrentService->lpGroup == CurrentGroup) &&
(CurrentService->dwStartType == SERVICE_AUTO_START) &&
(CurrentService->ServiceVisited == FALSE))
{
CurrentService->ServiceVisited = TRUE;
ScmLoadService(CurrentService, 0, NULL);
}
ServiceEntry = ServiceEntry->Flink;
}
GroupEntry = GroupEntry->Flink;
}
/* Start all services which are members of any non-existing group */
ServiceEntry = ServiceListHead.Flink;
while (ServiceEntry != &ServiceListHead)
{
CurrentService = CONTAINING_RECORD(ServiceEntry, SERVICE, ServiceListEntry);
if ((CurrentService->lpGroup != NULL) &&
(CurrentService->dwStartType == SERVICE_AUTO_START) &&
(CurrentService->ServiceVisited == FALSE))
{
CurrentService->ServiceVisited = TRUE;
ScmLoadService(CurrentService, 0, NULL);
}
ServiceEntry = ServiceEntry->Flink;
}
/* Start all services which are not a member of any group */
ServiceEntry = ServiceListHead.Flink;
while (ServiceEntry != &ServiceListHead)
{
CurrentService = CONTAINING_RECORD(ServiceEntry, SERVICE, ServiceListEntry);
if ((CurrentService->lpGroup == NULL) &&
(CurrentService->dwStartType == SERVICE_AUTO_START) &&
(CurrentService->ServiceVisited == FALSE))
{
CurrentService->ServiceVisited = TRUE;
ScmLoadService(CurrentService, 0, NULL);
}
ServiceEntry = ServiceEntry->Flink;
}
/* Clear 'ServiceVisited' flag again */
ServiceEntry = ServiceListHead.Flink;
while (ServiceEntry != &ServiceListHead)
{
CurrentService = CONTAINING_RECORD(ServiceEntry, SERVICE, ServiceListEntry);
CurrentService->ServiceVisited = FALSE;
ServiceEntry = ServiceEntry->Flink;
}
/* Release the critical section */
LeaveCriticalSection(&ControlServiceCriticalSection);
}
VOID
ScmAutoShutdownServices(VOID)
{
PLIST_ENTRY ServiceEntry;
PSERVICE CurrentService;
DPRINT("ScmAutoShutdownServices() called\n");
/* Lock the service database exclusively */
ScmLockDatabaseExclusive();
ServiceEntry = ServiceListHead.Flink;
while (ServiceEntry != &ServiceListHead)
{
CurrentService = CONTAINING_RECORD(ServiceEntry, SERVICE, ServiceListEntry);
if (CurrentService->Status.dwCurrentState == SERVICE_RUNNING ||
CurrentService->Status.dwCurrentState == SERVICE_START_PENDING)
{
/* shutdown service */
DPRINT("Shutdown service: %S\n", CurrentService->szServiceName);
ScmControlService(CurrentService, SERVICE_CONTROL_SHUTDOWN);
}
ServiceEntry = ServiceEntry->Flink;
}
/* Unlock the service database */
ScmUnlockDatabase();
DPRINT("ScmAutoShutdownServices() done\n");
}
BOOL
ScmLockDatabaseExclusive(VOID)
{
return RtlAcquireResourceExclusive(&DatabaseLock, TRUE);
}
BOOL
ScmLockDatabaseShared(VOID)
{
return RtlAcquireResourceShared(&DatabaseLock, TRUE);
}
VOID
ScmUnlockDatabase(VOID)
{
RtlReleaseResource(&DatabaseLock);
}
VOID
ScmInitNamedPipeCriticalSection(VOID)
{
HKEY hKey;
DWORD dwKeySize;
DWORD dwError;
InitializeCriticalSection(&ControlServiceCriticalSection);
dwError = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
L"SYSTEM\\CurrentControlSet\\Control",
0,
KEY_READ,
&hKey);
if (dwError == ERROR_SUCCESS)
{
dwKeySize = sizeof(DWORD);
RegQueryValueExW(hKey,
L"ServicesPipeTimeout",
0,
NULL,
(LPBYTE)&PipeTimeout,
&dwKeySize);
RegCloseKey(hKey);
}
}
VOID
ScmDeleteNamedPipeCriticalSection(VOID)
{
DeleteCriticalSection(&ControlServiceCriticalSection);
}
/* EOF */