winedevice: Track loaded drivers and update their status on load/unload.

Based on a patch by Aric Stewart.

Signed-off-by: Sebastian Lackner <sebastian@fds-team.de>
Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
Sebastian Lackner 2016-08-19 08:02:09 +02:00 committed by Alexandre Julliard
parent 039471ec37
commit aab54da669
1 changed files with 131 additions and 50 deletions

View File

@ -32,6 +32,8 @@
#include "winnls.h" #include "winnls.h"
#include "winsvc.h" #include "winsvc.h"
#include "ddk/wdm.h" #include "ddk/wdm.h"
#include "wine/rbtree.h"
#include "wine/svcctl.h"
#include "wine/unicode.h" #include "wine/unicode.h"
#include "wine/debug.h" #include "wine/debug.h"
@ -41,8 +43,49 @@ WINE_DECLARE_DEBUG_CHANNEL(relay);
extern NTSTATUS CDECL wine_ntoskrnl_main_loop( HANDLE stop_event ); extern NTSTATUS CDECL wine_ntoskrnl_main_loop( HANDLE stop_event );
static SERVICE_STATUS_HANDLE service_handle; static SERVICE_STATUS_HANDLE service_handle;
static SC_HANDLE manager_handle;
static HANDLE stop_event; static HANDLE stop_event;
struct wine_driver
{
struct wine_rb_entry entry;
SERVICE_STATUS_HANDLE handle;
DRIVER_OBJECT *driver_obj;
WCHAR name[1];
};
static struct wine_rb_tree wine_drivers;
static void *wine_drivers_rb_alloc( size_t size )
{
return HeapAlloc( GetProcessHeap(), 0, size );
}
static void *wine_drivers_rb_realloc( void *ptr, size_t size )
{
return HeapReAlloc( GetProcessHeap(), 0, ptr, size );
}
static void wine_drivers_rb_free( void *ptr )
{
HeapFree( GetProcessHeap(), 0, ptr );
}
static int wine_drivers_rb_compare( const void *key, const struct wine_rb_entry *entry )
{
const struct wine_driver *driver = WINE_RB_ENTRY_VALUE( entry, const struct wine_driver, entry );
return strcmpW( (const WCHAR *)key, driver->name );
}
static const struct wine_rb_functions wine_drivers_rb_functions =
{
wine_drivers_rb_alloc,
wine_drivers_rb_realloc,
wine_drivers_rb_free,
wine_drivers_rb_compare,
};
/* find the LDR_MODULE corresponding to the driver module */ /* find the LDR_MODULE corresponding to the driver module */
static LDR_MODULE *find_ldr_module( HMODULE module ) static LDR_MODULE *find_ldr_module( HMODULE module )
{ {
@ -250,11 +293,30 @@ static NTSTATUS WINAPI init_driver( DRIVER_OBJECT *driver_object, UNICODE_STRING
return status; return status;
} }
/* call the driver unload function */ /* helper function to update service status */
static void unload_driver( DRIVER_OBJECT *driver_obj ) static void set_service_status( SERVICE_STATUS_HANDLE handle, DWORD state, DWORD accepted )
{ {
SERVICE_STATUS status;
status.dwServiceType = SERVICE_WIN32;
status.dwCurrentState = state;
status.dwControlsAccepted = accepted;
status.dwWin32ExitCode = 0;
status.dwServiceSpecificExitCode = 0;
status.dwCheckPoint = 0;
status.dwWaitHint = (state == SERVICE_START_PENDING) ? 10000 : 0;
SetServiceStatus( handle, &status );
}
/* call the driver unload function */
static void unload_driver( struct wine_rb_entry *entry, void *context )
{
struct wine_driver *driver = WINE_RB_ENTRY_VALUE( entry, struct wine_driver, entry );
DRIVER_OBJECT *driver_obj = driver->driver_obj;
LDR_MODULE *ldr = driver_obj->DriverSection; LDR_MODULE *ldr = driver_obj->DriverSection;
TRACE( "stopping driver %s\n", wine_dbgstr_w(driver->name) );
set_service_status( driver->handle, SERVICE_STOP_PENDING, 0 );
if (driver_obj->DriverUnload) if (driver_obj->DriverUnload)
{ {
if (WINE_TRACE_ON(relay)) if (WINE_TRACE_ON(relay))
@ -271,26 +333,60 @@ static void unload_driver( DRIVER_OBJECT *driver_obj )
FreeLibrary( ldr->BaseAddress ); FreeLibrary( ldr->BaseAddress );
IoDeleteDriver( driver_obj ); IoDeleteDriver( driver_obj );
ObDereferenceObject( driver_obj ); ObDereferenceObject( driver_obj );
set_service_status( driver->handle, SERVICE_STOPPED, 0 );
CloseServiceHandle( (void *)driver->handle );
HeapFree( GetProcessHeap(), 0, driver );
} }
static NTSTATUS create_driver(const WCHAR *driver_name, DRIVER_OBJECT **driver_obj) /* load a driver and notify services.exe about the status change */
static NTSTATUS create_driver( const WCHAR *driver_name )
{ {
static const WCHAR driverW[] = {'\\','D','r','i','v','e','r','\\',0}; static const WCHAR driverW[] = {'\\','D','r','i','v','e','r','\\',0};
struct wine_driver *driver;
UNICODE_STRING drv_name; UNICODE_STRING drv_name;
NTSTATUS status; NTSTATUS status;
DWORD length;
WCHAR *str; WCHAR *str;
str = HeapAlloc( GetProcessHeap(), 0, sizeof(driverW) + strlenW(driver_name)*sizeof(WCHAR) ); length = FIELD_OFFSET( struct wine_driver, name[strlenW(driver_name) + 1] );
if (!(driver = HeapAlloc( GetProcessHeap(), 0, length )))
return STATUS_NO_MEMORY;
strcpyW( driver->name, driver_name );
driver->driver_obj = NULL;
if (!(driver->handle = (void *)OpenServiceW( manager_handle, driver_name, SERVICE_SET_STATUS )))
{
HeapFree( GetProcessHeap(), 0, driver );
return STATUS_UNSUCCESSFUL;
}
if (wine_rb_put( &wine_drivers, driver_name, &driver->entry ))
{
CloseServiceHandle( (void *)driver->handle );
HeapFree( GetProcessHeap(), 0, driver );
return STATUS_UNSUCCESSFUL;
}
if (!(str = HeapAlloc( GetProcessHeap(), 0, sizeof(driverW) + strlenW(driver_name)*sizeof(WCHAR) )))
{
status = STATUS_NO_MEMORY;
goto error;
}
TRACE( "starting driver %s\n", wine_dbgstr_w(driver_name) );
set_service_status( driver->handle, SERVICE_START_PENDING, 0 );
lstrcpyW( str, driverW); lstrcpyW( str, driverW);
lstrcatW( str, driver_name ); lstrcatW( str, driver_name );
RtlInitUnicodeString( &drv_name, str ); RtlInitUnicodeString( &drv_name, str );
status = IoCreateDriver( &drv_name, init_driver ); status = IoCreateDriver( &drv_name, init_driver );
if (status == STATUS_SUCCESS) if (status == STATUS_SUCCESS)
{ {
status = ObReferenceObjectByName( &drv_name, OBJ_CASE_INSENSITIVE, NULL, status = ObReferenceObjectByName( &drv_name, OBJ_CASE_INSENSITIVE, NULL,
0, NULL, KernelMode, NULL, (void **)driver_obj ); 0, NULL, KernelMode, NULL, (void **)&driver->driver_obj );
if (status != STATUS_SUCCESS) if (status != STATUS_SUCCESS)
{ {
ERR("Failed to locate loaded driver (%s)\n", wine_dbgstr_w(driver_name)); ERR("Failed to locate loaded driver (%s)\n", wine_dbgstr_w(driver_name));
@ -298,77 +394,62 @@ static NTSTATUS create_driver(const WCHAR *driver_name, DRIVER_OBJECT **driver_o
} }
RtlFreeUnicodeString( &drv_name ); RtlFreeUnicodeString( &drv_name );
if (status == STATUS_SUCCESS)
{
set_service_status( driver->handle, SERVICE_RUNNING,
SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN );
return STATUS_SUCCESS;
}
error:
set_service_status( driver->handle, SERVICE_STOPPED, 0 );
CloseServiceHandle( (void *)driver->handle );
wine_rb_remove( &wine_drivers, driver_name );
HeapFree( GetProcessHeap(), 0, driver );
return status; return status;
} }
static DWORD WINAPI service_handler( DWORD ctrl, DWORD event_type, LPVOID event_data, LPVOID context ) static DWORD WINAPI service_handler( DWORD ctrl, DWORD event_type, LPVOID event_data, LPVOID context )
{ {
SERVICE_STATUS status;
const WCHAR *driver_name = context; const WCHAR *driver_name = context;
status.dwServiceType = SERVICE_WIN32; switch (ctrl)
status.dwControlsAccepted = SERVICE_ACCEPT_STOP;
status.dwWin32ExitCode = 0;
status.dwServiceSpecificExitCode = 0;
status.dwCheckPoint = 0;
status.dwWaitHint = 0;
switch(ctrl)
{ {
case SERVICE_CONTROL_STOP: case SERVICE_CONTROL_STOP:
case SERVICE_CONTROL_SHUTDOWN: case SERVICE_CONTROL_SHUTDOWN:
WINE_TRACE( "shutting down %s\n", wine_dbgstr_w(driver_name) ); WINE_TRACE( "shutting down %s\n", wine_dbgstr_w(driver_name) );
status.dwCurrentState = SERVICE_STOP_PENDING; set_service_status( service_handle, SERVICE_STOP_PENDING, 0 );
status.dwControlsAccepted = 0;
SetServiceStatus( service_handle, &status );
SetEvent( stop_event ); SetEvent( stop_event );
return NO_ERROR; return NO_ERROR;
default: default:
WINE_FIXME( "got service ctrl %x for %s\n", ctrl, wine_dbgstr_w(driver_name) ); WINE_FIXME( "got service ctrl %x for %s\n", ctrl, wine_dbgstr_w(driver_name) );
status.dwCurrentState = SERVICE_RUNNING; set_service_status( service_handle, SERVICE_RUNNING,
SetServiceStatus( service_handle, &status ); SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN );
return NO_ERROR; return NO_ERROR;
} }
} }
static void WINAPI ServiceMain( DWORD argc, LPWSTR *argv ) static void WINAPI ServiceMain( DWORD argc, LPWSTR *argv )
{ {
SERVICE_STATUS status;
const WCHAR *driver_name = argv[0]; const WCHAR *driver_name = argv[0];
DRIVER_OBJECT *driver_obj;
WINE_TRACE( "starting service %s\n", wine_dbgstr_w(driver_name) ); if (wine_rb_init( &wine_drivers, &wine_drivers_rb_functions ))
return;
stop_event = CreateEventW( NULL, TRUE, FALSE, NULL ); if (!(stop_event = CreateEventW( NULL, TRUE, FALSE, NULL )))
return;
service_handle = RegisterServiceCtrlHandlerExW( driver_name, service_handler, (void *)driver_name ); if (!(manager_handle = OpenSCManagerW( NULL, NULL, SC_MANAGER_CONNECT )))
if (!service_handle) return;
if (!(service_handle = RegisterServiceCtrlHandlerExW( driver_name, service_handler, (void *)driver_name )))
return; return;
status.dwServiceType = SERVICE_WIN32; if (create_driver( driver_name ) == STATUS_SUCCESS)
status.dwCurrentState = SERVICE_START_PENDING;
status.dwControlsAccepted = 0;
status.dwWin32ExitCode = 0;
status.dwServiceSpecificExitCode = 0;
status.dwCheckPoint = 0;
status.dwWaitHint = 10000;
SetServiceStatus( service_handle, &status );
if (create_driver( driver_name, &driver_obj ) == STATUS_SUCCESS)
{
status.dwCurrentState = SERVICE_RUNNING;
status.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN;
SetServiceStatus( service_handle, &status );
wine_ntoskrnl_main_loop( stop_event ); wine_ntoskrnl_main_loop( stop_event );
unload_driver( driver_obj );
}
else WINE_ERR( "driver %s failed to load\n", wine_dbgstr_w(driver_name) );
status.dwCurrentState = SERVICE_STOPPED; wine_rb_destroy( &wine_drivers, unload_driver, NULL );
status.dwControlsAccepted = 0; set_service_status( service_handle, SERVICE_STOPPED, 0 );
SetServiceStatus( service_handle, &status ); CloseServiceHandle( manager_handle );
WINE_TRACE( "service %s stopped\n", wine_dbgstr_w(driver_name) ); CloseHandle( stop_event );
} }
int wmain( int argc, WCHAR *argv[] ) int wmain( int argc, WCHAR *argv[] )