reactos/reactos/subsys/system/services/database.c
Filip Navara 75a32eddd8 - Reverted my last changed, should go to branch!
svn path=/trunk/; revision=9112
2004-04-12 17:20:47 +00:00

723 lines
18 KiB
C

/* $Id: database.c,v 1.16 2004/04/12 17:20:47 navaraf Exp $
*
* service control manager
*
* ReactOS Operating System
*
* --------------------------------------------------------------------
*
* This software is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this software; see the file COPYING.LIB. If not, write
* to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge,
* MA 02139, USA.
*
*/
/* INCLUDES *****************************************************************/
#define NTOS_MODE_USER
#include <ntos.h>
#include <rosrtl/string.h>
#include <windows.h>
#include <tchar.h>
#include "services.h"
#define NDEBUG
#include <debug.h>
/* TYPES *********************************************************************/
typedef struct _SERVICE_GROUP
{
LIST_ENTRY GroupListEntry;
UNICODE_STRING GroupName;
BOOLEAN ServicesRunning;
} SERVICE_GROUP, *PSERVICE_GROUP;
typedef struct _SERVICE
{
LIST_ENTRY ServiceListEntry;
UNICODE_STRING ServiceName;
UNICODE_STRING RegistryPath;
UNICODE_STRING ServiceGroup;
ULONG Start;
ULONG Type;
ULONG ErrorControl;
ULONG Tag;
BOOLEAN ServiceRunning;
BOOLEAN ServiceVisited;
HANDLE ControlPipeHandle;
ULONG ProcessId;
ULONG ThreadId;
} SERVICE, *PSERVICE;
/* GLOBALS *******************************************************************/
LIST_ENTRY GroupListHead;
LIST_ENTRY ServiceListHead;
/* FUNCTIONS *****************************************************************/
static NTSTATUS STDCALL
CreateGroupListRoutine(PWSTR ValueName,
ULONG ValueType,
PVOID ValueData,
ULONG ValueLength,
PVOID Context,
PVOID EntryContext)
{
PSERVICE_GROUP Group;
if (ValueType == REG_SZ)
{
DPRINT("Data: '%S'\n", (PWCHAR)ValueData);
Group = (PSERVICE_GROUP)HeapAlloc(GetProcessHeap(),
HEAP_ZERO_MEMORY,
sizeof(SERVICE_GROUP));
if (Group == NULL)
{
return(STATUS_INSUFFICIENT_RESOURCES);
}
if (!RtlCreateUnicodeString(&Group->GroupName,
(PWSTR)ValueData))
{
return(STATUS_INSUFFICIENT_RESOURCES);
}
InsertTailList(&GroupListHead,
&Group->GroupListEntry);
}
return(STATUS_SUCCESS);
}
static NTSTATUS STDCALL
CreateServiceListEntry(PUNICODE_STRING ServiceName)
{
RTL_QUERY_REGISTRY_TABLE QueryTable[6];
PSERVICE Service = NULL;
NTSTATUS Status;
DPRINT("Service: '%wZ'\n", ServiceName);
/* Allocate service entry */
Service = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
sizeof(SERVICE));
if (Service == NULL)
{
return(STATUS_INSUFFICIENT_RESOURCES);
}
/* Copy service name */
Service->ServiceName.Length = ServiceName->Length;
Service->ServiceName.MaximumLength = ServiceName->Length + sizeof(WCHAR);
Service->ServiceName.Buffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
Service->ServiceName.MaximumLength);
if (Service->ServiceName.Buffer == NULL)
{
HeapFree(GetProcessHeap(), 0, Service);
return(STATUS_INSUFFICIENT_RESOURCES);
}
RtlCopyMemory(Service->ServiceName.Buffer,
ServiceName->Buffer,
ServiceName->Length);
Service->ServiceName.Buffer[ServiceName->Length / sizeof(WCHAR)] = 0;
/* Build registry path */
Service->RegistryPath.MaximumLength = MAX_PATH * sizeof(WCHAR);
Service->RegistryPath.Buffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
MAX_PATH * sizeof(WCHAR));
if (Service->ServiceName.Buffer == NULL)
{
HeapFree(GetProcessHeap(), 0, Service->ServiceName.Buffer);
HeapFree(GetProcessHeap(), 0, Service);
return(STATUS_INSUFFICIENT_RESOURCES);
}
wcscpy(Service->RegistryPath.Buffer,
L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\");
wcscat(Service->RegistryPath.Buffer,
Service->ServiceName.Buffer);
Service->RegistryPath.Length = wcslen(Service->RegistryPath.Buffer) * sizeof(WCHAR);
/* Get service data */
RtlZeroMemory(&QueryTable,
sizeof(QueryTable));
QueryTable[0].Name = L"Start";
QueryTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_REQUIRED;
QueryTable[0].EntryContext = &Service->Start;
QueryTable[1].Name = L"Type";
QueryTable[1].Flags = RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_REQUIRED;
QueryTable[1].EntryContext = &Service->Type;
QueryTable[2].Name = L"ErrorControl";
QueryTable[2].Flags = RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_REQUIRED;
QueryTable[2].EntryContext = &Service->ErrorControl;
QueryTable[3].Name = L"Group";
QueryTable[3].Flags = RTL_QUERY_REGISTRY_DIRECT;
QueryTable[3].EntryContext = &Service->ServiceGroup;
Status = RtlQueryRegistryValues(RTL_REGISTRY_SERVICES,
ServiceName->Buffer,
QueryTable,
NULL,
NULL);
if (!NT_SUCCESS(Status))
{
PrintString("RtlQueryRegistryValues() failed (Status %lx)\n", Status);
RtlFreeUnicodeString(&Service->RegistryPath);
RtlFreeUnicodeString(&Service->ServiceName);
HeapFree(GetProcessHeap(), 0, Service);
return(Status);
}
DPRINT("ServiceName: '%wZ'\n", &Service->ServiceName);
DPRINT("RegistryPath: '%wZ'\n", &Service->RegistryPath);
DPRINT("ServiceGroup: '%wZ'\n", &Service->ServiceGroup);
DPRINT("Start %lx Type %lx ErrorControl %lx\n",
Service->Start, Service->Type, Service->ErrorControl);
/* Append service entry */
InsertTailList(&ServiceListHead,
&Service->ServiceListEntry);
return(STATUS_SUCCESS);
}
NTSTATUS
ScmCreateServiceDataBase(VOID)
{
RTL_QUERY_REGISTRY_TABLE QueryTable[2];
OBJECT_ATTRIBUTES ObjectAttributes;
UNICODE_STRING ServicesKeyName;
UNICODE_STRING SubKeyName;
HKEY ServicesKey;
ULONG Index;
NTSTATUS Status;
PKEY_BASIC_INFORMATION KeyInfo = NULL;
ULONG KeyInfoLength = 0;
ULONG ReturnedLength;
DPRINT("ScmCreateServiceDataBase() called\n");
/* Initialize basic variables */
InitializeListHead(&GroupListHead);
InitializeListHead(&ServiceListHead);
/* Build group order list */
RtlZeroMemory(&QueryTable,
sizeof(QueryTable));
QueryTable[0].Name = L"List";
QueryTable[0].QueryRoutine = CreateGroupListRoutine;
Status = RtlQueryRegistryValues(RTL_REGISTRY_CONTROL,
L"ServiceGroupOrder",
QueryTable,
NULL,
NULL);
if (!NT_SUCCESS(Status))
return(Status);
RtlRosInitUnicodeStringFromLiteral(&ServicesKeyName,
L"\\Registry\\Machine\\System\\CurrentControlSet\\Services");
InitializeObjectAttributes(&ObjectAttributes,
&ServicesKeyName,
OBJ_CASE_INSENSITIVE,
NULL,
NULL);
Status = RtlpNtOpenKey(&ServicesKey,
0x10001,
&ObjectAttributes,
0);
if (!NT_SUCCESS(Status))
return(Status);
/* Allocate key info buffer */
KeyInfoLength = sizeof(KEY_BASIC_INFORMATION) + MAX_PATH * sizeof(WCHAR);
KeyInfo = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, KeyInfoLength);
if (KeyInfo == NULL)
{
NtClose(ServicesKey);
return(STATUS_INSUFFICIENT_RESOURCES);
}
Index = 0;
while (TRUE)
{
Status = NtEnumerateKey(ServicesKey,
Index,
KeyBasicInformation,
KeyInfo,
KeyInfoLength,
&ReturnedLength);
if (NT_SUCCESS(Status))
{
if (KeyInfo->NameLength < MAX_PATH * sizeof(WCHAR))
{
SubKeyName.Length = KeyInfo->NameLength;
SubKeyName.MaximumLength = KeyInfo->NameLength + sizeof(WCHAR);
SubKeyName.Buffer = KeyInfo->Name;
SubKeyName.Buffer[SubKeyName.Length / sizeof(WCHAR)] = 0;
DPRINT("KeyName: '%wZ'\n", &SubKeyName);
Status = CreateServiceListEntry(&SubKeyName);
}
}
if (!NT_SUCCESS(Status))
break;
Index++;
}
HeapFree(GetProcessHeap(), 0, KeyInfo);
NtClose(ServicesKey);
DPRINT("ScmCreateServiceDataBase() done\n");
return(STATUS_SUCCESS);
}
static NTSTATUS
ScmCheckDriver(PSERVICE Service)
{
OBJECT_ATTRIBUTES ObjectAttributes;
UNICODE_STRING DirName;
HANDLE DirHandle;
NTSTATUS Status;
PDIRECTORY_BASIC_INFORMATION DirInfo;
ULONG BufferLength;
ULONG DataLength;
ULONG Index;
PLIST_ENTRY GroupEntry;
PSERVICE_GROUP CurrentGroup;
DPRINT("ScmCheckDriver(%wZ) called\n", &Service->ServiceName);
if (Service->Type == SERVICE_KERNEL_DRIVER)
{
RtlRosInitUnicodeStringFromLiteral(&DirName,
L"\\Driver");
}
else
{
RtlRosInitUnicodeStringFromLiteral(&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(DIRECTORY_BASIC_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 '%wZ' failed\n", &Service->ServiceName);
break;
}
if (!NT_SUCCESS(Status))
break;
DPRINT("Comparing: '%wZ' '%wZ'\n", &Service->ServiceName, &DirInfo->ObjectName);
if (RtlEqualUnicodeString(&Service->ServiceName, &DirInfo->ObjectName, TRUE))
{
DPRINT("Found: '%wZ' '%wZ'\n", &Service->ServiceName, &DirInfo->ObjectName);
/* Mark service as 'running' */
Service->ServiceRunning = TRUE;
/* Find the driver's group and mark it as 'running' */
if (Service->ServiceGroup.Buffer != NULL)
{
GroupEntry = GroupListHead.Flink;
while (GroupEntry != &GroupListHead)
{
CurrentGroup = CONTAINING_RECORD(GroupEntry, SERVICE_GROUP, GroupListEntry);
DPRINT("Checking group '%wZ'\n", &CurrentGroup->GroupName);
if (RtlEqualUnicodeString(&Service->ServiceGroup, &CurrentGroup->GroupName, TRUE))
{
CurrentGroup->ServicesRunning = TRUE;
}
GroupEntry = GroupEntry->Flink;
}
}
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->Start == SERVICE_BOOT_START ||
CurrentService->Start == SERVICE_SYSTEM_START)
{
/* Check driver */
DPRINT(" Checking service: %wZ\n", &CurrentService->ServiceName);
ScmCheckDriver(CurrentService);
}
ServiceEntry = ServiceEntry->Flink;
}
DPRINT("ScmGetBootAndSystemDriverState() done\n");
}
static NTSTATUS
ScmStartService(PSERVICE Service,
PSERVICE_GROUP Group)
{
RTL_QUERY_REGISTRY_TABLE QueryTable[3];
PROCESS_INFORMATION ProcessInformation;
STARTUPINFOW StartupInfo;
UNICODE_STRING ImagePath;
NTSTATUS Status;
ULONG Type;
BOOL Result;
DPRINT("ScmStartService() called\n");
Service->ControlPipeHandle = INVALID_HANDLE_VALUE;
DPRINT("Service->Type: %u\n", Service->Type);
if (Service->Type == SERVICE_KERNEL_DRIVER ||
Service->Type == SERVICE_FILE_SYSTEM_DRIVER ||
Service->Type == SERVICE_RECOGNIZER_DRIVER)
{
/* Load driver */
DPRINT(" Path: %wZ\n", &Service->RegistryPath);
Status = NtLoadDriver(&Service->RegistryPath);
}
else
{
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->ServiceName.Buffer,
QueryTable,
NULL,
NULL);
if (NT_SUCCESS(Status))
{
DPRINT("ImagePath: '%S'\n", ImagePath.Buffer);
DPRINT("Type: %lx\n", Type);
/* FIXME: create '\\.\pipe\net\NtControlPipe' instance */
Service->ControlPipeHandle = CreateNamedPipeW(L"\\\\.\\pipe\\net\\NtControlPipe",
PIPE_ACCESS_DUPLEX,
PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT,
100,
8000,
4,
30000,
NULL);
DPRINT("CreateNamedPipeW() done\n");
if (Service->ControlPipeHandle == INVALID_HANDLE_VALUE)
{
DPRINT1("Failed to create control pipe!\n");
Status = STATUS_UNSUCCESSFUL;
goto Done;
}
StartupInfo.cb = sizeof(StartupInfo);
StartupInfo.lpReserved = NULL;
StartupInfo.lpDesktop = NULL;
StartupInfo.lpTitle = NULL;
StartupInfo.dwFlags = 0;
StartupInfo.cbReserved2 = 0;
StartupInfo.lpReserved2 = 0;
Result = CreateProcessW(ImagePath.Buffer,
NULL,
NULL,
NULL,
FALSE,
DETACHED_PROCESS | CREATE_SUSPENDED,
NULL,
NULL,
&StartupInfo,
&ProcessInformation);
RtlFreeUnicodeString(&ImagePath);
if (!Result)
{
/* Close control pipe */
CloseHandle(Service->ControlPipeHandle);
Service->ControlPipeHandle = INVALID_HANDLE_VALUE;
DPRINT("Starting '%S' failed!\n", Service->ServiceName.Buffer);
Status = STATUS_UNSUCCESSFUL;
}
else
{
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);
/* FIXME: connect control pipe */
if (ConnectNamedPipe(Service->ControlPipeHandle, NULL))
{
DPRINT("Control pipe connected!\n");
Status = STATUS_SUCCESS;
}
else
{
DPRINT1("Connecting control pipe failed!\n");
/* Close control pipe */
CloseHandle(Service->ControlPipeHandle);
Service->ControlPipeHandle = INVALID_HANDLE_VALUE;
Service->ProcessId = 0;
Service->ThreadId = 0;
Status = STATUS_UNSUCCESSFUL;
}
/* Close process and thread handle */
CloseHandle(ProcessInformation.hThread);
CloseHandle(ProcessInformation.hProcess);
}
}
}
Done:
if (NT_SUCCESS(Status))
{
if (Group != NULL)
{
Group->ServicesRunning = TRUE;
}
Service->ServiceRunning = TRUE;
}
#if 0
else
{
if (CurrentService->ErrorControl == 1)
{
/* Log error */
}
else if (CurrentService->ErrorControl == 2)
{
if (IsLastKnownGood == FALSE)
{
/* Boot last known good configuration */
}
}
else if (CurrentService->ErrorControl == 3)
{
if (IsLastKnownGood == FALSE)
{
/* Boot last known good configuration */
}
else
{
/* BSOD! */
}
}
}
#endif
return(STATUS_SUCCESS);
}
VOID
ScmAutoStartServices(VOID)
{
PLIST_ENTRY GroupEntry;
PLIST_ENTRY ServiceEntry;
PSERVICE_GROUP CurrentGroup;
PSERVICE CurrentService;
/* 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 '%wZ'\n", &CurrentGroup->GroupName);
ServiceEntry = ServiceListHead.Flink;
while (ServiceEntry != &ServiceListHead)
{
CurrentService = CONTAINING_RECORD(ServiceEntry, SERVICE, ServiceListEntry);
if ((RtlEqualUnicodeString(&CurrentGroup->GroupName, &CurrentService->ServiceGroup, TRUE)) &&
(CurrentService->Start == SERVICE_AUTO_START) &&
(CurrentService->ServiceVisited == FALSE))
{
CurrentService->ServiceVisited = TRUE;
ScmStartService(CurrentService,
CurrentGroup);
}
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 ((CurrentGroup->GroupName.Length > 0) &&
(CurrentService->Start == SERVICE_AUTO_START) &&
(CurrentService->ServiceVisited == FALSE))
{
CurrentService->ServiceVisited = TRUE;
ScmStartService(CurrentService,
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 ((CurrentGroup->GroupName.Length == 0) &&
(CurrentService->Start == SERVICE_AUTO_START) &&
(CurrentService->ServiceVisited == FALSE))
{
CurrentService->ServiceVisited = TRUE;
ScmStartService(CurrentService,
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;
}
}
/* EOF */