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, ®istration->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( ®istration->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 #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"