services: Move CreateService, OpenService and DeleteService implementations from advapi32.dll to services.exe.

This commit is contained in:
Mikołaj Zalewski 2008-03-15 16:42:38 +01:00 committed by Alexandre Julliard
parent 4275fbf603
commit a2156fc348
6 changed files with 551 additions and 182 deletions

View File

@ -1199,10 +1199,7 @@ CloseServiceHandle( SC_HANDLE hSCObject )
}
obj = (struct sc_handle *)hSCObject;
if (obj->server_handle) /* service handles currently don't have RPC connections */
err = svcctl_CloseServiceHandle(&obj->server_handle);
else
err = ERROR_SUCCESS;
err = svcctl_CloseServiceHandle(&obj->server_handle);
sc_handle_free( obj );
if (err != ERROR_SUCCESS)
@ -1253,8 +1250,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;
@ -1272,13 +1268,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,
@ -1286,19 +1275,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;
err = svcctl_OpenServiceW(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);
return (SC_HANDLE) &hsvc->hdr;
@ -1318,15 +1320,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));
@ -1344,164 +1340,55 @@ CreateServiceW( SC_HANDLE hSCManager, LPCWSTR lpServiceName,
return NULL;
}
if (!(hscm->dwAccess & SC_MANAGER_CREATE_SERVICE))
if (lpDependencies)
{
SetLastError(ERROR_ACCESS_DENIED);
return NULL;
const WCHAR *wptr = lpDependencies;
while (*wptr)
wptr += strlenW(wptr)+1;
depslen = (wptr - lpDependencies + 1)*sizeof(WCHAR);
}
else
depslen = 0;
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)
{
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;
}
/* 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)))
{
SetLastError(ERROR_INVALID_PARAMETER);
return NULL;
}
/* 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);
}
if (displayname_exists)
{
SetLastError(ERROR_DUPLICATE_SERVICE_NAME);
return NULL;
}
r = RegCreateKeyExW(hscm->hkey, lpServiceName, 0, NULL,
REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hKey, &dp);
if (r!=ERROR_SUCCESS)
{
/* FIXME: Should we set an error? */
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;
if (lpPassword)
passwdlen = (strlenW(lpPassword) + 1) * sizeof(WCHAR);
else
passwdlen = 0;
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;
{
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
return NULL;
}
lstrcpyW( hsvc->name, lpServiceName );
hsvc->hkey = hKey;
RtlMapGenericMask(&new_mask, &svc_generic);
hsvc->dwAccess = new_mask;
hsvc->scm = hscm;
hscm->hdr.ref_count++;
err = svcctl_CreateServiceW(hscm->hdr.server_handle, lpServiceName,
lpDisplayName, dwDesiredAccess, dwServiceType, dwStartType, dwErrorControl,
lpBinaryPathName, lpLoadOrderGroup, lpdwTagId, (LPBYTE)lpDependencies, depslen,
lpServiceStartName, (LPBYTE)lpPassword, passwdlen, &hsvc->hdr.server_handle);
if (err != ERROR_SUCCESS)
{
SetLastError(err);
sc_handle_free(&hsvc->hdr);
return NULL;
}
/* 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;
return (SC_HANDLE) &hsvc->hdr;
error:
RegCloseKey( hKey );
return NULL;
}
@ -1564,6 +1451,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)
@ -1572,20 +1460,16 @@ BOOL WINAPI DeleteService( SC_HANDLE hService )
return FALSE;
}
if (!(hsvc->dwAccess & DELETE))
err = svcctl_DeleteService(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;
}

View File

@ -48,6 +48,31 @@ interface svcctl
[in,out] SC_RPC_HANDLE *handle
);
/* Compatible with Windows function 0x02 */
DWORD svcctl_DeleteService(
[in] SC_RPC_HANDLE hService
);
/* Compatible with Windows function 0x0c */
DWORD svcctl_CreateServiceW(
[in] SC_RPC_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] SC_RPC_HANDLE *phService
);
/* Compatible with Windows function 0x0f */
DWORD svcctl_OpenSCManagerW(
[in,unique] MACHINE_HANDLEW MachineName,
@ -56,4 +81,11 @@ interface svcctl
[out] SC_RPC_HANDLE *handle
);
/* Compatible with Windows function 0x10 */
DWORD svcctl_OpenServiceW(
[in] SC_RPC_HANDLE hSCManager,
[in] LPCWSTR lpServiceName,
[in] DWORD dwDesiredAccess,
[out] SC_RPC_HANDLE *phService
);
}

View File

@ -56,6 +56,14 @@ 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 enum
{
SC_HTYPE_DONT_CARE = 0,
@ -74,6 +82,51 @@ struct sc_manager /* service control manager handle */
struct sc_handle hdr;
};
struct sc_service /* service handle */
{
struct sc_handle hdr;
struct service_entry *service_entry;
};
/* Check if the given handle is of the required type and allows the requested access. */
static DWORD validate_context_handle(SC_RPC_HANDLE handle, DWORD type, DWORD needed_access, struct sc_handle **out_hdr)
{
struct sc_handle *hdr = (struct sc_handle *)handle;
if (type != SC_HTYPE_DONT_CARE && hdr->type != type)
{
WINE_ERR("Handle is of an invalid type (%d, %d)\n", hdr->type, type);
return ERROR_INVALID_HANDLE;
}
if ((needed_access & hdr->access) != needed_access)
{
WINE_ERR("Access denied - handle created with access %x, needed %x\n", hdr->access, needed_access);
return ERROR_ACCESS_DENIED;
}
*out_hdr = hdr;
return ERROR_SUCCESS;
}
static DWORD validate_scm_handle(SC_RPC_HANDLE handle, DWORD needed_access, struct sc_manager **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;
return err;
}
static DWORD validate_service_handle(SC_RPC_HANDLE handle, DWORD needed_access, struct sc_service **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;
return err;
}
DWORD svcctl_OpenSCManagerW(
MACHINE_HANDLEW MachineName, /* Note: this parameter is ignored */
LPCWSTR DatabaseName,
@ -117,12 +170,179 @@ static void SC_RPC_HANDLE_destroy(SC_RPC_HANDLE handle)
HeapFree(GetProcessHeap(), 0, manager);
break;
}
case SC_HTYPE_SERVICE:
{
struct sc_service *service = (struct sc_service *)hdr;
release_service(service->service_entry);
HeapFree(GetProcessHeap(), 0, service);
break;
}
default:
WINE_ERR("invalid handle type %d\n", hdr->type);
RpcRaiseException(ERROR_INVALID_HANDLE);
}
}
static DWORD create_handle_for_service(struct service_entry *entry, DWORD dwDesiredAccess, SC_RPC_HANDLE *phService)
{
struct sc_service *service;
if (!(service = HeapAlloc(GetProcessHeap(), 0, sizeof(*service))))
{
release_service(entry);
return ERROR_NOT_ENOUGH_SERVER_MEMORY;
}
service->hdr.type = SC_HTYPE_SERVICE;
service->hdr.access = dwDesiredAccess;
RtlMapGenericMask(&service->hdr.access, &g_svc_generic);
service->service_entry = entry;
if (dwDesiredAccess & MAXIMUM_ALLOWED)
dwDesiredAccess |= SERVICE_ALL_ACCESS;
*phService = &service->hdr;
return ERROR_SUCCESS;
}
DWORD svcctl_OpenServiceW(
SC_RPC_HANDLE hSCManager,
LPCWSTR lpServiceName,
DWORD dwDesiredAccess,
SC_RPC_HANDLE *phService)
{
struct sc_manager *manager;
struct service_entry *entry;
DWORD err;
WINE_TRACE("(%s, 0x%x)\n", wine_dbgstr_w(lpServiceName), dwDesiredAccess);
if ((err = validate_scm_handle(hSCManager, 0, &manager)) != ERROR_SUCCESS)
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(
SC_RPC_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,
SC_RPC_HANDLE *phService)
{
struct sc_manager *manager;
struct service_entry *entry;
DWORD err;
WINE_TRACE("(%s, %s, 0x%x, %s)\n", wine_dbgstr_w(lpServiceName), wine_dbgstr_w(lpDisplayName), dwDesiredAccess, wine_dbgstr_w(lpBinaryPathName));
if ((err = validate_scm_handle(hSCManager, SC_MANAGER_CREATE_SERVICE, &manager)) != ERROR_SUCCESS)
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(
SC_RPC_HANDLE hService)
{
struct sc_service *service;
DWORD err;
if ((err = validate_service_handle(hService, DELETE, &service)) != ERROR_SUCCESS)
return err;
lock_services();
if (!is_marked_for_delete(service->service_entry))
err = remove_service(service->service_entry);
else
err = ERROR_SERVICE_MARKED_FOR_DELETE;
unlock_services();
return err;
}
DWORD svcctl_CloseServiceHandle(
SC_RPC_HANDLE *handle)
{

View File

@ -25,7 +25,6 @@
#include <winsvc.h>
#include <rpc.h>
#include "wine/list.h"
#include "wine/unicode.h"
#include "wine/debug.h"
#include "svcctl.h"
@ -40,6 +39,18 @@ 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 };
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','\\',
@ -59,7 +70,7 @@ 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};
static void free_service_entry(struct service_entry *entry)
void free_service_entry(struct service_entry *entry)
{
HeapFree(GetProcessHeap(), 0, entry->name);
HeapFree(GetProcessHeap(), 0, entry->config.lpBinaryPathName);
@ -114,16 +125,197 @@ static DWORD load_service_config(HKEY hKey, struct service_entry *entry)
return ERROR_SUCCESS;
}
static BOOL validate_service_config(struct service_entry *entry)
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(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);
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(struct 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(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);
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(struct service_entry *entry)
{
if (entry->config.dwServiceType & SERVICE_WIN32 && (entry->config.lpBinaryPathName == NULL || !entry->config.lpBinaryPathName[0]))
{
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(void)
{
EnterCriticalSection(&services_list_cs);
}
void unlock_services(void)
{
LeaveCriticalSection(&services_list_cs);
}
struct service_entry *find_service(LPCWSTR name)
{
struct service_entry *service;
LIST_FOR_EACH_ENTRY(service, &g_services, struct service_entry, entry)
{
if (strcmpiW(name, service->name) == 0)
return service;
}
return NULL;
}
struct service_entry *find_service_by_displayname(LPCWSTR name)
{
struct service_entry *service;
LIST_FOR_EACH_ENTRY(service, &g_services, struct service_entry, entry)
{
if (strcmpiW(name, service->config.lpDisplayName) == 0)
return service;
}
return NULL;
}
void release_service(struct service_entry *service)
{
if (InterlockedDecrement(&service->ref_count) == 0 && is_marked_for_delete(service))
free_service_entry(service);
}
static DWORD load_services(void)
{
HKEY hServicesRootKey;

View File

@ -26,6 +26,7 @@
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 @@ struct service_entry
LPWSTR dependOnGroups;
};
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);
void free_service_entry(struct service_entry *entry);
void release_service(struct service_entry *service);
void lock_services(void);
void unlock_services(void);
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(struct service_entry *service)
{
return service->config.lpDisplayName ? service->config.lpDisplayName : service->name;
}
static inline BOOL is_marked_for_delete(struct service_entry *service)
{
return service->entry.next == NULL;
}
#endif /*WINE_PROGRAMS_UTILS_H_*/

View File

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