services: Introduce an scmdatabase object to store the root key of the services database.
authorRob Shearman <rob@codeweavers.com>
Fri, 28 Mar 2008 17:08:12 +0000 (17:08 +0000)
committerAlexandre Julliard <julliard@winehq.org>
Sat, 29 Mar 2008 10:51:43 +0000 (11:51 +0100)
Make the functions that deal with the service database more object-oriented.

Add a finer-grained locking architecture.

programs/services/rpc.c
programs/services/services.c
programs/services/services.h

index c721e557ff7de847b4d480daa84efe80c0bc0e2b..b6f043b203f7bcc11c7cd3bcef970034c2b5e434 100644 (file)
@@ -38,16 +38,6 @@ extern HANDLE __wine_make_process_system(void);
 
 WINE_DEFAULT_DEBUG_CHANNEL(service);
 
-static CRITICAL_SECTION g_handle_table_cs;
-static CRITICAL_SECTION_DEBUG g_handle_table_cs_debug =
-{
-    0, 0, &g_handle_table_cs,
-    { &g_handle_table_cs_debug.ProcessLocksList,
-      &g_handle_table_cs_debug.ProcessLocksList },
-      0, 0, { (DWORD_PTR)(__FILE__ ": g_handle_table_cs") }
-};
-static CRITICAL_SECTION g_handle_table_cs = { &g_handle_table_cs_debug, -1, 0, 0, 0, 0 };
-
 static const GENERIC_MAPPING g_scm_generic =
 {
     (STANDARD_RIGHTS_READ | SC_MANAGER_ENUMERATE_SERVICE | SC_MANAGER_QUERY_LOCK_STATUS),
@@ -77,12 +67,13 @@ struct sc_handle
     DWORD access;
 };
 
-struct sc_manager       /* service control manager handle */
+struct sc_manager_handle       /* service control manager handle */
 {
     struct sc_handle hdr;
+    struct scmdatabase *db;
 };
 
-struct sc_service       /* service handle */
+struct sc_service_handle       /* service handle */
 {
     struct sc_handle hdr;
     struct service_entry *service_entry;
@@ -90,7 +81,7 @@ struct sc_service       /* service handle */
 
 struct sc_lock
 {
-    char dummy; /* no state currently used */
+    struct scmdatabase *db;
 };
 
 /* Check if the given handle is of the required type and allows the requested access. */
@@ -114,21 +105,21 @@ static DWORD validate_context_handle(SC_RPC_HANDLE handle, DWORD type, DWORD nee
     return ERROR_SUCCESS;
 }
 
-static DWORD validate_scm_handle(SC_RPC_HANDLE handle, DWORD needed_access, struct sc_manager **manager)
+static DWORD validate_scm_handle(SC_RPC_HANDLE handle, DWORD needed_access, struct sc_manager_handle **manager)
 {
     struct sc_handle *hdr;
     DWORD err = validate_context_handle(handle, SC_HTYPE_MANAGER, needed_access, &hdr);
     if (err == ERROR_SUCCESS)
-        *manager = (struct sc_manager *)hdr;
+        *manager = (struct sc_manager_handle *)hdr;
     return err;
 }
 
-static DWORD validate_service_handle(SC_RPC_HANDLE handle, DWORD needed_access, struct sc_service **service)
+static DWORD validate_service_handle(SC_RPC_HANDLE handle, DWORD needed_access, struct sc_service_handle **service)
 {
     struct sc_handle *hdr;
     DWORD err = validate_context_handle(handle, SC_HTYPE_SERVICE, needed_access, &hdr);
     if (err == ERROR_SUCCESS)
-        *service = (struct sc_service *)hdr;
+        *service = (struct sc_service_handle *)hdr;
     return err;
 }
 
@@ -138,7 +129,7 @@ DWORD svcctl_OpenSCManagerW(
     DWORD dwAccessMask,
     SC_RPC_HANDLE *handle)
 {
-    struct sc_manager *manager;
+    struct sc_manager_handle *manager;
 
     WINE_TRACE("(%s, %s, %x)\n", wine_dbgstr_w(MachineName), wine_dbgstr_w(DatabaseName), dwAccessMask);
 
@@ -159,6 +150,7 @@ DWORD svcctl_OpenSCManagerW(
         dwAccessMask |= SC_MANAGER_ALL_ACCESS;
     manager->hdr.access = dwAccessMask;
     RtlMapGenericMask(&manager->hdr.access, &g_scm_generic);
+    manager->db = active_database;
     *handle = &manager->hdr;
 
     return ERROR_SUCCESS;
@@ -171,13 +163,13 @@ static void SC_RPC_HANDLE_destroy(SC_RPC_HANDLE handle)
     {
         case SC_HTYPE_MANAGER:
         {
-            struct sc_manager *manager = (struct sc_manager *)hdr;
+            struct sc_manager_handle *manager = (struct sc_manager_handle *)hdr;
             HeapFree(GetProcessHeap(), 0, manager);
             break;
         }
         case SC_HTYPE_SERVICE:
         {
-            struct sc_service *service = (struct sc_service *)hdr;
+            struct sc_service_handle *service = (struct sc_service_handle *)hdr;
             release_service(service->service_entry);
             HeapFree(GetProcessHeap(), 0, service);
             break;
@@ -195,7 +187,7 @@ DWORD svcctl_GetServiceDisplayNameW(
     DWORD cchBufSize,
     DWORD *cchLength)
 {
-    struct sc_manager *manager;
+    struct sc_manager_handle *manager;
     struct service_entry *entry;
     DWORD err;
 
@@ -204,12 +196,14 @@ DWORD svcctl_GetServiceDisplayNameW(
     if ((err = validate_scm_handle(hSCManager, 0, &manager)) != ERROR_SUCCESS)
         return err;
 
-    lock_services();
+    scmdatabase_lock_shared(manager->db);
 
-    entry = find_service(lpServiceName);
+    entry = scmdatabase_find_service(manager->db, lpServiceName);
     if (entry != NULL)
     {
-        LPCWSTR name = get_display_name(entry);
+        LPCWSTR name;
+        service_lock_shared(entry);
+        name = get_display_name(entry);
         *cchLength = strlenW(name);
         if (*cchLength < cchBufSize)
         {
@@ -218,6 +212,7 @@ DWORD svcctl_GetServiceDisplayNameW(
         }
         else
             err = ERROR_INSUFFICIENT_BUFFER;
+        service_unlock(entry);
     }
     else
     {
@@ -225,9 +220,10 @@ DWORD svcctl_GetServiceDisplayNameW(
         err = ERROR_SERVICE_DOES_NOT_EXIST;
     }
 
+    scmdatabase_unlock(manager->db);
+
     if (err != ERROR_SUCCESS && cchBufSize > 0)
         lpBuffer[0] = 0;
-    unlock_services();
 
     return err;
 }
@@ -240,7 +236,7 @@ DWORD svcctl_GetServiceKeyNameW(
     DWORD *cchLength)
 {
     struct service_entry *entry;
-    struct sc_manager *manager;
+    struct sc_manager_handle *manager;
     DWORD err;
 
     WINE_TRACE("(%s, %d)\n", wine_dbgstr_w(lpServiceDisplayName), cchBufSize);
@@ -248,11 +244,12 @@ DWORD svcctl_GetServiceKeyNameW(
     if ((err = validate_scm_handle(hSCManager, 0, &manager)) != ERROR_SUCCESS)
         return err;
 
-    lock_services();
+    scmdatabase_lock_shared(manager->db);
 
-    entry = find_service_by_displayname(lpServiceDisplayName);
+    entry = scmdatabase_find_service_by_displayname(manager->db, lpServiceDisplayName);
     if (entry != NULL)
     {
+        service_lock_shared(entry);
         *cchLength = strlenW(entry->name);
         if (*cchLength < cchBufSize)
         {
@@ -261,6 +258,7 @@ DWORD svcctl_GetServiceKeyNameW(
         }
         else
             err = ERROR_INSUFFICIENT_BUFFER;
+        service_unlock(entry);
     }
     else
     {
@@ -268,16 +266,17 @@ DWORD svcctl_GetServiceKeyNameW(
         err = ERROR_SERVICE_DOES_NOT_EXIST;
     }
 
+    scmdatabase_unlock(manager->db);
+
     if (err != ERROR_SUCCESS && cchBufSize > 0)
         lpBuffer[0] = 0;
-    unlock_services();
 
     return err;
 }
 
 static DWORD create_handle_for_service(struct service_entry *entry, DWORD dwDesiredAccess, SC_RPC_HANDLE *phService)
 {
-    struct sc_service *service;
+    struct sc_service_handle *service;
 
     if (!(service = HeapAlloc(GetProcessHeap(), 0, sizeof(*service))))
     {
@@ -302,7 +301,7 @@ DWORD svcctl_OpenServiceW(
     DWORD dwDesiredAccess,
     SC_RPC_HANDLE *phService)
 {
-    struct sc_manager *manager;
+    struct sc_manager_handle *manager;
     struct service_entry *entry;
     DWORD err;
 
@@ -313,11 +312,11 @@ DWORD svcctl_OpenServiceW(
     if (!validate_service_name(lpServiceName))
         return ERROR_INVALID_NAME;
 
-    lock_services();
-    entry = find_service(lpServiceName);
+    scmdatabase_lock_shared(manager->db);
+    entry = scmdatabase_find_service(manager->db, lpServiceName);
     if (entry != NULL)
         entry->ref_count++;
-    unlock_services();
+    scmdatabase_unlock(manager->db);
 
     if (entry == NULL)
         return ERROR_SERVICE_DOES_NOT_EXIST;
@@ -343,7 +342,7 @@ DWORD svcctl_CreateServiceW(
     DWORD dwPasswordSize,
     SC_RPC_HANDLE *phService)
 {
-    struct sc_manager *manager;
+    struct sc_manager_handle *manager;
     struct service_entry *entry;
     DWORD err;
 
@@ -362,8 +361,9 @@ DWORD svcctl_CreateServiceW(
     if (lpDependencies)
         WINE_FIXME("Dependencies not supported yet\n");
 
-    entry = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*entry));
-    entry->name = strdupW(lpServiceName);
+    err = service_create(lpServiceName, &entry);
+    if (err != ERROR_SUCCESS)
+        return err;
     entry->config.dwServiceType = dwServiceType;
     entry->config.dwStartType = dwStartType;
     entry->config.dwErrorControl = dwErrorControl;
@@ -371,7 +371,6 @@ DWORD svcctl_CreateServiceW(
     entry->config.lpLoadOrderGroup = strdupW(lpLoadOrderGroup);
     entry->config.lpServiceStartName = strdupW(lpServiceStartName);
     entry->config.lpDisplayName = strdupW(lpDisplayName);
-    entry->control_pipe = INVALID_HANDLE_VALUE;
 
     if (lpdwTagId)      /* TODO: in most situations a non-NULL tagid will generate a ERROR_INVALID_PARAMETER */
         entry->config.dwTagId = *lpdwTagId;
@@ -387,30 +386,30 @@ DWORD svcctl_CreateServiceW(
         return ERROR_INVALID_PARAMETER;
     }
 
-    lock_services();
+    scmdatabase_lock_exclusive(manager->db);
 
-    if (find_service(lpServiceName))
+    if (scmdatabase_find_service(manager->db, lpServiceName))
     {
-        unlock_services();
+        scmdatabase_unlock(manager->db);
         free_service_entry(entry);
         return ERROR_SERVICE_EXISTS;
     }
 
-    if (find_service_by_displayname(get_display_name(entry)))
+    if (scmdatabase_find_service_by_displayname(manager->db, get_display_name(entry)))
     {
-        unlock_services();
+        scmdatabase_unlock(manager->db);
         free_service_entry(entry);
         return ERROR_DUPLICATE_SERVICE_NAME;
     }
 
-    err = add_service(entry);
+    err = scmdatabase_add_service(manager->db, entry);
     if (err != ERROR_SUCCESS)
     {
-        unlock_services();
+        scmdatabase_unlock(manager->db);
         free_service_entry(entry);
         return err;
     }
-    unlock_services();
+    scmdatabase_unlock(manager->db);
 
     return create_handle_for_service(entry, dwDesiredAccess, phService);
 }
@@ -418,20 +417,22 @@ DWORD svcctl_CreateServiceW(
 DWORD svcctl_DeleteService(
     SC_RPC_HANDLE hService)
 {
-    struct sc_service *service;
+    struct sc_service_handle *service;
     DWORD err;
 
     if ((err = validate_service_handle(hService, DELETE, &service)) != ERROR_SUCCESS)
         return err;
 
-    lock_services();
+    scmdatabase_lock_exclusive(service->service_entry->db);
+    service_lock_exclusive(service->service_entry);
 
     if (!is_marked_for_delete(service->service_entry))
-        err = remove_service(service->service_entry);
+        err = scmdatabase_remove_service(service->service_entry->db, service->service_entry);
     else
         err = ERROR_SERVICE_MARKED_FOR_DELETE;
 
-    unlock_services();
+    service_unlock(service->service_entry);
+    scmdatabase_unlock(service->service_entry->db);
 
     return err;
 }
@@ -440,7 +441,7 @@ DWORD svcctl_QueryServiceConfigW(
         SC_RPC_HANDLE hService,
         QUERY_SERVICE_CONFIGW *config)
 {
-    struct sc_service *service;
+    struct sc_service_handle *service;
     DWORD err;
 
     WINE_TRACE("(%p)\n", config);
@@ -448,7 +449,7 @@ DWORD svcctl_QueryServiceConfigW(
     if ((err = validate_service_handle(hService, SERVICE_QUERY_CONFIG, &service)) != 0)
         return err;
 
-    lock_services();
+    service_lock_shared(service->service_entry);
     config->dwServiceType = service->service_entry->config.dwServiceType;
     config->dwStartType = service->service_entry->config.dwStartType;
     config->dwErrorControl = service->service_entry->config.dwErrorControl;
@@ -458,7 +459,7 @@ DWORD svcctl_QueryServiceConfigW(
     config->lpDependencies = NULL; /* TODO */
     config->lpServiceStartName = strdupW(service->service_entry->config.lpServiceStartName);
     config->lpDisplayName = strdupW(service->service_entry->config.lpDisplayName);
-    unlock_services();
+    service_unlock(service->service_entry);
 
     return ERROR_SUCCESS;
 }
@@ -479,7 +480,7 @@ DWORD svcctl_ChangeServiceConfigW(
         LPCWSTR lpDisplayName)
 {
     struct service_entry new_entry;
-    struct sc_service *service;
+    struct sc_service_handle *service;
     DWORD err;
 
     WINE_TRACE("\n");
@@ -491,17 +492,18 @@ DWORD svcctl_ChangeServiceConfigW(
         return ERROR_INVALID_PARAMETER;
 
     /* first check if the new configuration is correct */
-    lock_services();
+    service_lock_exclusive(service->service_entry);
 
     if (is_marked_for_delete(service->service_entry))
     {
-        unlock_services();
+        service_unlock(service->service_entry);
         return ERROR_SERVICE_MARKED_FOR_DELETE;
     }
 
-    if (lpDisplayName != NULL && find_service_by_displayname(lpDisplayName))
+    if (lpDisplayName != NULL &&
+        scmdatabase_find_service_by_displayname(service->service_entry->db, lpDisplayName))
     {
-        unlock_services();
+        service_unlock(service->service_entry);
         return ERROR_DUPLICATE_SERVICE_NAME;
     }
 
@@ -540,7 +542,7 @@ DWORD svcctl_ChangeServiceConfigW(
     if (!validate_service_config(&new_entry))
     {
         WINE_ERR("The configuration after the change wouldn't be valid");
-        unlock_services();
+        service_unlock(service->service_entry);
         return ERROR_INVALID_PARAMETER;
     }
 
@@ -571,7 +573,7 @@ DWORD svcctl_ChangeServiceConfigW(
 
     *service->service_entry = new_entry;
     save_service_config(service->service_entry);
-    unlock_services();
+    service_unlock(service->service_entry);
 
     return ERROR_SUCCESS;
 }
@@ -580,7 +582,7 @@ DWORD svcctl_SetServiceStatus(
     SC_RPC_HANDLE hServiceStatus,
     LPSERVICE_STATUS lpServiceStatus)
 {
-    struct sc_service *service;
+    struct sc_service_handle *service;
     DWORD err;
 
     WINE_TRACE("(%p, %p)\n", hServiceStatus, lpServiceStatus);
@@ -588,7 +590,7 @@ DWORD svcctl_SetServiceStatus(
     if ((err = validate_service_handle(hServiceStatus, SERVICE_SET_STATUS, &service)) != 0)
         return err;
 
-    lock_services();
+    service_lock_exclusive(service->service_entry);
     /* FIXME: be a bit more discriminant about what parts of the status we set
      * and check that fields are valid */
     service->service_entry->status.dwServiceType = lpServiceStatus->dwServiceType;
@@ -598,7 +600,7 @@ DWORD svcctl_SetServiceStatus(
     service->service_entry->status.dwServiceSpecificExitCode = lpServiceStatus->dwServiceSpecificExitCode;
     service->service_entry->status.dwCheckPoint = lpServiceStatus->dwCheckPoint;
     service->service_entry->status.dwWaitHint = lpServiceStatus->dwWaitHint;
-    unlock_services();
+    service_unlock(service->service_entry);
 
     if (service->service_entry->status_changed_event)
         SetEvent(service->service_entry->status_changed_event);
@@ -613,7 +615,7 @@ DWORD svcctl_QueryServiceStatusEx(
     DWORD cbBufSize,
     LPDWORD pcbBytesNeeded)
 {
-    struct sc_service *service;
+    struct sc_service_handle *service;
     DWORD err;
     LPSERVICE_STATUS_PROCESS pSvcStatusData;
 
@@ -635,7 +637,7 @@ DWORD svcctl_QueryServiceStatusEx(
         return ERROR_INSUFFICIENT_BUFFER;
     }
 
-    lock_services();
+    service_lock_shared(service->service_entry);
 
     pSvcStatusData->dwServiceType = service->service_entry->status.dwServiceType;
     pSvcStatusData->dwCurrentState = service->service_entry->status.dwCurrentState;
@@ -647,7 +649,7 @@ DWORD svcctl_QueryServiceStatusEx(
     pSvcStatusData->dwProcessId = service->service_entry->status.dwProcessId;
     pSvcStatusData->dwServiceFlags = service->service_entry->status.dwServiceFlags;
 
-    unlock_services();
+    service_unlock(service->service_entry);
 
     return ERROR_SUCCESS;
 }
@@ -702,7 +704,7 @@ static DWORD service_start_process(struct service_entry *service_entry, HANDLE *
     DWORD size;
     BOOL r;
 
-    lock_services();
+    service_lock_exclusive(service_entry);
 
     if (service_entry->config.dwServiceType == SERVICE_KERNEL_DRIVER)
     {
@@ -732,17 +734,21 @@ static DWORD service_start_process(struct service_entry *service_entry, HANDLE *
         si.lpDesktop = desktopW;
     }
 
-    unlock_services();
+    service_entry->status.dwCurrentState = SERVICE_START_PENDING;
+    service_entry->status.dwProcessId = pi.dwProcessId;
+
+    service_unlock(service_entry);
 
     r = CreateProcessW(NULL, path, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi);
     HeapFree(GetProcessHeap(),0,path);
     if (!r)
+    {
+        service_lock_exclusive(service_entry);
+        service_entry->status.dwCurrentState = SERVICE_STOPPED;
+        service_entry->status.dwProcessId = 0;
+        service_unlock(service_entry);
         return GetLastError();
-
-    lock_services();
-    service_entry->status.dwCurrentState = SERVICE_START_PENDING;
-    service_entry->status.dwProcessId = pi.dwProcessId;
-    unlock_services();
+    }
 
     *process = pi.hProcess;
     CloseHandle( pi.hThread );
@@ -762,9 +768,9 @@ static DWORD service_wait_for_startup(struct service_entry *service_entry, HANDL
         ret = WaitForMultipleObjects(sizeof(handles)/sizeof(handles[0]), handles, FALSE, 20000);
         if (ret != WAIT_OBJECT_0)
             return ERROR_SERVICE_REQUEST_TIMEOUT;
-        lock_services();
+        service_lock_shared(service_entry);
         dwCurrentStatus = service_entry->status.dwCurrentState;
-        unlock_services();
+        service_unlock(service_entry);
         if (dwCurrentStatus == SERVICE_RUNNING)
         {
             WINE_TRACE("Service started successfully\n");
@@ -907,7 +913,7 @@ DWORD svcctl_StartServiceW(
     DWORD dwNumServiceArgs,
     LPCWSTR *lpServiceArgVectors)
 {
-    struct sc_service *service;
+    struct sc_service_handle *service;
     DWORD err;
     LPWSTR name;
     HANDLE process_handle = NULL;
@@ -917,13 +923,13 @@ DWORD svcctl_StartServiceW(
     if ((err = validate_service_handle(hService, SERVICE_START, &service)) != 0)
         return err;
 
-    err = lock_service_database();
+    err = scmdatabase_lock_startup(service->service_entry->db);
     if (err != ERROR_SUCCESS)
         return err;
 
     if (service->service_entry->control_pipe != INVALID_HANDLE_VALUE)
     {
-        unlock_service_database();
+        scmdatabase_unlock_startup(service->service_entry->db);
         return ERROR_SERVICE_ALREADY_RUNNING;
     }
 
@@ -940,7 +946,7 @@ DWORD svcctl_StartServiceW(
     {
         WINE_ERR("failed to create pipe for %s, error = %d\n",
             wine_dbgstr_w(service->service_entry->name), GetLastError());
-        unlock_service_database();
+        scmdatabase_unlock_startup(service->service_entry->db);
         return GetLastError();
     }
 
@@ -962,7 +968,7 @@ DWORD svcctl_StartServiceW(
         CloseHandle(process_handle);
 
     ReleaseMutex(service->service_entry->control_mutex);
-    unlock_service_database();
+    scmdatabase_unlock_startup(service->service_entry->db);
 
     return err;
 }
@@ -973,7 +979,7 @@ DWORD svcctl_ControlService(
     SERVICE_STATUS *lpServiceStatus)
 {
     DWORD access_required;
-    struct sc_service *service;
+    struct sc_service_handle *service;
     DWORD err;
     BOOL ret;
     HANDLE control_mutex;
@@ -1008,7 +1014,7 @@ DWORD svcctl_ControlService(
     if ((err = validate_service_handle(hService, access_required, &service)) != 0)
         return err;
 
-    lock_services();
+    service_lock_exclusive(service->service_entry);
 
     if (lpServiceStatus)
     {
@@ -1023,21 +1029,21 @@ DWORD svcctl_ControlService(
 
     if (!service_accepts_control(service->service_entry, dwControl))
     {
-        unlock_services();
+        service_unlock(service->service_entry);
         return ERROR_INVALID_SERVICE_CONTROL;
     }
 
     switch (service->service_entry->status.dwCurrentState)
     {
     case SERVICE_STOPPED:
-        unlock_services();
+        service_unlock(service->service_entry);
         return ERROR_SERVICE_NOT_ACTIVE;
     case SERVICE_START_PENDING:
         if (dwControl==SERVICE_CONTROL_STOP)
             break;
         /* fall thru */
     case SERVICE_STOP_PENDING:
-        unlock_services();
+        service_unlock(service->service_entry);
         return ERROR_SERVICE_CANNOT_ACCEPT_CTRL;
     }
 
@@ -1051,7 +1057,7 @@ DWORD svcctl_ControlService(
         service->service_entry->control_pipe = NULL;
     }
 
-    unlock_services();
+    service_unlock(service->service_entry);
 
     ret = WaitForSingleObject(control_mutex, 30000);
     if (ret)
@@ -1094,8 +1100,9 @@ DWORD svcctl_CloseServiceHandle(
 
 static void SC_RPC_LOCK_destroy(SC_RPC_LOCK hLock)
 {
-    unlock_service_database();
-    HeapFree(GetProcessHeap(), 0, hLock);
+    struct sc_lock *lock = hLock;
+    scmdatabase_unlock_startup(lock->db);
+    HeapFree(GetProcessHeap(), 0, lock);
 }
 
 void __RPC_USER SC_RPC_LOCK_rundown(SC_RPC_LOCK hLock)
@@ -1107,7 +1114,7 @@ DWORD svcctl_LockServiceDatabase(
     SC_RPC_HANDLE hSCManager,
     SC_RPC_LOCK *phLock)
 {
-    struct sc_manager *manager;
+    struct sc_manager_handle *manager;
     DWORD err;
 
     WINE_TRACE("(%p, %p)\n", hSCManager, phLock);
@@ -1115,13 +1122,16 @@ DWORD svcctl_LockServiceDatabase(
     if ((err = validate_scm_handle(hSCManager, SC_MANAGER_LOCK, &manager)) != ERROR_SUCCESS)
         return err;
 
-    err = lock_service_database();
+    err = scmdatabase_lock_startup(manager->db);
     if (err != ERROR_SUCCESS)
         return err;
 
     *phLock = HeapAlloc(GetProcessHeap(), 0, sizeof(struct sc_lock));
     if (!*phLock)
+    {
+        scmdatabase_unlock_startup(manager->db);
         return ERROR_NOT_ENOUGH_SERVER_MEMORY;
+    }
 
     return ERROR_SUCCESS;
 }
index ae189dc0e949a1f9ff87a89d947207b122e705ce..80a0a2db2e4f1da6e48865af23f3fabe891923be 100644 (file)
 WINE_DEFAULT_DEBUG_CHANNEL(service);
 
 HANDLE g_hStartedEvent;
-
-static struct list g_services;
-
-static CRITICAL_SECTION services_list_cs;
-static CRITICAL_SECTION_DEBUG services_list_cs_debug =
-{
-    0, 0, &services_list_cs,
-    { &services_list_cs_debug.ProcessLocksList,
-      &services_list_cs_debug.ProcessLocksList },
-      0, 0, { (DWORD_PTR)(__FILE__ ": services_list_cs") }
-};
-static CRITICAL_SECTION services_list_cs = { &services_list_cs_debug, -1, 0, 0, 0, 0 };
+struct scmdatabase *active_database;
 
 static const WCHAR SZ_LOCAL_SYSTEM[] = {'L','o','c','a','l','S','y','s','t','e','m',0};
 
@@ -70,6 +59,21 @@ static const WCHAR SZ_TAG[]               = {'T','a','g',0};
 static const WCHAR SZ_DESCRIPTION[]       = {'D','e','s','c','r','i','p','t','i','o','n',0};
 
 
+DWORD service_create(LPCWSTR name, struct service_entry **entry)
+{
+    *entry = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(**entry));
+    if (!*entry)
+        return ERROR_NOT_ENOUGH_SERVER_MEMORY;
+    (*entry)->name = strdupW(name);
+    if (!(*entry)->name)
+    {
+        HeapFree(GetProcessHeap(), 0, *entry);
+        return ERROR_NOT_ENOUGH_SERVER_MEMORY;
+    }
+    (*entry)->control_pipe = INVALID_HANDLE_VALUE;
+    return ERROR_SUCCESS;
+}
+
 void free_service_entry(struct service_entry *entry)
 {
     HeapFree(GetProcessHeap(), 0, entry->name);
@@ -145,15 +149,10 @@ static DWORD reg_set_string_value(HKEY hKey, LPCWSTR value_name, LPCWSTR string)
 
 DWORD save_service_config(struct service_entry *entry)
 {
-    HKEY hServicesRootKey;
     DWORD err;
     HKEY hKey = NULL;
 
-    if ((err = RegCreateKeyW(HKEY_LOCAL_MACHINE, SZ_SERVICES_KEY, &hServicesRootKey)) != 0)
-        goto cleanup;
-
-    err = RegCreateKeyW(hServicesRootKey, entry->name, &hKey);
-    RegCloseKey(hServicesRootKey);
+    err = RegCreateKeyW(entry->db->root_key, entry->name, &hKey);
     if (err != ERROR_SUCCESS)
         goto cleanup;
 
@@ -189,29 +188,25 @@ cleanup:
     return err;
 }
 
-DWORD add_service(struct service_entry *service)
+DWORD scmdatabase_add_service(struct scmdatabase *db, struct service_entry *service)
 {
     int err;
+    service->db = db;
     if ((err = save_service_config(service)) != ERROR_SUCCESS)
     {
         WINE_ERR("Couldn't store service configuration: error %u\n", err);
         return ERROR_GEN_FAILURE;
     }
 
-    list_add_tail(&g_services, &service->entry);
+    list_add_tail(&db->services, &service->entry);
     return ERROR_SUCCESS;
 }
 
-DWORD remove_service(struct service_entry *service)
+DWORD scmdatabase_remove_service(struct scmdatabase *db, struct service_entry *service)
 {
     int err;
-    HKEY hKey;
 
-    if ((err = RegOpenKeyW(HKEY_LOCAL_MACHINE, SZ_SERVICES_KEY, &hKey)) != 0)
-        return err;
-
-    err = RegDeleteTreeW(hKey, service->name);
-    RegCloseKey(hKey);
+    err = RegDeleteTreeW(db->root_key, service->name);
 
     if (err != 0)
         return err;
@@ -277,35 +272,12 @@ BOOL validate_service_config(struct service_entry *entry)
     return TRUE;
 }
 
-static LONG service_lock = FALSE;
-
-DWORD lock_service_database(void)
-{
-    if (InterlockedCompareExchange(&service_lock, TRUE, FALSE))
-        return ERROR_SERVICE_DATABASE_LOCKED;
-    return ERROR_SUCCESS;
-}
 
-void unlock_service_database(void)
-{
-    InterlockedCompareExchange(&service_lock, FALSE, TRUE);
-}
-
-void lock_services(void)
-{
-    EnterCriticalSection(&services_list_cs);
-}
-
-void unlock_services(void)
-{
-    LeaveCriticalSection(&services_list_cs);
-}
-
-struct service_entry *find_service(LPCWSTR name)
+struct service_entry *scmdatabase_find_service(struct scmdatabase *db, LPCWSTR name)
 {
     struct service_entry *service;
 
-    LIST_FOR_EACH_ENTRY(service, &g_services, struct service_entry, entry)
+    LIST_FOR_EACH_ENTRY(service, &db->services, struct service_entry, entry)
     {
         if (strcmpiW(name, service->name) == 0)
             return service;
@@ -314,11 +286,11 @@ struct service_entry *find_service(LPCWSTR name)
     return NULL;
 }
 
-struct service_entry *find_service_by_displayname(LPCWSTR name)
+struct service_entry *scmdatabase_find_service_by_displayname(struct scmdatabase *db, LPCWSTR name)
 {
     struct service_entry *service;
 
-    LIST_FOR_EACH_ENTRY(service, &g_services, struct service_entry, entry)
+    LIST_FOR_EACH_ENTRY(service, &db->services, struct service_entry, entry)
     {
         if (strcmpiW(name, service->config.lpDisplayName) == 0)
             return service;
@@ -333,19 +305,39 @@ void release_service(struct service_entry *service)
         free_service_entry(service);
 }
 
-static DWORD load_services(void)
+static DWORD scmdatabase_create(struct scmdatabase **db)
 {
-    HKEY hServicesRootKey;
     DWORD err;
-    int i;
 
-    if ((err = RegCreateKeyExW(HKEY_LOCAL_MACHINE, SZ_SERVICES_KEY, 0, NULL,
-                               REG_OPTION_NON_VOLATILE, KEY_READ, NULL,
-                               &hServicesRootKey, NULL)) != ERROR_SUCCESS)
-    {
-        WINE_ERR("Couldn't open services key\n");
-        return err;
-    }
+    *db = HeapAlloc(GetProcessHeap(), 0, sizeof(**db));
+    if (!*db)
+        return ERROR_NOT_ENOUGH_SERVER_MEMORY;
+
+    (*db)->service_start_lock = FALSE;
+    list_init(&(*db)->services);
+
+    InitializeCriticalSection(&(*db)->cs);
+
+    err = RegCreateKeyExW(HKEY_LOCAL_MACHINE, SZ_SERVICES_KEY, 0, NULL,
+                          REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL,
+                          &(*db)->root_key, NULL);
+    if (err != ERROR_SUCCESS)
+        HeapFree(GetProcessHeap(), 0, *db);
+
+    return err;
+}
+
+static void scmdatabase_destroy(struct scmdatabase *db)
+{
+    RegCloseKey(db->root_key);
+    DeleteCriticalSection(&db->cs);
+    HeapFree(GetProcessHeap(), 0, db);
+}
+
+static DWORD scmdatabase_load_services(struct scmdatabase *db)
+{
+    DWORD err;
+    int i;
 
     for (i = 0; TRUE; i++)
     {
@@ -353,7 +345,7 @@ static DWORD load_services(void)
         struct service_entry *entry;
         HKEY hServiceKey;
 
-        err = RegEnumKeyW(hServicesRootKey, i, szName, MAX_SERVICE_NAME);
+        err = RegEnumKeyW(db->root_key, i, szName, MAX_SERVICE_NAME);
         if (err == ERROR_NO_MORE_ITEMS)
             break;
 
@@ -363,12 +355,12 @@ static DWORD load_services(void)
             continue;
         }
 
-        entry = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*entry));
-        entry->name = strdupW(szName);
-        entry->control_pipe = INVALID_HANDLE_VALUE;
+        err = service_create(szName, &entry);
+        if (err != ERROR_SUCCESS)
+            break;
 
         WINE_TRACE("Loading service %s\n", wine_dbgstr_w(szName));
-        err = RegOpenKeyExW(hServicesRootKey, szName, 0, KEY_READ | KEY_WRITE, &hServiceKey);
+        err = RegOpenKeyExW(db->root_key, szName, 0, KEY_READ | KEY_WRITE, &hServiceKey);
         if (err == ERROR_SUCCESS)
         {
             err = load_service_config(hServiceKey, entry);
@@ -400,21 +392,67 @@ static DWORD load_services(void)
         entry->status.dwServiceType = entry->config.dwServiceType;
         entry->status.dwCurrentState = SERVICE_STOPPED;
         entry->status.dwWin32ExitCode = ERROR_SERVICE_NEVER_STARTED;
+        entry->db = db;
         /* all other fields are zero */
 
-        list_add_tail(&g_services, &entry->entry);
+        list_add_tail(&db->services, &entry->entry);
     }
-    RegCloseKey(hServicesRootKey);
     return ERROR_SUCCESS;
 }
 
+DWORD scmdatabase_lock_startup(struct scmdatabase *db)
+{
+    if (InterlockedCompareExchange(&db->service_start_lock, TRUE, FALSE))
+        return ERROR_SERVICE_DATABASE_LOCKED;
+    return ERROR_SUCCESS;
+}
+
+void scmdatabase_unlock_startup(struct scmdatabase *db)
+{
+    InterlockedCompareExchange(&db->service_start_lock, FALSE, TRUE);
+}
+
+void scmdatabase_lock_shared(struct scmdatabase *db)
+{
+    EnterCriticalSection(&db->cs);
+}
+
+void scmdatabase_lock_exclusive(struct scmdatabase *db)
+{
+    EnterCriticalSection(&db->cs);
+}
+
+void scmdatabase_unlock(struct scmdatabase *db)
+{
+    LeaveCriticalSection(&db->cs);
+}
+
+void service_lock_shared(struct service_entry *service)
+{
+    EnterCriticalSection(&service->db->cs);
+}
+
+void service_lock_exclusive(struct service_entry *service)
+{
+    EnterCriticalSection(&service->db->cs);
+}
+
+void service_unlock(struct service_entry *service)
+{
+    LeaveCriticalSection(&service->db->cs);
+}
+
 int main(int argc, char *argv[])
 {
     static const WCHAR svcctl_started_event[] = SVCCTL_STARTED_EVENT;
     DWORD err;
     g_hStartedEvent = CreateEventW(NULL, TRUE, FALSE, svcctl_started_event);
-    list_init(&g_services);
-    if ((err = load_services()) != ERROR_SUCCESS)
+    err = scmdatabase_create(&active_database);
+    if (err != ERROR_SUCCESS)
         return err;
-    return RPC_MainLoop();
+    if ((err = scmdatabase_load_services(active_database)) != ERROR_SUCCESS)
+        return err;
+    err = RPC_MainLoop();
+    scmdatabase_destroy(active_database);
+    return err;
 }
index b02616636ad7f05d212979e972556bf3fa18addd..246e1bb8eff1fa6d8edd94481f028f5a9f27e0f9 100644 (file)
 
 #include "wine/list.h"
 
+struct scmdatabase
+{
+    HKEY root_key;
+    LONG service_start_lock;
+    struct list services;
+    CRITICAL_SECTION cs;
+};
+
 struct service_entry
 {
     struct list entry;
+    struct scmdatabase *db;
     LONG ref_count;                    /* number of references - if goes to zero and the service is deleted the structure will be freed */
     LPWSTR name;
     SERVICE_STATUS_PROCESS status;
@@ -38,21 +47,33 @@ struct service_entry
     HANDLE status_changed_event;
 };
 
+extern struct scmdatabase *active_database;
+
+/* SCM database functions */
+
+struct service_entry *scmdatabase_find_service(struct scmdatabase *db, LPCWSTR name);
+struct service_entry *scmdatabase_find_service_by_displayname(struct scmdatabase *db, LPCWSTR name);
+DWORD scmdatabase_add_service(struct scmdatabase *db, struct service_entry *entry);
+DWORD scmdatabase_remove_service(struct scmdatabase *db, struct service_entry *entry);
+
+DWORD scmdatabase_lock_startup(struct scmdatabase *db);
+void scmdatabase_unlock_startup(struct scmdatabase *db);
+
+void scmdatabase_lock_shared(struct scmdatabase *db);
+void scmdatabase_lock_exclusive(struct scmdatabase *db);
+void scmdatabase_unlock(struct scmdatabase *db);
+
+/* Service functions */
+
+DWORD service_create(LPCWSTR name, struct service_entry **entry);
 BOOL validate_service_name(LPCWSTR name);
 BOOL validate_service_config(struct service_entry *entry);
-struct service_entry *find_service(LPCWSTR name);
-struct service_entry *find_service_by_displayname(LPCWSTR name);
-DWORD add_service(struct service_entry *entry);
-DWORD remove_service(struct service_entry *entry);
 DWORD save_service_config(struct service_entry *entry);
 void free_service_entry(struct service_entry *entry);
 void release_service(struct service_entry *service);
-
-DWORD lock_service_database(void);
-void unlock_service_database(void);
-
-void lock_services(void);
-void unlock_services(void);
+void service_lock_shared(struct service_entry *service);
+void service_lock_exclusive(struct service_entry *service);
+void service_unlock(struct service_entry *service);
 
 extern HANDLE g_hStartedEvent;