services: Move CreateService, OpenService and DeleteService implementations from advapi32.dll to services.exe.
This commit is contained in:
parent
4275fbf603
commit
a2156fc348
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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_*/
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue