First shot at CreateServiceW. It crashes due to a bug (aka missing feature) in widl...
[reactos.git] / reactos / subsys / system / services / database.c
index ceadcf3..8310327 100644 (file)
@@ -1,9 +1,9 @@
-/* $Id: database.c,v 1.11 2003/11/14 17:13:33 weiden Exp $
+/*
  *
  * service control manager
- * 
+ *
  * ReactOS Operating System
- * 
+ *
  * --------------------------------------------------------------------
  *
  * This software is free software; you can redistribute it and/or
 
 /* INCLUDES *****************************************************************/
 
+#include <windows.h>
 #define NTOS_MODE_USER
-#include <ntos.h>
+#include <ndk/ntndk.h>
 
-#include <windows.h>
-#include <tchar.h>
+#include <services/services.h>
 
 #include "services.h"
 
@@ -45,38 +45,99 @@ typedef struct _SERVICE_GROUP
   UNICODE_STRING GroupName;
 
   BOOLEAN ServicesRunning;
+  ULONG TagCount;
+  PULONG TagArray;
 
 } SERVICE_GROUP, *PSERVICE_GROUP;
 
 
-typedef struct _SERVICE
+/* GLOBALS *******************************************************************/
+
+LIST_ENTRY GroupListHead;
+LIST_ENTRY ServiceListHead;
+
+static RTL_RESOURCE DatabaseLock;
+
+
+/* FUNCTIONS *****************************************************************/
+
+PSERVICE
+ScmGetServiceEntryByName(PUNICODE_STRING ServiceName)
 {
-  LIST_ENTRY ServiceListEntry;
-  UNICODE_STRING ServiceName;
-  UNICODE_STRING RegistryPath;
-  UNICODE_STRING ServiceGroup;
+  PLIST_ENTRY ServiceEntry;
+  PSERVICE CurrentService;
 
-  ULONG Start;
-  ULONG Type;
-  ULONG ErrorControl;
-  ULONG Tag;
+  DPRINT("ScmGetServiceEntryByName() called\n");
+
+  ServiceEntry = ServiceListHead.Flink;
+  while (ServiceEntry != &ServiceListHead)
+    {
+      CurrentService = CONTAINING_RECORD(ServiceEntry,
+                                        SERVICE,
+                                        ServiceListEntry);
+      if (RtlEqualUnicodeString(&CurrentService->ServiceName, ServiceName, TRUE))
+       {
+         DPRINT("Found service: '%wZ'\n", &CurrentService->ServiceName);
+         return CurrentService;
+       }
 
-  BOOLEAN ServiceRunning;
-  BOOLEAN ServiceVisited;
+      ServiceEntry = ServiceEntry->Flink;
+    }
 
-  HANDLE ControlPipeHandle;
-  ULONG ProcessId;
-  ULONG ThreadId;
-} SERVICE, *PSERVICE;
+  DPRINT("Couldn't find a matching service\n");
 
+  return NULL;
+}
 
-/* GLOBALS *******************************************************************/
 
-LIST_ENTRY GroupListHead;
-LIST_ENTRY ServiceListHead;
+static NTSTATUS STDCALL
+CreateGroupOrderListRoutine(PWSTR ValueName,
+                            ULONG ValueType,
+                            PVOID ValueData,
+                            ULONG ValueLength,
+                            PVOID Context,
+                            PVOID EntryContext)
+{
+    PSERVICE_GROUP Group;
 
+    DPRINT("IopGetGroupOrderList(%S, %x, %x, %x, %x, %x)\n",
+           ValueName, ValueType, ValueData, ValueLength, Context, EntryContext);
+
+    if (ValueType == REG_BINARY &&
+        ValueData != NULL &&
+        ValueLength >= sizeof(DWORD) &&
+        ValueLength >= (*(PULONG)ValueData + 1) * sizeof(DWORD))
+    {
+        Group = (PSERVICE_GROUP)Context;
+        Group->TagCount = ((PULONG)ValueData)[0];
+        if (Group->TagCount > 0)
+        {
+            if (ValueLength >= (Group->TagCount + 1) * sizeof(DWORD))
+            {
+                Group->TagArray = (PULONG)HeapAlloc(GetProcessHeap(),
+                                                    HEAP_ZERO_MEMORY,
+                                                    Group->TagCount * sizeof(DWORD));
+                if (Group->TagArray == NULL)
+                {
+                    Group->TagCount = 0;
+                    return STATUS_INSUFFICIENT_RESOURCES;
+                }
+
+                RtlCopyMemory(Group->TagArray,
+                              (PULONG)ValueData + 1,
+                              Group->TagCount * sizeof(DWORD));
+            }
+            else
+            {
+                Group->TagCount = 0;
+                return STATUS_UNSUCCESSFUL;
+            }
+        }
+    }
+
+    return STATUS_SUCCESS;
+}
 
-/* FUNCTIONS *****************************************************************/
 
 static NTSTATUS STDCALL
 CreateGroupListRoutine(PWSTR ValueName,
@@ -86,32 +147,44 @@ CreateGroupListRoutine(PWSTR ValueName,
                       PVOID Context,
                       PVOID EntryContext)
 {
-  PSERVICE_GROUP Group;
+    PSERVICE_GROUP Group;
+    RTL_QUERY_REGISTRY_TABLE QueryTable[2];
+    NTSTATUS Status;
 
-  if (ValueType == REG_SZ)
+    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);
+        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;
+        }
+
+        RtlZeroMemory(&QueryTable, sizeof(QueryTable));
+        QueryTable[0].Name = (PWSTR)ValueData;
+        QueryTable[0].QueryRoutine = CreateGroupOrderListRoutine;
+
+        Status = RtlQueryRegistryValues(RTL_REGISTRY_CONTROL,
+                                        L"GroupOrderList",
+                                        QueryTable,
+                                        (PVOID)Group,
+                                        NULL);
+        DPRINT("%x %d %S\n", Status, Group->TagCount, (PWSTR)ValueData);
+
+        InsertTailList(&GroupListHead,
+                       &Group->GroupListEntry);
     }
 
-  return(STATUS_SUCCESS);
+    return STATUS_SUCCESS;
 }
 
 
@@ -129,7 +202,7 @@ CreateServiceListEntry(PUNICODE_STRING ServiceName)
                      sizeof(SERVICE));
   if (Service == NULL)
     {
-      return(STATUS_INSUFFICIENT_RESOURCES);
+      return STATUS_INSUFFICIENT_RESOURCES;
     }
 
   /* Copy service name */
@@ -140,8 +213,9 @@ CreateServiceListEntry(PUNICODE_STRING ServiceName)
   if (Service->ServiceName.Buffer == NULL)
     {
       HeapFree(GetProcessHeap(), 0, Service);
-      return(STATUS_INSUFFICIENT_RESOURCES);
+      return STATUS_INSUFFICIENT_RESOURCES;
     }
+
   RtlCopyMemory(Service->ServiceName.Buffer,
                ServiceName->Buffer,
                ServiceName->Length);
@@ -155,8 +229,9 @@ CreateServiceListEntry(PUNICODE_STRING ServiceName)
     {
       HeapFree(GetProcessHeap(), 0, Service->ServiceName.Buffer);
       HeapFree(GetProcessHeap(), 0, Service);
-      return(STATUS_INSUFFICIENT_RESOURCES);
+      return STATUS_INSUFFICIENT_RESOURCES;
     }
+
   wcscpy(Service->RegistryPath.Buffer,
         L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\");
   wcscat(Service->RegistryPath.Buffer,
@@ -183,6 +258,10 @@ CreateServiceListEntry(PUNICODE_STRING ServiceName)
   QueryTable[3].Flags = RTL_QUERY_REGISTRY_DIRECT;
   QueryTable[3].EntryContext = &Service->ServiceGroup;
 
+  QueryTable[4].Name = L"Tag";
+  QueryTable[4].Flags = RTL_QUERY_REGISTRY_DIRECT;
+  QueryTable[4].EntryContext = &Service->Tag;
+
   Status = RtlQueryRegistryValues(RTL_REGISTRY_SERVICES,
                                  ServiceName->Buffer,
                                  QueryTable,
@@ -194,20 +273,27 @@ CreateServiceListEntry(PUNICODE_STRING ServiceName)
       RtlFreeUnicodeString(&Service->RegistryPath);
       RtlFreeUnicodeString(&Service->ServiceName);
       HeapFree(GetProcessHeap(), 0, Service);
-      return(Status);
+      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);
+  DPRINT("Start %lx  Type %lx  Tag %lx  ErrorControl %lx\n",
+        Service->Start, Service->Type, Service->Tag, Service->ErrorControl);
 
   /* Append service entry */
   InsertTailList(&ServiceListHead,
                 &Service->ServiceListEntry);
 
-  return(STATUS_SUCCESS);
+  Service->CurrentState = SERVICE_STOPPED;
+  Service->ControlsAccepted = 0;
+  Service->Win32ExitCode = 0;
+  Service->ServiceSpecificExitCode = 0;
+  Service->CheckPoint = 0;
+  Service->WaitHint = 2000; /* 2 seconds */
+
+  return STATUS_SUCCESS;
 }
 
 
@@ -216,7 +302,8 @@ ScmCreateServiceDataBase(VOID)
 {
   RTL_QUERY_REGISTRY_TABLE QueryTable[2];
   OBJECT_ATTRIBUTES ObjectAttributes;
-  UNICODE_STRING ServicesKeyName;
+  UNICODE_STRING ServicesKeyName =
+  RTL_CONSTANT_STRING(L"\\Registry\\Machine\\System\\CurrentControlSet\\Services");
   UNICODE_STRING SubKeyName;
   HKEY ServicesKey;
   ULONG Index;
@@ -232,6 +319,9 @@ ScmCreateServiceDataBase(VOID)
   InitializeListHead(&GroupListHead);
   InitializeListHead(&ServiceListHead);
 
+  /* Initialize the database lock */
+  RtlInitializeResource(&DatabaseLock);
+
   /* Build group order list */
   RtlZeroMemory(&QueryTable,
                sizeof(QueryTable));
@@ -245,9 +335,9 @@ ScmCreateServiceDataBase(VOID)
                                  NULL,
                                  NULL);
   if (!NT_SUCCESS(Status))
-    return(Status);
+    return Status;
 
-  RtlInitUnicodeStringFromLiteral(&ServicesKeyName,
+  RtlInitUnicodeString(&ServicesKeyName,
                       L"\\Registry\\Machine\\System\\CurrentControlSet\\Services");
 
   InitializeObjectAttributes(&ObjectAttributes,
@@ -257,11 +347,11 @@ ScmCreateServiceDataBase(VOID)
                             NULL);
 
   Status = RtlpNtOpenKey(&ServicesKey,
-                        0x10001,
+                        KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS,
                         &ObjectAttributes,
                         0);
   if (!NT_SUCCESS(Status))
-    return(Status);
+    return Status;
 
   /* Allocate key info buffer */
   KeyInfoLength = sizeof(KEY_BASIC_INFORMATION) + MAX_PATH * sizeof(WCHAR);
@@ -269,7 +359,7 @@ ScmCreateServiceDataBase(VOID)
   if (KeyInfo == NULL)
     {
       NtClose(ServicesKey);
-      return(STATUS_INSUFFICIENT_RESOURCES);
+      return STATUS_INSUFFICIENT_RESOURCES;
     }
 
   Index = 0;
@@ -293,6 +383,12 @@ ScmCreateServiceDataBase(VOID)
 
              DPRINT("KeyName: '%wZ'\n", &SubKeyName);
              Status = CreateServiceListEntry(&SubKeyName);
+
+             /* Ignore services without proper registry. */
+             if (Status == STATUS_OBJECT_NAME_NOT_FOUND)
+               {
+                 Status = STATUS_SUCCESS;
+               }
            }
        }
 
@@ -307,7 +403,7 @@ ScmCreateServiceDataBase(VOID)
 
   DPRINT("ScmCreateServiceDataBase() done\n");
 
-  return(STATUS_SUCCESS);
+  return STATUS_SUCCESS;
 }
 
 
@@ -318,7 +414,7 @@ ScmCheckDriver(PSERVICE Service)
   UNICODE_STRING DirName;
   HANDLE DirHandle;
   NTSTATUS Status;
-  PDIRECTORY_BASIC_INFORMATION DirInfo;
+  POBJECT_DIRECTORY_INFORMATION DirInfo;
   ULONG BufferLength;
   ULONG DataLength;
   ULONG Index;
@@ -329,12 +425,12 @@ ScmCheckDriver(PSERVICE Service)
 
   if (Service->Type == SERVICE_KERNEL_DRIVER)
     {
-      RtlInitUnicodeStringFromLiteral(&DirName,
+      RtlInitUnicodeString(&DirName,
                           L"\\Driver");
     }
   else
     {
-      RtlInitUnicodeStringFromLiteral(&DirName,
+      RtlInitUnicodeString(&DirName,
                           L"\\FileSystem");
     }
 
@@ -349,10 +445,10 @@ ScmCheckDriver(PSERVICE Service)
                                 &ObjectAttributes);
   if (!NT_SUCCESS(Status))
     {
-      return(Status);
+      return Status;
     }
 
-  BufferLength = sizeof(DIRECTORY_BASIC_INFORMATION) +
+  BufferLength = sizeof(OBJECT_DIRECTORY_INFORMATION) +
                 2 * MAX_PATH * sizeof(WCHAR);
   DirInfo = HeapAlloc(GetProcessHeap(),
                      HEAP_ZERO_MEMORY,
@@ -385,7 +481,7 @@ ScmCheckDriver(PSERVICE Service)
          DPRINT("Found: '%wZ'  '%wZ'\n", &Service->ServiceName, &DirInfo->ObjectName);
 
          /* Mark service as 'running' */
-         Service->ServiceRunning = TRUE;
+         Service->CurrentState = SERVICE_RUNNING;
 
          /* Find the driver's group and mark it as 'running' */
          if (Service->ServiceGroup.Buffer != NULL)
@@ -413,7 +509,7 @@ ScmCheckDriver(PSERVICE Service)
           DirInfo);
   NtClose(DirHandle);
 
-  return(STATUS_SUCCESS);
+  return STATUS_SUCCESS;
 }
 
 
@@ -446,184 +542,284 @@ ScmGetBootAndSystemDriverState(VOID)
 
 
 static NTSTATUS
-ScmStartService(PSERVICE Service,
-               PSERVICE_GROUP Group)
+ScmSendStartCommand(PSERVICE Service, LPWSTR Arguments)
+{
+  PSCM_START_PACKET StartPacket;
+  DWORD TotalLength;
+#if 0
+  DWORD Length;
+#endif
+  PWSTR Ptr;
+  DWORD Count;
+
+  DPRINT("ScmSendStartCommand() called\n");
+
+  /* Calculate the total length of the start command line */
+  TotalLength = wcslen(Service->ServiceName.Buffer) + 1;
+#if 0
+  if (Arguments != NULL)
+  {
+    Ptr = Arguments;
+    while (*Ptr)
+    {
+      Length = wcslen(Ptr) + 1;
+      TotalLength += Length;
+      Ptr += Length;
+    }
+  }
+#endif
+  TotalLength++;
+
+  /* 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;
+
+  StartPacket->Command = SCM_START_COMMAND;
+  StartPacket->Size = TotalLength;
+  Ptr = &StartPacket->Arguments[0];
+  wcscpy(Ptr, Service->ServiceName.Buffer);
+  Ptr += (wcslen(Service->ServiceName.Buffer) + 1);
+
+  /* FIXME: Copy argument list */
+
+  *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
+ScmStartUserModeService(PSERVICE Service)
 {
   RTL_QUERY_REGISTRY_TABLE QueryTable[3];
   PROCESS_INFORMATION ProcessInformation;
   STARTUPINFOW StartupInfo;
   UNICODE_STRING ImagePath;
-  NTSTATUS Status;
   ULONG Type;
   BOOL Result;
+  NTSTATUS Status;
 
-  DPRINT("ScmStartService() called\n");
+  RtlInitUnicodeString(&ImagePath, NULL);
 
-  Service->ControlPipeHandle = INVALID_HANDLE_VALUE;
+  /* Get service data */
+  RtlZeroMemory(&QueryTable,
+               sizeof(QueryTable));
 
-  if (Service->Type == SERVICE_KERNEL_DRIVER ||
-      Service->Type == SERVICE_FILE_SYSTEM_DRIVER ||
-      Service->Type == SERVICE_RECOGNIZER_DRIVER)
+  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))
     {
-      /* Load driver */
-      DPRINT("  Path: %wZ\n", &Service->RegistryPath);
-      Status = NtLoadDriver(&Service->RegistryPath);
+      DPRINT1("RtlQueryRegistryValues() failed (Status %lx)\n", Status);
+      return Status;
     }
-  else
+  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)
     {
-      RtlInitUnicodeString(&ImagePath, NULL);
+      DPRINT1("Failed to create control pipe!\n");
+      return STATUS_UNSUCCESSFUL;
+    }
 
-      /* Get service data */
-      RtlZeroMemory(&QueryTable,
-                   sizeof(QueryTable));
+  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;
 
-      QueryTable[0].Name = L"Type";
-      QueryTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_REQUIRED;
-      QueryTable[0].EntryContext = &Type;
+      DPRINT1("Starting '%S' failed!\n", Service->ServiceName.Buffer);
+      return STATUS_UNSUCCESSFUL;
+    }
 
-      QueryTable[1].Name = L"ImagePath";
-      QueryTable[1].Flags = RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_REQUIRED;
-      QueryTable[1].EntryContext = &ImagePath;
+  DPRINT("Process Id: %lu  Handle %lx\n",
+        ProcessInformation.dwProcessId,
+        ProcessInformation.hProcess);
+  DPRINT("Thread Id: %lu  Handle %lx\n",
+        ProcessInformation.dwThreadId,
+        ProcessInformation.hThread);
 
-      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;
-           }
+  /* Get process and thread ids */
+  Service->ProcessId = ProcessInformation.dwProcessId;
+  Service->ThreadId = ProcessInformation.dwThreadId;
 
-         StartupInfo.cb = sizeof(StartupInfo);
-         StartupInfo.lpReserved = NULL;
-         StartupInfo.lpDesktop = NULL;
-         StartupInfo.lpTitle = NULL;
-         StartupInfo.dwFlags = 0;
-         StartupInfo.cbReserved2 = 0;
-         StartupInfo.lpReserved2 = 0;
+  /* Resume Thread */
+  ResumeThread(ProcessInformation.hThread);
 
-         Result = CreateProcessW(ImagePath.Buffer,
-                                 NULL,
-                                 NULL,
-                                 NULL,
-                                 FALSE,
-                                 DETACHED_PROCESS | CREATE_SUSPENDED,
-                                 NULL,
-                                 NULL,
-                                 &StartupInfo,
-                                 &ProcessInformation);
-         RtlFreeUnicodeString(&ImagePath);
+  /* Connect control pipe */
+  if (ConnectNamedPipe(Service->ControlPipeHandle, NULL))
+    {
+      DWORD dwProcessId = 0;
+      DWORD dwRead = 0;
 
-         if (!Result)
-           {
-             /* Close control pipe */
-             CloseHandle(Service->ControlPipeHandle);
-             Service->ControlPipeHandle = INVALID_HANDLE_VALUE;
+      DPRINT("Control pipe connected!\n");
 
-             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;
-               }
+      /* 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);
 
-             /* Close process and thread handle */
-             CloseHandle(ProcessInformation.hThread);
-             CloseHandle(ProcessInformation.hProcess);
-           }
+         /* FIXME: Send start command */
+
+         Status = STATUS_SUCCESS;
        }
     }
+  else
+    {
+      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;
+    }
+
+  ScmSendStartCommand(Service, NULL);
+
+  /* Close process and thread handle */
+  CloseHandle(ProcessInformation.hThread);
+  CloseHandle(ProcessInformation.hProcess);
+
+  return Status;
+}
+
+
+static NTSTATUS
+ScmStartService(PSERVICE Service,
+               PSERVICE_GROUP Group)
+{
+  NTSTATUS Status;
+
+  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
+    {
+      /* Start user-mode service */
+      Status = ScmStartUserModeService(Service);
+    }
+
+  DPRINT("ScmStartService() done (Status %lx)\n", Status);
 
-Done:
   if (NT_SUCCESS(Status))
     {
       if (Group != NULL)
        {
          Group->ServicesRunning = TRUE;
        }
-      Service->ServiceRunning = TRUE;
+      Service->CurrentState = SERVICE_RUNNING;
     }
 #if 0
   else
     {
-      if (CurrentService->ErrorControl == 1)
+      switch (Service->ErrorControl)
        {
-         /* 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! */
-
-           }
+         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;
 }
 
 
@@ -634,6 +830,7 @@ ScmAutoStartServices(VOID)
   PLIST_ENTRY ServiceEntry;
   PSERVICE_GROUP CurrentGroup;
   PSERVICE CurrentService;
+  ULONG i;
 
   /* Clear 'ServiceVisited' flag */
   ServiceEntry = ServiceListHead.Flink;
@@ -652,6 +849,29 @@ ScmAutoStartServices(VOID)
 
       DPRINT("Group '%wZ'\n", &CurrentGroup->GroupName);
 
+      /* 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 ((RtlEqualUnicodeString(&CurrentGroup->GroupName, &CurrentService->ServiceGroup, TRUE)) &&
+                 (CurrentService->Start == SERVICE_AUTO_START) &&
+                 (CurrentService->ServiceVisited == FALSE) &&
+                 (CurrentService->Tag == 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)
        {
@@ -678,7 +898,7 @@ ScmAutoStartServices(VOID)
     {
       CurrentService = CONTAINING_RECORD(ServiceEntry, SERVICE, ServiceListEntry);
 
-      if ((CurrentGroup->GroupName.Length > 0) &&
+      if ((CurrentService->ServiceGroup.Length != 0) &&
          (CurrentService->Start == SERVICE_AUTO_START) &&
          (CurrentService->ServiceVisited == FALSE))
        {
@@ -696,7 +916,7 @@ ScmAutoStartServices(VOID)
     {
       CurrentService = CONTAINING_RECORD(ServiceEntry, SERVICE, ServiceListEntry);
 
-      if ((CurrentGroup->GroupName.Length == 0) &&
+      if ((CurrentService->ServiceGroup.Length == 0) &&
          (CurrentService->Start == SERVICE_AUTO_START) &&
          (CurrentService->ServiceVisited == FALSE))
        {
@@ -718,4 +938,13 @@ ScmAutoStartServices(VOID)
     }
 }
 
+
+DWORD
+ScmMarkServiceForDelete(PSERVICE pService)
+{
+  DPRINT1("ScmMarkServiceForDelete() called\n");
+
+  return ERROR_SUCCESS;
+}
+
 /* EOF */