From 28f66d519fd0fc0c0bb09b386e96e50a1c79a8f7 Mon Sep 17 00:00:00 2001 From: Mikolaj Zalewski Date: Tue, 25 Sep 2007 16:19:54 -0700 Subject: [PATCH] service.exe/advapi32: move CreateService, OpenService and DeleteService to services.exe --- dlls/advapi32/service.c | 237 +++++++++++------------------------------ dlls/advapi32/tests/service.c | 2 include/wine/svcctl.idl | 35 ++++++ programs/services/rpc.c | 191 +++++++++++++++++++++++++++++++++ programs/services/services.c | 190 +++++++++++++++++++++++++++++++++ programs/services/services.h | 25 ++++ programs/services/utils.c | 20 +++ 7 files changed, 522 insertions(+), 178 deletions(-) diff --git a/dlls/advapi32/service.c b/dlls/advapi32/service.c index b2e9c25..f031021 100644 --- a/dlls/advapi32/service.c +++ b/dlls/advapi32/service.c @@ -1269,17 +1269,15 @@ CloseServiceHandle( SC_HANDLE hSCObject } obj = (struct sc_handle *)hSCObject; - if (obj->rpc_handle) /* service handles currently don't have RPC connections */ - err = svcctl_CloseServiceHandle(obj->rpc_handle, &obj->server_handle); - else - err = ERROR_SUCCESS; - sc_handle_free( obj ); + err = svcctl_CloseServiceHandle(obj->rpc_handle, &obj->server_handle); if (err != ERROR_SUCCESS) { SetLastError(err); return FALSE; } + + sc_handle_free( obj ); return TRUE; } @@ -1323,8 +1321,7 @@ SC_HANDLE WINAPI OpenServiceW( SC_HANDLE { struct sc_manager *hscm; struct sc_service *hsvc; - HKEY hKey; - long r; + DWORD err; DWORD len; DWORD new_mask = dwDesiredAccess; @@ -1342,13 +1339,6 @@ SC_HANDLE WINAPI OpenServiceW( SC_HANDLE SetLastError(ERROR_INVALID_ADDRESS); return NULL; } - - r = RegOpenKeyExW( hscm->hkey, lpServiceName, 0, KEY_ALL_ACCESS, &hKey ); - if (r!=ERROR_SUCCESS) - { - SetLastError( ERROR_SERVICE_DOES_NOT_EXIST ); - return NULL; - } len = strlenW(lpServiceName)+1; hsvc = sc_handle_alloc( SC_HTYPE_SERVICE, @@ -1356,18 +1346,32 @@ SC_HANDLE WINAPI OpenServiceW( SC_HANDLE sc_handle_destroy_service ); if (!hsvc) { - RegCloseKey(hKey); + SetLastError(ERROR_NOT_ENOUGH_MEMORY); return NULL; } strcpyW( hsvc->name, lpServiceName ); - hsvc->hkey = hKey; - - RtlMapGenericMask(&new_mask, &svc_generic); - hsvc->dwAccess = new_mask; /* add reference to SCM handle */ hscm->hdr.ref_count++; hsvc->scm = hscm; + hsvc->hdr.rpc_handle = hscm->hdr.rpc_handle; + + err = svcctl_OpenServiceW(hscm->hdr.rpc_handle, &hscm->hdr.server_handle, lpServiceName, dwDesiredAccess, &hsvc->hdr.server_handle); + + if (err != ERROR_SUCCESS) + { + sc_handle_free(&hsvc->hdr); + SetLastError(err); + return NULL; + } + + /* for parts of advapi not using services.exe */ + RtlMapGenericMask(&new_mask, &svc_generic); + hsvc->dwAccess = new_mask; + + err = RegOpenKeyExW( hscm->hkey, lpServiceName, 0, KEY_ALL_ACCESS, &hsvc->hkey ); + if (err != ERROR_SUCCESS) + ERR("Shouldn't hapen - service key for service validated by services.exe doesn't exist\n"); TRACE("returning %p\n",hsvc); @@ -1388,15 +1392,9 @@ CreateServiceW( SC_HANDLE hSCManager, LP { struct sc_manager *hscm; struct sc_service *hsvc = NULL; - HKEY hKey; - LONG r; - DWORD dp, len; - struct reg_value val[10]; - int n = 0; DWORD new_mask = dwDesiredAccess; - DWORD index = 0; - WCHAR buffer[MAX_PATH]; - BOOL displayname_exists = FALSE; + DWORD len, err; + SIZE_T depslen = 0, passwdlen; TRACE("%p %s %s\n", hSCManager, debugstr_w(lpServiceName), debugstr_w(lpDisplayName)); @@ -1407,170 +1405,64 @@ CreateServiceW( SC_HANDLE hSCManager, LP SetLastError( ERROR_INVALID_HANDLE ); return NULL; } - + if (!lpServiceName || !lpBinaryPathName) { SetLastError(ERROR_INVALID_ADDRESS); return NULL; } - if (!(hscm->dwAccess & SC_MANAGER_CREATE_SERVICE)) - { - SetLastError(ERROR_ACCESS_DENIED); - return NULL; - } - - if (!lpServiceName[0]) - { - SetLastError(ERROR_INVALID_NAME); - return NULL; - } - - if (!lpBinaryPathName[0]) - { - SetLastError(ERROR_INVALID_PARAMETER); - return NULL; - } - - /* ServiceType can only be one value (except for SERVICE_INTERACTIVE_PROCESS which can be used - * together with SERVICE_WIN32_OWN_PROCESS or SERVICE_WIN32_SHARE_PROCESS when the service - * runs under the LocalSystem account) - */ - switch (dwServiceType) + if (lpDependencies) { - case SERVICE_KERNEL_DRIVER: - case SERVICE_FILE_SYSTEM_DRIVER: - case SERVICE_WIN32_OWN_PROCESS: - case SERVICE_WIN32_SHARE_PROCESS: - /* No problem */ - break; - case SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS: - case SERVICE_WIN32_SHARE_PROCESS | SERVICE_INTERACTIVE_PROCESS: - /* FIXME : Do we need a more thorough check? */ - if (lpServiceStartName) - { - SetLastError(ERROR_INVALID_PARAMETER); - return NULL; - } - break; - default: - SetLastError(ERROR_INVALID_PARAMETER); - return NULL; + const WCHAR *wptr = lpDependencies; + while (*wptr) + wptr += strlenW(wptr)+1; + depslen = (wptr - lpDependencies + 1)*sizeof(WCHAR); } + else + depslen = 0; - /* StartType can only be a single value (if several values are mixed the result is probably not what was intended) */ - if (dwStartType > SERVICE_DISABLED) - { - SetLastError(ERROR_INVALID_PARAMETER); - return NULL; - } + if (lpPassword) + passwdlen = (strlenW(lpPassword) + 1) * sizeof(WCHAR); + else + passwdlen = 0; - /* SERVICE_BOOT_START and SERVICE_SYSTEM_START or only allowed for driver services */ - if (((dwStartType == SERVICE_BOOT_START) || (dwStartType == SERVICE_SYSTEM_START)) && - ((dwServiceType & SERVICE_WIN32_OWN_PROCESS) || (dwServiceType & SERVICE_WIN32_SHARE_PROCESS))) + len = strlenW(lpServiceName)+1; + len = sizeof (struct sc_service) + len*sizeof(WCHAR); + hsvc = sc_handle_alloc( SC_HTYPE_SERVICE, len, sc_handle_destroy_service ); + if( !hsvc ) { - SetLastError(ERROR_INVALID_PARAMETER); - return NULL; + SetLastError(ERROR_NOT_ENOUGH_MEMORY); + goto error; } + lstrcpyW( hsvc->name, lpServiceName ); - /* Loop through the registry to check if the service already exists and to - * check if we can use the given displayname. - * FIXME: Should we use EnumServicesStatusEx? - */ - len = sizeof(buffer); - while (RegEnumKeyExW(hscm->hkey, index, buffer, &len, NULL, NULL, NULL, NULL) == ERROR_SUCCESS) - { - HKEY service_key; - - /* The service already exists, so bail out */ - if(!lstrcmpiW(lpServiceName, buffer)) - { - SetLastError(ERROR_SERVICE_EXISTS); - return NULL; - } - - /* The given displayname matches the found servicename. We don't bail out - * as servicename is checked before a duplicate displayname - */ - if(!lstrcmpiW(lpDisplayName, buffer)) - displayname_exists = TRUE; - - if (RegOpenKeyExW(hscm->hkey, buffer, 0, KEY_READ, &service_key) == ERROR_SUCCESS) - { - WCHAR name[MAX_PATH]; - DWORD size = sizeof(name); - - if (RegQueryValueExW(service_key, szDisplayName, NULL, NULL, (LPBYTE)name, &size) == ERROR_SUCCESS) - { - /* The given displayname matches the found displayname */ - if (!lstrcmpiW(lpDisplayName, name)) - displayname_exists = TRUE; - } - RegCloseKey(service_key); - } - index++; - len = sizeof(buffer); - } + hsvc->hdr.rpc_handle = hscm->hdr.rpc_handle; + hsvc->scm = hscm; + hscm->hdr.ref_count++; - if (lpDisplayName && displayname_exists) - { - SetLastError(ERROR_DUPLICATE_SERVICE_NAME); - return NULL; - } + err = svcctl_CreateServiceW(hscm->hdr.rpc_handle, &hscm->hdr.server_handle, lpServiceName, + lpDisplayName, dwDesiredAccess, dwServiceType, dwStartType, dwErrorControl, + lpBinaryPathName, lpLoadOrderGroup, lpdwTagId, (LPBYTE)lpDependencies, depslen, + lpServiceStartName, (LPBYTE)lpPassword, passwdlen, &hsvc->hdr.server_handle); - r = RegCreateKeyExW(hscm->hkey, lpServiceName, 0, NULL, - REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hKey, &dp); - if (r!=ERROR_SUCCESS) + if (err != ERROR_SUCCESS) { - /* FIXME: Should we set an error? */ + SetLastError(err); + sc_handle_free(&hsvc->hdr); return NULL; } - if( lpDisplayName ) - service_set_string( &val[n++], szDisplayName, lpDisplayName ); - - service_set_dword( &val[n++], szType, &dwServiceType ); - service_set_dword( &val[n++], szStart, &dwStartType ); - service_set_dword( &val[n++], szError, &dwErrorControl ); - - service_set_string( &val[n++], szImagePath, lpBinaryPathName ); - - if( lpLoadOrderGroup ) - service_set_string( &val[n++], szGroup, lpLoadOrderGroup ); - - /* FIXME: lpDependencies is used to create both DependOnService and DependOnGroup - * There is no such key as what szDependencies refers to */ - if( lpDependencies ) - service_set_multi_string( &val[n++], szDependencies, lpDependencies ); - - if( lpPassword ) - FIXME("Don't know how to add a Password for a service.\n"); - - if( lpServiceStartName ) - service_set_string( &val[n++], szObjectName, lpServiceStartName ); - - r = service_write_values( hKey, val, n ); - if( r != ERROR_SUCCESS ) - goto error; - - len = strlenW(lpServiceName)+1; - len = sizeof (struct sc_service) + len*sizeof(WCHAR); - hsvc = sc_handle_alloc( SC_HTYPE_SERVICE, len, sc_handle_destroy_service ); - if( !hsvc ) - goto error; - lstrcpyW( hsvc->name, lpServiceName ); - hsvc->hkey = hKey; + err = RegOpenKeyW(hscm->hkey, lpServiceName, &hsvc->hkey); + if (err != ERROR_SUCCESS) + WINE_ERR("Couldn't open key that should have been created by services.exe\n"); RtlMapGenericMask(&new_mask, &svc_generic); hsvc->dwAccess = new_mask; - hsvc->scm = hscm; - hscm->hdr.ref_count++; - return (SC_HANDLE) &hsvc->hdr; error: - RegCloseKey( hKey ); return NULL; } @@ -1634,6 +1526,7 @@ CreateServiceA( SC_HANDLE hSCManager, LP BOOL WINAPI DeleteService( SC_HANDLE hService ) { struct sc_service *hsvc; + DWORD err; hsvc = sc_handle_get_handle_data(hService, SC_HTYPE_SERVICE); if (!hsvc) @@ -1641,21 +1534,17 @@ BOOL WINAPI DeleteService( SC_HANDLE hSe SetLastError( ERROR_INVALID_HANDLE ); return FALSE; } - - if (!(hsvc->dwAccess & DELETE)) + + err = svcctl_DeleteService(hsvc->hdr.rpc_handle, &hsvc->hdr.server_handle); + if (err != 0) { - SetLastError(ERROR_ACCESS_DENIED); + SetLastError(err); return FALSE; } - + /* Close the key to the service */ RegCloseKey(hsvc->hkey); - - /* Delete the service under the Service Control Manager key */ - RegDeleteTreeW(hsvc->scm->hkey, hsvc->name); - hsvc->hkey = NULL; - return TRUE; } diff --git a/dlls/advapi32/tests/service.c b/dlls/advapi32/tests/service.c index 7b93df8..34c0ea7 100644 --- a/dlls/advapi32/tests/service.c +++ b/dlls/advapi32/tests/service.c @@ -812,8 +812,8 @@ static void test_sequence(void) todo_wine { ok(!memcmp(config->lpDependencies, dependencies, sizeof(dependencies)), "Wrong string\n"); - ok(!strcmp(config->lpServiceStartName, localsystem), "Expected 'LocalSystem', got '%s'\n", config->lpServiceStartName); } + ok(!strcmp(config->lpServiceStartName, localsystem), "Expected 'LocalSystem', got '%s'\n", config->lpServiceStartName); ok(!strcmp(config->lpDisplayName, displayname), "Expected '%s', got '%s'\n", displayname, config->lpDisplayName); SetLastError(0xdeadbeef); diff --git a/include/wine/svcctl.idl b/include/wine/svcctl.idl index fefd12f..70ee29b 100644 --- a/include/wine/svcctl.idl +++ b/include/wine/svcctl.idl @@ -56,6 +56,33 @@ cpp_quote("#define SVCCTL_STARTED_EVENT [in,out] POLICY_HANDLE *handle ); + /* Compatible with Windows function 0x02 */ + DWORD svcctl_DeleteService( + [in] SvcCtlRpcHandle rpc_handle, + [in,ref] POLICY_HANDLE *hService + ); + + /* Compatible with Windows function 0x0c */ + DWORD svcctl_CreateServiceW( + SvcCtlRpcHandle rpc_handle, + [in] POLICY_HANDLE *hSCManager, + [in] LPCWSTR lpServiceName, + [in,unique] LPCWSTR lpDisplayName, + [in] DWORD dwDesiredAccess, + [in] DWORD dwServiceType, + [in] DWORD dwStartType, + [in] DWORD dwErrorControl, + [in] LPCWSTR lpBinaryPathName, + [in,unique] LPCWSTR lpLoadOrderGroup, + [in,out,unique] DWORD *lpdwTagId, + [in,unique,size_is(dwDependenciesSize)] const BYTE *lpDependencies, + [in] DWORD dwDependenciesSize, + [in,unique] LPCWSTR lpServiceStartName, + [in,unique,size_is(dwPasswordSize)] const BYTE *lpPassword, + [in] DWORD dwPasswordSize, + [out] POLICY_HANDLE *phService + ); + /* Compatible with Windows function 0x0f */ DWORD svcctl_OpenSCManagerW( SvcCtlRpcHandle rpc_handle, @@ -65,4 +92,12 @@ cpp_quote("#define SVCCTL_STARTED_EVENT [out] POLICY_HANDLE *handle ); + /* Compatible with Windows function 0x10 */ + DWORD svcctl_OpenServiceW( + SvcCtlRpcHandle rpc_handle, + [in] POLICY_HANDLE *hSCManager, + [in] LPCWSTR lpServiceName, + [in] DWORD dwDesiredAccess, + [out] POLICY_HANDLE *phService + ); } diff --git a/programs/services/rpc.c b/programs/services/rpc.c index 9bb936f..daa9ff2 100644 --- a/programs/services/rpc.c +++ b/programs/services/rpc.c @@ -55,6 +55,13 @@ static const GENERIC_MAPPING g_scm_gener SC_MANAGER_ALL_ACCESS }; +static const GENERIC_MAPPING g_svc_generic = { + (STANDARD_RIGHTS_READ | SERVICE_QUERY_CONFIG | SERVICE_QUERY_STATUS | SERVICE_INTERROGATE | SERVICE_ENUMERATE_DEPENDENTS), + (STANDARD_RIGHTS_WRITE | SERVICE_CHANGE_CONFIG), + (STANDARD_RIGHTS_EXECUTE | SERVICE_START | SERVICE_STOP | SERVICE_PAUSE_CONTINUE | SERVICE_USER_DEFINED_CONTROL), + SERVICE_ALL_ACCESS +}; + typedef struct _handle_uuid /* we interpret the uuid field of the POLICY_HANDLE as such a structure */ { ULONG index; @@ -74,6 +81,7 @@ typedef struct _handle_data DWORD dwType; DWORD dwAccess; LONGLONG dwHandleKey; + service_entry *service; /* valid only for service handles */ } handle_data; static void init_policy_handle(POLICY_HANDLE *handle, ULONG index, handle_data *data) @@ -137,6 +145,29 @@ static DWORD validate_policy_handle(POLI return ERROR_SUCCESS; } +static DWORD validate_scm_handle(POLICY_HANDLE *handle, DWORD dwNeededAccess) +{ + handle_data *data; + DWORD err; + + if ((err = validate_policy_handle(handle, HANDLE_TYPE_MANAGER, dwNeededAccess, &data)) != 0) + return err; + LeaveCriticalSection(&g_handle_table_cs); + return ERROR_SUCCESS; +} + +static DWORD validate_service_handle(POLICY_HANDLE *handle, DWORD dwNeededAccess, service_entry **service) +{ + handle_data *data; + DWORD err; + + if ((err = validate_policy_handle(handle, HANDLE_TYPE_SERVICE, dwNeededAccess, &data)) != 0) + return err; + *service = data->service; + LeaveCriticalSection(&g_handle_table_cs); + return ERROR_SUCCESS; +} + DWORD svcctl_OpenSCManagerW( SvcCtlRpcHandle rpc_handle, LPCWSTR MachineName, /* Note: this parameter is ignored */ @@ -170,6 +201,162 @@ DWORD svcctl_OpenSCManagerW( return ERROR_SUCCESS; } +static DWORD create_handle_for_service(service_entry *entry, DWORD dwDesiredAccess, POLICY_HANDLE *phService) +{ + handle_data *service_data; + ULONG index; + + if (!(service_data = (handle_data *)RtlAllocateHandle(&g_handle_table, &index))) + { + release_service(entry); + return ERROR_NOT_ENOUGH_SERVER_MEMORY; + } + service_data->ulHandleFlags |= 1; /* mark handle as used */ + + service_data->dwType = HANDLE_TYPE_SERVICE; + service_data->service = entry; + if (dwDesiredAccess & MAXIMUM_ALLOWED) + dwDesiredAccess |= SERVICE_ALL_ACCESS; + service_data->dwAccess = dwDesiredAccess; + RtlMapGenericMask(&service_data->dwAccess, &g_svc_generic); + + init_policy_handle(phService, index, service_data); + return ERROR_SUCCESS; +} + +DWORD svcctl_OpenServiceW( + SvcCtlRpcHandle rpc_handle, + POLICY_HANDLE *hSCManager, + LPCWSTR lpServiceName, + DWORD dwDesiredAccess, + POLICY_HANDLE *phService) +{ + service_entry *entry; + DWORD err; + + WINE_TRACE("conn=%p (%s, %s, %x)\n", rpc_handle, wine_dbgstr_guid(&hSCManager->uuid), wine_dbgstr_w(lpServiceName), dwDesiredAccess); + if ((err = validate_scm_handle(hSCManager, 0)) != 0) + return err; + if (!validate_service_name(lpServiceName)) + return ERROR_INVALID_NAME; + + lock_services(); + entry = find_service(lpServiceName); + if (entry != NULL) + entry->ref_count++; + unlock_services(); + + if (entry == NULL) + return ERROR_SERVICE_DOES_NOT_EXIST; + + return create_handle_for_service(entry, dwDesiredAccess, phService); +} + +DWORD svcctl_CreateServiceW( + SvcCtlRpcHandle rpc_handle, + POLICY_HANDLE *hSCManager, + LPCWSTR lpServiceName, + LPCWSTR lpDisplayName, + DWORD dwDesiredAccess, + DWORD dwServiceType, + DWORD dwStartType, + DWORD dwErrorControl, + LPCWSTR lpBinaryPathName, + LPCWSTR lpLoadOrderGroup, + DWORD *lpdwTagId, + const BYTE *lpDependencies, + DWORD dwDependenciesSize, + LPCWSTR lpServiceStartName, + const BYTE *lpPassword, + DWORD dwPasswordSize, + POLICY_HANDLE *phService) +{ + service_entry *entry; + DWORD err; + + WINE_TRACE("conn=%p (%s, %s, %s)\n", rpc_handle, wine_dbgstr_guid(&hSCManager->uuid), wine_dbgstr_w(lpServiceName), wine_dbgstr_w(lpBinaryPathName)); + if ((err = validate_scm_handle(hSCManager, SC_MANAGER_CREATE_SERVICE)) != 0) + return err; + + if (!validate_service_name(lpServiceName)) + return ERROR_INVALID_NAME; + if (!check_multisz((LPCWSTR)lpDependencies, dwDependenciesSize) || !lpServiceName[0] || !lpBinaryPathName[0]) + return ERROR_INVALID_PARAMETER; + + if (lpPassword) + WINE_FIXME("Don't know how to add a password\n"); /* I always get ERROR_GEN_FAILURE */ + if (lpDependencies) + WINE_FIXME("Dependencies not supported yet\n"); + + entry = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*entry)); + entry->name = strdupW(lpServiceName); + entry->config.dwServiceType = dwServiceType; + entry->config.dwStartType = dwStartType; + entry->config.dwErrorControl = dwErrorControl; + entry->config.lpBinaryPathName = strdupW(lpBinaryPathName); + entry->config.lpLoadOrderGroup = strdupW(lpLoadOrderGroup); + entry->config.lpServiceStartName = strdupW(lpServiceStartName); + entry->config.lpDisplayName = strdupW(lpDisplayName); + + if (lpdwTagId) /* TODO: in most situations a non-NULL tagid will generate a ERROR_INVALID_PARAMETER */ + entry->config.dwTagId = *lpdwTagId; + else + entry->config.dwTagId = 0; + + /* other fields NULL*/ + + if (!validate_service_config(entry)) + { + WINE_ERR("Invalid data while tring to create service\n"); + free_service_entry(entry); + return ERROR_INVALID_PARAMETER; + } + + lock_services(); + if (find_service(lpServiceName)) + { + unlock_services(); + free_service_entry(entry); + return ERROR_SERVICE_EXISTS; + } + if (find_service_by_displayname(get_display_name(entry))) + { + unlock_services(); + free_service_entry(entry); + return ERROR_DUPLICATE_SERVICE_NAME; + } + + err = add_service(entry); + if (err != ERROR_SUCCESS) + { + unlock_services(); + free_service_entry(entry); + return err; + } + unlock_services(); + + return create_handle_for_service(entry, dwDesiredAccess, phService); +} + +DWORD svcctl_DeleteService( + SvcCtlRpcHandle rpc_handle, + POLICY_HANDLE *hService) +{ + service_entry *service; + DWORD err; + + if ((err = validate_service_handle(hService, DELETE, &service)) != 0) + return err; + + if (is_marked_for_delete(service)) + return ERROR_SERVICE_MARKED_FOR_DELETE; + + lock_services(); + err = remove_service(service); + unlock_services(); + return err; +} + DWORD svcctl_CloseServiceHandle( SvcCtlRpcHandle rpc_handle, POLICY_HANDLE *handle) @@ -181,7 +368,11 @@ DWORD svcctl_CloseServiceHandle( if ((err = validate_policy_handle(handle, HANDLE_TYPE_DONT_CARE, 0, &data)) != ERROR_SUCCESS) return err; + if (data->service) + release_service(data->service); + RtlFreeHandle(&g_handle_table, (RTL_HANDLE *)data); + ZeroMemory(handle, sizeof(*handle)); LeaveCriticalSection(&g_handle_table_cs); return ERROR_SUCCESS; diff --git a/programs/services/services.c b/programs/services/services.c index a94da2c..c701896 100644 --- a/programs/services/services.c +++ b/programs/services/services.c @@ -25,7 +25,6 @@ #include #include #include -#include "wine/list.h" #include "wine/unicode.h" #include "wine/debug.h" #include "svcctl.h" @@ -40,6 +39,18 @@ HANDLE g_hStartedEvent; 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 }; + +static const WCHAR SZ_LOCAL_SYSTEM[] = {'L','o','c','a','l','S','y','s','t','e','m',0}; + /* Registry constants */ static const WCHAR SZ_SERVICES_KEY[] = { 'S','y','s','t','e','m','\\', 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\', @@ -114,6 +125,100 @@ DWORD load_service_config(HKEY hKey, ser return ERROR_SUCCESS; } +static DWORD reg_set_string_value(HKEY hKey, LPCWSTR value_name, LPCWSTR string) +{ + if (string) + return RegSetValueExW(hKey, value_name, 0, REG_SZ, (LPBYTE)string, sizeof(WCHAR)*(strlenW(string) + 1)); + else + { + DWORD err; + err = RegDeleteValueW(hKey, value_name); + if (err != ERROR_FILE_NOT_FOUND) + return err; + else + return ERROR_SUCCESS; + } +} + +static DWORD save_service_config(service_entry *entry) +{ + HKEY hServicesRootKey; + DWORD err; + HKEY hKey; + + if ((err = RegCreateKeyW(HKEY_LOCAL_MACHINE, SZ_SERVICES_KEY, &hServicesRootKey)) != 0) + return err; + + err = RegCreateKeyW(hServicesRootKey, entry->name, &hKey); + RegCloseKey(hServicesRootKey); + if (err != ERROR_SUCCESS) + return err; + + if ((err = reg_set_string_value(hKey, SZ_DISPLAY_NAME, entry->config.lpDisplayName)) != 0) + return err; + if ((err = reg_set_string_value(hKey, SZ_IMAGE_PATH, entry->config.lpBinaryPathName)) != 0) + return err; + if ((err = reg_set_string_value(hKey, SZ_GROUP, entry->config.lpLoadOrderGroup)) != 0) + return err; + if ((err = reg_set_string_value(hKey, SZ_OBJECT_NAME, entry->config.lpServiceStartName)) != 0) + return err; + if ((err = reg_set_string_value(hKey, SZ_DESCRIPTION, entry->description)) != 0) + return err; + if ((err = RegSetValueExW(hKey, SZ_START, 0, REG_DWORD, (LPBYTE)&entry->config.dwStartType, sizeof(DWORD))) != 0) + return err; + if ((err = RegSetValueExW(hKey, SZ_ERROR, 0, REG_DWORD, (LPBYTE)&entry->config.dwErrorControl, sizeof(DWORD))) != 0) + return err; + + if ((err = RegSetValueExW(hKey, SZ_TYPE, 0, REG_DWORD, (LPBYTE)&entry->config.dwServiceType, sizeof(DWORD))) != 0) + return err; + + if (entry->config.dwTagId) + err = RegSetValueExW(hKey, SZ_TAG, 0, REG_DWORD, (LPBYTE)&entry->config.dwTagId, sizeof(DWORD)); + else + err = RegDeleteValueW(hKey, SZ_TAG); + + if (err != 0 && err != ERROR_FILE_NOT_FOUND) + return err; + + return ERROR_SUCCESS; +} + +DWORD add_service(service_entry *service) +{ + int err; + 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); + return ERROR_SUCCESS; +} + +DWORD remove_service(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); + if (err != 0) + return err; + + list_remove(&service->entry); + service->entry.next = service->entry.prev = NULL; + return ERROR_SUCCESS; +} + +BOOL validate_service_name(LPCWSTR name) +{ + return (name && name[0] && !strchrW(name, '/') && !strchrW(name, '\\')); +} + BOOL validate_service_config(service_entry *entry) { if (entry->config.dwServiceType & SERVICE_WIN32 && (entry->config.lpBinaryPathName == NULL || !entry->config.lpBinaryPathName[0])) @@ -121,9 +226,92 @@ BOOL validate_service_config(service_ent WINE_ERR("Service %s is Win32 but have no image path set\n", wine_dbgstr_w(entry->name)); return FALSE; } + + switch (entry->config.dwServiceType) + { + case SERVICE_KERNEL_DRIVER: + case SERVICE_FILE_SYSTEM_DRIVER: + case SERVICE_WIN32_OWN_PROCESS: + case SERVICE_WIN32_SHARE_PROCESS: + /* No problem */ + break; + case SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS: + case SERVICE_WIN32_SHARE_PROCESS | SERVICE_INTERACTIVE_PROCESS: + /* These can be only run as LocalSystem */ + if (entry->config.lpServiceStartName && strcmpiW(entry->config.lpServiceStartName, SZ_LOCAL_SYSTEM) != 0) + { + WINE_ERR("Service %s is interactive but has a start name\n", wine_dbgstr_w(entry->name)); + return FALSE; + } + break; + default: + WINE_ERR("Service %s have unknown service type\n", wine_dbgstr_w(entry->name)); + return FALSE; + } + + /* StartType can only be a single value (if several values are mixed the result is probably not what was intended) */ + if (entry->config.dwStartType > SERVICE_DISABLED) + { + WINE_ERR("Service %s have unknown start type\n", wine_dbgstr_w(entry->name)); + return FALSE; + } + + /* SERVICE_BOOT_START and SERVICE_SYSTEM_START or only allowed for driver services */ + if (((entry->config.dwStartType == SERVICE_BOOT_START) || (entry->config.dwStartType == SERVICE_SYSTEM_START)) && + ((entry->config.dwServiceType & SERVICE_WIN32_OWN_PROCESS) || (entry->config.dwServiceType & SERVICE_WIN32_SHARE_PROCESS))) + { + WINE_ERR("Service %s - SERVICE_BOOT_START and SERVICE_SYSTEM_START are only allowed for driver services\n", wine_dbgstr_w(entry->name)); + return FALSE; + } + + if (entry->config.lpServiceStartName == NULL) + entry->config.lpServiceStartName = strdupW(SZ_LOCAL_SYSTEM); + return TRUE; } +void lock_services() +{ + EnterCriticalSection(&services_list_cs); +} + +void unlock_services() +{ + LeaveCriticalSection(&services_list_cs); +} + +service_entry *find_service(LPCWSTR name) +{ + service_entry *service; + + LIST_FOR_EACH_ENTRY(service, &g_services, service_entry, entry) + { + if (strcmpiW(name, service->name) == 0) + return service; + } + + return NULL; +} + +service_entry *find_service_by_displayname(LPCWSTR name) +{ + service_entry *service; + + LIST_FOR_EACH_ENTRY(service, &g_services, service_entry, entry) + { + if (strcmpiW(name, service->config.lpDisplayName) == 0) + return service; + } + + return NULL; +} + +void release_service(service_entry *service) +{ + if (InterlockedDecrement(&service->ref_count) == 0 && is_marked_for_delete(service)) + free_service_entry(service); +} + DWORD load_services() { HKEY hServicesRootKey; diff --git a/programs/services/services.h b/programs/services/services.h index 3ffe37c..5dfca89 100644 --- a/programs/services/services.h +++ b/programs/services/services.h @@ -26,6 +26,7 @@ #include "wine/list.h" typedef struct _service_entry { struct list entry; + 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; QUERY_SERVICE_CONFIGW config; @@ -34,6 +35,18 @@ typedef struct _service_entry LPWSTR dependOnGroups; } service_entry; +BOOL validate_service_name(LPCWSTR name); +BOOL validate_service_config(service_entry *entry); +service_entry *find_service(LPCWSTR name); +service_entry *find_service_by_displayname(LPCWSTR name); +DWORD add_service(service_entry *entry); +DWORD remove_service(service_entry *entry); +void free_service_entry(service_entry *entry); +void release_service(service_entry *service); + +void lock_services(); +void unlock_services(); + extern HANDLE g_hStartedEvent; DWORD RPC_MainLoop(void); @@ -41,8 +54,20 @@ DWORD RPC_MainLoop(void); /* from utils.c */ LPWSTR strdupW(LPCWSTR str); +BOOL check_multisz(LPCWSTR lpMultiSz, DWORD cbSize); + DWORD load_reg_string(HKEY hKey, LPCWSTR szValue, BOOL bExpand, LPWSTR *output); DWORD load_reg_multisz(HKEY hKey, LPCWSTR szValue, LPWSTR *output); DWORD load_reg_dword(HKEY hKey, LPCWSTR szValue, DWORD *output); +static inline LPCWSTR get_display_name(service_entry *service) +{ + return service->config.lpDisplayName ? service->config.lpDisplayName : service->name; +} + +static inline BOOL is_marked_for_delete(service_entry *service) +{ + return service->entry.next == NULL; +} + #endif /*WINE_PROGRAMS_UTILS_H_*/ diff --git a/programs/services/utils.c b/programs/services/utils.c index ac556b2..6afa00d 100644 --- a/programs/services/utils.c +++ b/programs/services/utils.c @@ -31,14 +31,30 @@ WINE_DEFAULT_DEBUG_CHANNEL(services); LPWSTR strdupW(LPCWSTR str) { - int len = strlenW(str); - WCHAR *buf = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR)*(len+1)); + int len; + WCHAR *buf; + + if (str == NULL) + return NULL; + len = strlenW(str); + buf = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR)*(len+1)); if (buf == NULL) return NULL; strcpyW(buf, str); return buf; } +BOOL check_multisz(LPCWSTR lpMultiSz, DWORD cbSize) +{ + if (cbSize == 0 || (cbSize == sizeof(WCHAR) && lpMultiSz[0] == 0)) + return TRUE; + if ((cbSize % sizeof(WCHAR)) != 0 || cbSize < 2*sizeof(WCHAR)) + return FALSE; + if (lpMultiSz[cbSize/2 - 1] || lpMultiSz[cbSize/2 - 2]) + return FALSE; + return TRUE; +} + DWORD load_reg_string(HKEY hKey, LPCWSTR szValue, BOOL bExpand, LPWSTR *output) { DWORD size, type; -- 1.4.1