reactos/base/system/services/database.c
Timo Kreuzer 9ea495ba33 Create a branch for header work.
svn path=/branches/header-work/; revision=45691
2010-02-26 22:57:55 +00:00

1248 lines
36 KiB
C

/*
* 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é Poussineau <hpoussin@reactos.org>
* Copyright 2007 Ged Murphy <gedmurphy@reactos.org>
* Gregor Brunmar <gregor.brunmar@home.se>
*
*/
/* INCLUDES *****************************************************************/
#include "services.h"
#define NDEBUG
#include <debug.h>
/*
* Uncomment the line below to start services
* using the SERVICE_START_PENDING state
*/
// #define USE_SERVICE_START_PENDING
/* GLOBALS *******************************************************************/
LIST_ENTRY ServiceListHead;
static RTL_RESOURCE DatabaseLock;
static DWORD dwResumeCount = 1;
/* FUNCTIONS *****************************************************************/
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;
}
PSERVICE
ScmGetServiceEntryByClientHandle(HANDLE Handle)
{
PLIST_ENTRY ServiceEntry;
PSERVICE CurrentService;
DPRINT("ScmGetServiceEntryByClientHandle() called\n");
DPRINT("looking for %p\n", Handle);
ServiceEntry = ServiceListHead.Flink;
while (ServiceEntry != &ServiceListHead)
{
CurrentService = CONTAINING_RECORD(ServiceEntry,
SERVICE,
ServiceListEntry);
if (CurrentService->hClient == Handle)
{
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 = (SERVICE*) HeapAlloc(GetProcessHeap(),
HEAP_ZERO_MEMORY,
sizeof(SERVICE) + ((wcslen(lpServiceName) + 1) * sizeof(WCHAR)));
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 = dwResumeCount++;
/* 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);
/* Decrement the image reference counter */
if (lpService->lpImage)
lpService->lpImage->dwServiceRefCount--;
/* Decrement the group reference counter */
if (lpService->lpGroup)
lpService->lpGroup->dwRefCount--;
/* FIXME: SecurityDescriptor */
/* Close the control pipe */
if (lpService->ControlPipeHandle != INVALID_HANDLE_VALUE)
CloseHandle(lpService->ControlPipeHandle);
/* 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);
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(), 0, 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);
}
}
}
VOID
WaitForLSA(VOID)
{
HANDLE hEvent;
DWORD dwError;
DPRINT("WaitForLSA() called\n");
hEvent = CreateEventW(NULL,
TRUE,
FALSE,
L"LSA_RPC_SERVER_ACTIVE");
if (hEvent == NULL)
{
dwError = GetLastError();
DPRINT1("Failed to create the notication event (Error %lu)\n", dwError);
if (dwError == ERROR_ALREADY_EXISTS)
{
hEvent = OpenEventW(SYNCHRONIZE,
FALSE,
L"LSA_RPC_SERVER_ACTIVE");
if (hEvent != NULL)
{
DPRINT1("Could not open the notification event!\n");
return;
}
}
}
DPRINT("Wait for LSA!\n");
WaitForSingleObject(hEvent, INFINITE);
DPRINT("LSA is available!\n");
CloseHandle(hEvent);
DPRINT("WaitForLSA() done\n");
}
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(&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 LSA */
WaitForLSA();
/* 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
{
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 = (OBJECT_DIRECTORY_INFORMATION*) 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,
LPSERVICE_STATUS lpServiceStatus)
{
PSCM_CONTROL_PACKET ControlPacket;
DWORD Count;
DWORD TotalLength;
DPRINT("ScmControlService() called\n");
TotalLength = wcslen(Service->lpServiceName) + 1;
ControlPacket = (SCM_CONTROL_PACKET*)HeapAlloc(GetProcessHeap(),
HEAP_ZERO_MEMORY,
sizeof(SCM_CONTROL_PACKET) + (TotalLength * sizeof(WCHAR)));
if (ControlPacket == NULL)
return ERROR_NOT_ENOUGH_MEMORY;
ControlPacket->dwControl = dwControl;
ControlPacket->hClient = Service->hClient;
ControlPacket->dwSize = TotalLength;
wcscpy(&ControlPacket->szArguments[0], Service->lpServiceName);
/* Send the control packet */
WriteFile(Service->ControlPipeHandle,
ControlPacket,
sizeof(SCM_CONTROL_PACKET) + (TotalLength * sizeof(WCHAR)),
&Count,
NULL);
/* FIXME: Read the reply */
/* Release the contol packet */
HeapFree(GetProcessHeap(),
0,
ControlPacket);
RtlCopyMemory(lpServiceStatus,
&Service->Status,
sizeof(SERVICE_STATUS));
DPRINT("ScmControlService) done\n");
return ERROR_SUCCESS;
}
static DWORD
ScmSendStartCommand(PSERVICE Service,
DWORD argc,
LPWSTR *argv)
{
PSCM_CONTROL_PACKET ControlPacket;
DWORD TotalLength;
DWORD ArgsLength = 0;
DWORD Length;
PWSTR Ptr;
DWORD Count;
DPRINT("ScmSendStartCommand() called\n");
/* Calculate the total length of the start command line */
TotalLength = wcslen(Service->lpServiceName) + 1;
if (argc > 0)
{
for (Count = 0; Count < argc; Count++)
{
DPRINT("Arg: %S\n", argv[Count]);
Length = wcslen(argv[Count]) + 1;
TotalLength += Length;
ArgsLength += Length;
}
}
TotalLength++;
DPRINT("ArgsLength: %ld TotalLength: %ld\n", ArgsLength, TotalLength);
/* Allocate a control packet */
ControlPacket = (SCM_CONTROL_PACKET*)HeapAlloc(GetProcessHeap(),
HEAP_ZERO_MEMORY,
sizeof(SCM_CONTROL_PACKET) + (TotalLength - 1) * sizeof(WCHAR));
if (ControlPacket == NULL)
return ERROR_NOT_ENOUGH_MEMORY;
ControlPacket->dwControl = SERVICE_CONTROL_START;
ControlPacket->hClient = Service->hClient;
ControlPacket->dwSize = TotalLength;
Ptr = &ControlPacket->szArguments[0];
wcscpy(Ptr, Service->lpServiceName);
Ptr += (wcslen(Service->lpServiceName) + 1);
/* Copy argument list */
if (argc > 0)
{
UNIMPLEMENTED;
DPRINT1("Arguments sent to service ignored!\n");
#if 0
memcpy(Ptr, Arguments, ArgsLength);
Ptr += ArgsLength;
#endif
}
/* Terminate the argument list */
*Ptr = 0;
/* Send the start command */
WriteFile(Service->ControlPipeHandle,
ControlPacket,
sizeof(SCM_CONTROL_PACKET) + (TotalLength - 1) * sizeof(WCHAR),
&Count,
NULL);
/* FIXME: Read the reply */
/* Release the contol packet */
HeapFree(GetProcessHeap(),
0,
ControlPacket);
DPRINT("ScmSendStartCommand() done\n");
return ERROR_SUCCESS;
}
static DWORD
ScmStartUserModeService(PSERVICE Service,
DWORD argc,
LPWSTR *argv)
{
RTL_QUERY_REGISTRY_TABLE QueryTable[3];
PROCESS_INFORMATION ProcessInformation;
STARTUPINFOW StartupInfo;
UNICODE_STRING ImagePath;
ULONG Type;
DWORD ServiceCurrent = 0;
BOOL Result;
NTSTATUS Status;
DWORD dwError = ERROR_SUCCESS;
WCHAR NtControlPipeName[MAX_PATH + 1];
HKEY hServiceCurrentKey = INVALID_HANDLE_VALUE;
DWORD KeyDisposition;
RtlInitUnicodeString(&ImagePath, NULL);
/* Get service data */
RtlZeroMemory(&QueryTable,
sizeof(QueryTable));
QueryTable[0].Name = L"Type";
QueryTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_REQUIRED;
QueryTable[0].EntryContext = &Type;
QueryTable[1].Name = L"ImagePath";
QueryTable[1].Flags = RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_REQUIRED;
QueryTable[1].EntryContext = &ImagePath;
Status = RtlQueryRegistryValues(RTL_REGISTRY_SERVICES,
Service->lpServiceName,
QueryTable,
NULL,
NULL);
if (!NT_SUCCESS(Status))
{
DPRINT1("RtlQueryRegistryValues() failed (Status %lx)\n", Status);
return RtlNtStatusToDosError(Status);
}
DPRINT("ImagePath: '%S'\n", ImagePath.Buffer);
DPRINT("Type: %lx\n", Type);
/* Get the service number */
/* TODO: Create registry entry with correct write access */
Status = RegCreateKeyExW(HKEY_LOCAL_MACHINE,
L"SYSTEM\\CurrentControlSet\\Control\\ServiceCurrent", 0, NULL,
REG_OPTION_VOLATILE,
KEY_WRITE | KEY_READ,
NULL,
&hServiceCurrentKey,
&KeyDisposition);
if (ERROR_SUCCESS != Status)
{
DPRINT1("RegCreateKeyEx() failed with status %u\n", Status);
return Status;
}
if (REG_OPENED_EXISTING_KEY == KeyDisposition)
{
DWORD KeySize = sizeof(ServiceCurrent);
Status = RegQueryValueExW(hServiceCurrentKey, L"", 0, NULL, (BYTE*)&ServiceCurrent, &KeySize);
if (ERROR_SUCCESS != Status)
{
RegCloseKey(hServiceCurrentKey);
DPRINT1("RegQueryValueEx() failed with status %u\n", Status);
return Status;
}
ServiceCurrent++;
}
Status = RegSetValueExW(hServiceCurrentKey, L"", 0, REG_DWORD, (BYTE*)&ServiceCurrent, sizeof(ServiceCurrent));
RegCloseKey(hServiceCurrentKey);
if (ERROR_SUCCESS != Status)
{
DPRINT1("RegSetValueExW() failed (Status %lx)\n", Status);
return Status;
}
/* Create '\\.\pipe\net\NtControlPipeXXX' instance */
swprintf(NtControlPipeName, L"\\\\.\\pipe\\net\\NtControlPipe%u", ServiceCurrent);
Service->ControlPipeHandle = CreateNamedPipeW(NtControlPipeName,
PIPE_ACCESS_DUPLEX,
PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT,
100,
8000,
4,
30000,
NULL);
DPRINT("CreateNamedPipeW(%S) done\n", NtControlPipeName);
if (Service->ControlPipeHandle == INVALID_HANDLE_VALUE)
{
DPRINT1("Failed to create control pipe!\n");
return GetLastError();
}
StartupInfo.cb = sizeof(StartupInfo);
StartupInfo.lpReserved = NULL;
StartupInfo.lpDesktop = NULL;
StartupInfo.lpTitle = NULL;
StartupInfo.dwFlags = 0;
StartupInfo.cbReserved2 = 0;
StartupInfo.lpReserved2 = 0;
Result = CreateProcessW(NULL,
ImagePath.Buffer,
NULL,
NULL,
FALSE,
DETACHED_PROCESS | CREATE_SUSPENDED,
NULL,
NULL,
&StartupInfo,
&ProcessInformation);
RtlFreeUnicodeString(&ImagePath);
if (!Result)
{
dwError = GetLastError();
/* Close control pipe */
CloseHandle(Service->ControlPipeHandle);
Service->ControlPipeHandle = INVALID_HANDLE_VALUE;
DPRINT1("Starting '%S' failed!\n", Service->lpServiceName);
return dwError;
}
DPRINT("Process Id: %lu Handle %lx\n",
ProcessInformation.dwProcessId,
ProcessInformation.hProcess);
DPRINT("Thread Id: %lu Handle %lx\n",
ProcessInformation.dwThreadId,
ProcessInformation.hThread);
/* Get process and thread ids */
Service->ProcessId = ProcessInformation.dwProcessId;
Service->ThreadId = ProcessInformation.dwThreadId;
/* Resume Thread */
ResumeThread(ProcessInformation.hThread);
/* Connect control pipe */
if (ConnectNamedPipe(Service->ControlPipeHandle, NULL) ?
TRUE : (dwError = GetLastError()) == ERROR_PIPE_CONNECTED)
{
DWORD dwRead = 0;
DPRINT("Control pipe connected!\n");
/* Read SERVICE_STATUS_HANDLE from pipe */
if (!ReadFile(Service->ControlPipeHandle,
(LPVOID)&Service->hClient,
sizeof(DWORD),
&dwRead,
NULL))
{
dwError = GetLastError();
DPRINT1("Reading the service control pipe failed (Error %lu)\n",
dwError);
}
else
{
DPRINT("Received service status %lu\n", Service->hClient);
/* Send start command */
dwError = ScmSendStartCommand(Service, argc, argv);
}
}
else
{
DPRINT1("Connecting control pipe failed! (Error %lu)\n", dwError);
/* Close control pipe */
CloseHandle(Service->ControlPipeHandle);
Service->ControlPipeHandle = INVALID_HANDLE_VALUE;
Service->ProcessId = 0;
Service->ThreadId = 0;
}
/* Close process and thread handle */
CloseHandle(ProcessInformation.hThread);
CloseHandle(ProcessInformation.hProcess);
return dwError;
}
DWORD
ScmStartService(PSERVICE Service, DWORD argc, LPWSTR *argv)
{
PSERVICE_GROUP Group = Service->lpGroup;
DWORD dwError = ERROR_SUCCESS;
DPRINT("ScmStartService() called\n");
Service->ControlPipeHandle = INVALID_HANDLE_VALUE;
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
{
/* Start user-mode service */
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
}
}
DPRINT("ScmStartService() done (Error %lu)\n", dwError);
if (dwError == ERROR_SUCCESS)
{
if (Group != NULL)
{
Group->ServicesRunning = TRUE;
}
}
#if 0
else
{
switch (Service->ErrorControl)
{
case SERVICE_ERROR_NORMAL:
/* FIXME: Log error */
break;
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;
}
VOID
ScmAutoStartServices(VOID)
{
PLIST_ENTRY GroupEntry;
PLIST_ENTRY ServiceEntry;
PSERVICE_GROUP CurrentGroup;
PSERVICE CurrentService;
ULONG i;
/* Clear 'ServiceVisited' flag */
ServiceEntry = ServiceListHead.Flink;
while (ServiceEntry != &ServiceListHead)
{
CurrentService = CONTAINING_RECORD(ServiceEntry, SERVICE, ServiceListEntry);
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;
ScmStartService(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;
ScmStartService(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;
ScmStartService(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;
ScmStartService(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;
}
}
VOID
ScmAutoShutdownServices(VOID)
{
PLIST_ENTRY ServiceEntry;
PSERVICE CurrentService;
SERVICE_STATUS ServiceStatus;
DPRINT("ScmAutoShutdownServices() called\n");
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 */
ScmControlService(CurrentService, SERVICE_CONTROL_STOP, &ServiceStatus);
}
ServiceEntry = ServiceEntry->Flink;
}
DPRINT("ScmGetBootAndSystemDriverState() done\n");
}
/* EOF */