[SERVICES]
[reactos.git] / reactos / base / system / services / database.c
index 3a4ab91..95bc302 100644 (file)
@@ -1,25 +1,12 @@
 /*
- *
- * service control manager
- *
- * ReactOS Operating System
- *
- * --------------------------------------------------------------------
- *
- * This software is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of the
- * License, or (at your option) any later version.
- *
- * This software is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this software; see the file COPYING.LIB. If not, write
- * to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge,
- * MA 02139, USA.
+ * PROJECT:     ReactOS Service Control Manager
+ * LICENSE:     GPL - See COPYING in the top level directory
+ * FILE:        base/system/services/database.c
+ * PURPOSE:     Database control interface
+ * COPYRIGHT:   Copyright 2002-2006 Eric Kohl
+ *              Copyright 2006 HervĂ© Poussineau <hpoussin@reactos.org>
+ *              Copyright 2007 Ged Murphy <gedmurphy@reactos.org>
+ *                             Gregor Brunmar <gregor.brunmar@home.se>
  *
  */
 
 #define NDEBUG
 #include <debug.h>
 
+/*
+ * Uncomment the line below to start services
+ *  using the SERVICE_START_PENDING state
+ */
+// #define USE_SERVICE_START_PENDING
 
 /* GLOBALS *******************************************************************/
 
@@ -41,8 +33,9 @@ static DWORD dwResumeCount = 1;
 
 /* FUNCTIONS *****************************************************************/
 
+
 PSERVICE
-ScmGetServiceEntryByName(LPWSTR lpServiceName)
+ScmGetServiceEntryByName(LPCWSTR lpServiceName)
 {
     PLIST_ENTRY ServiceEntry;
     PSERVICE CurrentService;
@@ -71,7 +64,7 @@ ScmGetServiceEntryByName(LPWSTR lpServiceName)
 
 
 PSERVICE
-ScmGetServiceEntryByDisplayName(LPWSTR lpDisplayName)
+ScmGetServiceEntryByDisplayName(LPCWSTR lpDisplayName)
 {
     PLIST_ENTRY ServiceEntry;
     PSERVICE CurrentService;
@@ -129,7 +122,7 @@ ScmGetServiceEntryByResumeCount(DWORD dwResumeCount)
 
 
 DWORD
-ScmCreateNewServiceRecord(LPWSTR lpServiceName,
+ScmCreateNewServiceRecord(LPCWSTR lpServiceName,
                           PSERVICE *lpServiceRecord)
 {
     PSERVICE lpService = NULL;
@@ -137,7 +130,7 @@ ScmCreateNewServiceRecord(LPWSTR lpServiceName,
     DPRINT("Service: '%S'\n", lpServiceName);
 
     /* Allocate service entry */
-    lpService = HeapAlloc(GetProcessHeap(),
+    lpService = (SERVICE*) HeapAlloc(GetProcessHeap(),
                           HEAP_ZERO_MEMORY,
                           sizeof(SERVICE) + ((wcslen(lpServiceName) + 1) * sizeof(WCHAR)));
     if (lpService == NULL)
@@ -153,10 +146,11 @@ ScmCreateNewServiceRecord(LPWSTR lpServiceName,
     /* Set the resume count */
     lpService->dwResumeCount = dwResumeCount++;
 
-    /* Append service entry */
+    /* Append service record */
     InsertTailList(&ServiceListHead,
                    &lpService->ServiceListEntry);
 
+    /* Initialize the service status */
     lpService->Status.dwCurrentState = SERVICE_STOPPED;
     lpService->Status.dwControlsAccepted = 0;
     lpService->Status.dwWin32ExitCode = ERROR_SERVICE_NEVER_STARTED;
@@ -168,8 +162,44 @@ ScmCreateNewServiceRecord(LPWSTR lpServiceName,
 }
 
 
+VOID
+ScmDeleteServiceRecord(PSERVICE lpService)
+{
+    DPRINT("Deleting Service %S\n", lpService->lpServiceName);
+
+    /* Delete the display name */
+    if (lpService->lpDisplayName != NULL &&
+        lpService->lpDisplayName != lpService->lpServiceName)
+        HeapFree(GetProcessHeap(), 0, lpService->lpDisplayName);
+
+    /* Decrement the image reference counter */
+    if (lpService->lpImage)
+        lpService->lpImage->dwServiceRefCount--;
+
+    /* Decrement the group reference counter */
+    if (lpService->lpGroup)
+        lpService->lpGroup->dwRefCount--;
+
+    /* FIXME: SecurityDescriptor */
+
+    /* Close the control pipe */
+    if (lpService->ControlPipeHandle != INVALID_HANDLE_VALUE)
+        CloseHandle(lpService->ControlPipeHandle);
+
+    /* Remove the Service from the List */
+    RemoveEntryList(&lpService->ServiceListEntry);
+
+    DPRINT("Deleted Service %S\n", lpService->lpServiceName);
+
+    /* Delete the service record */
+    HeapFree(GetProcessHeap(), 0, lpService);
+
+    DPRINT("Done\n");
+}
+
+
 static DWORD
-CreateServiceListEntry(LPWSTR lpServiceName,
+CreateServiceListEntry(LPCWSTR lpServiceName,
                        HKEY hServiceKey)
 {
     PSERVICE lpService = NULL;
@@ -279,7 +309,10 @@ CreateServiceListEntry(LPWSTR lpServiceName,
     }
 
     DPRINT("ServiceName: '%S'\n", lpService->lpServiceName);
-    DPRINT("Group: '%S'\n", lpService->lpGroup->lpGroupName);
+    if (lpService->lpGroup != NULL)
+    {
+        DPRINT("Group: '%S'\n", lpService->lpGroup->lpGroupName);
+    }
     DPRINT("Start %lx  Type %lx  Tag %lx  ErrorControl %lx\n",
            lpService->dwStartType,
            lpService->Status.dwServiceType,
@@ -300,11 +333,60 @@ done:;
 }
 
 
+DWORD
+ScmDeleteRegKey(HKEY hKey, LPCWSTR lpszSubKey)
+{
+    DWORD dwRet, dwMaxSubkeyLen = 0, dwSize;
+    WCHAR szNameBuf[MAX_PATH], *lpszName = szNameBuf;
+    HKEY hSubKey = 0;
+
+    dwRet = RegOpenKeyExW(hKey, lpszSubKey, 0, KEY_READ, &hSubKey);
+    if (!dwRet)
+    {
+        /* Find the maximum subkey length so that we can allocate a buffer */
+        dwRet = RegQueryInfoKeyW(hSubKey, NULL, NULL, NULL, NULL,
+                                                         &dwMaxSubkeyLen, NULL, NULL, NULL, NULL, NULL, NULL);
+        if (!dwRet)
+        {
+            dwMaxSubkeyLen++;
+            if (dwMaxSubkeyLen > sizeof(szNameBuf)/sizeof(WCHAR))
+                /* Name too big: alloc a buffer for it */
+                lpszName = HeapAlloc(GetProcessHeap(), 0, dwMaxSubkeyLen*sizeof(WCHAR));
+
+            if(!lpszName)
+                dwRet = ERROR_NOT_ENOUGH_MEMORY;
+            else
+            {
+                while (dwRet == ERROR_SUCCESS)
+                {
+                    dwSize = dwMaxSubkeyLen;
+                    dwRet = RegEnumKeyExW(hSubKey, 0, lpszName, &dwSize, NULL, NULL, NULL, NULL);
+                    if (dwRet == ERROR_SUCCESS || dwRet == ERROR_MORE_DATA)
+                        dwRet = ScmDeleteRegKey(hSubKey, lpszName);
+                }
+                if (dwRet == ERROR_NO_MORE_ITEMS)
+                    dwRet = ERROR_SUCCESS;
+
+                if (lpszName != szNameBuf)
+                    HeapFree(GetProcessHeap(), 0, lpszName); /* Free buffer if allocated */
+            }
+        }
+
+        RegCloseKey(hSubKey);
+        if (!dwRet)
+            dwRet = RegDeleteKeyW(hKey, lpszSubKey);
+    }
+    return dwRet;
+}
+
+
 VOID
 ScmDeleteMarkedServices(VOID)
 {
     PLIST_ENTRY ServiceEntry;
     PSERVICE CurrentService;
+    HKEY hServicesKey;
+    DWORD dwError;
 
     ServiceEntry = ServiceListHead.Flink;
     while (ServiceEntry != &ServiceListHead)
@@ -315,14 +397,66 @@ ScmDeleteMarkedServices(VOID)
 
         if (CurrentService->bDeleted == TRUE)
         {
-            DPRINT1("Delete service: %S\n", CurrentService->lpServiceName);
+            dwError = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
+                                    L"System\\CurrentControlSet\\Services",
+                                    0,
+                                    DELETE,
+                                    &hServicesKey);
+            if (dwError == ERROR_SUCCESS)
+            {
+                dwError = ScmDeleteRegKey(hServicesKey, CurrentService->lpServiceName);
+                RegCloseKey(hServicesKey);
+                if (dwError == ERROR_SUCCESS)
+                {
+                    RemoveEntryList(&CurrentService->ServiceListEntry);
+                    HeapFree(GetProcessHeap(), 0, CurrentService);
+                }
+            }
+            
+            if (dwError != ERROR_SUCCESS)
+                DPRINT1("Delete service failed: %S\n", CurrentService->lpServiceName);
+        }
+    }
+}
 
-            /* FIXME: Delete the registry keys */
 
-            /* FIXME: Delete the service record from the list */
+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");
 }
 
 
@@ -396,6 +530,9 @@ ScmCreateServiceDatabase(VOID)
 
     RegCloseKey(hServicesKey);
 
+    /* Wait for LSA */
+    WaitForLSA();
+
     /* Delete services that are marked for delete */
     ScmDeleteMarkedServices();
 
@@ -405,6 +542,18 @@ ScmCreateServiceDatabase(VOID)
 }
 
 
+VOID
+ScmShutdownServiceDatabase(VOID)
+{
+    DPRINT("ScmShutdownServiceDatabase() called\n");
+
+    ScmDeleteMarkedServices();
+    RtlDeleteResource(&DatabaseLock);
+
+    DPRINT("ScmShutdownServiceDatabase() done\n");
+}
+
+
 static NTSTATUS
 ScmCheckDriver(PSERVICE Service)
 {
@@ -446,7 +595,7 @@ ScmCheckDriver(PSERVICE Service)
 
     BufferLength = sizeof(OBJECT_DIRECTORY_INFORMATION) +
                    2 * MAX_PATH * sizeof(WCHAR);
-    DirInfo = HeapAlloc(GetProcessHeap(),
+    DirInfo = (OBJECT_DIRECTORY_INFORMATION*) HeapAlloc(GetProcessHeap(),
                         HEAP_ZERO_MEMORY,
                         BufferLength);
 
@@ -470,12 +619,12 @@ ScmCheckDriver(PSERVICE Service)
         if (!NT_SUCCESS(Status))
             break;
 
-        DPRINT("Comparing: '%S'  '%wZ'\n", Service->lpServiceName, &DirInfo->ObjectName);
+        DPRINT("Comparing: '%S'  '%wZ'\n", Service->lpServiceName, &DirInfo->Name);
 
-        if (_wcsicmp(Service->lpServiceName, DirInfo->ObjectName.Buffer) == 0)
+        if (_wcsicmp(Service->lpServiceName, DirInfo->Name.Buffer) == 0)
         {
             DPRINT("Found: '%S'  '%wZ'\n",
-                   Service->lpServiceName, &DirInfo->ObjectName);
+                   Service->lpServiceName, &DirInfo->Name);
 
             /* Mark service as 'running' */
             Service->Status.dwCurrentState = SERVICE_RUNNING;
@@ -528,14 +677,63 @@ ScmGetBootAndSystemDriverState(VOID)
 }
 
 
-static NTSTATUS
-ScmSendStartCommand(PSERVICE Service, LPWSTR Arguments)
+DWORD
+ScmControlService(PSERVICE Service,
+                  DWORD dwControl,
+                  LPSERVICE_STATUS lpServiceStatus)
 {
-    PSCM_START_PACKET StartPacket;
+    PSCM_CONTROL_PACKET ControlPacket;
+    DWORD Count;
     DWORD TotalLength;
-#if 0
+
+    DPRINT("ScmControlService() called\n");
+
+    TotalLength = wcslen(Service->lpServiceName) + 1;
+
+    ControlPacket = (SCM_CONTROL_PACKET*)HeapAlloc(GetProcessHeap(),
+                                                   HEAP_ZERO_MEMORY,
+                                                   sizeof(SCM_CONTROL_PACKET) + (TotalLength * sizeof(WCHAR)));
+    if (ControlPacket == NULL)
+        return ERROR_NOT_ENOUGH_MEMORY;
+
+    ControlPacket->dwControl = dwControl;
+    ControlPacket->dwSize = TotalLength;
+    ControlPacket->hServiceStatus = (SERVICE_STATUS_HANDLE)Service;
+    wcscpy(&ControlPacket->szArguments[0], Service->lpServiceName);
+
+    /* Send the control packet */
+    WriteFile(Service->ControlPipeHandle,
+              ControlPacket,
+              sizeof(SCM_CONTROL_PACKET) + (TotalLength * sizeof(WCHAR)),
+              &Count,
+              NULL);
+
+    /* FIXME: Read the reply */
+
+    /* Release the contol packet */
+    HeapFree(GetProcessHeap(),
+             0,
+             ControlPacket);
+
+    RtlCopyMemory(lpServiceStatus,
+                  &Service->Status,
+                  sizeof(SERVICE_STATUS));
+
+    DPRINT("ScmControlService) done\n");
+
+    return ERROR_SUCCESS;
+}
+
+
+static DWORD
+ScmSendStartCommand(PSERVICE Service,
+                    DWORD argc,
+                    LPWSTR *argv)
+{
+    PSCM_CONTROL_PACKET ControlPacket;
+    DWORD TotalLength;
+    DWORD ArgsLength = 0;
     DWORD Length;
-#endif
     PWSTR Ptr;
     DWORD Count;
 
@@ -543,66 +741,85 @@ ScmSendStartCommand(PSERVICE Service, LPWSTR Arguments)
 
     /* Calculate the total length of the start command line */
     TotalLength = wcslen(Service->lpServiceName) + 1;
-#if 0
-    if (Arguments != NULL)
+    if (argc > 0)
     {
-        Ptr = Arguments;
-        while (*Ptr)
+        for (Count = 0; Count < argc; Count++)
         {
-            Length = wcslen(Ptr) + 1;
+            DPRINT("Arg: %S\n", argv[Count]);
+            Length = wcslen(argv[Count]) + 1;
             TotalLength += Length;
-            Ptr += Length;
+            ArgsLength += Length;
         }
     }
-#endif
     TotalLength++;
+    DPRINT("ArgsLength: %ld TotalLength: %ld\n", ArgsLength, 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;
+    /* Allocate a control packet */
+    ControlPacket = (SCM_CONTROL_PACKET*)HeapAlloc(GetProcessHeap(),
+                                                   HEAP_ZERO_MEMORY,
+                                                   sizeof(SCM_CONTROL_PACKET) + (TotalLength - 1) * sizeof(WCHAR));
+    if (ControlPacket == NULL)
+        return ERROR_NOT_ENOUGH_MEMORY;
 
-    StartPacket->Command = SCM_START_COMMAND;
-    StartPacket->Size = TotalLength;
-    Ptr = &StartPacket->Arguments[0];
+    ControlPacket->dwControl = SERVICE_CONTROL_START;
+    ControlPacket->hServiceStatus = (SERVICE_STATUS_HANDLE)Service;
+    ControlPacket->dwSize = TotalLength;
+    Ptr = &ControlPacket->szArguments[0];
     wcscpy(Ptr, Service->lpServiceName);
     Ptr += (wcslen(Service->lpServiceName) + 1);
 
-    /* FIXME: Copy argument list */
+    /* Copy argument list */
+    if (argc > 0)
+    {
+        UNIMPLEMENTED;
+        DPRINT1("Arguments sent to service ignored!\n");
+#if 0
+        memcpy(Ptr, Arguments, ArgsLength);
+        Ptr += ArgsLength;
+#endif
+    }
 
+    /* Terminate the argument list */
     *Ptr = 0;
 
     /* Send the start command */
     WriteFile(Service->ControlPipeHandle,
-              StartPacket,
-              sizeof(SCM_START_PACKET) + (TotalLength - 1) * sizeof(WCHAR),
+              ControlPacket,
+              sizeof(SCM_CONTROL_PACKET) + (TotalLength - 1) * sizeof(WCHAR),
               &Count,
               NULL);
 
     /* FIXME: Read the reply */
 
+    /* Release the contol packet */
     HeapFree(GetProcessHeap(),
              0,
-             StartPacket);
+             ControlPacket);
 
     DPRINT("ScmSendStartCommand() done\n");
 
-    return STATUS_SUCCESS;
+    return ERROR_SUCCESS;
 }
 
 
-static NTSTATUS
-ScmStartUserModeService(PSERVICE Service)
+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;
 
     RtlInitUnicodeString(&ImagePath, NULL);
 
@@ -626,13 +843,55 @@ ScmStartUserModeService(PSERVICE Service)
     if (!NT_SUCCESS(Status))
     {
         DPRINT1("RtlQueryRegistryValues() failed (Status %lx)\n", Status);
-        return Status;
+        return RtlNtStatusToDosError(Status);
     }
     DPRINT("ImagePath: '%S'\n", ImagePath.Buffer);
     DPRINT("Type: %lx\n", Type);
 
-    /* Create '\\.\pipe\net\NtControlPipe' instance */
-    Service->ControlPipeHandle = CreateNamedPipeW(L"\\\\.\\pipe\\net\\NtControlPipe",
+    /* Get the service number */
+    /* TODO: Create registry entry with correct write access */
+    Status = RegCreateKeyExW(HKEY_LOCAL_MACHINE,
+                            L"SYSTEM\\CurrentControlSet\\Control\\ServiceCurrent", 0, NULL,
+                            REG_OPTION_VOLATILE,
+                            KEY_WRITE | KEY_READ,
+                            NULL,
+                            &hServiceCurrentKey,
+                            &KeyDisposition);
+
+    if (ERROR_SUCCESS != Status)
+    {
+        DPRINT1("RegCreateKeyEx() failed with status %u\n", Status);
+        return Status;
+    }
+
+    if (REG_OPENED_EXISTING_KEY == KeyDisposition)
+    {
+        DWORD KeySize = sizeof(ServiceCurrent);
+        Status = RegQueryValueExW(hServiceCurrentKey, L"", 0, NULL, (BYTE*)&ServiceCurrent, &KeySize);
+
+        if (ERROR_SUCCESS != Status)
+        {
+            RegCloseKey(hServiceCurrentKey);
+            DPRINT1("RegQueryValueEx() failed with status %u\n", Status);
+            return Status;
+        }
+
+        ServiceCurrent++;
+    }
+
+    Status = RegSetValueExW(hServiceCurrentKey, L"", 0, REG_DWORD, (BYTE*)&ServiceCurrent, sizeof(ServiceCurrent));
+
+    RegCloseKey(hServiceCurrentKey);
+
+    if (ERROR_SUCCESS != Status)
+    {
+        DPRINT1("RegSetValueExW() failed (Status %lx)\n", Status);
+        return Status;
+    }
+
+    /* Create '\\.\pipe\net\NtControlPipeXXX' instance */
+    swprintf(NtControlPipeName, L"\\\\.\\pipe\\net\\NtControlPipe%u", ServiceCurrent);
+    Service->ControlPipeHandle = CreateNamedPipeW(NtControlPipeName,
                                                   PIPE_ACCESS_DUPLEX,
                                                   PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT,
                                                   100,
@@ -640,11 +899,11 @@ ScmStartUserModeService(PSERVICE Service)
                                                   4,
                                                   30000,
                                                   NULL);
-    DPRINT("CreateNamedPipeW() done\n");
+    DPRINT("CreateNamedPipeW(%S) done\n", NtControlPipeName);
     if (Service->ControlPipeHandle == INVALID_HANDLE_VALUE)
     {
         DPRINT1("Failed to create control pipe!\n");
-        return STATUS_UNSUCCESSFUL;
+        return GetLastError();
     }
 
     StartupInfo.cb = sizeof(StartupInfo);
@@ -655,8 +914,8 @@ ScmStartUserModeService(PSERVICE Service)
     StartupInfo.cbReserved2 = 0;
     StartupInfo.lpReserved2 = 0;
 
-    Result = CreateProcessW(ImagePath.Buffer,
-                            NULL,
+    Result = CreateProcessW(NULL,
+                            ImagePath.Buffer,
                             NULL,
                             NULL,
                             FALSE,
@@ -669,12 +928,13 @@ ScmStartUserModeService(PSERVICE Service)
 
     if (!Result)
     {
+        dwError = GetLastError();
         /* Close control pipe */
         CloseHandle(Service->ControlPipeHandle);
         Service->ControlPipeHandle = INVALID_HANDLE_VALUE;
 
         DPRINT1("Starting '%S' failed!\n", Service->lpServiceName);
-        return STATUS_UNSUCCESSFUL;
+        return dwError;
     }
 
     DPRINT("Process Id: %lu  Handle %lx\n",
@@ -692,60 +952,56 @@ ScmStartUserModeService(PSERVICE Service)
     ResumeThread(ProcessInformation.hThread);
 
     /* Connect control pipe */
-    if (ConnectNamedPipe(Service->ControlPipeHandle, NULL))
+    if (ConnectNamedPipe(Service->ControlPipeHandle, NULL) ?
+        TRUE : (dwError = GetLastError()) == ERROR_PIPE_CONNECTED)
     {
-        DWORD dwProcessId = 0;
         DWORD dwRead = 0;
 
         DPRINT("Control pipe connected!\n");
 
-        /* Read thread id from pipe */
+        /* 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",
-                    GetLastError());
-            Status = STATUS_UNSUCCESSFUL;
+                    dwError);
         }
         else
         {
-            DPRINT("Received process id %lu\n", dwProcessId);
+            DPRINT("Received service process ID %lu\n", dwProcessId);
 
-            /* FIXME: Send start command */
-
-            Status = STATUS_SUCCESS;
+            /* Send start command */
+            dwError = ScmSendStartCommand(Service, argc, argv);
         }
     }
     else
     {
-        DPRINT("Connecting control pipe failed!\n");
+        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;
-        Status = STATUS_UNSUCCESSFUL;
     }
 
-    ScmSendStartCommand(Service, NULL);
-
     /* Close process and thread handle */
     CloseHandle(ProcessInformation.hThread);
     CloseHandle(ProcessInformation.hProcess);
 
-    return Status;
+    return dwError;
 }
 
 
-NTSTATUS
-ScmStartService(PSERVICE Service)
+DWORD
+ScmStartService(PSERVICE Service, DWORD argc, LPWSTR *argv)
 {
     PSERVICE_GROUP Group = Service->lpGroup;
-    NTSTATUS Status;
+    DWORD dwError = ERROR_SUCCESS;
 
     DPRINT("ScmStartService() called\n");
 
@@ -755,25 +1011,35 @@ ScmStartService(PSERVICE Service)
     if (Service->Status.dwServiceType & SERVICE_DRIVER)
     {
         /* Load driver */
-        Status = ScmLoadDriver(Service);
-        if (Status == STATUS_SUCCESS)
+        dwError = ScmLoadDriver(Service);
+        if (dwError == ERROR_SUCCESS)
+        {
             Service->Status.dwControlsAccepted = SERVICE_ACCEPT_STOP;
+            Service->Status.dwCurrentState = SERVICE_RUNNING;
+        }
     }
     else
     {
         /* Start user-mode service */
-        Status = ScmStartUserModeService(Service);
+        dwError = ScmStartUserModeService(Service, argc, argv);
+        if (dwError == ERROR_SUCCESS)
+        {
+#ifdef USE_SERVICE_START_PENDING
+            Service->Status.dwCurrentState = SERVICE_START_PENDING;
+#else
+            Service->Status.dwCurrentState = SERVICE_RUNNING;
+#endif
+        }
     }
 
-    DPRINT("ScmStartService() done (Status %lx)\n", Status);
+    DPRINT("ScmStartService() done (Error %lu)\n", dwError);
 
-    if (NT_SUCCESS(Status))
+    if (dwError == ERROR_SUCCESS)
     {
         if (Group != NULL)
         {
             Group->ServicesRunning = TRUE;
         }
-        Service->Status.dwCurrentState = SERVICE_RUNNING;
     }
 #if 0
     else
@@ -805,7 +1071,7 @@ ScmStartService(PSERVICE Service)
     }
 #endif
 
-    return Status;
+    return dwError;
 }
 
 
@@ -849,7 +1115,7 @@ ScmAutoStartServices(VOID)
                     (CurrentService->dwTag == CurrentGroup->TagArray[i]))
                 {
                     CurrentService->ServiceVisited = TRUE;
-                    ScmStartService(CurrentService);
+                    ScmStartService(CurrentService, 0, NULL);
                 }
 
                 ServiceEntry = ServiceEntry->Flink;
@@ -867,7 +1133,7 @@ ScmAutoStartServices(VOID)
                 (CurrentService->ServiceVisited == FALSE))
             {
                 CurrentService->ServiceVisited = TRUE;
-                ScmStartService(CurrentService);
+                ScmStartService(CurrentService, 0, NULL);
             }
 
             ServiceEntry = ServiceEntry->Flink;
@@ -887,7 +1153,7 @@ ScmAutoStartServices(VOID)
             (CurrentService->ServiceVisited == FALSE))
         {
             CurrentService->ServiceVisited = TRUE;
-            ScmStartService(CurrentService);
+            ScmStartService(CurrentService, 0, NULL);
         }
 
         ServiceEntry = ServiceEntry->Flink;
@@ -904,7 +1170,7 @@ ScmAutoStartServices(VOID)
             (CurrentService->ServiceVisited == FALSE))
         {
             CurrentService->ServiceVisited = TRUE;
-            ScmStartService(CurrentService);
+            ScmStartService(CurrentService, 0, NULL);
         }
 
         ServiceEntry = ServiceEntry->Flink;
@@ -920,4 +1186,53 @@ ScmAutoStartServices(VOID)
     }
 }
 
+
+VOID
+ScmAutoShutdownServices(VOID)
+{
+    PLIST_ENTRY ServiceEntry;
+    PSERVICE CurrentService;
+    SERVICE_STATUS ServiceStatus;
+
+    DPRINT("ScmAutoShutdownServices() called\n");
+
+    ServiceEntry = ServiceListHead.Flink;
+    while (ServiceEntry != &ServiceListHead)
+    {
+        CurrentService = CONTAINING_RECORD(ServiceEntry, SERVICE, ServiceListEntry);
+
+        if (CurrentService->Status.dwCurrentState == SERVICE_RUNNING ||
+            CurrentService->Status.dwCurrentState == SERVICE_START_PENDING)
+        {
+            /* shutdown service */
+            ScmControlService(CurrentService, SERVICE_CONTROL_STOP, &ServiceStatus);
+        }
+
+        ServiceEntry = ServiceEntry->Flink;
+    }
+
+    DPRINT("ScmGetBootAndSystemDriverState() done\n");
+}
+
+
+BOOL
+ScmLockDatabaseExclusive(VOID)
+{
+    return RtlAcquireResourceExclusive(&DatabaseLock, TRUE);
+}
+
+
+BOOL
+ScmLockDatabaseShared(VOID)
+{
+    return RtlAcquireResourceShared(&DatabaseLock, TRUE);
+}
+
+
+VOID
+ScmUnlockDatabase(VOID)
+{
+    RtlReleaseResource(&DatabaseLock);
+}
+
 /* EOF */