From 809d714f243be1259b8b1553ee183c592620a3a9 Mon Sep 17 00:00:00 2001 From: Rob Shearman Date: Fri, 28 Mar 2008 17:08:12 +0000 Subject: [PATCH] services: Introduce an scmdatabase object to store the root key of the services database. Make the functions that deal with the service database more object-oriented. Add a finer-grained locking architecture. --- programs/services/rpc.c | 190 ++++++++++++++++++----------------- programs/services/services.c | 186 ++++++++++++++++++++-------------- programs/services/services.h | 41 ++++++-- 3 files changed, 243 insertions(+), 174 deletions(-) diff --git a/programs/services/rpc.c b/programs/services/rpc.c index c721e557ff7..b6f043b203f 100644 --- a/programs/services/rpc.c +++ b/programs/services/rpc.c @@ -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; } diff --git a/programs/services/services.c b/programs/services/services.c index ae189dc0e94..80a0a2db2e4 100644 --- a/programs/services/services.c +++ b/programs/services/services.c @@ -36,18 +36,7 @@ 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,27 +305,47 @@ void release_service(struct service_entry *service) free_service_entry(service); } -static DWORD load_services(void) +static DWORD scmdatabase_create(struct scmdatabase **db) +{ + DWORD 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) { - 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; - } - for (i = 0; TRUE; i++) { WCHAR szName[MAX_SERVICE_NAME]; 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; } diff --git a/programs/services/services.h b/programs/services/services.h index b02616636ad..246e1bb8eff 100644 --- a/programs/services/services.h +++ b/programs/services/services.h @@ -23,9 +23,18 @@ #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;