services: Add support for service status change notifications.
Signed-off-by: Andrew Eikum <aeikum@codeweavers.com> Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
parent
7902e43245
commit
73e8d0e6a5
|
@ -59,7 +59,8 @@ typedef enum
|
|||
{
|
||||
SC_HTYPE_DONT_CARE = 0,
|
||||
SC_HTYPE_MANAGER,
|
||||
SC_HTYPE_SERVICE
|
||||
SC_HTYPE_SERVICE,
|
||||
SC_HTYPE_NOTIFY
|
||||
} SC_HANDLE_TYPE;
|
||||
|
||||
struct sc_handle
|
||||
|
@ -80,6 +81,32 @@ struct sc_service_handle /* service handle */
|
|||
struct service_entry *service_entry;
|
||||
};
|
||||
|
||||
struct sc_notify_handle
|
||||
{
|
||||
struct sc_handle hdr;
|
||||
struct sc_service_handle *service;
|
||||
HANDLE event;
|
||||
DWORD notify_mask;
|
||||
LONG ref;
|
||||
SC_RPC_NOTIFY_PARAMS_LIST *params_list;
|
||||
};
|
||||
|
||||
static void sc_notify_retain(struct sc_notify_handle *notify)
|
||||
{
|
||||
InterlockedIncrement(¬ify->ref);
|
||||
}
|
||||
|
||||
static void sc_notify_release(struct sc_notify_handle *notify)
|
||||
{
|
||||
ULONG r = InterlockedDecrement(¬ify->ref);
|
||||
if (r == 0)
|
||||
{
|
||||
CloseHandle(notify->event);
|
||||
HeapFree(GetProcessHeap(), 0, notify->params_list);
|
||||
HeapFree(GetProcessHeap(), 0, notify);
|
||||
}
|
||||
}
|
||||
|
||||
struct sc_lock
|
||||
{
|
||||
struct scmdatabase *db;
|
||||
|
@ -227,6 +254,15 @@ static DWORD validate_service_handle(SC_RPC_HANDLE handle, DWORD needed_access,
|
|||
return err;
|
||||
}
|
||||
|
||||
static DWORD validate_notify_handle(SC_RPC_HANDLE handle, DWORD needed_access, struct sc_notify_handle **notify)
|
||||
{
|
||||
struct sc_handle *hdr;
|
||||
DWORD err = validate_context_handle(handle, SC_HTYPE_NOTIFY, needed_access, &hdr);
|
||||
if (err == ERROR_SUCCESS)
|
||||
*notify = (struct sc_notify_handle *)hdr;
|
||||
return err;
|
||||
}
|
||||
|
||||
DWORD __cdecl svcctl_OpenSCManagerW(
|
||||
MACHINE_HANDLEW MachineName, /* Note: this parameter is ignored */
|
||||
LPCWSTR DatabaseName,
|
||||
|
@ -274,6 +310,15 @@ static void SC_RPC_HANDLE_destroy(SC_RPC_HANDLE handle)
|
|||
case SC_HTYPE_SERVICE:
|
||||
{
|
||||
struct sc_service_handle *service = (struct sc_service_handle *)hdr;
|
||||
service_lock(service->service_entry);
|
||||
if (service->service_entry->notify &&
|
||||
service->service_entry->notify->service == service)
|
||||
{
|
||||
SetEvent(service->service_entry->notify->event);
|
||||
sc_notify_release(service->service_entry->notify);
|
||||
service->service_entry->notify = NULL;
|
||||
}
|
||||
service_unlock(service->service_entry);
|
||||
release_service(service->service_entry);
|
||||
HeapFree(GetProcessHeap(), 0, service);
|
||||
break;
|
||||
|
@ -776,13 +821,42 @@ DWORD __cdecl svcctl_ChangeServiceConfigW(
|
|||
return err;
|
||||
}
|
||||
|
||||
static void fill_notify(struct sc_notify_handle *notify)
|
||||
{
|
||||
SC_RPC_NOTIFY_PARAMS_LIST *list;
|
||||
SERVICE_NOTIFY_STATUS_CHANGE_PARAMS_2 *cparams;
|
||||
|
||||
list = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
|
||||
sizeof(SC_RPC_NOTIFY_PARAMS_LIST) + sizeof(SERVICE_NOTIFY_STATUS_CHANGE_PARAMS_2));
|
||||
if (!list)
|
||||
return;
|
||||
|
||||
cparams = (SERVICE_NOTIFY_STATUS_CHANGE_PARAMS_2 *)(list + 1);
|
||||
|
||||
cparams->dwNotifyMask = notify->notify_mask;
|
||||
memcpy(&cparams->ServiceStatus, ¬ify->service->service_entry->status,
|
||||
sizeof(SERVICE_STATUS_PROCESS));
|
||||
cparams->dwNotificationStatus = ERROR_SUCCESS;
|
||||
cparams->dwNotificationTriggered = 1 << (cparams->ServiceStatus.dwCurrentState - SERVICE_STOPPED);
|
||||
cparams->pszServiceNames = NULL;
|
||||
|
||||
list->cElements = 1;
|
||||
|
||||
list->NotifyParamsArray[0].dwInfoLevel = 2;
|
||||
list->NotifyParamsArray[0].u.params = cparams;
|
||||
|
||||
InterlockedExchangePointer((void**)¬ify->params_list, list);
|
||||
|
||||
SetEvent(notify->event);
|
||||
}
|
||||
|
||||
DWORD __cdecl svcctl_SetServiceStatus(
|
||||
SC_RPC_HANDLE hServiceStatus,
|
||||
LPSERVICE_STATUS lpServiceStatus)
|
||||
{
|
||||
struct sc_service_handle *service;
|
||||
struct process_entry *process;
|
||||
DWORD err;
|
||||
DWORD err, mask;
|
||||
|
||||
WINE_TRACE("(%p, %p)\n", hServiceStatus, lpServiceStatus);
|
||||
|
||||
|
@ -807,6 +881,19 @@ DWORD __cdecl svcctl_SetServiceStatus(
|
|||
release_process(process);
|
||||
}
|
||||
|
||||
mask = 1 << (service->service_entry->status.dwCurrentState - SERVICE_STOPPED);
|
||||
if (service->service_entry->notify &&
|
||||
(service->service_entry->notify->notify_mask & mask))
|
||||
{
|
||||
struct sc_notify_handle *notify = service->service_entry->notify;
|
||||
fill_notify(notify);
|
||||
service->service_entry->notify = NULL;
|
||||
sc_notify_release(notify);
|
||||
service->service_entry->status_notified = TRUE;
|
||||
}
|
||||
else
|
||||
service->service_entry->status_notified = FALSE;
|
||||
|
||||
service_unlock(service->service_entry);
|
||||
|
||||
return ERROR_SUCCESS;
|
||||
|
@ -1598,31 +1685,142 @@ DWORD __cdecl svcctl_unknown46(void)
|
|||
}
|
||||
|
||||
DWORD __cdecl svcctl_NotifyServiceStatusChange(
|
||||
SC_RPC_HANDLE service,
|
||||
SC_RPC_HANDLE handle,
|
||||
SC_RPC_NOTIFY_PARAMS params,
|
||||
GUID *clientprocessguid,
|
||||
GUID *scmprocessguid,
|
||||
BOOL *createremotequeue,
|
||||
SC_NOTIFY_RPC_HANDLE *notify)
|
||||
SC_NOTIFY_RPC_HANDLE *hNotify)
|
||||
{
|
||||
WINE_FIXME("\n");
|
||||
DWORD err, mask;
|
||||
struct sc_manager_handle *manager = NULL;
|
||||
struct sc_service_handle *service = NULL;
|
||||
struct sc_notify_handle *notify;
|
||||
struct sc_handle *hdr = handle;
|
||||
|
||||
WINE_TRACE("(%p, NotifyMask: 0x%x, %p, %p, %p, %p)\n", handle,
|
||||
params.u.params->dwNotifyMask, clientprocessguid, scmprocessguid,
|
||||
createremotequeue, hNotify);
|
||||
|
||||
switch (hdr->type)
|
||||
{
|
||||
case SC_HTYPE_SERVICE:
|
||||
err = validate_service_handle(handle, SERVICE_QUERY_STATUS, &service);
|
||||
break;
|
||||
case SC_HTYPE_MANAGER:
|
||||
err = validate_scm_handle(handle, SC_MANAGER_ENUMERATE_SERVICE, &manager);
|
||||
break;
|
||||
default:
|
||||
err = ERROR_INVALID_HANDLE;
|
||||
break;
|
||||
}
|
||||
|
||||
if (err != ERROR_SUCCESS)
|
||||
return err;
|
||||
|
||||
if (manager)
|
||||
{
|
||||
WARN("Need support for service creation/deletion notifications\n");
|
||||
return ERROR_CALL_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
notify = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*notify));
|
||||
if (!notify)
|
||||
return ERROR_NOT_ENOUGH_SERVER_MEMORY;
|
||||
|
||||
notify->hdr.type = SC_HTYPE_NOTIFY;
|
||||
notify->hdr.access = 0;
|
||||
|
||||
notify->service = service;
|
||||
|
||||
notify->event = CreateEventW(NULL, TRUE, FALSE, NULL);
|
||||
|
||||
notify->notify_mask = params.u.params->dwNotifyMask;
|
||||
|
||||
service_lock(service->service_entry);
|
||||
|
||||
if (service->service_entry->notify)
|
||||
{
|
||||
service_unlock(service->service_entry);
|
||||
HeapFree(GetProcessHeap(), 0, notify);
|
||||
return ERROR_ALREADY_REGISTERED;
|
||||
}
|
||||
|
||||
mask = 1 << (service->service_entry->status.dwCurrentState - SERVICE_STOPPED);
|
||||
if (!service->service_entry->status_notified &&
|
||||
(notify->notify_mask & mask))
|
||||
{
|
||||
fill_notify(notify);
|
||||
service->service_entry->status_notified = TRUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
sc_notify_retain(notify);
|
||||
service->service_entry->notify = notify;
|
||||
}
|
||||
|
||||
sc_notify_retain(notify);
|
||||
*hNotify = ¬ify->hdr;
|
||||
|
||||
service_unlock(service->service_entry);
|
||||
|
||||
return ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
DWORD __cdecl svcctl_GetNotifyResults(
|
||||
SC_NOTIFY_RPC_HANDLE notify,
|
||||
SC_RPC_NOTIFY_PARAMS_LIST **params)
|
||||
SC_NOTIFY_RPC_HANDLE hNotify,
|
||||
SC_RPC_NOTIFY_PARAMS_LIST **pList)
|
||||
{
|
||||
WINE_FIXME("\n");
|
||||
return ERROR_CALL_NOT_IMPLEMENTED;
|
||||
DWORD err;
|
||||
struct sc_notify_handle *notify;
|
||||
|
||||
WINE_TRACE("(%p, %p)\n", hNotify, pList);
|
||||
|
||||
if (!pList)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
*pList = NULL;
|
||||
|
||||
if ((err = validate_notify_handle(hNotify, 0, ¬ify)) != 0)
|
||||
return err;
|
||||
|
||||
sc_notify_retain(notify);
|
||||
/* block until there is a result */
|
||||
err = WaitForSingleObject(notify->event, INFINITE);
|
||||
|
||||
if (err != WAIT_OBJECT_0)
|
||||
{
|
||||
sc_notify_release(notify);
|
||||
return err;
|
||||
}
|
||||
|
||||
*pList = InterlockedExchangePointer((void**)¬ify->params_list, NULL);
|
||||
if (!*pList)
|
||||
{
|
||||
sc_notify_release(notify);
|
||||
return ERROR_REQUEST_ABORTED;
|
||||
}
|
||||
|
||||
sc_notify_release(notify);
|
||||
|
||||
return ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
DWORD __cdecl svcctl_CloseNotifyHandle(
|
||||
SC_NOTIFY_RPC_HANDLE *notify,
|
||||
SC_NOTIFY_RPC_HANDLE *hNotify,
|
||||
BOOL *apc_fired)
|
||||
{
|
||||
WINE_FIXME("\n");
|
||||
return ERROR_CALL_NOT_IMPLEMENTED;
|
||||
struct sc_notify_handle *notify;
|
||||
DWORD err;
|
||||
|
||||
WINE_TRACE("(%p, %p)\n", hNotify, apc_fired);
|
||||
|
||||
if ((err = validate_notify_handle(*hNotify, 0, ¬ify)) != 0)
|
||||
return err;
|
||||
|
||||
sc_notify_release(notify);
|
||||
|
||||
return ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
DWORD __cdecl svcctl_ControlServiceExA(
|
||||
|
|
|
@ -45,6 +45,8 @@ struct process_entry
|
|||
HANDLE overlapped_event;
|
||||
};
|
||||
|
||||
struct sc_notify_handle;
|
||||
|
||||
struct service_entry
|
||||
{
|
||||
struct list entry;
|
||||
|
@ -63,6 +65,8 @@ struct service_entry
|
|||
BOOL force_shutdown;
|
||||
BOOL marked_for_delete;
|
||||
BOOL is_wow64;
|
||||
BOOL status_notified;
|
||||
struct sc_notify_handle *notify;
|
||||
};
|
||||
|
||||
extern struct scmdatabase *active_database;
|
||||
|
|
Loading…
Reference in New Issue