[NTOSKRNL/USERINIT/WIN32K]
[reactos.git] / reactos / base / system / services / database.c
index 93a99e5..0d7ec77 100644 (file)
 #define NDEBUG
 #include <debug.h>
 
+/*
+ * Uncomment the line below to start services
+ *  using the SERVICE_START_PENDING state
+ */
+// #define USE_SERVICE_START_PENDING
 
 /* GLOBALS *******************************************************************/
 
@@ -25,6 +30,7 @@ LIST_ENTRY ServiceListHead;
 static RTL_RESOURCE DatabaseLock;
 static DWORD dwResumeCount = 1;
 
+static CRITICAL_SECTION ControlServiceCriticalSection;
 
 /* FUNCTIONS *****************************************************************/
 
@@ -116,37 +122,6 @@ ScmGetServiceEntryByResumeCount(DWORD dwResumeCount)
 }
 
 
-PSERVICE
-ScmGetServiceEntryByClientHandle(HANDLE Handle)
-{
-    PLIST_ENTRY ServiceEntry;
-    PSERVICE CurrentService;
-
-    DPRINT("ScmGetServiceEntryByClientHandle() called\n");
-    DPRINT("looking for %p\n", Handle);
-
-    ServiceEntry = ServiceListHead.Flink;
-    while (ServiceEntry != &ServiceListHead)
-    {
-        CurrentService = CONTAINING_RECORD(ServiceEntry,
-                                           SERVICE,
-                                           ServiceListEntry);
-
-        if (CurrentService->hClient == Handle)
-        {
-            DPRINT("Found service: '%S'\n", CurrentService->lpDisplayName);
-            return CurrentService;
-        }
-
-        ServiceEntry = ServiceEntry->Flink;
-    }
-
-    DPRINT("Couldn't find a matching service\n");
-
-    return NULL;
-}
-
-
 DWORD
 ScmCreateNewServiceRecord(LPCWSTR lpServiceName,
                           PSERVICE *lpServiceRecord)
@@ -191,7 +166,7 @@ ScmCreateNewServiceRecord(LPCWSTR lpServiceName,
 VOID
 ScmDeleteServiceRecord(PSERVICE lpService)
 {
-    DPRINT1("Deleting Service %S\n", lpService->lpServiceName);
+    DPRINT("Deleting Service %S\n", lpService->lpServiceName);
 
     /* Delete the display name */
     if (lpService->lpDisplayName != NULL &&
@@ -215,12 +190,12 @@ ScmDeleteServiceRecord(PSERVICE lpService)
     /* Remove the Service from the List */
     RemoveEntryList(&lpService->ServiceListEntry);
 
-    DPRINT1("Deleted Service %S\n", lpService->lpServiceName);
+    DPRINT("Deleted Service %S\n", lpService->lpServiceName);
 
     /* Delete the service record */
     HeapFree(GetProcessHeap(), 0, lpService);
 
-    DPRINT1("Done\n");
+    DPRINT("Done\n");
 }
 
 
@@ -705,49 +680,65 @@ ScmGetBootAndSystemDriverState(VOID)
 
 DWORD
 ScmControlService(PSERVICE Service,
-                  DWORD dwControl,
-                  LPSERVICE_STATUS lpServiceStatus)
+                  DWORD dwControl)
 {
     PSCM_CONTROL_PACKET ControlPacket;
-    DWORD Count;
+    SCM_REPLY_PACKET ReplyPacket;
+
+    DWORD dwWriteCount = 0;
+    DWORD dwReadCount = 0;
     DWORD TotalLength;
+    DWORD dwError = ERROR_SUCCESS;
 
     DPRINT("ScmControlService() called\n");
 
+    EnterCriticalSection(&ControlServiceCriticalSection);
+
     TotalLength = wcslen(Service->lpServiceName) + 1;
 
     ControlPacket = (SCM_CONTROL_PACKET*)HeapAlloc(GetProcessHeap(),
                                                    HEAP_ZERO_MEMORY,
                                                    sizeof(SCM_CONTROL_PACKET) + (TotalLength * sizeof(WCHAR)));
     if (ControlPacket == NULL)
+    {
+        LeaveCriticalSection(&ControlServiceCriticalSection);
         return ERROR_NOT_ENOUGH_MEMORY;
+    }
 
     ControlPacket->dwControl = dwControl;
-    ControlPacket->hClient = Service->hClient;
     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,
+              &dwWriteCount,
               NULL);
 
-    /* FIXME: Read the reply */
+    /* Read the reply */
+    ReadFile(Service->ControlPipeHandle,
+             &ReplyPacket,
+             sizeof(SCM_REPLY_PACKET),
+             &dwReadCount,
+             NULL);
 
     /* Release the contol packet */
     HeapFree(GetProcessHeap(),
              0,
              ControlPacket);
 
-    RtlCopyMemory(lpServiceStatus,
-                  &Service->Status,
-                  sizeof(SERVICE_STATUS));
+    if (dwReadCount == sizeof(SCM_REPLY_PACKET))
+    {
+        dwError = ReplyPacket.dwError;
+    }
+
+    LeaveCriticalSection(&ControlServiceCriticalSection);
 
-    DPRINT("ScmControlService) done\n");
+    DPRINT("ScmControlService() done\n");
 
-    return ERROR_SUCCESS;
+    return dwError;
 }
 
 
@@ -757,11 +748,15 @@ ScmSendStartCommand(PSERVICE Service,
                     LPWSTR *argv)
 {
     PSCM_CONTROL_PACKET ControlPacket;
+    SCM_REPLY_PACKET ReplyPacket;
     DWORD TotalLength;
     DWORD ArgsLength = 0;
     DWORD Length;
     PWSTR Ptr;
-    DWORD Count;
+    DWORD dwWriteCount = 0;
+    DWORD dwReadCount = 0;
+    DWORD dwError = ERROR_SUCCESS;
+    DWORD i;
 
     DPRINT("ScmSendStartCommand() called\n");
 
@@ -769,10 +764,10 @@ ScmSendStartCommand(PSERVICE Service,
     TotalLength = wcslen(Service->lpServiceName) + 1;
     if (argc > 0)
     {
-        for (Count = 0; Count < argc; Count++)
+        for (i = 0; i < argc; i++)
         {
-            DPRINT("Arg: %S\n", argv[Count]);
-            Length = wcslen(argv[Count]) + 1;
+            DPRINT("Arg: %S\n", argv[i]);
+            Length = wcslen(argv[i]) + 1;
             TotalLength += Length;
             ArgsLength += Length;
         }
@@ -788,7 +783,7 @@ ScmSendStartCommand(PSERVICE Service,
         return ERROR_NOT_ENOUGH_MEMORY;
 
     ControlPacket->dwControl = SERVICE_CONTROL_START;
-    ControlPacket->hClient = Service->hClient;
+    ControlPacket->hServiceStatus = (SERVICE_STATUS_HANDLE)Service;
     ControlPacket->dwSize = TotalLength;
     Ptr = &ControlPacket->szArguments[0];
     wcscpy(Ptr, Service->lpServiceName);
@@ -812,19 +807,29 @@ ScmSendStartCommand(PSERVICE Service,
     WriteFile(Service->ControlPipeHandle,
               ControlPacket,
               sizeof(SCM_CONTROL_PACKET) + (TotalLength - 1) * sizeof(WCHAR),
-              &Count,
+              &dwWriteCount,
               NULL);
 
-    /* FIXME: Read the reply */
+    /* Read the reply */
+    ReadFile(Service->ControlPipeHandle,
+             &ReplyPacket,
+             sizeof(SCM_REPLY_PACKET),
+             &dwReadCount,
+             NULL);
 
     /* Release the contol packet */
     HeapFree(GetProcessHeap(),
              0,
              ControlPacket);
 
+    if (dwReadCount == sizeof(SCM_REPLY_PACKET))
+    {
+        dwError = ReplyPacket.dwError;
+    }
+
     DPRINT("ScmSendStartCommand() done\n");
 
-    return ERROR_SUCCESS;
+    return dwError;
 }
 
 
@@ -845,6 +850,7 @@ ScmStartUserModeService(PSERVICE Service,
     WCHAR NtControlPipeName[MAX_PATH + 1];
     HKEY hServiceCurrentKey = INVALID_HANDLE_VALUE;
     DWORD KeyDisposition;
+    DWORD dwProcessId;
 
     RtlInitUnicodeString(&ImagePath, NULL);
 
@@ -916,6 +922,9 @@ ScmStartUserModeService(PSERVICE Service,
 
     /* Create '\\.\pipe\net\NtControlPipeXXX' instance */
     swprintf(NtControlPipeName, L"\\\\.\\pipe\\net\\NtControlPipe%u", ServiceCurrent);
+
+    DPRINT("Service: %p  ImagePath: %wZ  PipeName: %S\n", Service, &ImagePath, NtControlPipeName);
+
     Service->ControlPipeHandle = CreateNamedPipeW(NtControlPipeName,
                                                   PIPE_ACCESS_DUPLEX,
                                                   PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT,
@@ -986,7 +995,7 @@ ScmStartUserModeService(PSERVICE Service,
 
         /* Read SERVICE_STATUS_HANDLE from pipe */
         if (!ReadFile(Service->ControlPipeHandle,
-                      (LPVOID)&Service->hClient,
+                      (LPVOID)&dwProcessId,
                       sizeof(DWORD),
                       &dwRead,
                       NULL))
@@ -997,7 +1006,7 @@ ScmStartUserModeService(PSERVICE Service,
         }
         else
         {
-            DPRINT("Received service status %lu\n", Service->hClient);
+            DPRINT("Received service process ID %lu\n", dwProcessId);
 
             /* Send start command */
             dwError = ScmSendStartCommand(Service, argc, argv);
@@ -1027,9 +1036,21 @@ ScmStartService(PSERVICE Service, DWORD argc, LPWSTR *argv)
 {
     PSERVICE_GROUP Group = Service->lpGroup;
     DWORD dwError = ERROR_SUCCESS;
+    LPCWSTR ErrorLogStrings[2];
 
     DPRINT("ScmStartService() called\n");
 
+    DPRINT("Start Service %p (%S)\n", Service, Service->lpServiceName);
+
+    EnterCriticalSection(&ControlServiceCriticalSection);
+
+    if (Service->Status.dwCurrentState != SERVICE_STOPPED)
+    {
+        DPRINT("Service %S is already running!\n", Service->lpServiceName);
+        LeaveCriticalSection(&ControlServiceCriticalSection);
+        return ERROR_SERVICE_ALREADY_RUNNING;
+    }
+
     Service->ControlPipeHandle = INVALID_HANDLE_VALUE;
     DPRINT("Service->Type: %lu\n", Service->Status.dwServiceType);
 
@@ -1038,14 +1059,27 @@ ScmStartService(PSERVICE Service, DWORD argc, LPWSTR *argv)
         /* Load driver */
         dwError = ScmLoadDriver(Service);
         if (dwError == ERROR_SUCCESS)
+        {
             Service->Status.dwControlsAccepted = SERVICE_ACCEPT_STOP;
+            Service->Status.dwCurrentState = SERVICE_RUNNING;
+        }
     }
     else
     {
         /* Start user-mode 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
+        }
     }
 
+    LeaveCriticalSection(&ControlServiceCriticalSection);
+
     DPRINT("ScmStartService() done (Error %lu)\n", dwError);
 
     if (dwError == ERROR_SUCCESS)
@@ -1054,17 +1088,21 @@ ScmStartService(PSERVICE Service, DWORD argc, LPWSTR *argv)
         {
             Group->ServicesRunning = TRUE;
         }
-        Service->Status.dwCurrentState = SERVICE_START_PENDING;
     }
-#if 0
     else
     {
-        switch (Service->ErrorControl)
+        if (Service->dwErrorControl != SERVICE_ERROR_IGNORE)
         {
-            case SERVICE_ERROR_NORMAL:
-                /* FIXME: Log error */
-                break;
+            ErrorLogStrings[0] = Service->lpServiceName;
+            ErrorLogStrings[1] = L"Test";
+            ScmLogError(EVENT_SERVICE_START_FAILED,
+                        2,
+                        ErrorLogStrings);
+        }
 
+#if 0
+        switch (Service->dwErrorControl)
+        {
             case SERVICE_ERROR_SEVERE:
                 if (IsLastKnownGood == FALSE)
                 {
@@ -1083,8 +1121,8 @@ ScmStartService(PSERVICE Service, DWORD argc, LPWSTR *argv)
                 }
                 break;
         }
-    }
 #endif
+    }
 
     return dwError;
 }
@@ -1097,14 +1135,65 @@ ScmAutoStartServices(VOID)
     PLIST_ENTRY ServiceEntry;
     PSERVICE_GROUP CurrentGroup;
     PSERVICE CurrentService;
+    WCHAR szSafeBootServicePath[MAX_PATH];
+    DWORD dwError;
+    HKEY hKey;
     ULONG i;
 
-    /* Clear 'ServiceVisited' flag */
+    /* 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;
+        /* 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;
     }
 
@@ -1207,10 +1296,12 @@ ScmAutoShutdownServices(VOID)
 {
     PLIST_ENTRY ServiceEntry;
     PSERVICE CurrentService;
-    SERVICE_STATUS ServiceStatus;
 
     DPRINT("ScmAutoShutdownServices() called\n");
 
+    /* Lock the service database exclusively */
+    ScmLockDatabaseExclusive();
+
     ServiceEntry = ServiceListHead.Flink;
     while (ServiceEntry != &ServiceListHead)
     {
@@ -1220,13 +1311,52 @@ ScmAutoShutdownServices(VOID)
             CurrentService->Status.dwCurrentState == SERVICE_START_PENDING)
         {
             /* shutdown service */
-            ScmControlService(CurrentService, SERVICE_CONTROL_STOP, &ServiceStatus);
+            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");
+}
+
+
+BOOL
+ScmLockDatabaseExclusive(VOID)
+{
+    return RtlAcquireResourceExclusive(&DatabaseLock, TRUE);
+}
+
+
+BOOL
+ScmLockDatabaseShared(VOID)
+{
+    return RtlAcquireResourceShared(&DatabaseLock, TRUE);
+}
+
+
+VOID
+ScmUnlockDatabase(VOID)
+{
+    RtlReleaseResource(&DatabaseLock);
+}
+
+
+VOID
+ScmInitNamedPipeCriticalSection(VOID)
+{
+    InitializeCriticalSection(&ControlServiceCriticalSection);
+}
+
+
+VOID
+ScmDeleteNamedPipeCriticalSection(VOID)
+{
+    DeleteCriticalSection(&ControlServiceCriticalSection);
 }
 
 /* EOF */