[SERVICES]
[reactos.git] / reactos / base / system / services / database.c
index 4bb7951..95bc302 100644 (file)
@@ -6,6 +6,7 @@
  * 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 *******************************************************************/
 
@@ -27,8 +33,9 @@ static DWORD dwResumeCount = 1;
 
 /* FUNCTIONS *****************************************************************/
 
+
 PSERVICE
-ScmGetServiceEntryByName(LPWSTR lpServiceName)
+ScmGetServiceEntryByName(LPCWSTR lpServiceName)
 {
     PLIST_ENTRY ServiceEntry;
     PSERVICE CurrentService;
@@ -57,7 +64,7 @@ ScmGetServiceEntryByName(LPWSTR lpServiceName)
 
 
 PSERVICE
-ScmGetServiceEntryByDisplayName(LPWSTR lpDisplayName)
+ScmGetServiceEntryByDisplayName(LPCWSTR lpDisplayName)
 {
     PLIST_ENTRY ServiceEntry;
     PSERVICE CurrentService;
@@ -114,39 +121,8 @@ ScmGetServiceEntryByResumeCount(DWORD dwResumeCount)
 }
 
 
-PSERVICE
-ScmGetServiceEntryByThreadId(ULONG ThreadId)
-{
-    PLIST_ENTRY ServiceEntry;
-    PSERVICE CurrentService;
-
-    DPRINT("ScmGetServiceEntryByThreadId() called\n");
-    DPRINT("Finding ThreadId %lu\n", ThreadId);
-
-    ServiceEntry = ServiceListHead.Flink;
-    while (ServiceEntry != &ServiceListHead)
-    {
-        CurrentService = CONTAINING_RECORD(ServiceEntry,
-                                           SERVICE,
-                                           ServiceListEntry);
-        DPRINT("Found threadId %lu\n", CurrentService->ThreadId);
-        if (CurrentService->ThreadId == ThreadId)
-        {
-            DPRINT("Found service: '%S'\n", CurrentService->lpDisplayName);
-            return CurrentService;
-        }
-
-        ServiceEntry = ServiceEntry->Flink;
-    }
-
-    DPRINT("Couldn't find a matching service\n");
-
-    return NULL;
-}
-
-
 DWORD
-ScmCreateNewServiceRecord(LPWSTR lpServiceName,
+ScmCreateNewServiceRecord(LPCWSTR lpServiceName,
                           PSERVICE *lpServiceRecord)
 {
     PSERVICE lpService = NULL;
@@ -170,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;
@@ -185,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;
@@ -320,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)
@@ -335,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");
 }
 
 
@@ -416,6 +530,9 @@ ScmCreateServiceDatabase(VOID)
 
     RegCloseKey(hServicesKey);
 
+    /* Wait for LSA */
+    WaitForLSA();
+
     /* Delete services that are marked for delete */
     ScmDeleteMarkedServices();
 
@@ -567,21 +684,27 @@ ScmControlService(PSERVICE Service,
 {
     PSCM_CONTROL_PACKET ControlPacket;
     DWORD Count;
+    DWORD TotalLength;
 
     DPRINT("ScmControlService() called\n");
 
-    ControlPacket = (SCM_CONTROL_PACKET*) HeapAlloc(GetProcessHeap(),
-                                                    HEAP_ZERO_MEMORY,
-                                                    sizeof(SCM_CONTROL_PACKET));
+    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 start command */
+    /* Send the control packet */
     WriteFile(Service->ControlPipeHandle,
               ControlPacket,
-              sizeof(SCM_CONTROL_PACKET),
+              sizeof(SCM_CONTROL_PACKET) + (TotalLength * sizeof(WCHAR)),
               &Count,
               NULL);
 
@@ -592,6 +715,10 @@ ScmControlService(PSERVICE Service,
              0,
              ControlPacket);
 
+    RtlCopyMemory(lpServiceStatus,
+                  &Service->Status,
+                  sizeof(SERVICE_STATUS));
+
     DPRINT("ScmControlService) done\n");
 
     return ERROR_SUCCESS;
@@ -600,7 +727,8 @@ ScmControlService(PSERVICE Service,
 
 static DWORD
 ScmSendStartCommand(PSERVICE Service,
-                    LPWSTR Arguments)
+                    DWORD argc,
+                    LPWSTR *argv)
 {
     PSCM_CONTROL_PACKET ControlPacket;
     DWORD TotalLength;
@@ -613,39 +741,42 @@ ScmSendStartCommand(PSERVICE Service,
 
     /* Calculate the total length of the start command line */
     TotalLength = wcslen(Service->lpServiceName) + 1;
-    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;
             ArgsLength += Length;
-            Ptr += Length;
-            DPRINT("Arg: %S\n", Ptr);
         }
     }
     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 = (SCM_CONTROL_PACKET*)HeapAlloc(GetProcessHeap(),
+                                                   HEAP_ZERO_MEMORY,
+                                                   sizeof(SCM_CONTROL_PACKET) + (TotalLength - 1) * sizeof(WCHAR));
     if (ControlPacket == NULL)
         return ERROR_NOT_ENOUGH_MEMORY;
 
     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);
 
     /* Copy argument list */
-    if (Arguments != NULL)
+    if (argc > 0)
     {
+        UNIMPLEMENTED;
+        DPRINT1("Arguments sent to service ignored!\n");
+#if 0
         memcpy(Ptr, Arguments, ArgsLength);
         Ptr += ArgsLength;
+#endif
     }
 
     /* Terminate the argument list */
@@ -673,16 +804,22 @@ ScmSendStartCommand(PSERVICE Service,
 
 static DWORD
 ScmStartUserModeService(PSERVICE Service,
-                        LPWSTR lpArgs)
+                        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);
 
@@ -711,8 +848,50 @@ ScmStartUserModeService(PSERVICE Service,
     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,
@@ -720,7 +899,7 @@ 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");
@@ -735,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,
@@ -776,12 +955,11 @@ ScmStartUserModeService(PSERVICE Service,
     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),
@@ -794,10 +972,10 @@ ScmStartUserModeService(PSERVICE Service,
         }
         else
         {
-            DPRINT("Received process id %lu\n", dwProcessId);
+            DPRINT("Received service process ID %lu\n", dwProcessId);
 
             /* Send start command */
-            dwError = ScmSendStartCommand(Service, lpArgs);
+            dwError = ScmSendStartCommand(Service, argc, argv);
         }
     }
     else
@@ -820,7 +998,7 @@ ScmStartUserModeService(PSERVICE Service,
 
 
 DWORD
-ScmStartService(PSERVICE Service, LPWSTR lpArgs)
+ScmStartService(PSERVICE Service, DWORD argc, LPWSTR *argv)
 {
     PSERVICE_GROUP Group = Service->lpGroup;
     DWORD dwError = ERROR_SUCCESS;
@@ -835,12 +1013,23 @@ ScmStartService(PSERVICE Service, LPWSTR lpArgs)
         /* 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, lpArgs);
+        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 (Error %lu)\n", dwError);
@@ -851,7 +1040,6 @@ ScmStartService(PSERVICE Service, LPWSTR lpArgs)
         {
             Group->ServicesRunning = TRUE;
         }
-        Service->Status.dwCurrentState = SERVICE_RUNNING;
     }
 #if 0
     else
@@ -927,7 +1115,7 @@ ScmAutoStartServices(VOID)
                     (CurrentService->dwTag == CurrentGroup->TagArray[i]))
                 {
                     CurrentService->ServiceVisited = TRUE;
-                    ScmStartService(CurrentService, NULL);
+                    ScmStartService(CurrentService, 0, NULL);
                 }
 
                 ServiceEntry = ServiceEntry->Flink;
@@ -945,7 +1133,7 @@ ScmAutoStartServices(VOID)
                 (CurrentService->ServiceVisited == FALSE))
             {
                 CurrentService->ServiceVisited = TRUE;
-                ScmStartService(CurrentService, NULL);
+                ScmStartService(CurrentService, 0, NULL);
             }
 
             ServiceEntry = ServiceEntry->Flink;
@@ -965,7 +1153,7 @@ ScmAutoStartServices(VOID)
             (CurrentService->ServiceVisited == FALSE))
         {
             CurrentService->ServiceVisited = TRUE;
-            ScmStartService(CurrentService, NULL);
+            ScmStartService(CurrentService, 0, NULL);
         }
 
         ServiceEntry = ServiceEntry->Flink;
@@ -982,7 +1170,7 @@ ScmAutoStartServices(VOID)
             (CurrentService->ServiceVisited == FALSE))
         {
             CurrentService->ServiceVisited = TRUE;
-            ScmStartService(CurrentService, NULL);
+            ScmStartService(CurrentService, 0, NULL);
         }
 
         ServiceEntry = ServiceEntry->Flink;
@@ -1026,4 +1214,25 @@ ScmAutoShutdownServices(VOID)
     DPRINT("ScmGetBootAndSystemDriverState() done\n");
 }
 
+
+BOOL
+ScmLockDatabaseExclusive(VOID)
+{
+    return RtlAcquireResourceExclusive(&DatabaseLock, TRUE);
+}
+
+
+BOOL
+ScmLockDatabaseShared(VOID)
+{
+    return RtlAcquireResourceShared(&DatabaseLock, TRUE);
+}
+
+
+VOID
+ScmUnlockDatabase(VOID)
+{
+    RtlReleaseResource(&DatabaseLock);
+}
+
 /* EOF */