[PATCH 1/4] sechost: Implement I_ScRegisterDeviceNotification().

Zebediah Figura z.figura12 at gmail.com
Tue May 5 11:25:38 CDT 2020


Based on a patch by Micah N Gorrell.

Signed-off-by: Zebediah Figura <zfigura at codeweavers.com>
---
Instead of implementing the interface in services.exe, I've elected to use a
separate RPC interface hosted by plugplay.exe, for the following reasons:

* device notifications don't have a lot to do with services;
* the svcctl interface is already a (somewhat) documented interface which does
  not include any functions related to device notification;
* it probably makes sense to move the BroadcastSystemMessage() call out of
  ntoskrnl and into the RPCserver [as in patch 0004], and services.exe can't
  import user32.

 dlls/sechost/Makefile.in       |   1 +
 dlls/sechost/plugplay.idl      |   2 +
 dlls/sechost/sechost.spec      |   4 +-
 dlls/sechost/service.c         | 148 +++++++++++++++++++++++++++++++++
 include/wine/plugplay.idl      |  32 +++++++
 programs/plugplay/Makefile.in  |   5 +-
 programs/plugplay/main.c       | 130 +++++++++++++++++++++++++++++
 programs/plugplay/plugplay.idl |   2 +
 8 files changed, 321 insertions(+), 3 deletions(-)
 create mode 100644 dlls/sechost/plugplay.idl
 create mode 100644 include/wine/plugplay.idl
 create mode 100644 programs/plugplay/plugplay.idl

diff --git a/dlls/sechost/Makefile.in b/dlls/sechost/Makefile.in
index 91016c67577..f4d88272857 100644
--- a/dlls/sechost/Makefile.in
+++ b/dlls/sechost/Makefile.in
@@ -11,4 +11,5 @@ C_SRCS = \
 	trace.c
 
 IDL_SRCS = \
+	plugplay.idl \
 	svcctl.idl
diff --git a/dlls/sechost/plugplay.idl b/dlls/sechost/plugplay.idl
new file mode 100644
index 00000000000..05e040388e4
--- /dev/null
+++ b/dlls/sechost/plugplay.idl
@@ -0,0 +1,2 @@
+#pragma makedep client
+#include "wine/plugplay.idl"
diff --git a/dlls/sechost/sechost.spec b/dlls/sechost/sechost.spec
index f21e2f1cda8..0867412c1cb 100644
--- a/dlls/sechost/sechost.spec
+++ b/dlls/sechost/sechost.spec
@@ -108,14 +108,14 @@
 @ stub I_ScIsSecurityProcess
 @ stub I_ScPnPGetServiceName
 @ stub I_ScQueryServiceConfig
-@ stub I_ScRegisterDeviceNotification
+@ stdcall I_ScRegisterDeviceNotification(ptr ptr long)
 @ stub I_ScRegisterPreshutdownRestart
 @ stub I_ScReparseServiceDatabase
 @ stub I_ScRpcBindA
 @ stub I_ScRpcBindW
 @ stub I_ScSendPnPMessage
 @ stub I_ScSendTSMessage
-@ stub I_ScUnregisterDeviceNotification
+@ stdcall I_ScUnregisterDeviceNotification(ptr)
 @ stub I_ScValidatePnPService
 @ stub LocalGetConditionForString
 @ stub LocalGetReferencedTokenTypesForCondition
diff --git a/dlls/sechost/service.c b/dlls/sechost/service.c
index 7a4211631e2..924a6c9264d 100644
--- a/dlls/sechost/service.c
+++ b/dlls/sechost/service.c
@@ -26,6 +26,8 @@
 #include "winbase.h"
 #include "winsvc.h"
 #include "winternl.h"
+#include "winuser.h"
+#include "dbt.h"
 
 #include "wine/debug.h"
 #include "wine/exception.h"
@@ -33,6 +35,7 @@
 #include "wine/list.h"
 
 #include "svcctl.h"
+#include "plugplay.h"
 
 WINE_DEFAULT_DEBUG_CHANNEL(service);
 
@@ -1967,3 +1970,148 @@ BOOL WINAPI DECLSPEC_HOTPATCH StartServiceCtrlDispatcherW( const SERVICE_TABLE_E
 
     return service_run_main_thread();
 }
+
+struct device_notification_details
+{
+    DWORD (CALLBACK *cb)(HANDLE handle, DWORD flags, DEV_BROADCAST_HDR *header);
+    HANDLE handle;
+};
+
+static HANDLE device_notify_thread;
+static struct list device_notify_list = LIST_INIT(device_notify_list);
+
+struct device_notify_registration
+{
+    struct list entry;
+    struct device_notification_details details;
+};
+
+static DWORD WINAPI device_notify_proc( void *arg )
+{
+    WCHAR endpoint[] = L"\\pipe\\wine_plugplay";
+    WCHAR protseq[] = L"ncalrpc";
+    RPC_WSTR binding_str;
+    DWORD err = ERROR_SUCCESS;
+    struct device_notify_registration *registration;
+    plugplay_rpc_handle handle = NULL;
+    DWORD code = 0;
+    unsigned int size;
+    BYTE *buf;
+
+    if ((err = RpcStringBindingComposeW( NULL, protseq, NULL, endpoint, NULL, &binding_str )))
+    {
+        ERR("RpcStringBindingCompose() failed, error %#x\n", err);
+        return err;
+    }
+    err = RpcBindingFromStringBindingW( binding_str, &plugplay_binding_handle );
+    RpcStringFreeW( &binding_str );
+    if (err)
+    {
+        ERR("RpcBindingFromStringBinding() failed, error %#x\n", err);
+        return err;
+    }
+
+    __TRY
+    {
+        handle = plugplay_register_listener();
+    }
+    __EXCEPT(rpc_filter)
+    {
+        err = map_exception_code( GetExceptionCode() );
+    }
+    __ENDTRY
+
+    if (!handle)
+    {
+        ERR("failed to open RPC handle, error %u\n", err);
+        return 1;
+    }
+
+    for (;;)
+    {
+        buf = NULL;
+        __TRY
+        {
+            code = plugplay_get_event( handle, &buf, &size );
+            err = ERROR_SUCCESS;
+        }
+        __EXCEPT(rpc_filter)
+        {
+            err = map_exception_code( GetExceptionCode() );
+        }
+        __ENDTRY
+
+        if (err)
+        {
+            ERR("failed to get event, error %u\n", err);
+            break;
+        }
+
+        EnterCriticalSection( &service_cs );
+        LIST_FOR_EACH_ENTRY(registration, &device_notify_list, struct device_notify_registration, entry)
+        {
+            registration->details.cb( registration->details.handle, code, (DEV_BROADCAST_HDR *)buf );
+        }
+        LeaveCriticalSection(&service_cs);
+        MIDL_user_free(buf);
+    }
+
+    __TRY
+    {
+        plugplay_unregister_listener( handle );
+    }
+    __EXCEPT(rpc_filter)
+    {
+    }
+    __ENDTRY
+
+    RpcBindingFree( &plugplay_binding_handle );
+    return 0;
+}
+
+/******************************************************************************
+ *     I_ScRegisterDeviceNotification   (sechost.@)
+ */
+HDEVNOTIFY WINAPI I_ScRegisterDeviceNotification( struct device_notification_details *details,
+        void *filter, DWORD flags )
+{
+    struct device_notify_registration *registration;
+
+    TRACE("callback %p, handle %p, filter %p, flags %#x\n", details->cb, details->handle, filter, flags);
+
+    if (filter) FIXME("Notification filters are not yet implemented.\n");
+
+    if (!(registration = heap_alloc(sizeof(struct device_notify_registration))))
+    {
+        SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+        return NULL;
+    }
+
+    registration->details = *details;
+
+    EnterCriticalSection( &service_cs );
+    list_add_tail( &device_notify_list, &registration->entry );
+
+    if (!device_notify_thread)
+        device_notify_thread = CreateThread( NULL, 0, device_notify_proc, NULL, 0, NULL );
+
+    LeaveCriticalSection( &service_cs );
+
+    return registration;
+}
+
+/******************************************************************************
+ *     I_ScUnregisterDeviceNotification   (sechost.@)
+ */
+BOOL WINAPI I_ScUnregisterDeviceNotification( HDEVNOTIFY handle )
+{
+    struct device_notify_registration *registration = handle;
+
+    TRACE("%p\n", handle);
+
+    EnterCriticalSection( &service_cs );
+    list_remove( &registration->entry );
+    LeaveCriticalSection(&service_cs);
+    heap_free( registration );
+    return TRUE;
+}
diff --git a/include/wine/plugplay.idl b/include/wine/plugplay.idl
new file mode 100644
index 00000000000..7cc59191248
--- /dev/null
+++ b/include/wine/plugplay.idl
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2020 Zebediah Figura for CodeWeavers
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+import "wtypes.idl";
+
+[
+    uuid(57c680ac-7bce-4f39-97fd-ffea566754d5),
+    implicit_handle(handle_t plugplay_binding_handle)
+]
+interface plugplay
+{
+    typedef [context_handle] void *plugplay_rpc_handle;
+
+    plugplay_rpc_handle plugplay_register_listener();
+    DWORD plugplay_get_event([in] plugplay_rpc_handle handle, [out, size_is(,*size)] BYTE **data, [out] unsigned int *size);
+    void plugplay_unregister_listener([in] plugplay_rpc_handle handle);
+}
diff --git a/programs/plugplay/Makefile.in b/programs/plugplay/Makefile.in
index e5d37e66f1d..1bc2aa505ab 100644
--- a/programs/plugplay/Makefile.in
+++ b/programs/plugplay/Makefile.in
@@ -1,7 +1,10 @@
 MODULE    = plugplay.exe
-IMPORTS   = advapi32
+IMPORTS   = advapi32 rpcrt4
 
 EXTRADLLFLAGS = -mconsole -municode -mno-cygwin
 
 C_SRCS = \
 	main.c
+
+IDL_SRCS = \
+	plugplay.idl
diff --git a/programs/plugplay/main.c b/programs/plugplay/main.c
index 73bf46d6f99..59561dca503 100644
--- a/programs/plugplay/main.c
+++ b/programs/plugplay/main.c
@@ -21,6 +21,8 @@
 #include <windows.h>
 #include "winsvc.h"
 #include "wine/debug.h"
+#include "wine/list.h"
+#include "plugplay.h"
 
 WINE_DEFAULT_DEBUG_CHANNEL(plugplay);
 
@@ -29,6 +31,111 @@ static WCHAR plugplayW[] = {'P','l','u','g','P','l','a','y',0};
 static SERVICE_STATUS_HANDLE service_handle;
 static HANDLE stop_event;
 
+void  __RPC_FAR * __RPC_USER MIDL_user_allocate( SIZE_T len )
+{
+    return malloc( len );
+}
+
+void __RPC_USER MIDL_user_free( void __RPC_FAR *ptr )
+{
+    free( ptr );
+}
+
+static CRITICAL_SECTION plugplay_cs;
+static CRITICAL_SECTION_DEBUG plugplay_cs_debug =
+{
+    0, 0, &plugplay_cs,
+    { &plugplay_cs_debug.ProcessLocksList, &plugplay_cs_debug.ProcessLocksList },
+      0, 0, { (DWORD_PTR)(__FILE__ ": plugplay_cs") }
+};
+static CRITICAL_SECTION plugplay_cs = { &plugplay_cs_debug, -1, 0, 0, 0, 0 };
+
+static struct list listener_list = LIST_INIT(listener_list);
+
+struct listener
+{
+    struct list entry;
+    struct list events;
+    CONDITION_VARIABLE cv;
+};
+
+struct event
+{
+    struct list entry;
+    DWORD code;
+    BYTE *data;
+    unsigned int size;
+};
+
+
+static void destroy_listener( struct listener *listener )
+{
+    struct event *event, *next;
+
+    EnterCriticalSection( &plugplay_cs );
+    list_remove( &listener->entry );
+    LeaveCriticalSection( &plugplay_cs );
+
+    LIST_FOR_EACH_ENTRY_SAFE(event, next, &listener->events, struct event, entry)
+    {
+        MIDL_user_free( event->data );
+        list_remove( &event->entry );
+        free( event );
+    }
+    free( listener );
+}
+
+void __RPC_USER plugplay_rpc_handle_rundown( plugplay_rpc_handle handle )
+{
+    destroy_listener( handle );
+}
+
+plugplay_rpc_handle __cdecl plugplay_register_listener(void)
+{
+    struct listener *listener;
+
+    if (!(listener = calloc( 1, sizeof(*listener) )))
+        return NULL;
+
+    list_init( &listener->events );
+    InitializeConditionVariable( &listener->cv );
+
+    EnterCriticalSection( &plugplay_cs );
+    list_add_tail( &listener_list, &listener->entry );
+    LeaveCriticalSection( &plugplay_cs );
+
+    return listener;
+}
+
+DWORD __cdecl plugplay_get_event( plugplay_rpc_handle handle, BYTE **data, unsigned int *size )
+{
+    struct listener *listener = handle;
+    struct event *event;
+    struct list *entry;
+    DWORD ret;
+
+    EnterCriticalSection( &plugplay_cs );
+
+    while (!(entry = list_head( &listener->events )))
+        SleepConditionVariableCS( &listener->cv, &plugplay_cs, INFINITE );
+
+    event = LIST_ENTRY(entry, struct event, entry);
+    list_remove( &event->entry );
+
+    LeaveCriticalSection( &plugplay_cs );
+
+    ret = event->code;
+    *data = event->data;
+    *size = event->size;
+    free( event );
+    return ret;
+}
+
+void __cdecl plugplay_unregister_listener( plugplay_rpc_handle handle )
+{
+    destroy_listener( handle );
+}
+
 static DWORD WINAPI service_handler( DWORD ctrl, DWORD event_type, LPVOID event_data, LPVOID context )
 {
     SERVICE_STATUS status;
@@ -60,10 +167,29 @@ static DWORD WINAPI service_handler( DWORD ctrl, DWORD event_type, LPVOID event_
 
 static void WINAPI ServiceMain( DWORD argc, LPWSTR *argv )
 {
+    unsigned char endpoint[] = "\\pipe\\wine_plugplay";
+    unsigned char protseq[] = "ncalrpc";
     SERVICE_STATUS status;
+    RPC_STATUS err;
 
     WINE_TRACE( "starting service\n" );
 
+    if ((err = RpcServerUseProtseqEpA( protseq, 0, endpoint, NULL )))
+    {
+        ERR("RpcServerUseProtseqEp() failed, error %u\n", err);
+        return;
+    }
+    if ((err = RpcServerRegisterIf( plugplay_v0_0_s_ifspec, NULL, NULL )))
+    {
+        ERR("RpcServerRegisterIf() failed, error %u\n", err);
+        return;
+    }
+    if ((err = RpcServerListen( 1, RPC_C_LISTEN_MAX_CALLS_DEFAULT, TRUE )))
+    {
+        ERR("RpcServerListen() failed, error %u\n", err);
+        return;
+    }
+
     stop_event = CreateEventW( NULL, TRUE, FALSE, NULL );
 
     service_handle = RegisterServiceCtrlHandlerExW( plugplayW, service_handler, NULL );
@@ -81,6 +207,10 @@ static void WINAPI ServiceMain( DWORD argc, LPWSTR *argv )
 
     WaitForSingleObject( stop_event, INFINITE );
 
+    RpcMgmtStopServerListening( NULL );
+    RpcServerUnregisterIf( plugplay_v0_0_s_ifspec, NULL, TRUE );
+    RpcMgmtWaitServerListen();
+
     status.dwCurrentState     = SERVICE_STOPPED;
     status.dwControlsAccepted = 0;
     SetServiceStatus( service_handle, &status );
diff --git a/programs/plugplay/plugplay.idl b/programs/plugplay/plugplay.idl
new file mode 100644
index 00000000000..0cb8884c2df
--- /dev/null
+++ b/programs/plugplay/plugplay.idl
@@ -0,0 +1,2 @@
+#pragma makedep server
+#include "wine/plugplay.idl"
-- 
2.26.2




More information about the wine-devel mailing list