Implement I_ScRegisterDeviceNotification and I_ScUnregisterDeviceNotification in sechost via RPC to services.exe
Signed-off-by: Micah N Gorrell mgorrell@codeweavers.com --- dlls/sechost/sechost.spec | 2 + dlls/sechost/service.c | 141 ++++++++++++++++++++++++++++++++++++++ include/wine/svcctl.idl | 13 ++++ include/winsvc.h | 14 ++++ programs/services/rpc.c | 124 ++++++++++++++++++++++++++++++++- 5 files changed, 293 insertions(+), 1 deletion(-)
diff --git a/dlls/sechost/sechost.spec b/dlls/sechost/sechost.spec index 985f638f8f..1deb340619 100644 --- a/dlls/sechost/sechost.spec +++ b/dlls/sechost/sechost.spec @@ -17,6 +17,8 @@ @ stdcall GetServiceDisplayNameW(ptr wstr ptr ptr) @ stdcall GetServiceKeyNameA(long str ptr ptr) @ stdcall GetServiceKeyNameW(long wstr ptr ptr) +@ stdcall I_ScRegisterDeviceNotification(ptr ptr long) +@ stdcall I_ScUnregisterDeviceNotification(ptr) @ stdcall LockServiceDatabase(ptr) @ stdcall NotifyServiceStatusChangeW(ptr long ptr) @ stdcall OpenSCManagerA(str str long) diff --git a/dlls/sechost/service.c b/dlls/sechost/service.c index 399fee4cb4..4605928858 100644 --- a/dlls/sechost/service.c +++ b/dlls/sechost/service.c @@ -96,6 +96,14 @@ typedef struct notify_data_t {
static struct list notify_list = LIST_INIT(notify_list);
+struct device_notify_registration { + DEVICE_NOTIFICATION_DETAILS details; + struct list entry; +}; + +static struct list device_notify_list = LIST_INIT(device_notify_list); +HANDLE device_notify_thread = NULL; + static CRITICAL_SECTION service_cs; static CRITICAL_SECTION_DEBUG service_cs_debug = { @@ -2774,3 +2782,136 @@ DWORD WINAPI NotifyServiceStatusChangeW(SC_HANDLE hService, DWORD dwNotifyMask,
return ERROR_SUCCESS; } + +static DWORD WINAPI device_notification_thread(void *user) +{ + DWORD err; + struct device_notify_registration *registration; + SC_DEV_NOTIFY_RPC_HANDLE handle = NULL; + DWORD code; + DWORD buf_size; + BYTE *buf; + + __TRY + { + err = svcctl_OpenDeviceNotificationHandle(NULL, &handle); + } + __EXCEPT(rpc_filter) + { + err = map_exception_code(GetExceptionCode()); + } + __ENDTRY + + if (!handle) + { + WARN("OpenDeviceNotificationHandle server call failed: %d\n", err); + return 1; + } + + for (;;) + { + buf = NULL; + __TRY + { + /* GetDeviceNotificationResults blocks until there is an event */ + err = svcctl_GetDeviceNotificationResults(handle, &code, &buf, &buf_size); + } + __EXCEPT(rpc_filter) + { + err = map_exception_code(GetExceptionCode()); + } + __ENDTRY + + if (err != ERROR_SUCCESS) + { + WARN("GetDeviceNotificationResults server call failed: %d\n", err); + if (buf) + MIDL_user_free(buf); + Sleep(100); + continue; + } + + EnterCriticalSection(&service_cs); + LIST_FOR_EACH_ENTRY(registration, &device_notify_list, struct device_notify_registration, entry) + { + registration->details.pNotificationCallback(registration->details.hRecipient, + code, (DEV_BROADCAST_HDR *) buf); + } + LeaveCriticalSection(&service_cs); + MIDL_user_free(buf); + } +} + +/****************************************************************************** + * I_ScRegisterDeviceNotification [SECHOST.@] + */ +HDEVNOTIFY WINAPI I_ScRegisterDeviceNotification(DEVICE_NOTIFICATION_DETAILS *details, LPVOID filter, DWORD flags) +{ + struct device_notify_registration *registration; + + TRACE("(%p)\n", details->hRecipient); + + /* This implementation is not overly concerned with sending too many + * messages, so support for filters is not yet implemented. + */ + if (filter) + FIXME("Notification filters are not yet implemented! All device notification events will be sent.\n"); + + if (!details || !details->pNotificationCallback) + { + SetLastError(ERROR_INVALID_PARAMETER); + return NULL; + } + + registration = heap_alloc(sizeof(struct device_notify_registration)); + if (!registration) + { + SetLastError(ERROR_NOT_ENOUGH_MEMORY); + return NULL; + } + + memcpy(®istration->details, details, sizeof(DEVICE_NOTIFICATION_DETAILS)); + + EnterCriticalSection(&service_cs); + list_add_tail(&device_notify_list, ®istration->entry); + + if (!device_notify_thread) + device_notify_thread = CreateThread(NULL, 0, &device_notification_thread, NULL, 0, NULL); + + LeaveCriticalSection(&service_cs); + + return (HDEVNOTIFY) registration; +} + +/****************************************************************************** + * I_ScUnregisterDeviceNotification [SECHOST.@] + */ +BOOL WINAPI I_ScUnregisterDeviceNotification(HDEVNOTIFY notificationHandle) +{ + struct device_notify_registration *item, *registration = NULL; + + TRACE("(%p)\n", notificationHandle); + + EnterCriticalSection(&service_cs); + LIST_FOR_EACH_ENTRY(item, &device_notify_list, struct device_notify_registration, entry) + { + if (item == notificationHandle) + { + registration = item; + break; + } + } + + if (registration) + list_remove(®istration->entry); + LeaveCriticalSection(&service_cs); + + if (!registration) + { + SetLastError(ERROR_INVALID_HANDLE); + return FALSE; + } + + heap_free(registration); + return TRUE; +} diff --git a/include/wine/svcctl.idl b/include/wine/svcctl.idl index c14cd6bb50..0d75cf73c4 100644 --- a/include/wine/svcctl.idl +++ b/include/wine/svcctl.idl @@ -62,6 +62,7 @@ interface svcctl typedef [context_handle] void *SC_RPC_HANDLE; typedef [context_handle] void *SC_RPC_LOCK; typedef [context_handle] void *SC_NOTIFY_RPC_HANDLE; + typedef [context_handle] void *SC_DEV_NOTIFY_RPC_HANDLE;
/* undocumented access rights */ cpp_quote("#define SERVICE_SET_STATUS 0x8000") @@ -805,4 +806,16 @@ typedef [switch_type(DWORD)] union _SC_RPC_SERVICE_CONTROL_OUT_PARAMSW { [in] DWORD info_level, [out] SC_RPC_CONFIG_INFOW *info ); + + /* Not compatible with Windows function 57 */ + DWORD svcctl_OpenDeviceNotificationHandle( + [in, string, unique] SVCCTL_HANDLEW machinename, + [out] SC_DEV_NOTIFY_RPC_HANDLE *handle); + + /* Not compatible with Windows function 58 */ + DWORD svcctl_GetDeviceNotificationResults( + [in] SC_DEV_NOTIFY_RPC_HANDLE handle, + [out] DWORD *event_code, + [out, size_is(,*event_size)] BYTE **event, + [out] DWORD *event_size); } diff --git a/include/winsvc.h b/include/winsvc.h index aa5e9f1ae8..d420535447 100644 --- a/include/winsvc.h +++ b/include/winsvc.h @@ -376,6 +376,18 @@ typedef struct _QUERY_SERVICE_LOCK_STATUSW
DECL_WINELIB_TYPE_AW(QUERY_SERVICE_LOCK_STATUS)
+#ifndef HDEVNOTIFY +typedef PVOID HDEVNOTIFY; +#endif +#include "dbt.h" +typedef DWORD (CALLBACK *REGISTER_DEVICE_NOTIFY_CALLBACK)(HANDLE hRecipient, + DWORD flags, DEV_BROADCAST_HDR *); +typedef struct _DEVICE_NOTIFICATION_DETAILS +{ + REGISTER_DEVICE_NOTIFY_CALLBACK pNotificationCallback; + HANDLE hRecipient; +} DEVICE_NOTIFICATION_DETAILS; + /* Service control handler function prototype */
typedef VOID (WINAPI *LPHANDLER_FUNCTION)(DWORD); @@ -445,6 +457,8 @@ WINADVAPI BOOL WINAPI StartServiceCtrlDispatcherA(const SERVICE_TABLE_ENT WINADVAPI BOOL WINAPI StartServiceCtrlDispatcherW(const SERVICE_TABLE_ENTRYW*); #define StartServiceCtrlDispatcher WINELIB_NAME_AW(StartServiceCtrlDispatcher) WINADVAPI BOOL WINAPI UnlockServiceDatabase(SC_LOCK); +WINADVAPI HDEVNOTIFY WINAPI I_ScRegisterDeviceNotification(DEVICE_NOTIFICATION_DETAILS *details, LPVOID filter, DWORD flags); +WINADVAPI BOOL WINAPI I_ScUnregisterDeviceNotification(HDEVNOTIFY notificationHandle);
#ifdef __cplusplus } /* extern "C" */ diff --git a/programs/services/rpc.c b/programs/services/rpc.c index a657492a5f..9bd43267b9 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_NOTIFY + SC_HTYPE_NOTIFY, + SC_HTYPE_DEV_NOTIFY } SC_HANDLE_TYPE;
struct sc_handle @@ -83,6 +84,23 @@ struct sc_notify_handle SC_RPC_NOTIFY_PARAMS_LIST *params_list; };
+struct devnotify_event +{ + struct list entry; + DWORD code; + BYTE *data; + DWORD data_size; +}; + +struct sc_dev_notify_handle +{ + struct sc_handle hdr; + struct list entry; + HANDLE event; + CRITICAL_SECTION cs; + struct list event_list; +}; + struct sc_service_handle /* service handle */ { struct sc_handle hdr; @@ -117,6 +135,9 @@ static const WCHAR emptyW[] = {0}; static PTP_CLEANUP_GROUP cleanup_group; HANDLE exit_event;
+static struct list devnotify_listeners = LIST_INIT(devnotify_listeners); +CRITICAL_SECTION device_notifications_cs; + static void CALLBACK group_cancel_callback(void *object, void *userdata) { struct process_entry *process = object; @@ -264,6 +285,15 @@ static DWORD validate_notify_handle(SC_RPC_HANDLE handle, DWORD needed_access, s return err; }
+static DWORD validate_dev_notify_handle(SC_RPC_HANDLE handle, DWORD needed_access, struct sc_dev_notify_handle **notify) +{ + struct sc_handle *hdr; + DWORD err = validate_context_handle(handle, SC_HTYPE_DEV_NOTIFY, needed_access, &hdr); + if (err == ERROR_SUCCESS) + *notify = (struct sc_dev_notify_handle *)hdr; + return err; +} + DWORD __cdecl svcctl_OpenSCManagerW( MACHINE_HANDLEW MachineName, /* Note: this parameter is ignored */ LPCWSTR DatabaseName, @@ -323,6 +353,28 @@ static void SC_RPC_HANDLE_destroy(SC_RPC_HANDLE handle) HeapFree(GetProcessHeap(), 0, service); break; } + case SC_HTYPE_DEV_NOTIFY: + { + struct devnotify_event *event, *next; + struct sc_dev_notify_handle *listener = (struct sc_dev_notify_handle *)hdr; + + /* Destroy this handle and stop sending events to this caller */ + EnterCriticalSection(&device_notifications_cs); + WINE_TRACE("Removing device notification listener from list (%p)\n", listener); + list_remove(&listener->entry); + LeaveCriticalSection(&device_notifications_cs); + + LIST_FOR_EACH_ENTRY_SAFE(event, next, &listener->event_list, struct devnotify_event, entry) + { + list_remove(&event->entry); + MIDL_user_free(event->data); + HeapFree(GetProcessHeap(), 0, event); + } + + CloseHandle(listener->event); + HeapFree(GetProcessHeap(), 0, listener); + break; + } default: WINE_ERR("invalid handle type %d\n", hdr->type); RpcRaiseException(ERROR_INVALID_HANDLE); @@ -2134,12 +2186,78 @@ DWORD __cdecl svcctl_QueryServiceConfig2A( return ERROR_CALL_NOT_IMPLEMENTED; }
+DWORD __cdecl svcctl_OpenDeviceNotificationHandle( + MACHINE_HANDLEW MachineName, /* Note: this parameter is ignored */ + SC_DEV_NOTIFY_RPC_HANDLE *handle) +{ + struct sc_dev_notify_handle *listener; + + if (!(listener = HeapAlloc(GetProcessHeap(), 0, sizeof(*listener)))) + return ERROR_NOT_ENOUGH_SERVER_MEMORY; + + listener->hdr.type = SC_HTYPE_DEV_NOTIFY; + listener->hdr.access = 0; + + InitializeCriticalSection(&listener->cs); + listener->event = CreateEventW(NULL, TRUE, FALSE, NULL); + list_init(&listener->event_list); + + WINE_TRACE("Adding listener to list (%p)\n", listener); + EnterCriticalSection(&device_notifications_cs); + list_add_tail(&devnotify_listeners, &listener->entry); + LeaveCriticalSection(&device_notifications_cs); + + *handle = &listener->hdr; + return ERROR_SUCCESS; +} + +DWORD __cdecl svcctl_GetDeviceNotificationResults( + SC_DEV_NOTIFY_RPC_HANDLE handle, + LPDWORD code, + BYTE **event_dest, + LPDWORD event_dest_size) +{ + struct devnotify_event *event; + struct sc_dev_notify_handle *listener; + DWORD err; + + if ((err = validate_dev_notify_handle(handle, 0, &listener)) != 0) + return err; + + if (!event_dest || !event_dest_size || !code) + return ERROR_INVALID_PARAMETER; + + do + { + /* block until there is a result */ + WaitForSingleObject(listener->event, INFINITE); + + EnterCriticalSection(&listener->cs); + if ((event = LIST_ENTRY(list_head(&listener->event_list), struct devnotify_event, entry))) + list_remove(&event->entry); + else + ResetEvent(listener->event); + LeaveCriticalSection(&listener->cs); + } while (!event); + + WINE_TRACE("Got an event (%p)\n", event); + *code = event->code; + + *event_dest = event->data; + *event_dest_size = event->data_size; + + HeapFree(GetProcessHeap(), 0, event); + return ERROR_SUCCESS; +} + DWORD RPC_Init(void) { WCHAR transport[] = SVCCTL_TRANSPORT; WCHAR endpoint[] = SVCCTL_ENDPOINT; DWORD err;
+ InitializeCriticalSection(&device_notifications_cs); + if (!(cleanup_group = CreateThreadpoolCleanupGroup())) { WINE_ERR("CreateThreadpoolCleanupGroup failed with error %u\n", GetLastError()); @@ -2188,6 +2306,10 @@ void __RPC_USER SC_NOTIFY_RPC_HANDLE_rundown(SC_NOTIFY_RPC_HANDLE handle) { }
+void __RPC_USER SC_DEV_NOTIFY_RPC_HANDLE_rundown(SC_DEV_NOTIFY_RPC_HANDLE handle) +{ +} + void __RPC_FAR * __RPC_USER MIDL_user_allocate(SIZE_T len) { return HeapAlloc(GetProcessHeap(), 0, len);