combase: Add stub for RoRegisterForApartmentShutdown.
[wine.git] / programs / winedevice / device.c
blobfa8b268494ea819c4cb969b79f33c1041123b110
1 /*
2 * Service process to load a kernel driver
4 * Copyright 2007 Alexandre Julliard
5 * Copyright 2016 Sebastian Lackner
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
22 #include "config.h"
23 #include "wine/port.h"
25 #include <stdarg.h>
27 #include "ntstatus.h"
28 #define WIN32_NO_STATUS
29 #include "windef.h"
30 #include "winbase.h"
31 #include "winternl.h"
32 #include "winreg.h"
33 #include "winnls.h"
34 #include "winsvc.h"
35 #include "ddk/wdm.h"
36 #include "wine/rbtree.h"
37 #include "wine/svcctl.h"
38 #include "wine/unicode.h"
39 #include "wine/debug.h"
41 WINE_DEFAULT_DEBUG_CHANNEL(winedevice);
42 WINE_DECLARE_DEBUG_CHANNEL(relay);
44 extern NTSTATUS CDECL wine_ntoskrnl_main_loop( HANDLE stop_event );
46 static const WCHAR winedeviceW[] = {'w','i','n','e','d','e','v','i','c','e',0};
47 static SERVICE_STATUS_HANDLE service_handle;
48 static PTP_CLEANUP_GROUP cleanup_group;
49 static SC_HANDLE manager_handle;
50 static BOOL shutdown_in_progress;
51 static HANDLE stop_event;
53 #define EVENT_STARTED 0
54 #define EVENT_ERROR 1
56 struct wine_driver
58 struct wine_rb_entry entry;
60 SERVICE_STATUS_HANDLE handle;
61 HANDLE events[2];
62 DRIVER_OBJECT *driver_obj;
63 WCHAR name[1];
66 static int wine_drivers_rb_compare( const void *key, const struct wine_rb_entry *entry )
68 const struct wine_driver *driver = WINE_RB_ENTRY_VALUE( entry, const struct wine_driver, entry );
69 return strcmpW( (const WCHAR *)key, driver->name );
72 static struct wine_rb_tree wine_drivers = { wine_drivers_rb_compare };
74 static CRITICAL_SECTION drivers_cs;
75 static CRITICAL_SECTION_DEBUG critsect_debug =
77 0, 0, &drivers_cs,
78 { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
79 0, 0, { (DWORD_PTR)(__FILE__ ": drivers_cs") }
81 static CRITICAL_SECTION drivers_cs = { &critsect_debug, -1, 0, 0, 0, 0 };
83 /* find the LDR_MODULE corresponding to the driver module */
84 static LDR_MODULE *find_ldr_module( HMODULE module )
86 LDR_MODULE *ldr;
87 ULONG_PTR magic;
89 LdrLockLoaderLock( 0, NULL, &magic );
90 if (LdrFindEntryForAddress( module, &ldr ))
92 WARN( "module not found for %p\n", module );
93 ldr = NULL;
95 LdrUnlockLoaderLock( 0, magic );
97 return ldr;
100 /* load the driver module file */
101 static HMODULE load_driver_module( const WCHAR *name )
103 IMAGE_NT_HEADERS *nt;
104 const IMAGE_IMPORT_DESCRIPTOR *imports;
105 SYSTEM_BASIC_INFORMATION info;
106 int i;
107 INT_PTR delta;
108 ULONG size;
109 HMODULE module = LoadLibraryW( name );
111 if (!module) return NULL;
112 nt = RtlImageNtHeader( module );
114 if (!(delta = (char *)module - (char *)nt->OptionalHeader.ImageBase)) return module;
116 /* the loader does not apply relocations to non page-aligned binaries or executables,
117 * we have to do it ourselves */
119 NtQuerySystemInformation( SystemBasicInformation, &info, sizeof(info), NULL );
120 if (nt->OptionalHeader.SectionAlignment < info.PageSize ||
121 !(nt->FileHeader.Characteristics & IMAGE_FILE_DLL))
123 DWORD old;
124 IMAGE_BASE_RELOCATION *rel, *end;
126 if ((rel = RtlImageDirectoryEntryToData( module, TRUE, IMAGE_DIRECTORY_ENTRY_BASERELOC, &size )))
128 WINE_TRACE( "%s: relocating from %p to %p\n",
129 wine_dbgstr_w(name), (char *)module - delta, module );
130 end = (IMAGE_BASE_RELOCATION *)((char *)rel + size);
131 while (rel < end && rel->SizeOfBlock)
133 void *page = (char *)module + rel->VirtualAddress;
134 VirtualProtect( page, info.PageSize, PAGE_EXECUTE_READWRITE, &old );
135 rel = LdrProcessRelocationBlock( page, (rel->SizeOfBlock - sizeof(*rel)) / sizeof(USHORT),
136 (USHORT *)(rel + 1), delta );
137 if (old != PAGE_EXECUTE_READWRITE) VirtualProtect( page, info.PageSize, old, &old );
138 if (!rel) goto error;
140 /* make sure we don't try again */
141 size = FIELD_OFFSET( IMAGE_NT_HEADERS, OptionalHeader ) + nt->FileHeader.SizeOfOptionalHeader;
142 VirtualProtect( nt, size, PAGE_READWRITE, &old );
143 nt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress = 0;
144 VirtualProtect( nt, size, old, &old );
148 /* make sure imports are relocated too */
150 if ((imports = RtlImageDirectoryEntryToData( module, TRUE, IMAGE_DIRECTORY_ENTRY_IMPORT, &size )))
152 for (i = 0; imports[i].Name && imports[i].FirstThunk; i++)
154 char *name = (char *)module + imports[i].Name;
155 WCHAR buffer[32], *p = buffer;
157 while (p < buffer + 32) if (!(*p++ = *name++)) break;
158 if (p <= buffer + 32) FreeLibrary( load_driver_module( buffer ) );
162 return module;
164 error:
165 FreeLibrary( module );
166 return NULL;
169 /* load the .sys module for a device driver */
170 static HMODULE load_driver( const WCHAR *driver_name, const UNICODE_STRING *keyname )
172 static const WCHAR driversW[] = {'\\','d','r','i','v','e','r','s','\\',0};
173 static const WCHAR systemrootW[] = {'\\','S','y','s','t','e','m','R','o','o','t','\\',0};
174 static const WCHAR postfixW[] = {'.','s','y','s',0};
175 static const WCHAR ntprefixW[] = {'\\','?','?','\\',0};
176 static const WCHAR ImagePathW[] = {'I','m','a','g','e','P','a','t','h',0};
177 HKEY driver_hkey;
178 HMODULE module;
179 LPWSTR path = NULL, str;
180 DWORD type, size;
182 if (RegOpenKeyW( HKEY_LOCAL_MACHINE, keyname->Buffer + 18 /* skip \registry\machine */, &driver_hkey ))
184 WINE_ERR( "cannot open key %s, err=%u\n", wine_dbgstr_w(keyname->Buffer), GetLastError() );
185 return NULL;
188 /* read the executable path from memory */
189 size = 0;
190 if (!RegQueryValueExW( driver_hkey, ImagePathW, NULL, &type, NULL, &size ))
192 str = HeapAlloc( GetProcessHeap(), 0, size );
193 if (!RegQueryValueExW( driver_hkey, ImagePathW, NULL, &type, (LPBYTE)str, &size ))
195 size = ExpandEnvironmentStringsW(str,NULL,0);
196 path = HeapAlloc(GetProcessHeap(),0,size*sizeof(WCHAR));
197 ExpandEnvironmentStringsW(str,path,size);
199 HeapFree( GetProcessHeap(), 0, str );
200 if (!path)
202 RegCloseKey( driver_hkey );
203 return NULL;
206 if (!strncmpiW( path, systemrootW, 12 ))
208 WCHAR buffer[MAX_PATH];
210 GetWindowsDirectoryW(buffer, MAX_PATH);
212 str = HeapAlloc(GetProcessHeap(), 0, (size -11 + strlenW(buffer))
213 * sizeof(WCHAR));
214 lstrcpyW(str, buffer);
215 lstrcatW(str, path + 11);
216 HeapFree( GetProcessHeap(), 0, path );
217 path = str;
219 else if (!strncmpW( path, ntprefixW, 4 ))
220 str = path + 4;
221 else
222 str = path;
224 else
226 /* default is to use the driver name + ".sys" */
227 WCHAR buffer[MAX_PATH];
228 GetSystemDirectoryW(buffer, MAX_PATH);
229 path = HeapAlloc(GetProcessHeap(),0,
230 (strlenW(buffer) + strlenW(driversW) + strlenW(driver_name) + strlenW(postfixW) + 1)
231 *sizeof(WCHAR));
232 lstrcpyW(path, buffer);
233 lstrcatW(path, driversW);
234 lstrcatW(path, driver_name);
235 lstrcatW(path, postfixW);
236 str = path;
238 RegCloseKey( driver_hkey );
240 WINE_TRACE( "loading driver %s\n", wine_dbgstr_w(str) );
242 module = load_driver_module( str );
243 HeapFree( GetProcessHeap(), 0, path );
244 return module;
247 /* call the driver init entry point */
248 static NTSTATUS WINAPI init_driver( DRIVER_OBJECT *driver_object, UNICODE_STRING *keyname )
250 unsigned int i;
251 NTSTATUS status;
252 const IMAGE_NT_HEADERS *nt;
253 const WCHAR *driver_name;
254 HMODULE module;
256 /* Retrieve driver name from the keyname */
257 driver_name = strrchrW( keyname->Buffer, '\\' );
258 driver_name++;
260 module = load_driver( driver_name, keyname );
261 if (!module)
262 return STATUS_DLL_INIT_FAILED;
264 driver_object->DriverSection = find_ldr_module( module );
266 nt = RtlImageNtHeader( module );
267 if (!nt->OptionalHeader.AddressOfEntryPoint) return STATUS_SUCCESS;
268 driver_object->DriverInit = (PDRIVER_INITIALIZE)((char *)module + nt->OptionalHeader.AddressOfEntryPoint);
270 TRACE_(relay)( "\1Call driver init %p (obj=%p,str=%s)\n",
271 driver_object->DriverInit, driver_object, wine_dbgstr_w(keyname->Buffer) );
273 status = driver_object->DriverInit( driver_object, keyname );
275 TRACE_(relay)( "\1Ret driver init %p (obj=%p,str=%s) retval=%08x\n",
276 driver_object->DriverInit, driver_object, wine_dbgstr_w(keyname->Buffer), status );
278 WINE_TRACE( "init done for %s obj %p\n", wine_dbgstr_w(driver_name), driver_object );
279 WINE_TRACE( "- DriverInit = %p\n", driver_object->DriverInit );
280 WINE_TRACE( "- DriverStartIo = %p\n", driver_object->DriverStartIo );
281 WINE_TRACE( "- DriverUnload = %p\n", driver_object->DriverUnload );
282 for (i = 0; i <= IRP_MJ_MAXIMUM_FUNCTION; i++)
283 WINE_TRACE( "- MajorFunction[%d] = %p\n", i, driver_object->MajorFunction[i] );
285 return status;
288 /* helper function to update service status */
289 static void set_service_status( SERVICE_STATUS_HANDLE handle, DWORD state, DWORD accepted )
291 SERVICE_STATUS status;
292 status.dwServiceType = SERVICE_WIN32;
293 status.dwCurrentState = state;
294 status.dwControlsAccepted = accepted;
295 status.dwWin32ExitCode = 0;
296 status.dwServiceSpecificExitCode = 0;
297 status.dwCheckPoint = 0;
298 status.dwWaitHint = (state == SERVICE_START_PENDING) ? 10000 : 0;
299 SetServiceStatus( handle, &status );
302 static void WINAPI async_unload_driver( PTP_CALLBACK_INSTANCE instance, void *context )
304 struct wine_driver *driver = context;
305 DRIVER_OBJECT *driver_obj = driver->driver_obj;
306 LDR_MODULE *ldr;
308 TRACE_(relay)( "\1Call driver unload %p (obj=%p)\n", driver_obj->DriverUnload, driver_obj );
310 driver_obj->DriverUnload( driver_obj );
312 TRACE_(relay)( "\1Ret driver unload %p (obj=%p)\n", driver_obj->DriverUnload, driver_obj );
314 ldr = driver_obj->DriverSection;
315 FreeLibrary( ldr->BaseAddress );
316 IoDeleteDriver( driver_obj );
317 ObDereferenceObject( driver_obj );
319 set_service_status( driver->handle, SERVICE_STOPPED, 0 );
320 CloseServiceHandle( (void *)driver->handle );
321 HeapFree( GetProcessHeap(), 0, driver );
324 /* call the driver unload function */
325 static NTSTATUS unload_driver( struct wine_rb_entry *entry, BOOL destroy )
327 TP_CALLBACK_ENVIRON environment;
328 struct wine_driver *driver = WINE_RB_ENTRY_VALUE( entry, struct wine_driver, entry );
329 DRIVER_OBJECT *driver_obj = driver->driver_obj;
331 if (!driver_obj)
333 TRACE( "driver %s has not finished loading yet\n", wine_dbgstr_w(driver->name) );
334 return STATUS_UNSUCCESSFUL;
336 if (!driver_obj->DriverUnload)
338 TRACE( "driver %s does not support unloading\n", wine_dbgstr_w(driver->name) );
339 return STATUS_UNSUCCESSFUL;
342 TRACE( "stopping driver %s\n", wine_dbgstr_w(driver->name) );
343 set_service_status( driver->handle, SERVICE_STOP_PENDING, 0 );
345 if (destroy)
347 async_unload_driver( NULL, driver );
348 return STATUS_SUCCESS;
351 wine_rb_remove( &wine_drivers, &driver->entry );
353 memset( &environment, 0, sizeof(environment) );
354 environment.Version = 1;
355 environment.CleanupGroup = cleanup_group;
357 /* don't block the service control handler */
358 if (!TrySubmitThreadpoolCallback( async_unload_driver, driver, &environment ))
359 async_unload_driver( NULL, driver );
361 return STATUS_SUCCESS;
364 static void WINAPI async_create_driver( PTP_CALLBACK_INSTANCE instance, void *context )
366 static const WCHAR driverW[] = {'\\','D','r','i','v','e','r','\\',0};
367 struct wine_driver *driver = context;
368 DRIVER_OBJECT *driver_obj;
369 UNICODE_STRING drv_name;
370 NTSTATUS status;
371 WCHAR *str;
373 if (!(str = HeapAlloc( GetProcessHeap(), 0, sizeof(driverW) + strlenW(driver->name)*sizeof(WCHAR) )))
374 goto error;
376 lstrcpyW( str, driverW);
377 lstrcatW( str, driver->name );
378 RtlInitUnicodeString( &drv_name, str );
380 status = IoCreateDriver( &drv_name, init_driver );
381 if (status != STATUS_SUCCESS)
383 ERR( "failed to create driver %s: %08x\n", debugstr_w(driver->name), status );
384 RtlFreeUnicodeString( &drv_name );
385 goto error;
388 status = ObReferenceObjectByName( &drv_name, OBJ_CASE_INSENSITIVE, NULL,
389 0, NULL, KernelMode, NULL, (void **)&driver_obj );
390 RtlFreeUnicodeString( &drv_name );
391 if (status != STATUS_SUCCESS)
393 ERR( "failed to locate driver %s: %08x\n", debugstr_w(driver->name), status );
394 goto error;
397 SetEvent(driver->events[EVENT_STARTED]);
399 EnterCriticalSection( &drivers_cs );
400 driver->driver_obj = driver_obj;
401 set_service_status( driver->handle, SERVICE_RUNNING,
402 SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN );
403 LeaveCriticalSection( &drivers_cs );
404 return;
406 error:
407 SetEvent(driver->events[EVENT_ERROR]);
408 EnterCriticalSection( &drivers_cs );
409 wine_rb_remove( &wine_drivers, &driver->entry );
410 LeaveCriticalSection( &drivers_cs );
412 set_service_status( driver->handle, SERVICE_STOPPED, 0 );
413 CloseServiceHandle( (void *)driver->handle );
414 HeapFree( GetProcessHeap(), 0, driver );
417 /* load a driver and notify services.exe about the status change */
418 static NTSTATUS create_driver( const WCHAR *driver_name )
420 TP_CALLBACK_ENVIRON environment;
421 struct wine_driver *driver;
422 DWORD length;
423 DWORD ret;
425 length = FIELD_OFFSET( struct wine_driver, name[strlenW(driver_name) + 1] );
426 if (!(driver = HeapAlloc( GetProcessHeap(), 0, length )))
427 return STATUS_NO_MEMORY;
429 strcpyW( driver->name, driver_name );
430 driver->driver_obj = NULL;
432 if (!(driver->handle = (void *)OpenServiceW( manager_handle, driver_name, SERVICE_SET_STATUS )))
434 HeapFree( GetProcessHeap(), 0, driver );
435 return STATUS_UNSUCCESSFUL;
438 if (wine_rb_put( &wine_drivers, driver_name, &driver->entry ))
440 CloseServiceHandle( (void *)driver->handle );
441 HeapFree( GetProcessHeap(), 0, driver );
442 return STATUS_UNSUCCESSFUL;
445 TRACE( "starting driver %s\n", wine_dbgstr_w(driver_name) );
446 set_service_status( driver->handle, SERVICE_START_PENDING, 0 );
448 memset( &environment, 0, sizeof(environment) );
449 environment.Version = 1;
450 environment.CleanupGroup = cleanup_group;
452 driver->events[EVENT_STARTED] = CreateEventW(NULL, TRUE, FALSE, NULL);
453 driver->events[EVENT_ERROR] = CreateEventW(NULL, TRUE, FALSE, NULL);
455 /* don't block the service control handler */
456 if (!TrySubmitThreadpoolCallback( async_create_driver, driver, &environment ))
457 async_create_driver( NULL, driver );
459 /* Windows wait 30 Seconds */
460 ret = WaitForMultipleObjects(2, driver->events, FALSE, 30000);
461 if(ret == WAIT_OBJECT_0 + EVENT_ERROR)
462 return STATUS_UNSUCCESSFUL;
463 else if(ret == WAIT_TIMEOUT)
464 return ERROR_SERVICE_REQUEST_TIMEOUT;
466 return STATUS_SUCCESS;
469 static void wine_drivers_rb_destroy( struct wine_rb_entry *entry, void *context )
471 if (unload_driver( entry, TRUE ) != STATUS_SUCCESS)
473 struct wine_driver *driver = WINE_RB_ENTRY_VALUE( entry, struct wine_driver, entry );
474 CloseHandle(driver->events[EVENT_STARTED]);
475 CloseHandle(driver->events[EVENT_ERROR]);
476 ObDereferenceObject( driver->driver_obj );
477 CloseServiceHandle( (void *)driver->handle );
478 HeapFree( GetProcessHeap(), 0, driver );
482 static void WINAPI async_shutdown_drivers( PTP_CALLBACK_INSTANCE instance, void *context )
484 CloseThreadpoolCleanupGroupMembers( cleanup_group, FALSE, NULL );
486 EnterCriticalSection( &drivers_cs );
487 wine_rb_destroy( &wine_drivers, wine_drivers_rb_destroy, NULL );
488 LeaveCriticalSection( &drivers_cs );
490 SetEvent( stop_event );
493 static void shutdown_drivers( void )
495 if (shutdown_in_progress) return;
497 /* don't block the service control handler */
498 if (!TrySubmitThreadpoolCallback( async_shutdown_drivers, NULL, NULL ))
499 async_shutdown_drivers( NULL, NULL );
501 shutdown_in_progress = TRUE;
504 static DWORD device_handler( DWORD ctrl, const WCHAR *driver_name )
506 struct wine_rb_entry *entry;
507 DWORD result = NO_ERROR;
509 if (shutdown_in_progress)
510 return ERROR_SERVICE_CANNOT_ACCEPT_CTRL;
512 EnterCriticalSection( &drivers_cs );
513 entry = wine_rb_get( &wine_drivers, driver_name );
515 switch (ctrl)
517 case SERVICE_CONTROL_START:
518 if (entry) break;
519 result = RtlNtStatusToDosError(create_driver( driver_name ));
520 break;
522 case SERVICE_CONTROL_STOP:
523 if (!entry) break;
524 result = RtlNtStatusToDosError(unload_driver( entry, FALSE ));
525 break;
527 default:
528 FIXME( "got driver ctrl %x for %s\n", ctrl, wine_dbgstr_w(driver_name) );
529 break;
531 LeaveCriticalSection( &drivers_cs );
532 return result;
535 static DWORD WINAPI service_handler( DWORD ctrl, DWORD event_type, LPVOID event_data, LPVOID context )
537 const WCHAR *service_group = context;
539 if (ctrl & SERVICE_CONTROL_FORWARD_FLAG)
541 if (!event_data) return ERROR_INVALID_PARAMETER;
542 return device_handler( ctrl & ~SERVICE_CONTROL_FORWARD_FLAG, (const WCHAR *)event_data );
545 switch (ctrl)
547 case SERVICE_CONTROL_STOP:
548 case SERVICE_CONTROL_SHUTDOWN:
549 TRACE( "shutting down %s\n", wine_dbgstr_w(service_group) );
550 set_service_status( service_handle, SERVICE_STOP_PENDING, 0 );
551 shutdown_drivers();
552 return NO_ERROR;
553 default:
554 FIXME( "got service ctrl %x for %s\n", ctrl, wine_dbgstr_w(service_group) );
555 set_service_status( service_handle, SERVICE_RUNNING,
556 SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN );
557 return NO_ERROR;
561 static void WINAPI ServiceMain( DWORD argc, LPWSTR *argv )
563 const WCHAR *service_group = (argc >= 2) ? argv[1] : argv[0];
565 if (!(stop_event = CreateEventW( NULL, TRUE, FALSE, NULL )))
566 return;
567 if (!(cleanup_group = CreateThreadpoolCleanupGroup()))
568 return;
569 if (!(manager_handle = OpenSCManagerW( NULL, NULL, SC_MANAGER_CONNECT )))
570 return;
571 if (!(service_handle = RegisterServiceCtrlHandlerExW( winedeviceW, service_handler, (void *)service_group )))
572 return;
574 TRACE( "starting service group %s\n", wine_dbgstr_w(service_group) );
575 set_service_status( service_handle, SERVICE_RUNNING,
576 SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN );
578 wine_ntoskrnl_main_loop( stop_event );
580 TRACE( "service group %s stopped\n", wine_dbgstr_w(service_group) );
581 set_service_status( service_handle, SERVICE_STOPPED, 0 );
582 CloseServiceHandle( manager_handle );
583 CloseThreadpoolCleanupGroup( cleanup_group );
584 CloseHandle( stop_event );
587 int wmain( int argc, WCHAR *argv[] )
589 SERVICE_TABLE_ENTRYW service_table[2];
591 service_table[0].lpServiceName = (void *)winedeviceW;
592 service_table[0].lpServiceProc = ServiceMain;
593 service_table[1].lpServiceName = NULL;
594 service_table[1].lpServiceProc = NULL;
596 StartServiceCtrlDispatcherW( service_table );
597 return 0;