[PATCH v3 4/8] sechost: Implement I_ScRegisterDeviceNotification()

Micah N Gorrell mgorrell at codeweavers.com
Thu Sep 12 13:03:16 CDT 2019


Implement I_ScRegisterDeviceNotification and
I_ScUnregisterDeviceNotification in sechost via RPC to services.exe

Signed-off-by: Micah N Gorrell <mgorrell at 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 71eda08605..ac9d868499 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 =
 {
@@ -2767,3 +2775,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(&registration->details, details, sizeof(DEVICE_NOTIFICATION_DETAILS));
+
+    EnterCriticalSection(&service_cs);
+    list_add_tail(&device_notify_list, &registration->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(&registration->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);
-- 
2.23.0




More information about the wine-devel mailing list