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(DWORD dwState) 209 { 210 ServiceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS; 211 ServiceStatus.dwCurrentState = dwState; 212 ServiceStatus.dwControlsAccepted = 0; 213 ServiceStatus.dwWin32ExitCode = 0; 214 ServiceStatus.dwServiceSpecificExitCode = 0; 215 ServiceStatus.dwCheckPoint = 0; 216 217 if (dwState == SERVICE_START_PENDING || 218 dwState == SERVICE_STOP_PENDING || 219 dwState == SERVICE_PAUSE_PENDING || 220 dwState == SERVICE_CONTINUE_PENDING) 221 ServiceStatus.dwWaitHint = 10000; 222 else 223 ServiceStatus.dwWaitHint = 0; 224 225 SetServiceStatus(ServiceStatusHandle, 226 &ServiceStatus); 227 } 228 229 230 static DWORD WINAPI 231 ServiceControlHandler(DWORD dwControl, 232 DWORD dwEventType, 233 LPVOID lpEventData, 234 LPVOID lpContext) 235 { 236 DPRINT1("ServiceControlHandler() called\n"); 237 238 switch (dwControl) 239 { 240 case SERVICE_CONTROL_STOP: 241 DPRINT1(" SERVICE_CONTROL_STOP received\n"); 242 /* Stop listening to RPC Messages */ 243 RpcMgmtStopServerListening(NULL); 244 UpdateServiceStatus(SERVICE_STOPPED); 245 return ERROR_SUCCESS; 246 247 case SERVICE_CONTROL_PAUSE: 248 DPRINT1(" SERVICE_CONTROL_PAUSE received\n"); 249 UpdateServiceStatus(SERVICE_PAUSED); 250 return ERROR_SUCCESS; 251 252 case SERVICE_CONTROL_CONTINUE: 253 DPRINT1(" SERVICE_CONTROL_CONTINUE received\n"); 254 UpdateServiceStatus(SERVICE_RUNNING); 255 return ERROR_SUCCESS; 256 257 case SERVICE_CONTROL_INTERROGATE: 258 DPRINT1(" SERVICE_CONTROL_INTERROGATE received\n"); 259 SetServiceStatus(ServiceStatusHandle, 260 &ServiceStatus); 261 return ERROR_SUCCESS; 262 263 case SERVICE_CONTROL_SHUTDOWN: 264 DPRINT1(" SERVICE_CONTROL_SHUTDOWN received\n"); 265 /* Stop listening to RPC Messages */ 266 RpcMgmtStopServerListening(NULL); 267 UpdateServiceStatus(SERVICE_STOPPED); 268 return ERROR_SUCCESS; 269 270 default : 271 DPRINT1(" Control %lu received\n", dwControl); 272 return ERROR_CALL_NOT_IMPLEMENTED; 273 } 274 } 275 276 static DWORD 277 GetBooleanRegValue( 278 IN HKEY hKey, 279 IN PCWSTR lpSubKey, 280 IN PCWSTR lpValue, 281 OUT PBOOL pValue) 282 { 283 DWORD dwError, dwType, dwData; 284 DWORD cbData = sizeof(dwData); 285 HKEY hSubKey = NULL; 286 287 /* Default value */ 288 *pValue = FALSE; 289 290 dwError = RegOpenKeyExW(hKey, 291 lpSubKey, 292 0, 293 KEY_READ, 294 &hSubKey); 295 if (dwError != ERROR_SUCCESS) 296 { 297 DPRINT("GetBooleanRegValue(): RegOpenKeyExW() has failed to open '%S' key! (Error: %lu)\n", 298 lpSubKey, dwError); 299 return dwError; 300 } 301 302 dwError = RegQueryValueExW(hSubKey, 303 lpValue, 304 0, 305 &dwType, 306 (PBYTE)&dwData, 307 &cbData); 308 if (dwError != ERROR_SUCCESS) 309 { 310 DPRINT("GetBooleanRegValue(): RegQueryValueExW() has failed to query '%S' value! (Error: %lu)\n", 311 lpValue, dwError); 312 goto Cleanup; 313 } 314 if (dwType != REG_DWORD) 315 { 316 DPRINT("GetBooleanRegValue(): The value is not of REG_DWORD type!\n"); 317 goto Cleanup; 318 } 319 320 /* Return the value */ 321 *pValue = (dwData == 1); 322 323 Cleanup: 324 RegCloseKey(hSubKey); 325 326 return dwError; 327 } 328 329 BOOL 330 GetSuppressNewUIValue(VOID) 331 { 332 BOOL bSuppressNewHWUI = FALSE; 333 334 /* 335 * Query the SuppressNewHWUI policy registry value. Don't cache it 336 * as we want to update our behaviour in consequence. 337 */ 338 GetBooleanRegValue(HKEY_LOCAL_MACHINE, 339 L"Software\\Policies\\Microsoft\\Windows\\DeviceInstall\\Settings", 340 L"SuppressNewHWUI", 341 &bSuppressNewHWUI); 342 if (bSuppressNewHWUI) 343 DPRINT("GetSuppressNewUIValue(): newdev.dll's wizard UI won't be shown!\n"); 344 345 return bSuppressNewHWUI; 346 } 347 348 VOID WINAPI 349 ServiceMain(DWORD argc, LPTSTR *argv) 350 { 351 HANDLE hThread; 352 DWORD dwThreadId; 353 354 UNREFERENCED_PARAMETER(argc); 355 UNREFERENCED_PARAMETER(argv); 356 357 DPRINT("ServiceMain() called\n"); 358 359 ServiceStatusHandle = RegisterServiceCtrlHandlerExW(ServiceName, 360 ServiceControlHandler, 361 NULL); 362 if (!ServiceStatusHandle) 363 { 364 DPRINT1("RegisterServiceCtrlHandlerExW() failed! (Error %lu)\n", GetLastError()); 365 return; 366 } 367 368 UpdateServiceStatus(SERVICE_START_PENDING); 369 370 hThread = CreateThread(NULL, 371 0, 372 PnpEventThread, 373 NULL, 374 0, 375 &dwThreadId); 376 if (hThread != NULL) 377 CloseHandle(hThread); 378 379 hThread = CreateThread(NULL, 380 0, 381 RpcServerThread, 382 NULL, 383 0, 384 &dwThreadId); 385 if (hThread != NULL) 386 CloseHandle(hThread); 387 388 hThread = CreateThread(NULL, 389 0, 390 DeviceInstallThread, 391 NULL, 392 0, 393 &dwThreadId); 394 if (hThread != NULL) 395 CloseHandle(hThread); 396 397 UpdateServiceStatus(SERVICE_RUNNING); 398 399 DPRINT("ServiceMain() done\n"); 400 } 401 402 static DWORD 403 InitializePnPManager(VOID) 404 { 405 BOOLEAN OldValue; 406 DWORD dwError; 407 408 DPRINT("UMPNPMGR: InitializePnPManager() started\n"); 409 410 /* We need this privilege for using CreateProcessAsUserW */ 411 RtlAdjustPrivilege(SE_ASSIGNPRIMARYTOKEN_PRIVILEGE, TRUE, FALSE, &OldValue); 412 413 hInstallEvent = CreateEventW(NULL, TRUE, SetupIsActive()/*FALSE*/, NULL); 414 if (hInstallEvent == NULL) 415 { 416 dwError = GetLastError(); 417 DPRINT1("Could not create the Install Event! (Error %lu)\n", dwError); 418 return dwError; 419 } 420 421 hNoPendingInstalls = CreateEventW(NULL, 422 TRUE, 423 FALSE, 424 L"Global\\PnP_No_Pending_Install_Events"); 425 if (hNoPendingInstalls == NULL) 426 { 427 dwError = GetLastError(); 428 DPRINT1("Could not create the Pending-Install Event! (Error %lu)\n", dwError); 429 return dwError; 430 } 431 432 /* 433 * Initialize the device-install event list 434 */ 435 436 hDeviceInstallListNotEmpty = CreateEventW(NULL, FALSE, FALSE, NULL); 437 if (hDeviceInstallListNotEmpty == NULL) 438 { 439 dwError = GetLastError(); 440 DPRINT1("Could not create the List Event! (Error %lu)\n", dwError); 441 return dwError; 442 } 443 444 hDeviceInstallListMutex = CreateMutexW(NULL, FALSE, NULL); 445 if (hDeviceInstallListMutex == NULL) 446 { 447 dwError = GetLastError(); 448 DPRINT1("Could not create the List Mutex! (Error %lu)\n", dwError); 449 return dwError; 450 } 451 InitializeListHead(&DeviceInstallListHead); 452 453 /* Query the SuppressUI registry value and cache it for our whole lifetime */ 454 GetBooleanRegValue(HKEY_LOCAL_MACHINE, 455 L"System\\CurrentControlSet\\Services\\PlugPlay\\Parameters", 456 L"SuppressUI", 457 &g_IsUISuppressed); 458 if (g_IsUISuppressed) 459 DPRINT("UMPNPMGR: newdev.dll's wizard UI won't be shown!\n"); 460 461 dwError = RegOpenKeyExW(HKEY_LOCAL_MACHINE, 462 L"System\\CurrentControlSet\\Enum", 463 0, 464 KEY_ALL_ACCESS, 465 &hEnumKey); 466 if (dwError != ERROR_SUCCESS) 467 { 468 DPRINT1("Could not open the Enum Key! (Error %lu)\n", dwError); 469 return dwError; 470 } 471 472 dwError = RegOpenKeyExW(HKEY_LOCAL_MACHINE, 473 L"System\\CurrentControlSet\\Control\\Class", 474 0, 475 KEY_ALL_ACCESS, 476 &hClassKey); 477 if (dwError != ERROR_SUCCESS) 478 { 479 DPRINT1("Could not open the Class Key! (Error %lu)\n", dwError); 480 return dwError; 481 } 482 483 DPRINT("UMPNPMGR: InitializePnPManager() done\n"); 484 485 return 0; 486 } 487 488 BOOL WINAPI 489 DllMain(HINSTANCE hinstDLL, 490 DWORD fdwReason, 491 LPVOID lpvReserved) 492 { 493 switch (fdwReason) 494 { 495 case DLL_PROCESS_ATTACH: 496 DisableThreadLibraryCalls(hinstDLL); 497 InitializePnPManager(); 498 break; 499 500 case DLL_PROCESS_DETACH: 501 break; 502 } 503 504 return TRUE; 505 } 506 507 /* EOF */ 508