Move group-list related code to a separate file and keep the group name in the group...
[reactos.git] / reactos / subsys / system / services / database.c
index 6300207..34bf1eb 100644 (file)
@@ -1,9 +1,9 @@
-/* $Id: database.c,v 1.1 2002/06/07 20:09:56 ekohl Exp $
+/*
  *
  * service control manager
- * 
+ *
  * ReactOS Operating System
- * 
+ *
  * --------------------------------------------------------------------
  *
  * This software is free software; you can redistribute it and/or
 
 /* INCLUDES *****************************************************************/
 
-#define NTOS_MODE_USER
-#include <ntos.h>
-
-#include <windows.h>
-#include <tchar.h>
-
 #include "services.h"
 
 #define NDEBUG
 #include <debug.h>
 
 
-/* TYPES *********************************************************************/
+/* GLOBALS *******************************************************************/
 
-typedef struct _SERVICE_GROUP
-{
-  LIST_ENTRY GroupListEntry;
-  PWSTR GroupName;
+LIST_ENTRY ServiceListHead;
 
-  BOOLEAN ServicesRunning;
+static RTL_RESOURCE DatabaseLock;
+static DWORD dwResumeCount = 1;
 
-} SERVICE_GROUP, *PSERVICE_GROUP;
 
+/* FUNCTIONS *****************************************************************/
 
-typedef struct _SERVICE
+PSERVICE
+ScmGetServiceEntryByName(LPWSTR lpServiceName)
 {
-  LIST_ENTRY ServiceListEntry;
-  PWSTR ServiceName;
-  PWSTR GroupName;
+    PLIST_ENTRY ServiceEntry;
+    PSERVICE CurrentService;
 
-  PWSTR ImagePath;
+    DPRINT("ScmGetServiceEntryByName() called\n");
 
-  ULONG Start;
-  ULONG Type;
-  ULONG ErrorControl;
-  ULONG Tag;
+    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;
+    }
 
-  BOOLEAN ServiceRunning;      // needed ??
+    DPRINT("Couldn't find a matching service\n");
 
-} SERVICE, *PSERVICE;
+    return NULL;
+}
 
 
-/* GLOBALS *******************************************************************/
+PSERVICE
+ScmGetServiceEntryByDisplayName(LPWSTR lpDisplayName)
+{
+    PLIST_ENTRY ServiceEntry;
+    PSERVICE CurrentService;
 
-LIST_ENTRY GroupListHead = {NULL, NULL};
+    DPRINT("ScmGetServiceEntryByDisplayName() called\n");
 
-LIST_ENTRY ServiceListHead  = {NULL, NULL};
+    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");
 
-/* FUNCTIONS *****************************************************************/
+    return NULL;
+}
 
 
-static NTSTATUS STDCALL
-CreateGroupListRoutine(PWSTR ValueName,
-                      ULONG ValueType,
-                      PVOID ValueData,
-                      ULONG ValueLength,
-                      PVOID Context,
-                      PVOID EntryContext)
+PSERVICE
+ScmGetServiceEntryByResumeCount(DWORD dwResumeCount)
 {
-  PSERVICE_GROUP Group;
+    PLIST_ENTRY ServiceEntry;
+    PSERVICE CurrentService;
 
-  if (ValueType == REG_SZ)
+    DPRINT("ScmGetServiceEntryByResumeCount() called\n");
+
+    ServiceEntry = ServiceListHead.Flink;
+    while (ServiceEntry != &ServiceListHead)
     {
-//      PrintString("Data: '%S'\n", (PWCHAR)ValueData);
+        CurrentService = CONTAINING_RECORD(ServiceEntry,
+                                           SERVICE,
+                                           ServiceListEntry);
+        if (CurrentService->dwResumeCount > dwResumeCount)
+        {
+            DPRINT("Found service: '%S'\n", CurrentService->lpDisplayName);
+            return CurrentService;
+        }
+
+        ServiceEntry = ServiceEntry->Flink;
+    }
 
-      Group = (PSERVICE_GROUP)HeapAlloc(GetProcessHeap(),
-                                       HEAP_ZERO_MEMORY,
-                                       sizeof(SERVICE_GROUP));
-      if (Group == NULL)
-       return(STATUS_INSUFFICIENT_RESOURCES);
+    DPRINT("Couldn't find a matching service\n");
 
+    return NULL;
+}
 
-      Group->GroupName = (PWSTR)HeapAlloc(GetProcessHeap(),
-                                         HEAP_ZERO_MEMORY,
-                                         ValueLength);
-      if (Group->GroupName == NULL)
-       return(STATUS_INSUFFICIENT_RESOURCES);
 
-      wcscpy(Group->GroupName,
-            (PWSTR)ValueData);
+DWORD
+ScmCreateNewServiceRecord(LPWSTR lpServiceName,
+                          PSERVICE *lpServiceRecord)
+{
+    PSERVICE lpService = NULL;
 
+    DPRINT("Service: '%S'\n", lpServiceName);
 
-      InsertTailList(&GroupListHead,
-                    &Group->GroupListEntry);
+    /* Allocate service entry */
+    lpService = 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;
 
-  return(STATUS_SUCCESS);
+    /* Set the resume count */
+    lpService->dwResumeCount = dwResumeCount++;
+
+    /* Append service entry */
+    InsertTailList(&ServiceListHead,
+                   &lpService->ServiceListEntry);
+
+    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;
 }
 
 
-static NTSTATUS STDCALL
-CreateServiceListEntry(PUNICODE_STRING ServiceName)
+static DWORD
+CreateServiceListEntry(LPWSTR lpServiceName,
+                       HKEY hServiceKey)
 {
-  RTL_QUERY_REGISTRY_TABLE QueryTable[6];
-  WCHAR ServiceGroupBuffer[MAX_PATH];
-  WCHAR ImagePathBuffer[MAX_PATH];
-  UNICODE_STRING ServiceGroup;
-  UNICODE_STRING ImagePath;
-  PSERVICE_GROUP Group;
-  PSERVICE Service;
-  NTSTATUS Status;
-
-//  PrintString("Service: '%wZ'\n", ServiceName);
-
-  Service = (PSERVICE)HeapAlloc(GetProcessHeap(),
-                               HEAP_ZERO_MEMORY,
-                               sizeof(SERVICE));
-  if (Service == NULL)
+    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)
     {
-      PrintString(" - HeapAlloc() (1) failed\n");
-      return(STATUS_INSUFFICIENT_RESOURCES);
+        lpService->lpServiceGroup = lpGroup;
+        lpGroup = NULL;
     }
 
-  Service->ServiceName = (PWSTR)HeapAlloc(GetProcessHeap(),
-                                         HEAP_ZERO_MEMORY,
-                                         ServiceName->Length);
-  if (Service->ServiceName == NULL)
+    if (lpDisplayName != NULL)
     {
-      PrintString(" - HeapAlloc() (2) failed\n");
-      return(STATUS_INSUFFICIENT_RESOURCES);
+        lpService->lpDisplayName = lpDisplayName;
+        lpDisplayName = NULL;
     }
 
-  wcscpy(Service->ServiceName,
-        ServiceName->Buffer);
+    DPRINT("ServiceName: '%S'\n", lpService->lpServiceName);
+    DPRINT("Group: '%S'\n", lpService->lpServiceGroup);
+    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;
 
-  ServiceGroup.Length = 0;
-  ServiceGroup.MaximumLength = MAX_PATH * sizeof(WCHAR);
-  ServiceGroup.Buffer = ServiceGroupBuffer;
-  RtlZeroMemory(ServiceGroupBuffer,
-               MAX_PATH * sizeof(WCHAR));
+done:;
+    if (lpGroup != NULL)
+        HeapFree(GetProcessHeap(), 0, lpGroup);
 
-  ImagePath.Length = 0;
-  ImagePath.MaximumLength = MAX_PATH * sizeof(WCHAR);
-  ImagePath.Buffer = ImagePathBuffer;
-  RtlZeroMemory(ImagePathBuffer,
-               MAX_PATH * sizeof(WCHAR));
+    if (lpDisplayName != NULL)
+        HeapFree(GetProcessHeap(), 0, lpDisplayName);
 
+    return dwError;
+}
 
-  /* 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;
+VOID
+ScmDeleteMarkedServices(VOID)
+{
+    PLIST_ENTRY ServiceEntry;
+    PSERVICE CurrentService;
 
-  QueryTable[1].Name = L"Type";
-  QueryTable[1].Flags = RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_REQUIRED;
-  QueryTable[1].EntryContext = &Service->Type;
+    ServiceEntry = ServiceListHead.Flink;
+    while (ServiceEntry != &ServiceListHead)
+    {
+        CurrentService = CONTAINING_RECORD(ServiceEntry, SERVICE, ServiceListEntry);
+
+        ServiceEntry = ServiceEntry->Flink;
 
-  QueryTable[2].Name = L"ErrorControl";
-  QueryTable[2].Flags = RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_REQUIRED;
-  QueryTable[2].EntryContext = &Service->ErrorControl;
+        if (CurrentService->bDeleted == TRUE)
+        {
+            DPRINT1("Delete service: %S\n", CurrentService->lpServiceName);
 
-  QueryTable[3].Name = L"Group";
-  QueryTable[3].Flags = RTL_QUERY_REGISTRY_DIRECT;
-  QueryTable[3].EntryContext = &ServiceGroup;
+            /* FIXME: Delete the registry keys */
 
-  QueryTable[4].Name = L"ImagePath";
-  QueryTable[4].Flags = RTL_QUERY_REGISTRY_DIRECT;
-  QueryTable[4].EntryContext = &ImagePath;
+            /* FIXME: Delete the service record from the list */
 
+        }
+    }
+}
 
-  Status = RtlQueryRegistryValues(RTL_REGISTRY_SERVICES,
-                                 ServiceName->Buffer,
-                                 QueryTable,
-                                 NULL,
-                                 NULL);
-  if (!NT_SUCCESS(Status))
+
+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 (;;)
     {
-      PrintString("RtlQueryRegistryValues() failed (Status %lx)\n", Status);
-      return(Status);
+        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++;
     }
 
-  /* Copy the service group name */
-  if (ServiceGroup.Length > 0)
+    RegCloseKey(hServicesKey);
+
+    /* Delete services that are marked for delete */
+    ScmDeleteMarkedServices();
+
+    DPRINT("ScmCreateServiceDatabase() done\n");
+
+    return ERROR_SUCCESS;
+}
+
+
+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;
+    PLIST_ENTRY GroupEntry;
+    PSERVICE_GROUP CurrentGroup;
+
+    DPRINT("ScmCheckDriver(%S) called\n", Service->lpServiceName);
+
+    if (Service->Status.dwServiceType == SERVICE_KERNEL_DRIVER)
     {
-      Service->GroupName = (PWSTR)HeapAlloc(GetProcessHeap(),
-                                           HEAP_ZERO_MEMORY,
-                                           ServiceGroup.Length + sizeof(WCHAR));
-      if (Service->GroupName == NULL)
-       {
-         PrintString(" - HeapAlloc() (3) failed\n");
-         return(STATUS_INSUFFICIENT_RESOURCES);
-       }
-
-      memcpy(Service->GroupName,
-            ServiceGroup.Buffer,
-            ServiceGroup.Length);
+        RtlInitUnicodeString(&DirName,
+                             L"\\Driver");
     }
-  else
+    else
     {
-      Service->GroupName = NULL;
+        RtlInitUnicodeString(&DirName,
+                             L"\\FileSystem");
     }
 
-  /* Copy the image path */
-  if (ImagePath.Length > 0)
+    InitializeObjectAttributes(&ObjectAttributes,
+                               &DirName,
+                               0,
+                               NULL,
+                               NULL);
+
+    Status = NtOpenDirectoryObject(&DirHandle,
+                                   DIRECTORY_QUERY | DIRECTORY_TRAVERSE,
+                                   &ObjectAttributes);
+    if (!NT_SUCCESS(Status))
     {
-      Service->ImagePath = (PWSTR)HeapAlloc(GetProcessHeap(),
-                                           HEAP_ZERO_MEMORY,
-                                           ImagePath.Length + sizeof(WCHAR));
-      if (Service->ImagePath == NULL)
-       {
-         PrintString(" - HeapAlloc() (4) failed\n");
-         return(STATUS_INSUFFICIENT_RESOURCES);
-       }
-
-      memcpy(Service->ImagePath,
-            ImagePath.Buffer,
-            ImagePath.Length);
+        return Status;
     }
-  else
+
+    BufferLength = sizeof(OBJECT_DIRECTORY_INFORMATION) +
+                   2 * MAX_PATH * sizeof(WCHAR);
+    DirInfo = HeapAlloc(GetProcessHeap(),
+                        HEAP_ZERO_MEMORY,
+                        BufferLength);
+
+    Index = 0;
+    while (TRUE)
     {
-      Service->ImagePath = NULL;
+        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->ObjectName);
+
+        if (_wcsicmp(Service->lpServiceName, DirInfo->ObjectName.Buffer) == 0)
+        {
+            DPRINT("Found: '%S'  '%wZ'\n",
+                   Service->lpServiceName, &DirInfo->ObjectName);
+
+            /* Mark service as 'running' */
+            Service->Status.dwCurrentState = SERVICE_RUNNING;
+
+            /* Find the driver's group and mark it as 'running' */
+            if (Service->lpServiceGroup != NULL)
+            {
+                GroupEntry = GroupListHead.Flink;
+                while (GroupEntry != &GroupListHead)
+                {
+                    CurrentGroup = CONTAINING_RECORD(GroupEntry, SERVICE_GROUP, GroupListEntry);
+
+                    DPRINT("Checking group '%S'\n", &CurrentGroup->lpGroupName);
+                    if (Service->lpServiceGroup != NULL &&
+                        _wcsicmp(Service->lpServiceGroup, CurrentGroup->lpGroupName) == 0)
+                    {
+                        CurrentGroup->ServicesRunning = TRUE;
+                    }
+
+                    GroupEntry = GroupEntry->Flink;
+                }
+            }
+            break;
+        }
     }
 
-//  PrintString("  Type: %lx\n", Service->Type);
-//  PrintString("  Start: %lx\n", Service->Start);
-//  PrintString("  Group: '%wZ'\n", &ServiceGroup);
+    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);
 
-  /* Append service entry */
-  InsertTailList(&ServiceListHead,
-                &Service->ServiceListEntry);
+            ScmCheckDriver(CurrentService);
+        }
 
+        ServiceEntry = ServiceEntry->Flink;
+    }
 
-  return(STATUS_SUCCESS);
+    DPRINT("ScmGetBootAndSystemDriverState() done\n");
 }
 
 
-NTSTATUS
-ScmCreateServiceDataBase(VOID)
+static NTSTATUS
+ScmSendStartCommand(PSERVICE Service, LPWSTR Arguments)
 {
-  RTL_QUERY_REGISTRY_TABLE QueryTable[2];
-  WCHAR NameBuffer[MAX_PATH];
-  OBJECT_ATTRIBUTES ObjectAttributes;
-  UNICODE_STRING ServicesKeyName;
-  UNICODE_STRING SubKeyName;
-  HKEY ServicesKey;
-  NTSTATUS Status;
-  ULONG Index;
-
-  /* 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);
-
-
-  RtlInitUnicodeString(&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);
-
-  SubKeyName.Length = 0;
-  SubKeyName.MaximumLength = MAX_PATH * sizeof(WCHAR);
-  SubKeyName.Buffer = NameBuffer;
-
-  Index = 0;
-  while (TRUE)
-    {
-      Status = RtlpNtEnumerateSubKey(ServicesKey,
-                                    &SubKeyName,
-                                    Index,
-                                    0);
-      if (!NT_SUCCESS(Status))
-       break;
+    PSCM_START_PACKET StartPacket;
+    DWORD TotalLength;
+#if 0
+    DWORD Length;
+#endif
+    PWSTR Ptr;
+    DWORD Count;
 
-      CreateServiceListEntry(&SubKeyName);
+    DPRINT("ScmSendStartCommand() called\n");
 
-      Index++;
+    /* Calculate the total length of the start command line */
+    TotalLength = wcslen(Service->lpServiceName) + 1;
+#if 0
+    if (Arguments != NULL)
+    {
+        Ptr = Arguments;
+        while (*Ptr)
+        {
+            Length = wcslen(Ptr) + 1;
+            TotalLength += Length;
+            Ptr += Length;
+        }
     }
+#endif
+    TotalLength++;
 
-//  PrintString("ScmCreateServiceDataBase() done\n");
+    /* Allocate start command packet */
+    StartPacket = HeapAlloc(GetProcessHeap(),
+                            HEAP_ZERO_MEMORY,
+                            sizeof(SCM_START_PACKET) + (TotalLength - 1) * sizeof(WCHAR));
+    if (StartPacket == NULL)
+        return STATUS_INSUFFICIENT_RESOURCES;
 
-  return(STATUS_SUCCESS);
-}
+    StartPacket->Command = SCM_START_COMMAND;
+    StartPacket->Size = TotalLength;
+    Ptr = &StartPacket->Arguments[0];
+    wcscpy(Ptr, Service->lpServiceName);
+    Ptr += (wcslen(Service->lpServiceName) + 1);
 
+    /* FIXME: Copy argument list */
 
-VOID
-ScmGetBootAndSystemDriverState(VOID)
-{
+    *Ptr = 0;
+
+    /* Send the start command */
+    WriteFile(Service->ControlPipeHandle,
+              StartPacket,
+              sizeof(SCM_START_PACKET) + (TotalLength - 1) * sizeof(WCHAR),
+              &Count,
+              NULL);
 
+    /* FIXME: Read the reply */
+
+    HeapFree(GetProcessHeap(),
+             0,
+             StartPacket);
+
+    DPRINT("ScmSendStartCommand() done\n");
+
+    return STATUS_SUCCESS;
 }
 
 
 static NTSTATUS
-ScmLoadDriver(PSERVICE Service)
+ScmStartUserModeService(PSERVICE Service)
 {
-  WCHAR ServicePath[MAX_PATH];
-  UNICODE_STRING DriverPath;
+    RTL_QUERY_REGISTRY_TABLE QueryTable[3];
+    PROCESS_INFORMATION ProcessInformation;
+    STARTUPINFOW StartupInfo;
+    UNICODE_STRING ImagePath;
+    ULONG Type;
+    BOOL Result;
+    NTSTATUS Status;
+
+    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 Status;
+    }
+    DPRINT("ImagePath: '%S'\n", ImagePath.Buffer);
+    DPRINT("Type: %lx\n", Type);
+
+    /* 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");
+        return STATUS_UNSUCCESSFUL;
+    }
+
+    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;
+
+        DPRINT1("Starting '%S' failed!\n", Service->lpServiceName);
+        return STATUS_UNSUCCESSFUL;
+    }
+
+    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;
 
-//  PrintString("ScmLoadDriver(%S) called\n", Service->ServiceName);
+    /* Resume Thread */
+    ResumeThread(ProcessInformation.hThread);
 
-  if (Service->ImagePath == NULL)
+    /* Connect control pipe */
+    if (ConnectNamedPipe(Service->ControlPipeHandle, NULL))
     {
-      wcscpy(ServicePath, L"\\SystemRoot\\system32\\drivers\\");
-      wcscat(ServicePath, Service->ServiceName);
-      wcscat(ServicePath, L".sys");
+        DWORD dwProcessId = 0;
+        DWORD dwRead = 0;
+
+        DPRINT("Control pipe connected!\n");
+
+        /* Read thread id from pipe */
+        if (!ReadFile(Service->ControlPipeHandle,
+                      (LPVOID)&dwProcessId,
+                      sizeof(DWORD),
+                      &dwRead,
+                      NULL))
+        {
+            DPRINT1("Reading the service control pipe failed (Error %lu)\n",
+                    GetLastError());
+            Status = STATUS_UNSUCCESSFUL;
+        }
+        else
+        {
+            DPRINT("Received process id %lu\n", dwProcessId);
+
+            /* FIXME: Send start command */
+
+            Status = STATUS_SUCCESS;
+        }
     }
-  else
+    else
     {
-      wcscpy(ServicePath, L"\\SystemRoot\\");
-      wcscat(ServicePath, Service->ImagePath);
+        DPRINT("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;
     }
 
-  RtlInitUnicodeString(&DriverPath, ServicePath);
+    ScmSendStartCommand(Service, NULL);
 
-//  PrintString("  DriverPath: '%wZ'\n", &DriverPath);
+    /* Close process and thread handle */
+    CloseHandle(ProcessInformation.hThread);
+    CloseHandle(ProcessInformation.hProcess);
 
-  return(NtLoadDriver(&DriverPath));
+    return Status;
 }
 
 
 static NTSTATUS
-ScmStartService(PSERVICE Service)
+ScmStartService(PSERVICE Service,
+                PSERVICE_GROUP Group)
 {
-#if 0
-  PROCESS_INFORMATION ProcessInformation;
-  STARTUPINFO StartupInfo;
-  WCHAR CommandLine[MAX_PATH];
-  BOOL Result;
-#endif
+    NTSTATUS Status;
 
-  PrintString("ScmStartService(%S) called\n", Service->ServiceName);
+    DPRINT("ScmStartService() called\n");
 
-#if 0
-  GetSystemDirectoryW(CommandLine, MAX_PATH);
-  _tcscat(CommandLine, "\\");
-  _tcscat(CommandLine, FileName);
-
-  PrintString("SCM: %s\n", CommandLine);
-
-  /* FIXME: create '\\.\pipe\net\NtControlPipe' instance */
-
-  StartupInfo.cb = sizeof(StartupInfo);
-  StartupInfo.lpReserved = NULL;
-  StartupInfo.lpDesktop = NULL;
-  StartupInfo.lpTitle = NULL;
-  StartupInfo.dwFlags = 0;
-  StartupInfo.cbReserved2 = 0;
-  StartupInfo.lpReserved2 = 0;
-
-  Result = CreateProcessW(CommandLine,
-                         NULL,
-                         NULL,
-                         NULL,
-                         FALSE,
-                         DETACHED_PROCESS,
-                         NULL,
-                         NULL,
-                         &StartupInfo,
-                         &ProcessInformation);
-  if (!Result)
-    {
-      /* FIXME: close control pipe */
+    Service->ControlPipeHandle = INVALID_HANDLE_VALUE;
+    DPRINT("Service->Type: %lu\n", Service->Status.dwServiceType);
 
-      PrintString("SCM: Failed to start '%s'\n", FileName);
-      return(STATUS_UNSUCCESSFUL);
+    if (Service->Status.dwServiceType & SERVICE_DRIVER)
+    {
+        /* Load driver */
+        Status = ScmLoadDriver(Service);
+        if (Status == STATUS_SUCCESS)
+            Service->Status.dwControlsAccepted = SERVICE_ACCEPT_STOP;
+    }
+    else
+    {
+        /* Start user-mode service */
+        Status = ScmStartUserModeService(Service);
     }
 
-  /* FIXME: connect control pipe */
+    DPRINT("ScmStartService() done (Status %lx)\n", Status);
+
+    if (NT_SUCCESS(Status))
+    {
+        if (Group != NULL)
+        {
+            Group->ServicesRunning = TRUE;
+        }
+        Service->Status.dwCurrentState = SERVICE_RUNNING;
+    }
+#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(STATUS_SUCCESS);
+    return Status;
 }
 
 
 VOID
 ScmAutoStartServices(VOID)
 {
-  PLIST_ENTRY GroupEntry;
-  PLIST_ENTRY ServiceEntry;
-  PSERVICE_GROUP CurrentGroup;
-  PSERVICE CurrentService;
-  NTSTATUS Status;
-
-  GroupEntry = GroupListHead.Flink;
-  while (GroupEntry != &GroupListHead)
+    PLIST_ENTRY GroupEntry;
+    PLIST_ENTRY ServiceEntry;
+    PSERVICE_GROUP CurrentGroup;
+    PSERVICE CurrentService;
+    ULONG i;
+
+    /* Clear 'ServiceVisited' flag */
+    ServiceEntry = ServiceListHead.Flink;
+    while (ServiceEntry != &ServiceListHead)
     {
-      CurrentGroup = CONTAINING_RECORD(GroupEntry, SERVICE_GROUP, GroupListEntry);
-
-//      PrintString("  %S\n", CurrentGroup->GroupName);
-
-      ServiceEntry = ServiceListHead.Flink;
-      while (ServiceEntry != &ServiceListHead)
-       {
-         CurrentService = CONTAINING_RECORD(ServiceEntry, SERVICE, ServiceListEntry);
-
-         if ((wcsicmp(CurrentGroup->GroupName, CurrentService->GroupName) == 0) &&
-             (CurrentService->Start == SERVICE_AUTO_START))
-           {
-             if (CurrentService->Type == SERVICE_KERNEL_DRIVER ||
-                 CurrentService->Type == SERVICE_FILE_SYSTEM_DRIVER ||
-                 CurrentService->Type == SERVICE_RECOGNIZER_DRIVER)
-               {
-                 /* Load driver */
-                 Status = ScmLoadDriver(CurrentService);
-               }
-             else
-               {
-                 /* Start service */
-                 Status = ScmStartService(CurrentService);
-               }
-
-             if (NT_SUCCESS(Status))
-               {
-                 CurrentGroup->ServicesRunning = TRUE;
-                 CurrentService->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
+      CurrentService = CONTAINING_RECORD(ServiceEntry, SERVICE, ServiceListEntry);
+      CurrentService->ServiceVisited = FALSE;
+      ServiceEntry = ServiceEntry->Flink;
+    }
 
-           }
-         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->lpServiceGroup != NULL) &&
+                    (_wcsicmp(CurrentGroup->lpGroupName, CurrentService->lpServiceGroup) == 0) &&
+                    (CurrentService->dwStartType == SERVICE_AUTO_START) &&
+                    (CurrentService->ServiceVisited == FALSE) &&
+                    (CurrentService->dwTag == CurrentGroup->TagArray[i]))
+                {
+                    CurrentService->ServiceVisited = TRUE;
+                    ScmStartService(CurrentService,
+                                    CurrentGroup);
+                }
+
+                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->lpServiceGroup != NULL) &&
+                (_wcsicmp(CurrentGroup->lpGroupName, CurrentService->lpServiceGroup) == 0) &&
+                (CurrentService->dwStartType == SERVICE_AUTO_START) &&
+                (CurrentService->ServiceVisited == FALSE))
+            {
+                CurrentService->ServiceVisited = TRUE;
+                ScmStartService(CurrentService,
+                                CurrentGroup);
+            }
+
+            ServiceEntry = ServiceEntry->Flink;
+        }
+
+        GroupEntry = GroupEntry->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->lpServiceGroup != NULL) &&
+            (CurrentService->dwStartType == 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 ((CurrentService->lpServiceGroup == NULL) &&
+            (CurrentService->dwStartType == 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 */
\ No newline at end of file
+/* EOF */