Synchronize with trunk revision 59781.
[reactos.git] / base / system / services / database.c
index 7a9819e..a5c24d2 100644 (file)
 
 /*
  * Uncomment the line below to start services
- *  using the SERVICE_START_PENDING state
+ * using the SERVICE_START_PENDING state.
  */
-// #define USE_SERVICE_START_PENDING
+#define USE_SERVICE_START_PENDING
+
+/*
+ * Uncomment the line below to use asynchronous IO operations
+ * on the service control pipes.
+ */
+// #define USE_ASYNCHRONOUS_IO
+
 
 /* GLOBALS *******************************************************************/
 
+LIST_ENTRY ImageListHead;
 LIST_ENTRY ServiceListHead;
 
 static RTL_RESOURCE DatabaseLock;
-static DWORD dwResumeCount = 1;
+static DWORD ResumeCount = 1;
+
+/* The critical section synchronizes service control requests */
+static CRITICAL_SECTION ControlServiceCriticalSection;
+static DWORD PipeTimeout = 30000; /* 30 Seconds */
 
 
 /* FUNCTIONS *****************************************************************/
 
+static DWORD
+ScmCreateNewControlPipe(PSERVICE_IMAGE pServiceImage)
+{
+    WCHAR szControlPipeName[MAX_PATH + 1];
+    HKEY hServiceCurrentKey = INVALID_HANDLE_VALUE;
+    DWORD ServiceCurrent = 0;
+    DWORD KeyDisposition;
+    DWORD dwKeySize;
+    DWORD dwError;
+
+    /* Get the service number */
+    /* TODO: Create registry entry with correct write access */
+    dwError = RegCreateKeyExW(HKEY_LOCAL_MACHINE,
+                              L"SYSTEM\\CurrentControlSet\\Control\\ServiceCurrent", 0, NULL,
+                              REG_OPTION_VOLATILE,
+                              KEY_WRITE | KEY_READ,
+                              NULL,
+                              &hServiceCurrentKey,
+                              &KeyDisposition);
+    if (dwError != ERROR_SUCCESS)
+    {
+        DPRINT1("RegCreateKeyEx() failed with error %lu\n", dwError);
+        return dwError;
+    }
+
+    if (KeyDisposition == REG_OPENED_EXISTING_KEY)
+    {
+        dwKeySize = sizeof(DWORD);
+        dwError = RegQueryValueExW(hServiceCurrentKey,
+                                   L"", 0, NULL, (BYTE*)&ServiceCurrent, &dwKeySize);
+
+        if (dwError != ERROR_SUCCESS)
+        {
+            RegCloseKey(hServiceCurrentKey);
+            DPRINT1("RegQueryValueEx() failed with error %lu\n", dwError);
+            return dwError;
+        }
+
+        ServiceCurrent++;
+    }
+
+    dwError = RegSetValueExW(hServiceCurrentKey, L"", 0, REG_DWORD, (BYTE*)&ServiceCurrent, sizeof(ServiceCurrent));
+
+    RegCloseKey(hServiceCurrentKey);
+
+    if (dwError != ERROR_SUCCESS)
+    {
+        DPRINT1("RegSetValueExW() failed (Error %lu)\n", dwError);
+        return dwError;
+    }
+
+    /* Create '\\.\pipe\net\NtControlPipeXXX' instance */
+    swprintf(szControlPipeName, L"\\\\.\\pipe\\net\\NtControlPipe%u", ServiceCurrent);
+
+    DPRINT("PipeName: %S\n", szControlPipeName);
+
+    pServiceImage->hControlPipe = CreateNamedPipeW(szControlPipeName,
+#ifdef USE_ASYNCHRONOUS_IO
+                                                   PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
+#else
+                                                   PIPE_ACCESS_DUPLEX,
+#endif
+                                                   PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT,
+                                                   100,
+                                                   8000,
+                                                   4,
+                                                   PipeTimeout,
+                                                   NULL);
+    DPRINT("CreateNamedPipeW(%S) done\n", szControlPipeName);
+    if (pServiceImage->hControlPipe == INVALID_HANDLE_VALUE)
+    {
+        DPRINT1("Failed to create control pipe!\n");
+        return GetLastError();
+    }
+
+    return ERROR_SUCCESS;
+}
+
+
+static PSERVICE_IMAGE
+ScmGetServiceImageByImagePath(LPWSTR lpImagePath)
+{
+    PLIST_ENTRY ImageEntry;
+    PSERVICE_IMAGE CurrentImage;
+
+    DPRINT("ScmGetServiceImageByImagePath(%S) called\n", lpImagePath);
+
+    ImageEntry = ImageListHead.Flink;
+    while (ImageEntry != &ImageListHead)
+    {
+        CurrentImage = CONTAINING_RECORD(ImageEntry,
+                                         SERVICE_IMAGE,
+                                         ImageListEntry);
+        if (_wcsicmp(CurrentImage->szImagePath, lpImagePath) == 0)
+        {
+            DPRINT("Found image: '%S'\n", CurrentImage->szImagePath);
+            return CurrentImage;
+        }
+
+        ImageEntry = ImageEntry->Flink;
+    }
+
+    DPRINT("Couldn't find a matching image\n");
+
+    return NULL;
+
+}
+
+
+static DWORD
+ScmCreateOrReferenceServiceImage(PSERVICE pService)
+{
+    RTL_QUERY_REGISTRY_TABLE QueryTable[2];
+    UNICODE_STRING ImagePath;
+    PSERVICE_IMAGE pServiceImage = NULL;
+    NTSTATUS Status;
+    DWORD dwError = ERROR_SUCCESS;
+
+    DPRINT("ScmCreateOrReferenceServiceImage(%p)\n", pService);
+
+    RtlInitUnicodeString(&ImagePath, NULL);
+
+    /* Get service data */
+    RtlZeroMemory(&QueryTable,
+                  sizeof(QueryTable));
+
+    QueryTable[0].Name = L"ImagePath";
+    QueryTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_REQUIRED;
+    QueryTable[0].EntryContext = &ImagePath;
+
+    Status = RtlQueryRegistryValues(RTL_REGISTRY_SERVICES,
+                                    pService->lpServiceName,
+                                    QueryTable,
+                                    NULL,
+                                    NULL);
+    if (!NT_SUCCESS(Status))
+    {
+        DPRINT1("RtlQueryRegistryValues() failed (Status %lx)\n", Status);
+        return RtlNtStatusToDosError(Status);
+    }
+
+    DPRINT("ImagePath: '%wZ'\n", &ImagePath);
+
+    pServiceImage = ScmGetServiceImageByImagePath(ImagePath.Buffer);
+    if (pServiceImage == NULL)
+    {
+        /* Create a new service image */
+        pServiceImage = HeapAlloc(GetProcessHeap(),
+                                  HEAP_ZERO_MEMORY,
+                                  FIELD_OFFSET(SERVICE_IMAGE, szImagePath[ImagePath.Length / sizeof(WCHAR) + 1]));
+        if (pServiceImage == NULL)
+        {
+            dwError = ERROR_NOT_ENOUGH_MEMORY;
+            goto done;
+        }
+
+        pServiceImage->dwImageRunCount = 1;
+        pServiceImage->hControlPipe = INVALID_HANDLE_VALUE;
+
+        /* Set the image path */
+        wcscpy(pServiceImage->szImagePath,
+               ImagePath.Buffer);
+
+        RtlFreeUnicodeString(&ImagePath);
+
+        /* Create the control pipe */
+        dwError = ScmCreateNewControlPipe(pServiceImage);
+        if (dwError != ERROR_SUCCESS)
+        {
+            HeapFree(GetProcessHeap(), 0, pServiceImage);
+            goto done;
+        }
+
+        /* FIXME: Add more initialization code here */
+
+
+        /* Append service record */
+        InsertTailList(&ImageListHead,
+                       &pServiceImage->ImageListEntry);
+    }
+    else
+    {
+        /* Increment the run counter */
+        pServiceImage->dwImageRunCount++;
+    }
+
+    DPRINT("pServiceImage->dwImageRunCount: %lu\n", pServiceImage->dwImageRunCount);
+
+    /* Link the service image to the service */
+    pService->lpImage = pServiceImage;
+
+done:;
+    RtlFreeUnicodeString(&ImagePath);
+
+    DPRINT("ScmCreateOrReferenceServiceImage() done (Error: %lu)\n", dwError);
+
+    return dwError;
+}
+
+
+static VOID
+ScmDereferenceServiceImage(PSERVICE_IMAGE pServiceImage)
+{
+    DPRINT1("ScmDereferenceServiceImage() called\n");
+
+    pServiceImage->dwImageRunCount--;
+
+    if (pServiceImage->dwImageRunCount == 0)
+    {
+        DPRINT1("dwImageRunCount == 0\n");
+
+        /* FIXME: Terminate the process */
+
+        /* Remove the service image from the list */
+        RemoveEntryList(&pServiceImage->ImageListEntry);
+
+        /* Close the control pipe */
+        if (pServiceImage->hControlPipe != INVALID_HANDLE_VALUE)
+            CloseHandle(pServiceImage->hControlPipe);
+
+        /* Release the service image */
+        HeapFree(GetProcessHeap(), 0, pServiceImage);
+    }
+}
+
 
 PSERVICE
 ScmGetServiceEntryByName(LPCWSTR lpServiceName)
@@ -123,16 +360,16 @@ ScmGetServiceEntryByResumeCount(DWORD dwResumeCount)
 
 DWORD
 ScmCreateNewServiceRecord(LPCWSTR lpServiceName,
-                          PSERVICE *lpServiceRecord)
+                          PSERVICElpServiceRecord)
 {
     PSERVICE lpService = NULL;
 
     DPRINT("Service: '%S'\n", lpServiceName);
 
     /* Allocate service entry */
-    lpService = (SERVICE*) HeapAlloc(GetProcessHeap(),
+    lpService = HeapAlloc(GetProcessHeap(),
                           HEAP_ZERO_MEMORY,
-                          sizeof(SERVICE) + ((wcslen(lpServiceName) + 1) * sizeof(WCHAR)));
+                          FIELD_OFFSET(SERVICE, szServiceName[wcslen(lpServiceName) + 1]));
     if (lpService == NULL)
         return ERROR_NOT_ENOUGH_MEMORY;
 
@@ -144,7 +381,7 @@ ScmCreateNewServiceRecord(LPCWSTR lpServiceName,
     lpService->lpDisplayName = lpService->lpServiceName;
 
     /* Set the resume count */
-    lpService->dwResumeCount = dwResumeCount++;
+    lpService->dwResumeCount = ResumeCount++;
 
     /* Append service record */
     InsertTailList(&ServiceListHead,
@@ -172,9 +409,9 @@ ScmDeleteServiceRecord(PSERVICE lpService)
         lpService->lpDisplayName != lpService->lpServiceName)
         HeapFree(GetProcessHeap(), 0, lpService->lpDisplayName);
 
-    /* Decrement the image reference counter */
+    /* Dereference the service image */
     if (lpService->lpImage)
-        lpService->lpImage->dwServiceRefCount--;
+        ScmDereferenceServiceImage(lpService->lpImage);
 
     /* Decrement the group reference counter */
     if (lpService->lpGroup)
@@ -182,9 +419,6 @@ ScmDeleteServiceRecord(PSERVICE lpService)
 
     /* FIXME: SecurityDescriptor */
 
-    /* Close the control pipe */
-    if (lpService->ControlPipeHandle != INVALID_HANDLE_VALUE)
-        CloseHandle(lpService->ControlPipeHandle);
 
     /* Remove the Service from the List */
     RemoveEntryList(&lpService->ServiceListEntry);
@@ -329,6 +563,12 @@ done:;
     if (lpDisplayName != NULL)
         HeapFree(GetProcessHeap(), 0, lpDisplayName);
 
+    if (lpService != NULL)
+    {
+        if (lpService->lpImage != NULL)
+            ScmDereferenceServiceImage(lpService->lpImage);
+    }
+
     return dwError;
 }
 
@@ -345,15 +585,17 @@ ScmDeleteRegKey(HKEY hKey, LPCWSTR lpszSubKey)
     {
         /* 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);
+                                 &dwMaxSubkeyLen, NULL, NULL, NULL, NULL, NULL, NULL);
         if (!dwRet)
         {
             dwMaxSubkeyLen++;
-            if (dwMaxSubkeyLen > sizeof(szNameBuf)/sizeof(WCHAR))
+            if (dwMaxSubkeyLen > sizeof(szNameBuf) / sizeof(WCHAR))
+            {
                 /* Name too big: alloc a buffer for it */
-                lpszName = HeapAlloc(GetProcessHeap(), 0, dwMaxSubkeyLen*sizeof(WCHAR));
+                lpszName = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dwMaxSubkeyLen * sizeof(WCHAR));
+            }
 
-            if(!lpszName)
+            if (!lpszName)
                 dwRet = ERROR_NOT_ENOUGH_MEMORY;
             else
             {
@@ -412,7 +654,7 @@ ScmDeleteMarkedServices(VOID)
                     HeapFree(GetProcessHeap(), 0, CurrentService);
                 }
             }
-            
+
             if (dwError != ERROR_SUCCESS)
                 DPRINT1("Delete service failed: %S\n", CurrentService->lpServiceName);
         }
@@ -420,46 +662,6 @@ ScmDeleteMarkedServices(VOID)
 }
 
 
-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)
 {
@@ -478,6 +680,7 @@ ScmCreateServiceDatabase(VOID)
         return dwError;
 
     /* Initialize basic variables */
+    InitializeListHead(&ImageListHead);
     InitializeListHead(&ServiceListHead);
 
     /* Initialize the database lock */
@@ -530,8 +733,8 @@ ScmCreateServiceDatabase(VOID)
 
     RegCloseKey(hServicesKey);
 
-    /* Wait for LSA */
-    WaitForLSA();
+    /* Wait for the LSA server */
+    ScmWaitForLsa();
 
     /* Delete services that are marked for delete */
     ScmDeleteMarkedServices();
@@ -570,13 +773,12 @@ ScmCheckDriver(PSERVICE Service)
 
     if (Service->Status.dwServiceType == SERVICE_KERNEL_DRIVER)
     {
-        RtlInitUnicodeString(&DirName,
-                             L"\\Driver");
+        RtlInitUnicodeString(&DirName, L"\\Driver");
     }
-    else
+    else // if (Service->Status.dwServiceType == SERVICE_FILE_SYSTEM_DRIVER)
     {
-        RtlInitUnicodeString(&DirName,
-                             L"\\FileSystem");
+        ASSERT(Service->Status.dwServiceType == SERVICE_FILE_SYSTEM_DRIVER);
+        RtlInitUnicodeString(&DirName, L"\\FileSystem");
     }
 
     InitializeObjectAttributes(&ObjectAttributes,
@@ -594,8 +796,8 @@ ScmCheckDriver(PSERVICE Service)
     }
 
     BufferLength = sizeof(OBJECT_DIRECTORY_INFORMATION) +
-                   2 * MAX_PATH * sizeof(WCHAR);
-    DirInfo = (OBJECT_DIRECTORY_INFORMATION*) HeapAlloc(GetProcessHeap(),
+                       2 * MAX_PATH * sizeof(WCHAR);
+    DirInfo = HeapAlloc(GetProcessHeap(),
                         HEAP_ZERO_MEMORY,
                         BufferLength);
 
@@ -686,38 +888,188 @@ ScmControlService(PSERVICE Service,
 
     DWORD dwWriteCount = 0;
     DWORD dwReadCount = 0;
-    DWORD TotalLength;
+    DWORD PacketSize;
+    PWSTR Ptr;
     DWORD dwError = ERROR_SUCCESS;
+    BOOL bResult;
+#ifdef USE_ASYNCHRONOUS_IO
+    OVERLAPPED Overlapped = {0, 0, 0, 0, 0};
+#endif
 
     DPRINT("ScmControlService() called\n");
 
-    TotalLength = wcslen(Service->lpServiceName) + 1;
+    /* Acquire the service control critical section, to synchronize requests */
+    EnterCriticalSection(&ControlServiceCriticalSection);
+
+    /* Calculate the total length of the start command line */
+    PacketSize = sizeof(SCM_CONTROL_PACKET);
+    PacketSize += (DWORD)((wcslen(Service->lpServiceName) + 1) * sizeof(WCHAR));
 
-    ControlPacket = (SCM_CONTROL_PACKET*)HeapAlloc(GetProcessHeap(),
-                                                   HEAP_ZERO_MEMORY,
-                                                   sizeof(SCM_CONTROL_PACKET) + (TotalLength * sizeof(WCHAR)));
+    ControlPacket = HeapAlloc(GetProcessHeap(),
+                              HEAP_ZERO_MEMORY,
+                              PacketSize);
     if (ControlPacket == NULL)
+    {
+        LeaveCriticalSection(&ControlServiceCriticalSection);
         return ERROR_NOT_ENOUGH_MEMORY;
+    }
 
+    ControlPacket->dwSize = PacketSize;
     ControlPacket->dwControl = dwControl;
-    ControlPacket->dwSize = TotalLength;
     ControlPacket->hServiceStatus = (SERVICE_STATUS_HANDLE)Service;
-    wcscpy(&ControlPacket->szArguments[0], Service->lpServiceName);
 
+    ControlPacket->dwServiceNameOffset = sizeof(SCM_CONTROL_PACKET);
+
+    Ptr = (PWSTR)((PBYTE)ControlPacket + ControlPacket->dwServiceNameOffset);
+    wcscpy(Ptr, Service->lpServiceName);
+
+    ControlPacket->dwArgumentsCount = 0;
+    ControlPacket->dwArgumentsOffset = 0;
+
+#ifdef USE_ASYNCHRONOUS_IO
+    bResult = WriteFile(Service->lpImage->hControlPipe,
+                        ControlPacket,
+                        PacketSize,
+                        &dwWriteCount,
+                        &Overlapped);
+    if (bResult == FALSE)
+    {
+        DPRINT1("WriteFile() returned FALSE\n");
+
+        dwError = GetLastError();
+        if (dwError == ERROR_IO_PENDING)
+        {
+            DPRINT1("dwError: ERROR_IO_PENDING\n");
+
+            dwError = WaitForSingleObject(Service->lpImage->hControlPipe,
+                                          PipeTimeout);
+            DPRINT1("WaitForSingleObject() returned %lu\n", dwError);
+
+            if (dwError == WAIT_TIMEOUT)
+            {
+                bResult = CancelIo(Service->lpImage->hControlPipe);
+                if (bResult == FALSE)
+                {
+                    DPRINT1("CancelIo() failed (Error: %lu)\n", GetLastError());
+                }
+
+                dwError = ERROR_SERVICE_REQUEST_TIMEOUT;
+                goto Done;
+            }
+            else if (dwError == WAIT_OBJECT_0)
+            {
+                bResult = GetOverlappedResult(Service->lpImage->hControlPipe,
+                                              &Overlapped,
+                                              &dwWriteCount,
+                                              TRUE);
+                if (bResult == FALSE)
+                {
+                    dwError = GetLastError();
+                    DPRINT1("GetOverlappedResult() failed (Error %lu)\n", dwError);
+
+                    goto Done;
+                }
+            }
+        }
+        else
+        {
+            DPRINT1("WriteFile() failed (Error %lu)\n", dwError);
+            goto Done;
+        }
+    }
+
+    /* Read the reply */
+    Overlapped.hEvent = (HANDLE) NULL;
+
+    bResult = ReadFile(Service->lpImage->hControlPipe,
+                       &ReplyPacket,
+                       sizeof(SCM_REPLY_PACKET),
+                       &dwReadCount,
+                       &Overlapped);
+    if (bResult == FALSE)
+    {
+        DPRINT1("ReadFile() returned FALSE\n");
+
+        dwError = GetLastError();
+        if (dwError == ERROR_IO_PENDING)
+        {
+            DPRINT1("dwError: ERROR_IO_PENDING\n");
+
+            dwError = WaitForSingleObject(Service->lpImage->hControlPipe,
+                                          PipeTimeout);
+            DPRINT1("WaitForSingleObject() returned %lu\n", dwError);
+
+            if (dwError == WAIT_TIMEOUT)
+            {
+                bResult = CancelIo(Service->lpImage->hControlPipe);
+                if (bResult == FALSE)
+                {
+                    DPRINT1("CancelIo() failed (Error: %lu)\n", GetLastError());
+                }
+
+                dwError = ERROR_SERVICE_REQUEST_TIMEOUT;
+                goto Done;
+            }
+            else if (dwError == WAIT_OBJECT_0)
+            {
+                bResult = GetOverlappedResult(Service->lpImage->hControlPipe,
+                                              &Overlapped,
+                                              &dwReadCount,
+                                              TRUE);
+                if (bResult == FALSE)
+                {
+                    dwError = GetLastError();
+                    DPRINT1("GetOverlappedResult() failed (Error %lu)\n", dwError);
+
+                    goto Done;
+                }
+            }
+        }
+        else
+        {
+            DPRINT1("ReadFile() failed (Error %lu)\n", dwError);
+            goto Done;
+        }
+    }
+
+#else
     /* Send the control packet */
-    WriteFile(Service->ControlPipeHandle,
-              ControlPacket,
-              sizeof(SCM_CONTROL_PACKET) + (TotalLength * sizeof(WCHAR)),
-              &dwWriteCount,
-              NULL);
+    bResult = WriteFile(Service->lpImage->hControlPipe,
+                        ControlPacket,
+                        PacketSize,
+                        &dwWriteCount,
+                        NULL);
+    if (bResult == FALSE)
+    {
+        dwError = GetLastError();
+        DPRINT("WriteFile() failed (Error %lu)\n", dwError);
+
+        if ((dwError == ERROR_GEN_FAILURE) &&
+            (dwControl == SERVICE_CONTROL_STOP))
+        {
+            /* Service is already terminated */
+            Service->Status.dwCurrentState = SERVICE_STOPPED;
+            Service->Status.dwControlsAccepted = 0;
+            Service->Status.dwWin32ExitCode = ERROR_SERVICE_NOT_ACTIVE;
+            dwError = ERROR_SUCCESS;
+        }
+        goto Done;
+    }
 
     /* Read the reply */
-    ReadFile(Service->ControlPipeHandle,
-             &ReplyPacket,
-             sizeof(SCM_REPLY_PACKET),
-             &dwReadCount,
-             NULL);
+    bResult = ReadFile(Service->lpImage->hControlPipe,
+                       &ReplyPacket,
+                       sizeof(SCM_REPLY_PACKET),
+                       &dwReadCount,
+                       NULL);
+    if (bResult == FALSE)
+    {
+        dwError = GetLastError();
+        DPRINT("ReadFile() failed (Error %lu)\n", dwError);
+    }
+#endif
 
+Done:
     /* Release the contol packet */
     HeapFree(GetProcessHeap(),
              0,
@@ -728,6 +1080,14 @@ ScmControlService(PSERVICE Service,
         dwError = ReplyPacket.dwError;
     }
 
+    if (dwError == ERROR_SUCCESS &&
+        dwControl == SERVICE_CONTROL_STOP)
+    {
+        ScmDereferenceServiceImage(Service->lpImage);
+    }
+
+    LeaveCriticalSection(&ControlServiceCriticalSection);
+
     DPRINT("ScmControlService() done\n");
 
     return dwError;
@@ -737,208 +1097,429 @@ ScmControlService(PSERVICE Service,
 static DWORD
 ScmSendStartCommand(PSERVICE Service,
                     DWORD argc,
-                    LPWSTR *argv)
+                    LPWSTRargv)
 {
     PSCM_CONTROL_PACKET ControlPacket;
     SCM_REPLY_PACKET ReplyPacket;
-    DWORD TotalLength;
-    DWORD ArgsLength = 0;
-    DWORD Length;
+    DWORD PacketSize;
     PWSTR Ptr;
     DWORD dwWriteCount = 0;
     DWORD dwReadCount = 0;
     DWORD dwError = ERROR_SUCCESS;
     DWORD i;
+    PWSTR *pOffPtr;
+    PWSTR pArgPtr;
+    BOOL bResult;
+#ifdef USE_ASYNCHRONOUS_IO
+    OVERLAPPED Overlapped = {0, 0, 0, 0, 0};
+#endif
 
     DPRINT("ScmSendStartCommand() called\n");
 
     /* Calculate the total length of the start command line */
-    TotalLength = wcslen(Service->lpServiceName) + 1;
-    if (argc > 0)
+    PacketSize = sizeof(SCM_CONTROL_PACKET) +
+                 (DWORD)((wcslen(Service->lpServiceName) + 1) * sizeof(WCHAR));
+
+    /* Calculate the required packet size for the start arguments */
+    if (argc > 0 && argv != NULL)
     {
+        PacketSize = ALIGN_UP(PacketSize, LPWSTR);
+
+        DPRINT("Argc: %lu\n", argc);
         for (i = 0; i < argc; i++)
         {
-            DPRINT("Arg: %S\n", argv[i]);
-            Length = wcslen(argv[i]) + 1;
-            TotalLength += Length;
-            ArgsLength += Length;
+            DPRINT("Argv[%lu]: %S\n", i, argv[i]);
+            PacketSize += (DWORD)((wcslen(argv[i]) + 1) * sizeof(WCHAR) + sizeof(PWSTR));
         }
     }
-    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));
+    ControlPacket = HeapAlloc(GetProcessHeap(),
+                              HEAP_ZERO_MEMORY,
+                              PacketSize);
     if (ControlPacket == NULL)
         return ERROR_NOT_ENOUGH_MEMORY;
 
-    ControlPacket->dwControl = SERVICE_CONTROL_START;
+    ControlPacket->dwSize = PacketSize;
+    ControlPacket->dwControl = (Service->Status.dwServiceType & SERVICE_WIN32_OWN_PROCESS)
+                               ? SERVICE_CONTROL_START_OWN
+                               : SERVICE_CONTROL_START_SHARE;
     ControlPacket->hServiceStatus = (SERVICE_STATUS_HANDLE)Service;
-    ControlPacket->dwSize = TotalLength;
-    Ptr = &ControlPacket->szArguments[0];
+    ControlPacket->dwServiceNameOffset = sizeof(SCM_CONTROL_PACKET);
+
+    Ptr = (PWSTR)((PBYTE)ControlPacket + ControlPacket->dwServiceNameOffset);
     wcscpy(Ptr, Service->lpServiceName);
-    Ptr += (wcslen(Service->lpServiceName) + 1);
+
+    ControlPacket->dwArgumentsCount = 0;
+    ControlPacket->dwArgumentsOffset = 0;
 
     /* Copy argument list */
-    if (argc > 0)
+    if (argc > 0 && argv != NULL)
     {
-        UNIMPLEMENTED;
-        DPRINT1("Arguments sent to service ignored!\n");
-#if 0
-        memcpy(Ptr, Arguments, ArgsLength);
-        Ptr += ArgsLength;
-#endif
-    }
-
-    /* Terminate the argument list */
-    *Ptr = 0;
+        Ptr += wcslen(Service->lpServiceName) + 1;
+        pOffPtr = (PWSTR*)ALIGN_UP_POINTER(Ptr, PWSTR);
+        pArgPtr = (PWSTR)((ULONG_PTR)pOffPtr + argc * sizeof(PWSTR));
 
-    /* Send the start command */
-    WriteFile(Service->ControlPipeHandle,
-              ControlPacket,
-              sizeof(SCM_CONTROL_PACKET) + (TotalLength - 1) * sizeof(WCHAR),
-              &dwWriteCount,
-              NULL);
+        ControlPacket->dwArgumentsCount = argc;
+        ControlPacket->dwArgumentsOffset = (DWORD)((ULONG_PTR)pOffPtr - (ULONG_PTR)ControlPacket);
 
-    /* Read the reply */
-    ReadFile(Service->ControlPipeHandle,
-             &ReplyPacket,
-             sizeof(SCM_REPLY_PACKET),
-             &dwReadCount,
-             NULL);
+        DPRINT("dwArgumentsCount: %lu\n", ControlPacket->dwArgumentsCount);
+        DPRINT("dwArgumentsOffset: %lu\n", ControlPacket->dwArgumentsOffset);
 
-    /* Release the contol packet */
-    HeapFree(GetProcessHeap(),
-             0,
-             ControlPacket);
+        for (i = 0; i < argc; i++)
+        {
+             wcscpy(pArgPtr, argv[i]);
+             *pOffPtr = (PWSTR)((ULONG_PTR)pArgPtr - (ULONG_PTR)pOffPtr);
+             DPRINT("offset: %p\n", *pOffPtr);
 
-    if (dwReadCount == sizeof(SCM_REPLY_PACKET))
-    {
-        dwError = ReplyPacket.dwError;
+             pArgPtr += wcslen(argv[i]) + 1;
+             pOffPtr++;
+        }
     }
 
-    DPRINT("ScmSendStartCommand() done\n");
+#ifdef USE_ASYNCHRONOUS_IO
+    bResult = WriteFile(Service->lpImage->hControlPipe,
+                        ControlPacket,
+                        PacketSize,
+                        &dwWriteCount,
+                        &Overlapped);
+    if (bResult == FALSE)
+    {
+        DPRINT1("WriteFile() returned FALSE\n");
 
-    return dwError;
-}
+        dwError = GetLastError();
+        if (dwError == ERROR_IO_PENDING)
+        {
+            DPRINT1("dwError: ERROR_IO_PENDING\n");
 
+            dwError = WaitForSingleObject(Service->lpImage->hControlPipe,
+                                          PipeTimeout);
+            DPRINT1("WaitForSingleObject() returned %lu\n", dwError);
 
-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;
-    DWORD dwProcessId;
+            if (dwError == WAIT_TIMEOUT)
+            {
+                bResult = CancelIo(Service->lpImage->hControlPipe);
+                if (bResult == FALSE)
+                {
+                    DPRINT1("CancelIo() failed (Error: %lu)\n", GetLastError());
+                }
 
-    RtlInitUnicodeString(&ImagePath, NULL);
+                dwError = ERROR_SERVICE_REQUEST_TIMEOUT;
+                goto Done;
+            }
+            else if (dwError == WAIT_OBJECT_0)
+            {
+                bResult = GetOverlappedResult(Service->lpImage->hControlPipe,
+                                              &Overlapped,
+                                              &dwWriteCount,
+                                              TRUE);
+                if (bResult == FALSE)
+                {
+                    dwError = GetLastError();
+                    DPRINT1("GetOverlappedResult() failed (Error %lu)\n", dwError);
 
-    /* Get service data */
-    RtlZeroMemory(&QueryTable,
-                  sizeof(QueryTable));
+                    goto Done;
+                }
+            }
+        }
+        else
+        {
+            DPRINT1("WriteFile() failed (Error %lu)\n", dwError);
+            goto Done;
+        }
+    }
 
-    QueryTable[0].Name = L"Type";
-    QueryTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_REQUIRED;
-    QueryTable[0].EntryContext = &Type;
+    /* Read the reply */
+    Overlapped.hEvent = (HANDLE) NULL;
+
+    bResult = ReadFile(Service->lpImage->hControlPipe,
+                       &ReplyPacket,
+                       sizeof(SCM_REPLY_PACKET),
+                       &dwReadCount,
+                       &Overlapped);
+    if (bResult == FALSE)
+    {
+        DPRINT1("ReadFile() returned FALSE\n");
 
-    QueryTable[1].Name = L"ImagePath";
-    QueryTable[1].Flags = RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_REQUIRED;
-    QueryTable[1].EntryContext = &ImagePath;
+        dwError = GetLastError();
+        if (dwError == ERROR_IO_PENDING)
+        {
+            DPRINT1("dwError: ERROR_IO_PENDING\n");
 
-    Status = RtlQueryRegistryValues(RTL_REGISTRY_SERVICES,
-                                    Service->lpServiceName,
-                                    QueryTable,
-                                    NULL,
-                                    NULL);
-    if (!NT_SUCCESS(Status))
+            dwError = WaitForSingleObject(Service->lpImage->hControlPipe,
+                                          PipeTimeout);
+            DPRINT1("WaitForSingleObject() returned %lu\n", dwError);
+
+            if (dwError == WAIT_TIMEOUT)
+            {
+                bResult = CancelIo(Service->lpImage->hControlPipe);
+                if (bResult == FALSE)
+                {
+                    DPRINT1("CancelIo() failed (Error: %lu)\n", GetLastError());
+                }
+
+                dwError = ERROR_SERVICE_REQUEST_TIMEOUT;
+                goto Done;
+            }
+            else if (dwError == WAIT_OBJECT_0)
+            {
+                bResult = GetOverlappedResult(Service->lpImage->hControlPipe,
+                                              &Overlapped,
+                                              &dwReadCount,
+                                              TRUE);
+                if (bResult == FALSE)
+                {
+                    dwError = GetLastError();
+                    DPRINT1("GetOverlappedResult() failed (Error %lu)\n", dwError);
+
+                    goto Done;
+                }
+            }
+        }
+        else
+        {
+            DPRINT1("ReadFile() failed (Error %lu)\n", dwError);
+            goto Done;
+        }
+    }
+
+#else
+    /* Send the start command */
+    bResult = WriteFile(Service->lpImage->hControlPipe,
+                        ControlPacket,
+                        PacketSize,
+                        &dwWriteCount,
+                        NULL);
+    if (bResult == FALSE)
     {
-        DPRINT1("RtlQueryRegistryValues() failed (Status %lx)\n", Status);
-        return RtlNtStatusToDosError(Status);
+        dwError = GetLastError();
+        DPRINT("WriteFile() failed (Error %lu)\n", dwError);
+        goto Done;
     }
-    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);
+    /* Read the reply */
+    bResult = ReadFile(Service->lpImage->hControlPipe,
+                       &ReplyPacket,
+                       sizeof(SCM_REPLY_PACKET),
+                       &dwReadCount,
+                       NULL);
+    if (bResult == FALSE)
+    {
+        dwError = GetLastError();
+        DPRINT("ReadFile() failed (Error %lu)\n", dwError);
+    }
+#endif
+
+Done:
+    /* Release the contol packet */
+    HeapFree(GetProcessHeap(),
+             0,
+             ControlPacket);
 
-    if (ERROR_SUCCESS != Status)
+    if (dwReadCount == sizeof(SCM_REPLY_PACKET))
     {
-        DPRINT1("RegCreateKeyEx() failed with status %u\n", Status);
-        return Status;
+        dwError = ReplyPacket.dwError;
     }
 
-    if (REG_OPENED_EXISTING_KEY == KeyDisposition)
+    DPRINT("ScmSendStartCommand() done\n");
+
+    return dwError;
+}
+
+
+static DWORD
+ScmWaitForServiceConnect(PSERVICE Service)
+{
+    DWORD dwRead = 0;
+    DWORD dwProcessId = 0;
+    DWORD dwError = ERROR_SUCCESS;
+    BOOL bResult;
+#ifdef USE_ASYNCHRONOUS_IO
+    OVERLAPPED Overlapped = {0, 0, 0, 0, 0};
+#endif
+
+    DPRINT("ScmWaitForServiceConnect()\n");
+
+#ifdef USE_ASYNCHRONOUS_IO
+    Overlapped.hEvent = (HANDLE)NULL;
+
+    bResult = ConnectNamedPipe(Service->lpImage->hControlPipe,
+                               &Overlapped);
+    if (bResult == FALSE)
     {
-        DWORD KeySize = sizeof(ServiceCurrent);
-        Status = RegQueryValueExW(hServiceCurrentKey, L"", 0, NULL, (BYTE*)&ServiceCurrent, &KeySize);
+        DPRINT("ConnectNamedPipe() returned FALSE\n");
 
-        if (ERROR_SUCCESS != Status)
+        dwError = GetLastError();
+        if (dwError == ERROR_IO_PENDING)
         {
-            RegCloseKey(hServiceCurrentKey);
-            DPRINT1("RegQueryValueEx() failed with status %u\n", Status);
-            return Status;
+            DPRINT("dwError: ERROR_IO_PENDING\n");
+
+            dwError = WaitForSingleObject(Service->lpImage->hControlPipe,
+                                          PipeTimeout);
+            DPRINT("WaitForSingleObject() returned %lu\n", dwError);
+
+            if (dwError == WAIT_TIMEOUT)
+            {
+                DPRINT("WaitForSingleObject() returned WAIT_TIMEOUT\n");
+
+                bResult = CancelIo(Service->lpImage->hControlPipe);
+                if (bResult == FALSE)
+                {
+                    DPRINT1("CancelIo() failed (Error: %lu)\n", GetLastError());
+                }
+
+                return ERROR_SERVICE_REQUEST_TIMEOUT;
+            }
+            else if (dwError == WAIT_OBJECT_0)
+            {
+                bResult = GetOverlappedResult(Service->lpImage->hControlPipe,
+                                              &Overlapped,
+                                              &dwRead,
+                                              TRUE);
+                if (bResult == FALSE)
+                {
+                    dwError = GetLastError();
+                    DPRINT1("GetOverlappedResult failed (Error %lu)\n", dwError);
+
+                    return dwError;
+                }
+            }
+        }
+        else if (dwError != ERROR_PIPE_CONNECTED)
+        {
+            DPRINT1("ConnectNamedPipe failed (Error %lu)\n", dwError);
+            return dwError;
         }
+    }
 
-        ServiceCurrent++;
+    DPRINT("Control pipe connected!\n");
+
+    Overlapped.hEvent = (HANDLE) NULL;
+
+    /* Read the process id from pipe */
+    bResult = ReadFile(Service->lpImage->hControlPipe,
+                       (LPVOID)&dwProcessId,
+                       sizeof(DWORD),
+                       &dwRead,
+                       &Overlapped);
+    if (bResult == FALSE)
+    {
+        DPRINT("ReadFile() returned FALSE\n");
+
+        dwError = GetLastError();
+        if (dwError == ERROR_IO_PENDING)
+        {
+            DPRINT("dwError: ERROR_IO_PENDING\n");
+
+            dwError = WaitForSingleObject(Service->lpImage->hControlPipe,
+                                          PipeTimeout);
+            if (dwError == WAIT_TIMEOUT)
+            {
+                DPRINT("WaitForSingleObject() returned WAIT_TIMEOUT\n");
+
+                bResult = CancelIo(Service->lpImage->hControlPipe);
+                if (bResult == FALSE)
+                {
+                    DPRINT1("CancelIo() failed (Error: %lu)\n", GetLastError());
+                }
+
+                return ERROR_SERVICE_REQUEST_TIMEOUT;
+            }
+            else if (dwError == WAIT_OBJECT_0)
+            {
+                DPRINT("WaitForSingleObject() returned WAIT_OBJECT_0\n");
+
+                DPRINT("Process Id: %lu\n", dwProcessId);
+
+                bResult = GetOverlappedResult(Service->lpImage->hControlPipe,
+                                              &Overlapped,
+                                              &dwRead,
+                                              TRUE);
+                if (bResult == FALSE)
+                {
+                    dwError = GetLastError();
+                    DPRINT1("GetOverlappedResult() failed (Error %lu)\n", dwError);
+
+                    return dwError;
+                }
+            }
+            else
+            {
+                DPRINT1("WaitForSingleObject() returned %lu\n", dwError);
+            }
+        }
+        else
+        {
+            DPRINT1("ReadFile() failed (Error %lu)\n", dwError);
+            return dwError;
+        }
     }
 
-    Status = RegSetValueExW(hServiceCurrentKey, L"", 0, REG_DWORD, (BYTE*)&ServiceCurrent, sizeof(ServiceCurrent));
+    DPRINT1("ScmWaitForServiceConnect() done\n");
 
-    RegCloseKey(hServiceCurrentKey);
+    return ERROR_SUCCESS;
+#else
 
-    if (ERROR_SUCCESS != Status)
+    /* Connect control pipe */
+    if (ConnectNamedPipe(Service->lpImage->hControlPipe, NULL) ?
+        TRUE : (dwError = GetLastError()) == ERROR_PIPE_CONNECTED)
     {
-        DPRINT1("RegSetValueExW() failed (Status %lx)\n", Status);
-        return Status;
+        DPRINT("Control pipe connected!\n");
+
+        /* Read SERVICE_STATUS_HANDLE from pipe */
+        bResult = ReadFile(Service->lpImage->hControlPipe,
+                           (LPVOID)&dwProcessId,
+                           sizeof(DWORD),
+                           &dwRead,
+                           NULL);
+        if (bResult == FALSE)
+        {
+            dwError = GetLastError();
+            DPRINT1("Reading the service control pipe failed (Error %lu)\n",
+                    dwError);
+        }
+        else
+        {
+            dwError = ERROR_SUCCESS;
+            DPRINT("Read control pipe successfully\n");
+        }
+    }
+    else
+    {
+        DPRINT1("Connecting control pipe failed! (Error %lu)\n", dwError);
     }
 
-    /* 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)
+    return dwError;
+#endif
+}
+
+
+static DWORD
+ScmStartUserModeService(PSERVICE Service,
+                        DWORD argc,
+                        LPWSTR* argv)
+{
+    PROCESS_INFORMATION ProcessInformation;
+    STARTUPINFOW StartupInfo;
+    BOOL Result;
+    DWORD dwError = ERROR_SUCCESS;
+
+    DPRINT("ScmStartUserModeService(%p)\n", Service);
+
+    /* If the image is already running ... */
+    if (Service->lpImage->dwImageRunCount > 1)
     {
-        DPRINT1("Failed to create control pipe!\n");
-        return GetLastError();
+        /* ... just send a start command */
+        return ScmSendStartCommand(Service, argc, argv);
     }
 
+    /* Otherwise start its process */
+    ZeroMemory(&StartupInfo, sizeof(StartupInfo));
     StartupInfo.cb = sizeof(StartupInfo);
-    StartupInfo.lpReserved = NULL;
-    StartupInfo.lpDesktop = NULL;
-    StartupInfo.lpTitle = NULL;
-    StartupInfo.dwFlags = 0;
-    StartupInfo.cbReserved2 = 0;
-    StartupInfo.lpReserved2 = 0;
+    ZeroMemory(&ProcessInformation, sizeof(ProcessInformation));
 
     Result = CreateProcessW(NULL,
-                            ImagePath.Buffer,
+                            Service->lpImage->szImagePath,
                             NULL,
                             NULL,
                             FALSE,
@@ -947,72 +1528,40 @@ ScmStartUserModeService(PSERVICE Service,
                             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",
+    DPRINT("Process Id: %lu  Handle %p\n",
            ProcessInformation.dwProcessId,
            ProcessInformation.hProcess);
-    DPRINT("Thread Id: %lu  Handle %lx\n",
+    DPRINT("Thread Id: %lu  Handle %p\n",
            ProcessInformation.dwThreadId,
            ProcessInformation.hThread);
 
-    /* Get process and thread ids */
-    Service->ProcessId = ProcessInformation.dwProcessId;
-    Service->ThreadId = ProcessInformation.dwThreadId;
+    /* Get process handle and id */
+    Service->lpImage->dwProcessId = ProcessInformation.dwProcessId;
 
     /* Resume Thread */
     ResumeThread(ProcessInformation.hThread);
 
     /* Connect control pipe */
-    if (ConnectNamedPipe(Service->ControlPipeHandle, NULL) ?
-        TRUE : (dwError = GetLastError()) == ERROR_PIPE_CONNECTED)
+    dwError = ScmWaitForServiceConnect(Service);
+    if (dwError == ERROR_SUCCESS)
     {
-        DWORD dwRead = 0;
-
-        DPRINT("Control pipe connected!\n");
-
-        /* Read SERVICE_STATUS_HANDLE from pipe */
-        if (!ReadFile(Service->ControlPipeHandle,
-                      (LPVOID)&dwProcessId,
-                      sizeof(DWORD),
-                      &dwRead,
-                      NULL))
-        {
-            dwError = GetLastError();
-            DPRINT1("Reading the service control pipe failed (Error %lu)\n",
-                    dwError);
-        }
-        else
-        {
-            DPRINT("Received service process ID %lu\n", dwProcessId);
-
-            /* Send start command */
-            dwError = ScmSendStartCommand(Service, argc, argv);
-        }
+        /* 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;
+        Service->lpImage->dwProcessId = 0;
     }
 
-    /* Close process and thread handle */
+    /* Close thread and process handle */
     CloseHandle(ProcessInformation.hThread);
     CloseHandle(ProcessInformation.hProcess);
 
@@ -1020,15 +1569,25 @@ ScmStartUserModeService(PSERVICE Service,
 }
 
 
-DWORD
-ScmStartService(PSERVICE Service, DWORD argc, LPWSTR *argv)
+static DWORD
+ScmLoadService(PSERVICE Service,
+               DWORD argc,
+               LPWSTR* argv)
 {
     PSERVICE_GROUP Group = Service->lpGroup;
     DWORD dwError = ERROR_SUCCESS;
+    LPCWSTR ErrorLogStrings[2];
+    WCHAR szErrorBuffer[32];
 
-    DPRINT("ScmStartService() called\n");
+    DPRINT("ScmLoadService() called\n");
+    DPRINT("Start Service %p (%S)\n", Service, Service->lpServiceName);
+
+    if (Service->Status.dwCurrentState != SERVICE_STOPPED)
+    {
+        DPRINT("Service %S is already running!\n", Service->lpServiceName);
+        return ERROR_SERVICE_ALREADY_RUNNING;
+    }
 
-    Service->ControlPipeHandle = INVALID_HANDLE_VALUE;
     DPRINT("Service->Type: %lu\n", Service->Status.dwServiceType);
 
     if (Service->Status.dwServiceType & SERVICE_DRIVER)
@@ -1041,21 +1600,30 @@ ScmStartService(PSERVICE Service, DWORD argc, LPWSTR *argv)
             Service->Status.dwCurrentState = SERVICE_RUNNING;
         }
     }
-    else
+    else // if (Service->Status.dwServiceType & (SERVICE_WIN32 | SERVICE_INTERACTIVE_PROCESS))
     {
         /* Start user-mode service */
-        dwError = ScmStartUserModeService(Service, argc, argv);
+        dwError = ScmCreateOrReferenceServiceImage(Service);
         if (dwError == ERROR_SUCCESS)
         {
+            dwError = ScmStartUserModeService(Service, argc, argv);
+            if (dwError == ERROR_SUCCESS)
+            {
 #ifdef USE_SERVICE_START_PENDING
-            Service->Status.dwCurrentState = SERVICE_START_PENDING;
+                Service->Status.dwCurrentState = SERVICE_START_PENDING;
 #else
-            Service->Status.dwCurrentState = SERVICE_RUNNING;
+                Service->Status.dwCurrentState = SERVICE_RUNNING;
 #endif
+            }
+            else
+            {
+                ScmDereferenceServiceImage(Service->lpImage);
+                Service->lpImage = NULL;
+            }
         }
     }
 
-    DPRINT("ScmStartService() done (Error %lu)\n", dwError);
+    DPRINT("ScmLoadService() done (Error %lu)\n", dwError);
 
     if (dwError == ERROR_SUCCESS)
     {
@@ -1064,15 +1632,22 @@ ScmStartService(PSERVICE Service, DWORD argc, LPWSTR *argv)
             Group->ServicesRunning = TRUE;
         }
     }
-#if 0
     else
     {
-        switch (Service->ErrorControl)
+        if (Service->dwErrorControl != SERVICE_ERROR_IGNORE)
         {
-            case SERVICE_ERROR_NORMAL:
-                /* FIXME: Log error */
-                break;
+            /* Log a failed service start */
+            swprintf(szErrorBuffer, L"%lu", dwError);
+            ErrorLogStrings[0] = Service->lpServiceName;
+            ErrorLogStrings[1] = szErrorBuffer;
+            ScmLogError(EVENT_SERVICE_START_FAILED,
+                        2,
+                        ErrorLogStrings);
+        }
 
+#if 0
+        switch (Service->dwErrorControl)
+        {
             case SERVICE_ERROR_SEVERE:
                 if (IsLastKnownGood == FALSE)
                 {
@@ -1091,8 +1666,48 @@ ScmStartService(PSERVICE Service, DWORD argc, LPWSTR *argv)
                 }
                 break;
         }
-    }
 #endif
+    }
+
+    return dwError;
+}
+
+
+DWORD
+ScmStartService(PSERVICE Service,
+                DWORD argc,
+                LPWSTR* argv)
+{
+    DWORD dwError = ERROR_SUCCESS;
+    SC_RPC_LOCK Lock = NULL;
+
+    DPRINT("ScmStartService() called\n");
+    DPRINT("Start Service %p (%S)\n", Service, Service->lpServiceName);
+
+    /* Acquire the service control critical section, to synchronize starts */
+    EnterCriticalSection(&ControlServiceCriticalSection);
+
+    /*
+     * Acquire the user service start lock while the service is starting, if
+     * needed (i.e. if we are not starting it during the initialization phase).
+     * If we don't success, bail out.
+     */
+    if (!ScmInitialize)
+    {
+        dwError = ScmAcquireServiceStartLock(TRUE, &Lock);
+        if (dwError != ERROR_SUCCESS) goto done;
+    }
+
+    /* Really start the service */
+    dwError = ScmLoadService(Service, argc, argv);
+
+    /* Release the service start lock, if needed, and the critical section */
+    if (Lock) ScmReleaseServiceStartLock(&Lock);
+
+done:
+    LeaveCriticalSection(&ControlServiceCriticalSection);
+
+    DPRINT("ScmStartService() done (Error %lu)\n", dwError);
 
     return dwError;
 }
@@ -1101,19 +1716,90 @@ ScmStartService(PSERVICE Service, DWORD argc, LPWSTR *argv)
 VOID
 ScmAutoStartServices(VOID)
 {
+    DWORD dwError = ERROR_SUCCESS;
     PLIST_ENTRY GroupEntry;
     PLIST_ENTRY ServiceEntry;
     PSERVICE_GROUP CurrentGroup;
     PSERVICE CurrentService;
+    WCHAR szSafeBootServicePath[MAX_PATH];
+    HKEY hKey;
     ULONG i;
 
-    /* Clear 'ServiceVisited' flag */
+    /*
+     * This function MUST be called ONLY at initialization time.
+     * Therefore, no need to acquire the user service start lock.
+     */
+    ASSERT(ScmInitialize);
+
+    /* Acquire the service control critical section, to synchronize starts */
+    EnterCriticalSection(&ControlServiceCriticalSection);
+
+    /* Clear 'ServiceVisited' flag (or set if not to start in Safe Mode) */
     ServiceEntry = ServiceListHead.Flink;
     while (ServiceEntry != &ServiceListHead)
     {
-      CurrentService = CONTAINING_RECORD(ServiceEntry, SERVICE, ServiceListEntry);
-      CurrentService->ServiceVisited = FALSE;
-      ServiceEntry = ServiceEntry->Flink;
+        CurrentService = CONTAINING_RECORD(ServiceEntry, SERVICE, ServiceListEntry);
+
+        /* Build the safe boot path */
+        wcscpy(szSafeBootServicePath,
+               L"SYSTEM\\CurrentControlSet\\Control\\SafeBoot");
+
+        switch (GetSystemMetrics(SM_CLEANBOOT))
+        {
+            /* NOTE: Assumes MINIMAL (1) and DSREPAIR (3) load same items */
+            case 1:
+            case 3:
+                wcscat(szSafeBootServicePath, L"\\Minimal\\");
+                break;
+
+            case 2:
+                wcscat(szSafeBootServicePath, L"\\Network\\");
+                break;
+        }
+
+        if (GetSystemMetrics(SM_CLEANBOOT))
+        {
+            /* If key does not exist then do not assume safe mode */
+            dwError = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
+                                    szSafeBootServicePath,
+                                    0,
+                                    KEY_READ,
+                                    &hKey);
+            if (dwError == ERROR_SUCCESS)
+            {
+                RegCloseKey(hKey);
+
+                /* Finish Safe Boot path off */
+                wcsncat(szSafeBootServicePath,
+                        CurrentService->lpServiceName,
+                        MAX_PATH - wcslen(szSafeBootServicePath));
+
+                /* Check that the key is in the Safe Boot path */
+                dwError = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
+                                        szSafeBootServicePath,
+                                        0,
+                                        KEY_READ,
+                                        &hKey);
+                if (dwError != ERROR_SUCCESS)
+                {
+                    /* Mark service as visited so it is not auto-started */
+                    CurrentService->ServiceVisited = TRUE;
+                }
+                else
+                {
+                    /* Must be auto-started in safe mode - mark as unvisited */
+                    RegCloseKey(hKey);
+                    CurrentService->ServiceVisited = FALSE;
+                }
+            }
+            else
+            {
+                DPRINT1("WARNING: Could not open the associated Safe Boot key!");
+                CurrentService->ServiceVisited = FALSE;
+            }
+        }
+
+        ServiceEntry = ServiceEntry->Flink;
     }
 
     /* Start all services which are members of an existing group */
@@ -1138,7 +1824,7 @@ ScmAutoStartServices(VOID)
                     (CurrentService->dwTag == CurrentGroup->TagArray[i]))
                 {
                     CurrentService->ServiceVisited = TRUE;
-                    ScmStartService(CurrentService, 0, NULL);
+                    ScmLoadService(CurrentService, 0, NULL);
                 }
 
                 ServiceEntry = ServiceEntry->Flink;
@@ -1156,7 +1842,7 @@ ScmAutoStartServices(VOID)
                 (CurrentService->ServiceVisited == FALSE))
             {
                 CurrentService->ServiceVisited = TRUE;
-                ScmStartService(CurrentService, 0, NULL);
+                ScmLoadService(CurrentService, 0, NULL);
             }
 
             ServiceEntry = ServiceEntry->Flink;
@@ -1176,7 +1862,7 @@ ScmAutoStartServices(VOID)
             (CurrentService->ServiceVisited == FALSE))
         {
             CurrentService->ServiceVisited = TRUE;
-            ScmStartService(CurrentService, 0, NULL);
+            ScmLoadService(CurrentService, 0, NULL);
         }
 
         ServiceEntry = ServiceEntry->Flink;
@@ -1193,7 +1879,7 @@ ScmAutoStartServices(VOID)
             (CurrentService->ServiceVisited == FALSE))
         {
             CurrentService->ServiceVisited = TRUE;
-            ScmStartService(CurrentService, 0, NULL);
+            ScmLoadService(CurrentService, 0, NULL);
         }
 
         ServiceEntry = ServiceEntry->Flink;
@@ -1207,6 +1893,9 @@ ScmAutoStartServices(VOID)
         CurrentService->ServiceVisited = FALSE;
         ServiceEntry = ServiceEntry->Flink;
     }
+
+    /* Release the critical section */
+    LeaveCriticalSection(&ControlServiceCriticalSection);
 }
 
 
@@ -1218,6 +1907,9 @@ ScmAutoShutdownServices(VOID)
 
     DPRINT("ScmAutoShutdownServices() called\n");
 
+    /* Lock the service database exclusively */
+    ScmLockDatabaseExclusive();
+
     ServiceEntry = ServiceListHead.Flink;
     while (ServiceEntry != &ServiceListHead)
     {
@@ -1227,13 +1919,17 @@ ScmAutoShutdownServices(VOID)
             CurrentService->Status.dwCurrentState == SERVICE_START_PENDING)
         {
             /* shutdown service */
-            ScmControlService(CurrentService, SERVICE_CONTROL_STOP);
+            DPRINT("Shutdown service: %S\n", CurrentService->szServiceName);
+            ScmControlService(CurrentService, SERVICE_CONTROL_SHUTDOWN);
         }
 
         ServiceEntry = ServiceEntry->Flink;
     }
 
-    DPRINT("ScmGetBootAndSystemDriverState() done\n");
+    /* Unlock the service database */
+    ScmUnlockDatabase();
+
+    DPRINT("ScmAutoShutdownServices() done\n");
 }
 
 
@@ -1257,4 +1953,40 @@ ScmUnlockDatabase(VOID)
     RtlReleaseResource(&DatabaseLock);
 }
 
+
+VOID
+ScmInitNamedPipeCriticalSection(VOID)
+{
+    HKEY hKey;
+    DWORD dwKeySize;
+    DWORD dwError;
+
+    InitializeCriticalSection(&ControlServiceCriticalSection);
+
+    dwError = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
+                            L"SYSTEM\\CurrentControlSet\\Control",
+                            0,
+                            KEY_READ,
+                            &hKey);
+   if (dwError == ERROR_SUCCESS)
+   {
+        dwKeySize = sizeof(DWORD);
+        RegQueryValueExW(hKey,
+                         L"ServicesPipeTimeout",
+                         0,
+                         NULL,
+                         (LPBYTE)&PipeTimeout,
+                         &dwKeySize);
+
+       RegCloseKey(hKey);
+   }
+}
+
+
+VOID
+ScmDeleteNamedPipeCriticalSection(VOID)
+{
+    DeleteCriticalSection(&ControlServiceCriticalSection);
+}
+
 /* EOF */