[PATCH] services.exe/advapi32: move CreateService, OpenService and DeleteService to services.exe
Mikołaj Zalewski
mikolaj at zalewski.pl
Sat Mar 15 10:42:38 CDT 2008
---
dlls/advapi32/service.c | 237 +++++++++++-------------------------------
include/wine/svcctl.idl | 35 ++++++
programs/services/rpc.c | 196 ++++++++++++++++++++++++++++++++++
programs/services/services.c | 194 ++++++++++++++++++++++++++++++++++-
programs/services/services.h | 25 +++++
programs/services/utils.c | 20 +++-
6 files changed, 529 insertions(+), 178 deletions(-)
diff --git a/dlls/advapi32/service.c b/dlls/advapi32/service.c
index 8ac5bbd..52a3f1c 100644
--- a/dlls/advapi32/service.c
+++ b/dlls/advapi32/service.c
@@ -1208,17 +1208,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;
}
@@ -1262,8 +1260,7 @@ SC_HANDLE WINAPI OpenServiceW( SC_HANDLE hSCManager, LPCWSTR lpServiceName,
{
struct sc_manager *hscm;
struct sc_service *hsvc;
- HKEY hKey;
- long r;
+ DWORD err;
DWORD len;
DWORD new_mask = dwDesiredAccess;
@@ -1281,13 +1278,6 @@ SC_HANDLE WINAPI OpenServiceW( SC_HANDLE hSCManager, LPCWSTR lpServiceName,
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,
@@ -1295,18 +1285,32 @@ SC_HANDLE WINAPI OpenServiceW( SC_HANDLE hSCManager, LPCWSTR lpServiceName,
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 advapi32 not using services.exe yet */
+ 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);
@@ -1327,15 +1331,9 @@ CreateServiceW( SC_HANDLE hSCManager, LPCWSTR lpServiceName,
{
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));
@@ -1353,164 +1351,56 @@ CreateServiceW( SC_HANDLE hSCManager, LPCWSTR lpServiceName,
return NULL;
}
- if (!(hscm->dwAccess & SC_MANAGER_CREATE_SERVICE))
+ if (lpDependencies)
{
- SetLastError(ERROR_ACCESS_DENIED);
- return NULL;
- }
-
- if (!lpServiceName[0])
- {
- SetLastError(ERROR_INVALID_NAME);
- return NULL;
+ const WCHAR *wptr = lpDependencies;
+ while (*wptr)
+ wptr += strlenW(wptr)+1;
+ depslen = (wptr - lpDependencies + 1)*sizeof(WCHAR);
}
+ else
+ depslen = 0;
- 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)
- {
- 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;
- }
-
- if (!lpServiceStartName && (dwServiceType & SERVICE_WIN32))
- lpServiceStartName = szLocalSystem;
-
- /* 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);
+ SetLastError(ERROR_NOT_ENOUGH_MEMORY);
return NULL;
}
+ 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;
-
- /* Open service first before deciding whether it already exists or not
- * It could be that it's not a valid service, but only the registry key itself exists
- */
- 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)
- {
- if (lpDisplayName && (!lstrcmpiW(lpDisplayName, name)
- || !lstrcmpiW(lpDisplayName, buffer)))
- displayname_exists = TRUE;
-
- if (!lstrcmpiW(lpServiceName, buffer))
- {
- RegCloseKey(service_key);
- SetLastError(ERROR_SERVICE_EXISTS);
- return NULL;
- }
- }
- RegCloseKey(service_key);
- }
- index++;
- len = sizeof(buffer);
- }
+ hsvc->hdr.rpc_handle = hscm->hdr.rpc_handle;
+ hsvc->scm = hscm;
+ hscm->hdr.ref_count++;
- if (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;
+ /* for parts of advapi32 not using services.exe yet */
+ 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;
}
@@ -1573,6 +1463,7 @@ CreateServiceA( SC_HANDLE hSCManager, LPCSTR lpServiceName,
BOOL WINAPI DeleteService( SC_HANDLE hService )
{
struct sc_service *hsvc;
+ DWORD err;
hsvc = sc_handle_get_handle_data(hService, SC_HTYPE_SERVICE);
if (!hsvc)
@@ -1580,21 +1471,17 @@ BOOL WINAPI DeleteService( SC_HANDLE hService )
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/include/wine/svcctl.idl b/include/wine/svcctl.idl
index fefd12f..f25d4a3 100644
--- a/include/wine/svcctl.idl
+++ b/include/wine/svcctl.idl
@@ -56,6 +56,33 @@ cpp_quote("#define SVCCTL_STARTED_EVENT (const WCHAR[]){'_','_','w','i','n','e',
[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 (const WCHAR[]){'_','_','w','i','n','e',
[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 123db66..af5c346 100644
--- a/programs/services/rpc.c
+++ b/programs/services/rpc.c
@@ -55,6 +55,13 @@ static const GENERIC_MAPPING g_scm_generic = {
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(POLICY_HANDLE *handle, DWORD dwType, DWORD d
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,167 @@ 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 trying 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;
+
+ lock_services();
+
+ if (!is_marked_for_delete(service))
+ err = remove_service(service);
+ else
+ err = ERROR_SERVICE_MARKED_FOR_DELETE;
+
+ unlock_services();
+
+ return err;
+}
+
DWORD svcctl_CloseServiceHandle(
SvcCtlRpcHandle rpc_handle,
POLICY_HANDLE *handle)
@@ -181,7 +373,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 aaa55c5..b78fc5f 100644
--- a/programs/services/services.c
+++ b/programs/services/services.c
@@ -26,7 +26,6 @@
#include <winsvc.h>
#include <rpc.h>
-#include "wine/list.h"
#include "wine/unicode.h"
#include "wine/debug.h"
#include "svcctl.h"
@@ -41,6 +40,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','\\',
@@ -115,6 +126,104 @@ DWORD load_service_config(HKEY hKey, service_entry *entry)
return ERROR_SUCCESS;
}
+static DWORD reg_set_string_value(HKEY hKey, LPCWSTR value_name, LPCWSTR string)
+{
+ if (!string)
+ {
+ DWORD err;
+ err = RegDeleteValueW(hKey, value_name);
+ if (err != ERROR_FILE_NOT_FOUND)
+ return err;
+
+ return ERROR_SUCCESS;
+ }
+
+ return RegSetValueExW(hKey, value_name, 0, REG_SZ, (LPBYTE)string, sizeof(WCHAR)*(strlenW(string) + 1));
+}
+
+static DWORD save_service_config(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);
+ if (err != ERROR_SUCCESS)
+ goto cleanup;
+
+ if ((err = reg_set_string_value(hKey, SZ_DISPLAY_NAME, entry->config.lpDisplayName)) != 0)
+ goto cleanup;
+ if ((err = reg_set_string_value(hKey, SZ_IMAGE_PATH, entry->config.lpBinaryPathName)) != 0)
+ goto cleanup;
+ if ((err = reg_set_string_value(hKey, SZ_GROUP, entry->config.lpLoadOrderGroup)) != 0)
+ goto cleanup;
+ if ((err = reg_set_string_value(hKey, SZ_OBJECT_NAME, entry->config.lpServiceStartName)) != 0)
+ goto cleanup;
+ if ((err = reg_set_string_value(hKey, SZ_DESCRIPTION, entry->description)) != 0)
+ goto cleanup;
+ if ((err = RegSetValueExW(hKey, SZ_START, 0, REG_DWORD, (LPBYTE)&entry->config.dwStartType, sizeof(DWORD))) != 0)
+ goto cleanup;
+ if ((err = RegSetValueExW(hKey, SZ_ERROR, 0, REG_DWORD, (LPBYTE)&entry->config.dwErrorControl, sizeof(DWORD))) != 0)
+ goto cleanup;
+
+ if ((err = RegSetValueExW(hKey, SZ_TYPE, 0, REG_DWORD, (LPBYTE)&entry->config.dwServiceType, sizeof(DWORD))) != 0)
+ goto cleanup;
+
+ 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)
+ goto cleanup;
+
+ err = ERROR_SUCCESS;
+cleanup:
+ RegCloseKey(hKey);
+ return err;
+}
+
+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]))
@@ -122,9 +231,92 @@ BOOL validate_service_config(service_entry *entry)
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 @@
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 cc03c45..7c9dc4a 100644
--- a/programs/services/utils.c
+++ b/programs/services/utils.c
@@ -31,14 +31,30 @@ WINE_DEFAULT_DEBUG_CHANNEL(service);
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.5.4
--------------030905090709060309030007--
More information about the wine-patches
mailing list