From 73e8d0e6a51780dce15fc538acfee1406a79b611 Mon Sep 17 00:00:00 2001 From: Andrew Eikum Date: Tue, 23 Jan 2018 08:37:10 -0600 Subject: [PATCH] services: Add support for service status change notifications. Signed-off-by: Andrew Eikum Signed-off-by: Alexandre Julliard --- programs/services/rpc.c | 224 ++++++++++++++++++++++++++++++++++++++++--- programs/services/services.h | 4 + 2 files changed, 215 insertions(+), 13 deletions(-) diff --git a/programs/services/rpc.c b/programs/services/rpc.c index 20c5a2761f3..7c3fa1e5e9d 100644 --- a/programs/services/rpc.c +++ b/programs/services/rpc.c @@ -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"); - return ERROR_CALL_NOT_IMPLEMENTED; + 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( diff --git a/programs/services/services.h b/programs/services/services.h index cda70e0b604..fd19ed32f4b 100644 --- a/programs/services/services.h +++ b/programs/services/services.h @@ -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; -- 2.11.4.GIT