[PATCH v2 4/8] sechost: Implement I_ScRegisterDeviceNotification()
Micah N Gorrell
mgorrell at codeweavers.com
Tue Sep 10 16:59:14 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 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);
--
2.23.0
More information about the wine-devel
mailing list