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/install.c 23 * PURPOSE: Device installer 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 HANDLE hUserToken = NULL; 40 HANDLE hInstallEvent = NULL; 41 HANDLE hNoPendingInstalls = NULL; 42 43 SLIST_HEADER DeviceInstallListHead; 44 HANDLE hDeviceInstallListNotEmpty; 45 46 47 /* FUNCTIONS *****************************************************************/ 48 49 static BOOL 50 InstallDevice(PCWSTR DeviceInstance, BOOL ShowWizard) 51 { 52 BOOL DeviceInstalled = FALSE; 53 DWORD BytesWritten; 54 DWORD Value; 55 HANDLE hInstallEvent; 56 HANDLE hPipe = INVALID_HANDLE_VALUE; 57 LPVOID Environment = NULL; 58 PROCESS_INFORMATION ProcessInfo; 59 STARTUPINFOW StartupInfo; 60 UUID RandomUuid; 61 HKEY DeviceKey; 62 63 /* The following lengths are constant (see below), they cannot overflow */ 64 WCHAR CommandLine[116]; 65 WCHAR InstallEventName[73]; 66 WCHAR PipeName[74]; 67 WCHAR UuidString[39]; 68 69 DPRINT("InstallDevice(%S, %d)\n", DeviceInstance, ShowWizard); 70 71 ZeroMemory(&ProcessInfo, sizeof(ProcessInfo)); 72 73 if (RegOpenKeyExW(hEnumKey, 74 DeviceInstance, 75 0, 76 KEY_QUERY_VALUE, 77 &DeviceKey) == ERROR_SUCCESS) 78 { 79 if (RegQueryValueExW(DeviceKey, 80 L"Class", 81 NULL, 82 NULL, 83 NULL, 84 NULL) == ERROR_SUCCESS) 85 { 86 DPRINT("No need to install: %S\n", DeviceInstance); 87 RegCloseKey(DeviceKey); 88 return TRUE; 89 } 90 91 BytesWritten = sizeof(DWORD); 92 if (RegQueryValueExW(DeviceKey, 93 L"ConfigFlags", 94 NULL, 95 NULL, 96 (PBYTE)&Value, 97 &BytesWritten) == ERROR_SUCCESS) 98 { 99 if (Value & CONFIGFLAG_FAILEDINSTALL) 100 { 101 DPRINT("No need to install: %S\n", DeviceInstance); 102 RegCloseKey(DeviceKey); 103 return TRUE; 104 } 105 } 106 107 RegCloseKey(DeviceKey); 108 } 109 110 DPRINT1("Installing: %S\n", DeviceInstance); 111 112 /* Create a random UUID for the named pipe & event*/ 113 UuidCreate(&RandomUuid); 114 swprintf(UuidString, L"{%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X}", 115 RandomUuid.Data1, RandomUuid.Data2, RandomUuid.Data3, 116 RandomUuid.Data4[0], RandomUuid.Data4[1], RandomUuid.Data4[2], 117 RandomUuid.Data4[3], RandomUuid.Data4[4], RandomUuid.Data4[5], 118 RandomUuid.Data4[6], RandomUuid.Data4[7]); 119 120 /* Create the event */ 121 wcscpy(InstallEventName, L"Global\\PNP_Device_Install_Event_0."); 122 wcscat(InstallEventName, UuidString); 123 hInstallEvent = CreateEventW(NULL, TRUE, FALSE, InstallEventName); 124 if (!hInstallEvent) 125 { 126 DPRINT1("CreateEventW('%ls') failed with error %lu\n", InstallEventName, GetLastError()); 127 goto cleanup; 128 } 129 130 /* Create the named pipe */ 131 wcscpy(PipeName, L"\\\\.\\pipe\\PNP_Device_Install_Pipe_0."); 132 wcscat(PipeName, UuidString); 133 hPipe = CreateNamedPipeW(PipeName, PIPE_ACCESS_OUTBOUND, PIPE_TYPE_BYTE, 1, 512, 512, 0, NULL); 134 if (hPipe == INVALID_HANDLE_VALUE) 135 { 136 DPRINT1("CreateNamedPipeW failed with error %u\n", GetLastError()); 137 goto cleanup; 138 } 139 140 /* Launch rundll32 to call ClientSideInstallW */ 141 wcscpy(CommandLine, L"rundll32.exe newdev.dll,ClientSideInstall "); 142 wcscat(CommandLine, PipeName); 143 144 ZeroMemory(&StartupInfo, sizeof(StartupInfo)); 145 StartupInfo.cb = sizeof(StartupInfo); 146 147 if (hUserToken) 148 { 149 /* newdev has to run under the environment of the current user */ 150 if (!CreateEnvironmentBlock(&Environment, hUserToken, FALSE)) 151 { 152 DPRINT1("CreateEnvironmentBlock failed with error %d\n", GetLastError()); 153 goto cleanup; 154 } 155 156 if (!CreateProcessAsUserW(hUserToken, NULL, CommandLine, NULL, NULL, FALSE, CREATE_UNICODE_ENVIRONMENT, Environment, NULL, &StartupInfo, &ProcessInfo)) 157 { 158 DPRINT1("CreateProcessAsUserW failed with error %u\n", GetLastError()); 159 goto cleanup; 160 } 161 } 162 else 163 { 164 /* FIXME: This is probably not correct, I guess newdev should never be run with SYSTEM privileges. 165 166 Still, we currently do that in 2nd stage setup and probably Console mode as well, so allow it here. 167 (ShowWizard is only set to FALSE for these two modes) */ 168 ASSERT(!ShowWizard); 169 170 if (!CreateProcessW(NULL, CommandLine, NULL, NULL, FALSE, 0, NULL, NULL, &StartupInfo, &ProcessInfo)) 171 { 172 DPRINT1("CreateProcessW failed with error %u\n", GetLastError()); 173 goto cleanup; 174 } 175 } 176 177 /* Wait for the function to connect to our pipe */ 178 if (!ConnectNamedPipe(hPipe, NULL)) 179 { 180 if (GetLastError() != ERROR_PIPE_CONNECTED) 181 { 182 DPRINT1("ConnectNamedPipe failed with error %u\n", GetLastError()); 183 goto cleanup; 184 } 185 } 186 187 /* Pass the data. The following output is partly compatible to Windows XP SP2 (researched using a modified newdev.dll to log this stuff) */ 188 Value = sizeof(InstallEventName); 189 WriteFile(hPipe, &Value, sizeof(Value), &BytesWritten, NULL); 190 WriteFile(hPipe, InstallEventName, Value, &BytesWritten, NULL); 191 192 /* I couldn't figure out what the following value means under WinXP. It's usually 0 in my tests, but was also 5 once. 193 Therefore the following line is entirely ReactOS-specific. We use the value here to pass the ShowWizard variable. */ 194 WriteFile(hPipe, &ShowWizard, sizeof(ShowWizard), &BytesWritten, NULL); 195 196 Value = (wcslen(DeviceInstance) + 1) * sizeof(WCHAR); 197 WriteFile(hPipe, &Value, sizeof(Value), &BytesWritten, NULL); 198 WriteFile(hPipe, DeviceInstance, Value, &BytesWritten, NULL); 199 200 /* Wait for newdev.dll to finish processing */ 201 WaitForSingleObject(ProcessInfo.hProcess, INFINITE); 202 203 /* If the event got signalled, this is success */ 204 DeviceInstalled = WaitForSingleObject(hInstallEvent, 0) == WAIT_OBJECT_0; 205 206 cleanup: 207 if (hInstallEvent) 208 CloseHandle(hInstallEvent); 209 210 if (hPipe != INVALID_HANDLE_VALUE) 211 CloseHandle(hPipe); 212 213 if (Environment) 214 DestroyEnvironmentBlock(Environment); 215 216 if (ProcessInfo.hProcess) 217 CloseHandle(ProcessInfo.hProcess); 218 219 if (ProcessInfo.hThread) 220 CloseHandle(ProcessInfo.hThread); 221 222 if (!DeviceInstalled) 223 { 224 DPRINT1("InstallDevice failed for DeviceInstance '%ws'\n", DeviceInstance); 225 } 226 227 return DeviceInstalled; 228 } 229 230 231 static LONG 232 ReadRegSzKey( 233 IN HKEY hKey, 234 IN LPCWSTR pszKey, 235 OUT LPWSTR* pValue) 236 { 237 LONG rc; 238 DWORD dwType; 239 DWORD cbData = 0; 240 LPWSTR Value; 241 242 if (!pValue) 243 return ERROR_INVALID_PARAMETER; 244 245 *pValue = NULL; 246 rc = RegQueryValueExW(hKey, pszKey, NULL, &dwType, NULL, &cbData); 247 if (rc != ERROR_SUCCESS) 248 return rc; 249 if (dwType != REG_SZ) 250 return ERROR_FILE_NOT_FOUND; 251 Value = HeapAlloc(GetProcessHeap(), 0, cbData + sizeof(WCHAR)); 252 if (!Value) 253 return ERROR_NOT_ENOUGH_MEMORY; 254 rc = RegQueryValueExW(hKey, pszKey, NULL, NULL, (LPBYTE)Value, &cbData); 255 if (rc != ERROR_SUCCESS) 256 { 257 HeapFree(GetProcessHeap(), 0, Value); 258 return rc; 259 } 260 /* NULL-terminate the string */ 261 Value[cbData / sizeof(WCHAR)] = '\0'; 262 263 *pValue = Value; 264 return ERROR_SUCCESS; 265 } 266 267 268 BOOL 269 SetupIsActive(VOID) 270 { 271 HKEY hKey = NULL; 272 DWORD regType, active, size; 273 LONG rc; 274 BOOL ret = FALSE; 275 276 rc = RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"SYSTEM\\Setup", 0, KEY_QUERY_VALUE, &hKey); 277 if (rc != ERROR_SUCCESS) 278 goto cleanup; 279 280 size = sizeof(DWORD); 281 rc = RegQueryValueExW(hKey, L"SystemSetupInProgress", NULL, ®Type, (LPBYTE)&active, &size); 282 if (rc != ERROR_SUCCESS) 283 goto cleanup; 284 if (regType != REG_DWORD || size != sizeof(DWORD)) 285 goto cleanup; 286 287 ret = (active != 0); 288 289 cleanup: 290 if (hKey != NULL) 291 RegCloseKey(hKey); 292 293 DPRINT("System setup in progress? %S\n", ret ? L"YES" : L"NO"); 294 295 return ret; 296 } 297 298 299 static BOOL 300 IsConsoleBoot(VOID) 301 { 302 HKEY ControlKey = NULL; 303 LPWSTR SystemStartOptions = NULL; 304 LPWSTR CurrentOption, NextOption; /* Pointers into SystemStartOptions */ 305 BOOL ConsoleBoot = FALSE; 306 LONG rc; 307 308 rc = RegOpenKeyExW( 309 HKEY_LOCAL_MACHINE, 310 L"SYSTEM\\CurrentControlSet\\Control", 311 0, 312 KEY_QUERY_VALUE, 313 &ControlKey); 314 315 rc = ReadRegSzKey(ControlKey, L"SystemStartOptions", &SystemStartOptions); 316 if (rc != ERROR_SUCCESS) 317 goto cleanup; 318 319 /* Check for CONSOLE switch in SystemStartOptions */ 320 CurrentOption = SystemStartOptions; 321 while (CurrentOption) 322 { 323 NextOption = wcschr(CurrentOption, L' '); 324 if (NextOption) 325 *NextOption = L'\0'; 326 if (_wcsicmp(CurrentOption, L"CONSOLE") == 0) 327 { 328 DPRINT("Found %S. Switching to console boot\n", CurrentOption); 329 ConsoleBoot = TRUE; 330 goto cleanup; 331 } 332 CurrentOption = NextOption ? NextOption + 1 : NULL; 333 } 334 335 cleanup: 336 if (ControlKey != NULL) 337 RegCloseKey(ControlKey); 338 HeapFree(GetProcessHeap(), 0, SystemStartOptions); 339 return ConsoleBoot; 340 } 341 342 343 FORCEINLINE 344 BOOL 345 IsUISuppressionAllowed(VOID) 346 { 347 /* Display the newdev.dll wizard UI only if it's allowed */ 348 return (g_IsUISuppressed || GetSuppressNewUIValue()); 349 } 350 351 352 /* Loop to install all queued devices installations */ 353 DWORD 354 WINAPI 355 DeviceInstallThread(LPVOID lpParameter) 356 { 357 PSLIST_ENTRY ListEntry; 358 DeviceInstallParams* Params; 359 BOOL showWizard; 360 361 UNREFERENCED_PARAMETER(lpParameter); 362 363 WaitForSingleObject(hInstallEvent, INFINITE); 364 365 showWizard = !SetupIsActive() && !IsConsoleBoot(); 366 367 while (TRUE) 368 { 369 ListEntry = InterlockedPopEntrySList(&DeviceInstallListHead); 370 371 if (ListEntry == NULL) 372 { 373 SetEvent(hNoPendingInstalls); 374 WaitForSingleObject(hDeviceInstallListNotEmpty, INFINITE); 375 } 376 else 377 { 378 ResetEvent(hNoPendingInstalls); 379 Params = CONTAINING_RECORD(ListEntry, DeviceInstallParams, ListEntry); 380 InstallDevice(Params->DeviceIds, showWizard && !IsUISuppressionAllowed()); 381 HeapFree(GetProcessHeap(), 0, Params); 382 } 383 } 384 385 return 0; 386 } 387