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 /* Queue device install (will be dequeued by DeviceInstallThread) */ 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 InterlockedPushEntrySList(&DeviceInstallListHead, &Params->ListEntry); 114 SetEvent(hDeviceInstallListNotEmpty); 115 } 116 } 117 } 118 else if (UuidEqual(&PnpEvent->EventGuid, (UUID*)&GUID_DEVICE_ARRIVAL, &RpcStatus)) 119 { 120 // DWORD dwRecipient; 121 122 DPRINT("Device arrival: %S\n", PnpEvent->TargetDevice.DeviceIds); 123 124 // dwRecipient = BSM_ALLDESKTOPS | BSM_APPLICATIONS; 125 // BroadcastSystemMessageW(BSF_POSTMESSAGE, 126 // &dwRecipient, 127 // WM_DEVICECHANGE, 128 // DBT_DEVNODES_CHANGED, 129 // 0); 130 SendMessageW(HWND_BROADCAST, WM_DEVICECHANGE, DBT_DEVNODES_CHANGED, 0); 131 } 132 else if (UuidEqual(&PnpEvent->EventGuid, (UUID*)&GUID_DEVICE_EJECT_VETOED, &RpcStatus)) 133 { 134 DPRINT1("Eject vetoed: %S\n", PnpEvent->TargetDevice.DeviceIds); 135 } 136 else if (UuidEqual(&PnpEvent->EventGuid, (UUID*)&GUID_DEVICE_KERNEL_INITIATED_EJECT, &RpcStatus)) 137 { 138 DPRINT1("Kernel initiated eject: %S\n", PnpEvent->TargetDevice.DeviceIds); 139 } 140 else if (UuidEqual(&PnpEvent->EventGuid, (UUID*)&GUID_DEVICE_SAFE_REMOVAL, &RpcStatus)) 141 { 142 // DWORD dwRecipient; 143 144 DPRINT1("Safe removal: %S\n", PnpEvent->TargetDevice.DeviceIds); 145 146 // dwRecipient = BSM_ALLDESKTOPS | BSM_APPLICATIONS; 147 // BroadcastSystemMessageW(BSF_POSTMESSAGE, 148 // &dwRecipient, 149 // WM_DEVICECHANGE, 150 // DBT_DEVNODES_CHANGED, 151 // 0); 152 SendMessageW(HWND_BROADCAST, WM_DEVICECHANGE, DBT_DEVNODES_CHANGED, 0); 153 } 154 else if (UuidEqual(&PnpEvent->EventGuid, (UUID*)&GUID_DEVICE_SURPRISE_REMOVAL, &RpcStatus)) 155 { 156 // DWORD dwRecipient; 157 158 DPRINT1("Surprise removal: %S\n", PnpEvent->TargetDevice.DeviceIds); 159 160 // dwRecipient = BSM_ALLDESKTOPS | BSM_APPLICATIONS; 161 // BroadcastSystemMessageW(BSF_POSTMESSAGE, 162 // &dwRecipient, 163 // WM_DEVICECHANGE, 164 // DBT_DEVNODES_CHANGED, 165 // 0); 166 SendMessageW(HWND_BROADCAST, WM_DEVICECHANGE, DBT_DEVNODES_CHANGED, 0); 167 } 168 else if (UuidEqual(&PnpEvent->EventGuid, (UUID*)&GUID_DEVICE_REMOVAL_VETOED, &RpcStatus)) 169 { 170 DPRINT1("Removal vetoed: %S\n", PnpEvent->TargetDevice.DeviceIds); 171 } 172 else if (UuidEqual(&PnpEvent->EventGuid, (UUID*)&GUID_DEVICE_REMOVE_PENDING, &RpcStatus)) 173 { 174 DPRINT1("Removal pending: %S\n", PnpEvent->TargetDevice.DeviceIds); 175 } 176 else 177 { 178 DPRINT1("Unknown event, GUID {%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X}\n", 179 PnpEvent->EventGuid.Data1, PnpEvent->EventGuid.Data2, PnpEvent->EventGuid.Data3, 180 PnpEvent->EventGuid.Data4[0], PnpEvent->EventGuid.Data4[1], PnpEvent->EventGuid.Data4[2], 181 PnpEvent->EventGuid.Data4[3], PnpEvent->EventGuid.Data4[4], PnpEvent->EventGuid.Data4[5], 182 PnpEvent->EventGuid.Data4[6], PnpEvent->EventGuid.Data4[7]); 183 } 184 185 /* Dequeue the current PnP event and signal the next one */ 186 Status = NtPlugPlayControl(PlugPlayControlUserResponse, 187 &ResponseData, 188 sizeof(ResponseData)); 189 if (!NT_SUCCESS(Status)) 190 { 191 DPRINT1("NtPlugPlayControl(PlugPlayControlUserResponse) failed (Status 0x%08lx)\n", Status); 192 break; 193 } 194 } 195 196 HeapFree(GetProcessHeap(), 0, PnpEvent); 197 198 return dwRet; 199 } 200 201 202 static VOID 203 UpdateServiceStatus(DWORD dwState) 204 { 205 ServiceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS; 206 ServiceStatus.dwCurrentState = dwState; 207 ServiceStatus.dwControlsAccepted = 0; 208 ServiceStatus.dwWin32ExitCode = 0; 209 ServiceStatus.dwServiceSpecificExitCode = 0; 210 ServiceStatus.dwCheckPoint = 0; 211 212 if (dwState == SERVICE_START_PENDING || 213 dwState == SERVICE_STOP_PENDING || 214 dwState == SERVICE_PAUSE_PENDING || 215 dwState == SERVICE_CONTINUE_PENDING) 216 ServiceStatus.dwWaitHint = 10000; 217 else 218 ServiceStatus.dwWaitHint = 0; 219 220 SetServiceStatus(ServiceStatusHandle, 221 &ServiceStatus); 222 } 223 224 225 static DWORD WINAPI 226 ServiceControlHandler(DWORD dwControl, 227 DWORD dwEventType, 228 LPVOID lpEventData, 229 LPVOID lpContext) 230 { 231 DPRINT1("ServiceControlHandler() called\n"); 232 233 switch (dwControl) 234 { 235 case SERVICE_CONTROL_STOP: 236 DPRINT1(" SERVICE_CONTROL_STOP received\n"); 237 /* Stop listening to RPC Messages */ 238 RpcMgmtStopServerListening(NULL); 239 UpdateServiceStatus(SERVICE_STOPPED); 240 return ERROR_SUCCESS; 241 242 case SERVICE_CONTROL_PAUSE: 243 DPRINT1(" SERVICE_CONTROL_PAUSE received\n"); 244 UpdateServiceStatus(SERVICE_PAUSED); 245 return ERROR_SUCCESS; 246 247 case SERVICE_CONTROL_CONTINUE: 248 DPRINT1(" SERVICE_CONTROL_CONTINUE received\n"); 249 UpdateServiceStatus(SERVICE_RUNNING); 250 return ERROR_SUCCESS; 251 252 case SERVICE_CONTROL_INTERROGATE: 253 DPRINT1(" SERVICE_CONTROL_INTERROGATE received\n"); 254 SetServiceStatus(ServiceStatusHandle, 255 &ServiceStatus); 256 return ERROR_SUCCESS; 257 258 case SERVICE_CONTROL_SHUTDOWN: 259 DPRINT1(" SERVICE_CONTROL_SHUTDOWN received\n"); 260 /* Stop listening to RPC Messages */ 261 RpcMgmtStopServerListening(NULL); 262 UpdateServiceStatus(SERVICE_STOPPED); 263 return ERROR_SUCCESS; 264 265 default : 266 DPRINT1(" Control %lu received\n", dwControl); 267 return ERROR_CALL_NOT_IMPLEMENTED; 268 } 269 } 270 271 static DWORD 272 GetBooleanRegValue( 273 IN HKEY hKey, 274 IN PCWSTR lpSubKey, 275 IN PCWSTR lpValue, 276 OUT PBOOL pValue) 277 { 278 DWORD dwError, dwType, dwData; 279 DWORD cbData = sizeof(dwData); 280 HKEY hSubKey = NULL; 281 282 /* Default value */ 283 *pValue = FALSE; 284 285 dwError = RegOpenKeyExW(hKey, 286 lpSubKey, 287 0, 288 KEY_READ, 289 &hSubKey); 290 if (dwError != ERROR_SUCCESS) 291 { 292 DPRINT("GetBooleanRegValue(): RegOpenKeyExW() has failed to open '%S' key! (Error: %lu)\n", 293 lpSubKey, dwError); 294 return dwError; 295 } 296 297 dwError = RegQueryValueExW(hSubKey, 298 lpValue, 299 0, 300 &dwType, 301 (PBYTE)&dwData, 302 &cbData); 303 if (dwError != ERROR_SUCCESS) 304 { 305 DPRINT("GetBooleanRegValue(): RegQueryValueExW() has failed to query '%S' value! (Error: %lu)\n", 306 lpValue, dwError); 307 goto Cleanup; 308 } 309 if (dwType != REG_DWORD) 310 { 311 DPRINT("GetBooleanRegValue(): The value is not of REG_DWORD type!\n"); 312 goto Cleanup; 313 } 314 315 /* Return the value */ 316 *pValue = (dwData == 1); 317 318 Cleanup: 319 RegCloseKey(hSubKey); 320 321 return dwError; 322 } 323 324 BOOL 325 GetSuppressNewUIValue(VOID) 326 { 327 BOOL bSuppressNewHWUI = FALSE; 328 329 /* 330 * Query the SuppressNewHWUI policy registry value. Don't cache it 331 * as we want to update our behaviour in consequence. 332 */ 333 GetBooleanRegValue(HKEY_LOCAL_MACHINE, 334 L"Software\\Policies\\Microsoft\\Windows\\DeviceInstall\\Settings", 335 L"SuppressNewHWUI", 336 &bSuppressNewHWUI); 337 if (bSuppressNewHWUI) 338 DPRINT("GetSuppressNewUIValue(): newdev.dll's wizard UI won't be shown!\n"); 339 340 return bSuppressNewHWUI; 341 } 342 343 VOID WINAPI 344 ServiceMain(DWORD argc, LPTSTR *argv) 345 { 346 HANDLE hThread; 347 DWORD dwThreadId; 348 349 UNREFERENCED_PARAMETER(argc); 350 UNREFERENCED_PARAMETER(argv); 351 352 DPRINT("ServiceMain() called\n"); 353 354 ServiceStatusHandle = RegisterServiceCtrlHandlerExW(ServiceName, 355 ServiceControlHandler, 356 NULL); 357 if (!ServiceStatusHandle) 358 { 359 DPRINT1("RegisterServiceCtrlHandlerExW() failed! (Error %lu)\n", GetLastError()); 360 return; 361 } 362 363 UpdateServiceStatus(SERVICE_START_PENDING); 364 365 hThread = CreateThread(NULL, 366 0, 367 PnpEventThread, 368 NULL, 369 0, 370 &dwThreadId); 371 if (hThread != NULL) 372 CloseHandle(hThread); 373 374 hThread = CreateThread(NULL, 375 0, 376 RpcServerThread, 377 NULL, 378 0, 379 &dwThreadId); 380 if (hThread != NULL) 381 CloseHandle(hThread); 382 383 hThread = CreateThread(NULL, 384 0, 385 DeviceInstallThread, 386 NULL, 387 0, 388 &dwThreadId); 389 if (hThread != NULL) 390 CloseHandle(hThread); 391 392 UpdateServiceStatus(SERVICE_RUNNING); 393 394 DPRINT("ServiceMain() done\n"); 395 } 396 397 static DWORD 398 InitializePnPManager(VOID) 399 { 400 BOOLEAN OldValue; 401 DWORD dwError; 402 403 DPRINT("UMPNPMGR: InitializePnPManager() started\n"); 404 405 /* We need this privilege for using CreateProcessAsUserW */ 406 RtlAdjustPrivilege(SE_ASSIGNPRIMARYTOKEN_PRIVILEGE, TRUE, FALSE, &OldValue); 407 408 hInstallEvent = CreateEventW(NULL, TRUE, SetupIsActive()/*FALSE*/, NULL); 409 if (hInstallEvent == NULL) 410 { 411 dwError = GetLastError(); 412 DPRINT1("Could not create the Install Event! (Error %lu)\n", dwError); 413 return dwError; 414 } 415 416 hDeviceInstallListNotEmpty = CreateEventW(NULL, FALSE, FALSE, NULL); 417 if (hDeviceInstallListNotEmpty == NULL) 418 { 419 dwError = GetLastError(); 420 DPRINT1("Could not create the Event! (Error %lu)\n", dwError); 421 return dwError; 422 } 423 424 hNoPendingInstalls = CreateEventW(NULL, 425 TRUE, 426 FALSE, 427 L"Global\\PnP_No_Pending_Install_Events"); 428 if (hNoPendingInstalls == NULL) 429 { 430 dwError = GetLastError(); 431 DPRINT1("Could not create the Event! (Error %lu)\n", dwError); 432 return dwError; 433 } 434 435 InitializeSListHead(&DeviceInstallListHead); 436 437 /* Query the SuppressUI registry value and cache it for our whole lifetime */ 438 GetBooleanRegValue(HKEY_LOCAL_MACHINE, 439 L"System\\CurrentControlSet\\Services\\PlugPlay\\Parameters", 440 L"SuppressUI", 441 &g_IsUISuppressed); 442 if (g_IsUISuppressed) 443 DPRINT("UMPNPMGR: newdev.dll's wizard UI won't be shown!\n"); 444 445 dwError = RegOpenKeyExW(HKEY_LOCAL_MACHINE, 446 L"System\\CurrentControlSet\\Enum", 447 0, 448 KEY_ALL_ACCESS, 449 &hEnumKey); 450 if (dwError != ERROR_SUCCESS) 451 { 452 DPRINT1("Could not open the Enum Key! (Error %lu)\n", dwError); 453 return dwError; 454 } 455 456 dwError = RegOpenKeyExW(HKEY_LOCAL_MACHINE, 457 L"System\\CurrentControlSet\\Control\\Class", 458 0, 459 KEY_ALL_ACCESS, 460 &hClassKey); 461 if (dwError != ERROR_SUCCESS) 462 { 463 DPRINT1("Could not open the Class Key! (Error %lu)\n", dwError); 464 return dwError; 465 } 466 467 DPRINT("UMPNPMGR: InitializePnPManager() done\n"); 468 469 return 0; 470 } 471 472 BOOL WINAPI 473 DllMain(HINSTANCE hinstDLL, 474 DWORD fdwReason, 475 LPVOID lpvReserved) 476 { 477 switch (fdwReason) 478 { 479 case DLL_PROCESS_ATTACH: 480 DisableThreadLibraryCalls(hinstDLL); 481 InitializePnPManager(); 482 break; 483 484 case DLL_PROCESS_DETACH: 485 break; 486 } 487 488 return TRUE; 489 } 490 491 /* EOF */ 492