1 /* 2 * ReactOS kernel 3 * Copyright (C) 2005 ReactOS Team 4 * 5 * This program is free software; you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License as published by 7 * the Free Software Foundation; either version 2 of the License, or 8 * (at your option) any later version. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License along 16 * with this program; if not, write to the Free Software Foundation, Inc., 17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 */ 19 /* 20 * COPYRIGHT: See COPYING in the top level directory 21 * PROJECT: ReactOS kernel 22 * FILE: base/services/umpnpmgr/umpnpmgr.c 23 * PURPOSE: User-mode Plug and Play manager 24 * PROGRAMMER: Eric Kohl (eric.kohl@reactos.org) 25 * Hervé Poussineau (hpoussin@reactos.org) 26 * Colin Finck (colin@reactos.org) 27 */ 28 29 /* INCLUDES *****************************************************************/ 30 31 #include "precomp.h" 32 33 #define NDEBUG 34 #include <debug.h> 35 36 37 /* GLOBALS ******************************************************************/ 38 39 static WCHAR ServiceName[] = L"PlugPlay"; 40 41 static SERVICE_STATUS_HANDLE ServiceStatusHandle; 42 static SERVICE_STATUS ServiceStatus; 43 44 HKEY hEnumKey = NULL; 45 HKEY hClassKey = NULL; 46 BOOL g_IsUISuppressed = FALSE; 47 48 /* FUNCTIONS *****************************************************************/ 49 50 static DWORD WINAPI 51 PnpEventThread(LPVOID lpParameter) 52 { 53 PLUGPLAY_CONTROL_USER_RESPONSE_DATA ResponseData = {0, 0, 0, 0}; 54 DWORD dwRet = ERROR_SUCCESS; 55 NTSTATUS Status; 56 RPC_STATUS RpcStatus; 57 PPLUGPLAY_EVENT_BLOCK PnpEvent, NewPnpEvent; 58 ULONG PnpEventSize; 59 60 UNREFERENCED_PARAMETER(lpParameter); 61 62 PnpEventSize = 0x1000; 63 PnpEvent = HeapAlloc(GetProcessHeap(), 0, PnpEventSize); 64 if (PnpEvent == NULL) 65 return ERROR_OUTOFMEMORY; 66 67 for (;;) 68 { 69 DPRINT("Calling NtGetPlugPlayEvent()\n"); 70 71 /* Wait for the next PnP event */ 72 Status = NtGetPlugPlayEvent(0, 0, PnpEvent, PnpEventSize); 73 74 /* Resize the buffer for the PnP event if it's too small */ 75 if (Status == STATUS_BUFFER_TOO_SMALL) 76 { 77 PnpEventSize += 0x400; 78 NewPnpEvent = HeapReAlloc(GetProcessHeap(), 0, PnpEvent, PnpEventSize); 79 if (NewPnpEvent == NULL) 80 { 81 dwRet = ERROR_OUTOFMEMORY; 82 break; 83 } 84 PnpEvent = NewPnpEvent; 85 continue; 86 } 87 88 if (!NT_SUCCESS(Status)) 89 { 90 DPRINT1("NtGetPlugPlayEvent() failed (Status 0x%08lx)\n", Status); 91 break; 92 } 93 94 /* Process the PnP event */ 95 DPRINT("Received PnP Event\n"); 96 if (UuidEqual(&PnpEvent->EventGuid, (UUID*)&GUID_DEVICE_ENUMERATED, &RpcStatus)) 97 { 98 DeviceInstallParams* Params; 99 DWORD len; 100 DWORD DeviceIdLength; 101 102 DPRINT("Device enumerated: %S\n", PnpEvent->TargetDevice.DeviceIds); 103 104 DeviceIdLength = lstrlenW(PnpEvent->TargetDevice.DeviceIds); 105 if (DeviceIdLength) 106 { 107 /* Allocate a new device-install event */ 108 len = FIELD_OFFSET(DeviceInstallParams, DeviceIds) + (DeviceIdLength + 1) * sizeof(WCHAR); 109 Params = HeapAlloc(GetProcessHeap(), 0, len); 110 if (Params) 111 { 112 wcscpy(Params->DeviceIds, PnpEvent->TargetDevice.DeviceIds); 113 114 /* Queue the event (will be dequeued by DeviceInstallThread) */ 115 WaitForSingleObject(hDeviceInstallListMutex, INFINITE); 116 InsertTailList(&DeviceInstallListHead, &Params->ListEntry); 117 ReleaseMutex(hDeviceInstallListMutex); 118 119 SetEvent(hDeviceInstallListNotEmpty); 120 } 121 } 122 } 123 else if (UuidEqual(&PnpEvent->EventGuid, (UUID*)&GUID_DEVICE_ARRIVAL, &RpcStatus)) 124 { 125 // DWORD dwRecipient; 126 127 DPRINT("Device arrival: %S\n", PnpEvent->TargetDevice.DeviceIds); 128 129 // dwRecipient = BSM_ALLDESKTOPS | BSM_APPLICATIONS; 130 // BroadcastSystemMessageW(BSF_POSTMESSAGE, 131 // &dwRecipient, 132 // WM_DEVICECHANGE, 133 // DBT_DEVNODES_CHANGED, 134 // 0); 135 SendMessageW(HWND_BROADCAST, WM_DEVICECHANGE, DBT_DEVNODES_CHANGED, 0); 136 } 137 else if (UuidEqual(&PnpEvent->EventGuid, (UUID*)&GUID_DEVICE_EJECT_VETOED, &RpcStatus)) 138 { 139 DPRINT1("Eject vetoed: %S\n", PnpEvent->TargetDevice.DeviceIds); 140 } 141 else if (UuidEqual(&PnpEvent->EventGuid, (UUID*)&GUID_DEVICE_KERNEL_INITIATED_EJECT, &RpcStatus)) 142 { 143 DPRINT1("Kernel initiated eject: %S\n", PnpEvent->TargetDevice.DeviceIds); 144 } 145 else if (UuidEqual(&PnpEvent->EventGuid, (UUID*)&GUID_DEVICE_SAFE_REMOVAL, &RpcStatus)) 146 { 147 // DWORD dwRecipient; 148 149 DPRINT1("Safe removal: %S\n", PnpEvent->TargetDevice.DeviceIds); 150 151 // dwRecipient = BSM_ALLDESKTOPS | BSM_APPLICATIONS; 152 // BroadcastSystemMessageW(BSF_POSTMESSAGE, 153 // &dwRecipient, 154 // WM_DEVICECHANGE, 155 // DBT_DEVNODES_CHANGED, 156 // 0); 157 SendMessageW(HWND_BROADCAST, WM_DEVICECHANGE, DBT_DEVNODES_CHANGED, 0); 158 } 159 else if (UuidEqual(&PnpEvent->EventGuid, (UUID*)&GUID_DEVICE_SURPRISE_REMOVAL, &RpcStatus)) 160 { 161 // DWORD dwRecipient; 162 163 DPRINT1("Surprise removal: %S\n", PnpEvent->TargetDevice.DeviceIds); 164 165 // dwRecipient = BSM_ALLDESKTOPS | BSM_APPLICATIONS; 166 // BroadcastSystemMessageW(BSF_POSTMESSAGE, 167 // &dwRecipient, 168 // WM_DEVICECHANGE, 169 // DBT_DEVNODES_CHANGED, 170 // 0); 171 SendMessageW(HWND_BROADCAST, WM_DEVICECHANGE, DBT_DEVNODES_CHANGED, 0); 172 } 173 else if (UuidEqual(&PnpEvent->EventGuid, (UUID*)&GUID_DEVICE_REMOVAL_VETOED, &RpcStatus)) 174 { 175 DPRINT1("Removal vetoed: %S\n", PnpEvent->TargetDevice.DeviceIds); 176 } 177 else if (UuidEqual(&PnpEvent->EventGuid, (UUID*)&GUID_DEVICE_REMOVE_PENDING, &RpcStatus)) 178 { 179 DPRINT1("Removal pending: %S\n", PnpEvent->TargetDevice.DeviceIds); 180 } 181 else 182 { 183 DPRINT1("Unknown event, GUID {%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X}\n", 184 PnpEvent->EventGuid.Data1, PnpEvent->EventGuid.Data2, PnpEvent->EventGuid.Data3, 185 PnpEvent->EventGuid.Data4[0], PnpEvent->EventGuid.Data4[1], PnpEvent->EventGuid.Data4[2], 186 PnpEvent->EventGuid.Data4[3], PnpEvent->EventGuid.Data4[4], PnpEvent->EventGuid.Data4[5], 187 PnpEvent->EventGuid.Data4[6], PnpEvent->EventGuid.Data4[7]); 188 } 189 190 /* Dequeue the current PnP event and signal the next one */ 191 Status = NtPlugPlayControl(PlugPlayControlUserResponse, 192 &ResponseData, 193 sizeof(ResponseData)); 194 if (!NT_SUCCESS(Status)) 195 { 196 DPRINT1("NtPlugPlayControl(PlugPlayControlUserResponse) failed (Status 0x%08lx)\n", Status); 197 break; 198 } 199 } 200 201 HeapFree(GetProcessHeap(), 0, PnpEvent); 202 203 return dwRet; 204 } 205 206 207 static VOID 208 UpdateServiceStatus( 209 _In_ DWORD dwState, 210 _In_ DWORD dwCheckPoint) 211 { 212 ServiceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS; 213 ServiceStatus.dwCurrentState = dwState; 214 ServiceStatus.dwWin32ExitCode = 0; 215 ServiceStatus.dwServiceSpecificExitCode = 0; 216 ServiceStatus.dwCheckPoint = dwCheckPoint; 217 218 if (dwState == SERVICE_RUNNING) 219 ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_SHUTDOWN; 220 else 221 ServiceStatus.dwControlsAccepted = 0; 222 223 if (dwState == SERVICE_START_PENDING || 224 dwState == SERVICE_STOP_PENDING || 225 dwState == SERVICE_PAUSE_PENDING || 226 dwState == SERVICE_CONTINUE_PENDING) 227 ServiceStatus.dwWaitHint = 10000; 228 else 229 ServiceStatus.dwWaitHint = 0; 230 231 SetServiceStatus(ServiceStatusHandle, 232 &ServiceStatus); 233 } 234 235 236 static DWORD WINAPI 237 ServiceControlHandler(DWORD dwControl, 238 DWORD dwEventType, 239 LPVOID lpEventData, 240 LPVOID lpContext) 241 { 242 DPRINT1("ServiceControlHandler() called\n"); 243 244 switch (dwControl) 245 { 246 case SERVICE_CONTROL_STOP: 247 DPRINT1(" SERVICE_CONTROL_STOP received\n"); 248 UpdateServiceStatus(SERVICE_STOP_PENDING, 1); 249 /* Stop listening to RPC Messages */ 250 RpcMgmtStopServerListening(NULL); 251 UpdateServiceStatus(SERVICE_STOPPED, 0); 252 return ERROR_SUCCESS; 253 254 case SERVICE_CONTROL_PAUSE: 255 DPRINT1(" SERVICE_CONTROL_PAUSE received\n"); 256 UpdateServiceStatus(SERVICE_PAUSED, 0); 257 return ERROR_SUCCESS; 258 259 case SERVICE_CONTROL_CONTINUE: 260 DPRINT1(" SERVICE_CONTROL_CONTINUE received\n"); 261 UpdateServiceStatus(SERVICE_RUNNING, 0); 262 return ERROR_SUCCESS; 263 264 case SERVICE_CONTROL_INTERROGATE: 265 DPRINT1(" SERVICE_CONTROL_INTERROGATE received\n"); 266 SetServiceStatus(ServiceStatusHandle, 267 &ServiceStatus); 268 return ERROR_SUCCESS; 269 270 case SERVICE_CONTROL_SHUTDOWN: 271 DPRINT1(" SERVICE_CONTROL_SHUTDOWN received\n"); 272 UpdateServiceStatus(SERVICE_STOP_PENDING, 1); 273 /* Stop listening to RPC Messages */ 274 RpcMgmtStopServerListening(NULL); 275 UpdateServiceStatus(SERVICE_STOPPED, 0); 276 return ERROR_SUCCESS; 277 278 default : 279 DPRINT1(" Control %lu received\n", dwControl); 280 return ERROR_CALL_NOT_IMPLEMENTED; 281 } 282 } 283 284 static DWORD 285 GetBooleanRegValue( 286 IN HKEY hKey, 287 IN PCWSTR lpSubKey, 288 IN PCWSTR lpValue, 289 OUT PBOOL pValue) 290 { 291 DWORD dwError, dwType, dwData; 292 DWORD cbData = sizeof(dwData); 293 HKEY hSubKey = NULL; 294 295 /* Default value */ 296 *pValue = FALSE; 297 298 dwError = RegOpenKeyExW(hKey, 299 lpSubKey, 300 0, 301 KEY_READ, 302 &hSubKey); 303 if (dwError != ERROR_SUCCESS) 304 { 305 DPRINT("GetBooleanRegValue(): RegOpenKeyExW() has failed to open '%S' key! (Error: %lu)\n", 306 lpSubKey, dwError); 307 return dwError; 308 } 309 310 dwError = RegQueryValueExW(hSubKey, 311 lpValue, 312 0, 313 &dwType, 314 (PBYTE)&dwData, 315 &cbData); 316 if (dwError != ERROR_SUCCESS) 317 { 318 DPRINT("GetBooleanRegValue(): RegQueryValueExW() has failed to query '%S' value! (Error: %lu)\n", 319 lpValue, dwError); 320 goto Cleanup; 321 } 322 if (dwType != REG_DWORD) 323 { 324 DPRINT("GetBooleanRegValue(): The value is not of REG_DWORD type!\n"); 325 goto Cleanup; 326 } 327 328 /* Return the value */ 329 *pValue = (dwData == 1); 330 331 Cleanup: 332 RegCloseKey(hSubKey); 333 334 return dwError; 335 } 336 337 BOOL 338 GetSuppressNewUIValue(VOID) 339 { 340 BOOL bSuppressNewHWUI = FALSE; 341 342 /* 343 * Query the SuppressNewHWUI policy registry value. Don't cache it 344 * as we want to update our behaviour in consequence. 345 */ 346 GetBooleanRegValue(HKEY_LOCAL_MACHINE, 347 L"Software\\Policies\\Microsoft\\Windows\\DeviceInstall\\Settings", 348 L"SuppressNewHWUI", 349 &bSuppressNewHWUI); 350 if (bSuppressNewHWUI) 351 DPRINT("GetSuppressNewUIValue(): newdev.dll's wizard UI won't be shown!\n"); 352 353 return bSuppressNewHWUI; 354 } 355 356 VOID WINAPI 357 ServiceMain(DWORD argc, LPTSTR *argv) 358 { 359 HANDLE hThread; 360 DWORD dwThreadId; 361 362 UNREFERENCED_PARAMETER(argc); 363 UNREFERENCED_PARAMETER(argv); 364 365 DPRINT("ServiceMain() called\n"); 366 367 ServiceStatusHandle = RegisterServiceCtrlHandlerExW(ServiceName, 368 ServiceControlHandler, 369 NULL); 370 if (!ServiceStatusHandle) 371 { 372 DPRINT1("RegisterServiceCtrlHandlerExW() failed! (Error %lu)\n", GetLastError()); 373 return; 374 } 375 376 UpdateServiceStatus(SERVICE_START_PENDING, 1); 377 378 hThread = CreateThread(NULL, 379 0, 380 PnpEventThread, 381 NULL, 382 0, 383 &dwThreadId); 384 if (hThread != NULL) 385 CloseHandle(hThread); 386 387 UpdateServiceStatus(SERVICE_START_PENDING, 2); 388 389 hThread = CreateThread(NULL, 390 0, 391 RpcServerThread, 392 NULL, 393 0, 394 &dwThreadId); 395 if (hThread != NULL) 396 CloseHandle(hThread); 397 398 UpdateServiceStatus(SERVICE_START_PENDING, 3); 399 400 hThread = CreateThread(NULL, 401 0, 402 DeviceInstallThread, 403 NULL, 404 0, 405 &dwThreadId); 406 if (hThread != NULL) 407 CloseHandle(hThread); 408 409 UpdateServiceStatus(SERVICE_RUNNING, 0); 410 411 DPRINT("ServiceMain() done\n"); 412 } 413 414 static DWORD 415 InitializePnPManager(VOID) 416 { 417 BOOLEAN OldValue; 418 DWORD dwError; 419 420 DPRINT("UMPNPMGR: InitializePnPManager() started\n"); 421 422 /* We need this privilege for using CreateProcessAsUserW */ 423 RtlAdjustPrivilege(SE_ASSIGNPRIMARYTOKEN_PRIVILEGE, TRUE, FALSE, &OldValue); 424 425 hInstallEvent = CreateEventW(NULL, TRUE, SetupIsActive()/*FALSE*/, NULL); 426 if (hInstallEvent == NULL) 427 { 428 dwError = GetLastError(); 429 DPRINT1("Could not create the Install Event! (Error %lu)\n", dwError); 430 return dwError; 431 } 432 433 hNoPendingInstalls = CreateEventW(NULL, 434 TRUE, 435 FALSE, 436 L"Global\\PnP_No_Pending_Install_Events"); 437 if (hNoPendingInstalls == NULL) 438 { 439 dwError = GetLastError(); 440 DPRINT1("Could not create the Pending-Install Event! (Error %lu)\n", dwError); 441 return dwError; 442 } 443 444 /* 445 * Initialize the device-install event list 446 */ 447 448 hDeviceInstallListNotEmpty = CreateEventW(NULL, FALSE, FALSE, NULL); 449 if (hDeviceInstallListNotEmpty == NULL) 450 { 451 dwError = GetLastError(); 452 DPRINT1("Could not create the List Event! (Error %lu)\n", dwError); 453 return dwError; 454 } 455 456 hDeviceInstallListMutex = CreateMutexW(NULL, FALSE, NULL); 457 if (hDeviceInstallListMutex == NULL) 458 { 459 dwError = GetLastError(); 460 DPRINT1("Could not create the List Mutex! (Error %lu)\n", dwError); 461 return dwError; 462 } 463 InitializeListHead(&DeviceInstallListHead); 464 465 /* Query the SuppressUI registry value and cache it for our whole lifetime */ 466 GetBooleanRegValue(HKEY_LOCAL_MACHINE, 467 L"System\\CurrentControlSet\\Services\\PlugPlay\\Parameters", 468 L"SuppressUI", 469 &g_IsUISuppressed); 470 if (g_IsUISuppressed) 471 DPRINT("UMPNPMGR: newdev.dll's wizard UI won't be shown!\n"); 472 473 dwError = RegOpenKeyExW(HKEY_LOCAL_MACHINE, 474 L"System\\CurrentControlSet\\Enum", 475 0, 476 KEY_ALL_ACCESS, 477 &hEnumKey); 478 if (dwError != ERROR_SUCCESS) 479 { 480 DPRINT1("Could not open the Enum Key! (Error %lu)\n", dwError); 481 return dwError; 482 } 483 484 dwError = RegOpenKeyExW(HKEY_LOCAL_MACHINE, 485 L"System\\CurrentControlSet\\Control\\Class", 486 0, 487 KEY_ALL_ACCESS, 488 &hClassKey); 489 if (dwError != ERROR_SUCCESS) 490 { 491 DPRINT1("Could not open the Class Key! (Error %lu)\n", dwError); 492 return dwError; 493 } 494 495 DPRINT("UMPNPMGR: InitializePnPManager() done\n"); 496 497 return 0; 498 } 499 500 BOOL WINAPI 501 DllMain(HINSTANCE hinstDLL, 502 DWORD fdwReason, 503 LPVOID lpvReserved) 504 { 505 switch (fdwReason) 506 { 507 case DLL_PROCESS_ATTACH: 508 DisableThreadLibraryCalls(hinstDLL); 509 InitializePnPManager(); 510 break; 511 512 case DLL_PROCESS_DETACH: 513 break; 514 } 515 516 return TRUE; 517 } 518 519 /* EOF */ 520