- Add a reference counter to the service record.
authorEric Kohl <eric.kohl@reactos.org>
Fri, 29 Aug 2008 20:43:12 +0000 (20:43 +0000)
committerEric Kohl <eric.kohl@reactos.org>
Fri, 29 Aug 2008 20:43:12 +0000 (20:43 +0000)
- Implement a common service record delete function.
- RCloseServiceHandle: Remove a service if it has been marked for deletion and the reference counter reaches 0.
- RControlService: Stop a service only if there are no dependent services running.

Patch based on bug report #3669 by Michael Martin (aka bugboy) <martinmnet@hotmail.com> just like the patches r35748, r35750, r35752 and r35753.

svn path=/trunk/; revision=35767

reactos/base/system/services/rpcserver.c
reactos/base/system/services/services.h

index 45d9187..5e11db3 100644 (file)
@@ -404,6 +404,12 @@ DWORD RCloseServiceHandle(
     LPSC_RPC_HANDLE hSCObject)
 {
     PMANAGER_HANDLE hManager;
+    PSERVICE_HANDLE hService;
+    PSERVICE lpService;
+    HKEY hServicesKey;
+    DWORD dwError;
+    DWORD pcbBytesNeeded = 0;
+    DWORD dwServicesReturned = 0;
 
     DPRINT("RCloseServiceHandle() called\n");
 
@@ -413,6 +419,7 @@ DWORD RCloseServiceHandle(
         return ERROR_INVALID_HANDLE;
 
     hManager = (PMANAGER_HANDLE)*hSCObject;
+    hService = (PSERVICE_HANDLE)*hSCObject;
     if (hManager->Handle.Tag == MANAGER_TAG)
     {
         DPRINT("Found manager handle\n");
@@ -420,24 +427,92 @@ DWORD RCloseServiceHandle(
         hManager->Handle.RefCount--;
         if (hManager->Handle.RefCount == 0)
         {
-            /* FIXME: add cleanup code */
+            /* FIXME: add handle cleanup code */
 
             HeapFree(GetProcessHeap(), 0, hManager);
+            hManager = NULL;
         }
 
         DPRINT("RCloseServiceHandle() done\n");
         return ERROR_SUCCESS;
     }
-    else if (hManager->Handle.Tag == SERVICE_TAG)
+    else if (hService->Handle.Tag == SERVICE_TAG)
     {
         DPRINT("Found service handle\n");
 
-        hManager->Handle.RefCount--;
-        if (hManager->Handle.RefCount == 0)
+        /* Get the pointer to the service record */
+        lpService = hService->ServiceEntry;
+
+        ASSERT(hService->Handle.RefCount > 0);
+
+        hService->Handle.RefCount--;
+        if (hService->Handle.RefCount == 0)
         {
-            /* FIXME: add cleanup code */
+            /* FIXME: add handle cleanup code */
 
-            HeapFree(GetProcessHeap(), 0, hManager);
+            /* Free the handle */
+            HeapFree(GetProcessHeap(), 0, hService);
+            hService = NULL;
+        }
+
+        ASSERT(lpService->dwRefCount > 0);
+
+        lpService->dwRefCount--;
+        DPRINT1("CloseServiceHandle - lpService->dwRefCount %u\n",
+                lpService->dwRefCount);
+
+        if (lpService->dwRefCount == 0)
+        {
+            /* If this service has been marked for deletion */
+            if (lpService->bDeleted)
+            {
+                /* Open the Services Reg key */
+                dwError = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
+                                        L"System\\CurrentControlSet\\Services",
+                                        0,
+                                        KEY_SET_VALUE | KEY_READ,
+                                        &hServicesKey);
+                if (dwError != ERROR_SUCCESS)
+                {
+                    DPRINT1("Failed to open services key\n");
+                    return dwError;
+                }
+
+                /* Call the internal function with NULL, just to get bytes we need */
+                Int_EnumDependentServicesW(hServicesKey,
+                                           lpService,
+                                           SERVICE_ACTIVE,
+                                           NULL,
+                                           &pcbBytesNeeded,
+                                           &dwServicesReturned);
+
+                /* if pcbBytesNeeded returned a value then there are services running that are dependent on this service*/
+                if (pcbBytesNeeded)
+                {
+                    DPRINT1("Deletion failed due to running dependencies.\n",
+                            lpService->lpServiceName);
+                    RegCloseKey(hServicesKey);
+                    return ERROR_SUCCESS;
+                }
+
+                /* There are no references and no runnning dependencies,
+                   it is now safe to delete the service */
+
+                /* Delete the Service Key */
+                dwError = RegDeleteKey(hServicesKey,
+                                       lpService->lpServiceName);
+
+                RegCloseKey(hServicesKey);
+
+                if (dwError != ERROR_SUCCESS)
+                {
+                    DPRINT1("Failed to Delete the Service Registry key\n");
+                    return dwError;
+                }
+
+                /* Delete the Service */
+                ScmDeleteServiceRecord(lpService);
+            }
         }
 
         DPRINT("RCloseServiceHandle() done\n");
@@ -461,6 +536,9 @@ DWORD RControlService(
     PSERVICE lpService;
     ACCESS_MASK DesiredAccess;
     DWORD dwError = ERROR_SUCCESS;
+    DWORD pcbBytesNeeded = 0;
+    DWORD dwServicesReturned = 0;
+    HKEY hServicesKey = NULL;
 
     DPRINT("RControlService() called\n");
 
@@ -475,6 +553,14 @@ DWORD RControlService(
         return ERROR_INVALID_HANDLE;
     }
 
+    /* Check the service entry point */
+    lpService = hSvc->ServiceEntry;
+    if (lpService == NULL)
+    {
+        DPRINT1("lpService == NULL!\n"); 
+        return ERROR_INVALID_HANDLE;
+    }
+
     /* Check access rights */
     switch (dwControl)
     {
@@ -507,12 +593,40 @@ DWORD RControlService(
                                   DesiredAccess))
         return ERROR_ACCESS_DENIED;
 
-    /* Check the service entry point */
-    lpService = hSvc->ServiceEntry;
-    if (lpService == NULL)
+    if (dwControl == SERVICE_CONTROL_STOP)
     {
-        DPRINT1("lpService == NULL!\n");
-        return ERROR_INVALID_HANDLE;
+        /* Check if the service has dependencies running as windows
+           doesn't stop a service that does */
+
+        /* Open the Services Reg key */
+        dwError = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
+                                L"System\\CurrentControlSet\\Services",
+                                0,
+                                KEY_READ,
+                                &hServicesKey);
+        if (dwError != ERROR_SUCCESS)
+        {
+            DPRINT1("Failed to open services key\n");
+            return dwError;
+        }
+
+        /* Call the internal function with NULL, just to get bytes we need */
+        Int_EnumDependentServicesW(hServicesKey,
+                                   lpService,
+                                   SERVICE_ACTIVE,
+                                   NULL,
+                                   &pcbBytesNeeded,
+                                   &dwServicesReturned);
+
+        RegCloseKey(hServicesKey);
+
+        /* If pcbBytesNeeded is not zero then there are services running that
+           are dependent on this service */
+        if (pcbBytesNeeded != 0)
+        {
+            DPRINT("Service has running dependencies. Failed to stop service.\n");
+            return ERROR_DEPENDENT_SERVICES_RUNNING;
+        }
     }
 
     if (lpService->Status.dwServiceType & SERVICE_DRIVER)
@@ -530,6 +644,9 @@ DWORD RControlService(
                                     lpServiceStatus);
     }
 
+    if ((dwError == ERROR_SUCCESS) && (pcbBytesNeeded))
+        dwError = ERROR_DEPENDENT_SERVICES_RUNNING;
+
     /* Return service status information */
     RtlCopyMemory(lpServiceStatus,
                   &lpService->Status,
@@ -1120,7 +1237,7 @@ DWORD RChangeServiceConfigW(
                                  (wcslen(lpLoadOrderGroup) + 1) * sizeof(WCHAR));
         if (dwError != ERROR_SUCCESS)
             goto done;
-        /* FIXME: update lpService->lpServiceGroup */
+        /* FIXME: Update lpService->lpServiceGroup */
     }
 
     if (lpdwTagId != NULL)
@@ -1836,6 +1953,9 @@ DWORD RCreateServiceW(
     if (dwError != ERROR_SUCCESS)
         goto done;
 
+    lpService->dwRefCount = 1;
+    DPRINT1("CreateService - lpService->dwRefCount %u\n", lpService->dwRefCount);
+
 done:;
     if (hServiceKey != NULL)
         RegCloseKey(hServiceKey);
@@ -2309,6 +2429,9 @@ DWORD ROpenServiceW(
         return dwError;
     }
 
+    lpService->dwRefCount++;
+    DPRINT1("OpenService - lpService->dwRefCount %u\n",lpService->dwRefCount);
+
     *lpServiceHandle = (unsigned long)hHandle; /* FIXME: 64 bit portability */
     DPRINT("*hService = %p\n", *lpServiceHandle);
 
index 72343ac..9bfb452 100644 (file)
@@ -40,6 +40,7 @@ typedef struct _SERVICE
     PSERVICE_IMAGE lpImage;
     BOOL bDeleted;
     DWORD dwResumeCount;
+    DWORD dwRefCount;
 
     CLIENT_HANDLE hClient;
     SERVICE_STATUS Status;
@@ -109,6 +110,7 @@ PSERVICE ScmGetServiceEntryByResumeCount(DWORD dwResumeCount);
 PSERVICE ScmGetServiceEntryByClientHandle(ULONG ThreadId);
 DWORD ScmCreateNewServiceRecord(LPWSTR lpServiceName,
                                 PSERVICE *lpServiceRecord);
+VOID ScmDeleteServiceRecord(PSERVICE lpService);
 DWORD ScmMarkServiceForDelete(PSERVICE pService);
 
 DWORD ScmControlService(PSERVICE Service,