1
0
mirror of https://github.com/wine-mirror/wine synced 2024-07-05 17:28:47 +00:00

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.
This commit is contained in:
Rob Shearman 2008-03-28 17:08:12 +00:00 committed by Alexandre Julliard
parent b8348b95a6
commit 809d714f24
3 changed files with 243 additions and 174 deletions

View 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;
}

View File

@ -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;
}

View File

@ -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;