diff options
| author | Sean Hall <r.sean.hall@gmail.com> | 2018-12-29 22:12:08 -0600 |
|---|---|---|
| committer | Sean Hall <r.sean.hall@gmail.com> | 2018-12-29 22:12:08 -0600 |
| commit | 61847dddd4fd497057c780658e383c4627de19ec (patch) | |
| tree | f85a845182922538ab9aa6ee85b0db3ab40c1f6e /src/engine/engine.cpp | |
| parent | 8295f5f8fd28042e1a0a172d5afbba79178064c2 (diff) | |
| download | wix-61847dddd4fd497057c780658e383c4627de19ec.tar.gz wix-61847dddd4fd497057c780658e383c4627de19ec.tar.bz2 wix-61847dddd4fd497057c780658e383c4627de19ec.zip | |
Import code from old v4 repo
Diffstat (limited to 'src/engine/engine.cpp')
| -rw-r--r-- | src/engine/engine.cpp | 889 |
1 files changed, 889 insertions, 0 deletions
diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp new file mode 100644 index 00000000..3c0f09c9 --- /dev/null +++ b/src/engine/engine.cpp | |||
| @@ -0,0 +1,889 @@ | |||
| 1 | // Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. | ||
| 2 | |||
| 3 | #include "precomp.h" | ||
| 4 | |||
| 5 | |||
| 6 | // constants | ||
| 7 | |||
| 8 | const DWORD RESTART_RETRIES = 10; | ||
| 9 | |||
| 10 | // internal function declarations | ||
| 11 | |||
| 12 | static HRESULT InitializeEngineState( | ||
| 13 | __in BURN_ENGINE_STATE* pEngineState, | ||
| 14 | __in HANDLE hEngineFile | ||
| 15 | ); | ||
| 16 | static void UninitializeEngineState( | ||
| 17 | __in BURN_ENGINE_STATE* pEngineState | ||
| 18 | ); | ||
| 19 | static HRESULT RunUntrusted( | ||
| 20 | __in LPCWSTR wzCommandLine, | ||
| 21 | __in BURN_ENGINE_STATE* pEngineState | ||
| 22 | ); | ||
| 23 | static HRESULT RunNormal( | ||
| 24 | __in HINSTANCE hInstance, | ||
| 25 | __in BURN_ENGINE_STATE* pEngineState | ||
| 26 | ); | ||
| 27 | static HRESULT RunElevated( | ||
| 28 | __in HINSTANCE hInstance, | ||
| 29 | __in LPCWSTR wzCommandLine, | ||
| 30 | __in BURN_ENGINE_STATE* pEngineState | ||
| 31 | ); | ||
| 32 | static HRESULT RunEmbedded( | ||
| 33 | __in HINSTANCE hInstance, | ||
| 34 | __in BURN_ENGINE_STATE* pEngineState | ||
| 35 | ); | ||
| 36 | static HRESULT RunRunOnce( | ||
| 37 | __in const BURN_REGISTRATION* pRegistration, | ||
| 38 | __in int nCmdShow | ||
| 39 | ); | ||
| 40 | static HRESULT RunApplication( | ||
| 41 | __in BURN_ENGINE_STATE* pEngineState, | ||
| 42 | __out BOOL* pfReloadApp | ||
| 43 | ); | ||
| 44 | static HRESULT ProcessMessage( | ||
| 45 | __in BURN_ENGINE_STATE* pEngineState, | ||
| 46 | __in const MSG* pmsg | ||
| 47 | ); | ||
| 48 | static HRESULT DAPI RedirectLoggingOverPipe( | ||
| 49 | __in_z LPCSTR szString, | ||
| 50 | __in_opt LPVOID pvContext | ||
| 51 | ); | ||
| 52 | static HRESULT Restart(); | ||
| 53 | |||
| 54 | |||
| 55 | // function definitions | ||
| 56 | |||
| 57 | extern "C" BOOL EngineInCleanRoom( | ||
| 58 | __in_z_opt LPCWSTR wzCommandLine | ||
| 59 | ) | ||
| 60 | { | ||
| 61 | // Be very careful with the functions you call from here. | ||
| 62 | // This function will be called before ::SetDefaultDllDirectories() | ||
| 63 | // has been called so dependencies outside of kernel32.dll are | ||
| 64 | // very likely to introduce DLL hijacking opportunities. | ||
| 65 | |||
| 66 | static DWORD cchCleanRoomSwitch = lstrlenW(BURN_COMMANDLINE_SWITCH_CLEAN_ROOM); | ||
| 67 | |||
| 68 | // This check is wholly dependent on the clean room command line switch being | ||
| 69 | // present at the beginning of the command line. Since Burn is the only thing | ||
| 70 | // that should be setting this command line option, that is in our control. | ||
| 71 | BOOL fInCleanRoom = (wzCommandLine && | ||
| 72 | (wzCommandLine[0] == L'-' || wzCommandLine[0] == L'/') && | ||
| 73 | CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, wzCommandLine + 1, cchCleanRoomSwitch, BURN_COMMANDLINE_SWITCH_CLEAN_ROOM, cchCleanRoomSwitch) && | ||
| 74 | wzCommandLine[1 + cchCleanRoomSwitch] == L'=' | ||
| 75 | ); | ||
| 76 | |||
| 77 | return fInCleanRoom; | ||
| 78 | } | ||
| 79 | |||
| 80 | extern "C" HRESULT EngineRun( | ||
| 81 | __in HINSTANCE hInstance, | ||
| 82 | __in HANDLE hEngineFile, | ||
| 83 | __in_z_opt LPCWSTR wzCommandLine, | ||
| 84 | __in int nCmdShow, | ||
| 85 | __out DWORD* pdwExitCode | ||
| 86 | ) | ||
| 87 | { | ||
| 88 | HRESULT hr = S_OK; | ||
| 89 | BOOL fComInitialized = FALSE; | ||
| 90 | BOOL fLogInitialized = FALSE; | ||
| 91 | BOOL fCrypInitialized = FALSE; | ||
| 92 | BOOL fRegInitialized = FALSE; | ||
| 93 | BOOL fWiuInitialized = FALSE; | ||
| 94 | BOOL fXmlInitialized = FALSE; | ||
| 95 | OSVERSIONINFOEXW ovix = { }; | ||
| 96 | LPWSTR sczExePath = NULL; | ||
| 97 | BOOL fRunNormal = FALSE; | ||
| 98 | BOOL fRestart = FALSE; | ||
| 99 | |||
| 100 | BURN_ENGINE_STATE engineState = { }; | ||
| 101 | |||
| 102 | // Always initialize logging first | ||
| 103 | LogInitialize(::GetModuleHandleW(NULL)); | ||
| 104 | fLogInitialized = TRUE; | ||
| 105 | |||
| 106 | // Ensure that log contains approriate level of information | ||
| 107 | #ifdef _DEBUG | ||
| 108 | LogSetLevel(REPORT_DEBUG, FALSE); | ||
| 109 | #else | ||
| 110 | LogSetLevel(REPORT_VERBOSE, FALSE); // FALSE means don't write an additional text line to the log saying the level changed | ||
| 111 | #endif | ||
| 112 | |||
| 113 | hr = AppParseCommandLine(wzCommandLine, &engineState.argc, &engineState.argv); | ||
| 114 | ExitOnFailure(hr, "Failed to parse command line."); | ||
| 115 | |||
| 116 | hr = InitializeEngineState(&engineState, hEngineFile); | ||
| 117 | ExitOnFailure(hr, "Failed to initialize engine state."); | ||
| 118 | |||
| 119 | engineState.command.nCmdShow = nCmdShow; | ||
| 120 | |||
| 121 | // initialize platform layer | ||
| 122 | PlatformInitialize(); | ||
| 123 | |||
| 124 | // initialize COM | ||
| 125 | hr = ::CoInitializeEx(NULL, COINIT_MULTITHREADED); | ||
| 126 | ExitOnFailure(hr, "Failed to initialize COM."); | ||
| 127 | fComInitialized = TRUE; | ||
| 128 | |||
| 129 | // Initialize dutil. | ||
| 130 | hr = CrypInitialize(); | ||
| 131 | ExitOnFailure(hr, "Failed to initialize Cryputil."); | ||
| 132 | fCrypInitialized = TRUE; | ||
| 133 | |||
| 134 | hr = RegInitialize(); | ||
| 135 | ExitOnFailure(hr, "Failed to initialize Regutil."); | ||
| 136 | fRegInitialized = TRUE; | ||
| 137 | |||
| 138 | hr = WiuInitialize(); | ||
| 139 | ExitOnFailure(hr, "Failed to initialize Wiutil."); | ||
| 140 | fWiuInitialized = TRUE; | ||
| 141 | |||
| 142 | hr = XmlInitialize(); | ||
| 143 | ExitOnFailure(hr, "Failed to initialize XML util."); | ||
| 144 | fXmlInitialized = TRUE; | ||
| 145 | |||
| 146 | ovix.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEXW); | ||
| 147 | if (!::GetVersionExW((LPOSVERSIONINFOW)&ovix)) | ||
| 148 | { | ||
| 149 | ExitWithLastError(hr, "Failed to get OS info."); | ||
| 150 | } | ||
| 151 | |||
| 152 | PathForCurrentProcess(&sczExePath, NULL); // Ignore failure. | ||
| 153 | LogId(REPORT_STANDARD, MSG_BURN_INFO, szVerMajorMinorBuild, ovix.dwMajorVersion, ovix.dwMinorVersion, ovix.dwBuildNumber, ovix.wServicePackMajor, sczExePath); | ||
| 154 | ReleaseNullStr(sczExePath); | ||
| 155 | |||
| 156 | // initialize core | ||
| 157 | hr = CoreInitialize(&engineState); | ||
| 158 | ExitOnFailure(hr, "Failed to initialize core."); | ||
| 159 | |||
| 160 | // Select run mode. | ||
| 161 | switch (engineState.mode) | ||
| 162 | { | ||
| 163 | case BURN_MODE_UNTRUSTED: | ||
| 164 | hr = RunUntrusted(wzCommandLine, &engineState); | ||
| 165 | ExitOnFailure(hr, "Failed to run untrusted mode."); | ||
| 166 | break; | ||
| 167 | |||
| 168 | case BURN_MODE_NORMAL: | ||
| 169 | fRunNormal = TRUE; | ||
| 170 | |||
| 171 | hr = RunNormal(hInstance, &engineState); | ||
| 172 | ExitOnFailure(hr, "Failed to run per-user mode."); | ||
| 173 | break; | ||
| 174 | |||
| 175 | case BURN_MODE_ELEVATED: | ||
| 176 | hr = RunElevated(hInstance, wzCommandLine, &engineState); | ||
| 177 | ExitOnFailure(hr, "Failed to run per-machine mode."); | ||
| 178 | break; | ||
| 179 | |||
| 180 | case BURN_MODE_EMBEDDED: | ||
| 181 | fRunNormal = TRUE; | ||
| 182 | |||
| 183 | hr = RunEmbedded(hInstance, &engineState); | ||
| 184 | ExitOnFailure(hr, "Failed to run embedded mode."); | ||
| 185 | break; | ||
| 186 | |||
| 187 | case BURN_MODE_RUNONCE: | ||
| 188 | hr = RunRunOnce(&engineState.registration, nCmdShow); | ||
| 189 | ExitOnFailure(hr, "Failed to run RunOnce mode."); | ||
| 190 | break; | ||
| 191 | |||
| 192 | default: | ||
| 193 | hr = E_UNEXPECTED; | ||
| 194 | ExitOnFailure(hr, "Invalid run mode."); | ||
| 195 | } | ||
| 196 | |||
| 197 | // set exit code and remember if we are supposed to restart. | ||
| 198 | *pdwExitCode = engineState.userExperience.dwExitCode; | ||
| 199 | fRestart = engineState.fRestart; | ||
| 200 | |||
| 201 | LExit: | ||
| 202 | ReleaseStr(sczExePath); | ||
| 203 | |||
| 204 | // If anything went wrong but the log was never open, try to open a "failure" log | ||
| 205 | // and that will dump anything captured in the log memory buffer to the log. | ||
| 206 | if (FAILED(hr) && BURN_LOGGING_STATE_CLOSED == engineState.log.state) | ||
| 207 | { | ||
| 208 | LoggingOpenFailed(); | ||
| 209 | } | ||
| 210 | |||
| 211 | UserExperienceRemove(&engineState.userExperience); | ||
| 212 | |||
| 213 | CacheRemoveWorkingFolder(engineState.registration.sczId); | ||
| 214 | CacheUninitialize(); | ||
| 215 | |||
| 216 | // If this is a related bundle (but not an update) suppress restart and return the standard restart error code. | ||
| 217 | if (fRestart && BOOTSTRAPPER_RELATION_NONE != engineState.command.relationType && BOOTSTRAPPER_RELATION_UPDATE != engineState.command.relationType) | ||
| 218 | { | ||
| 219 | LogId(REPORT_STANDARD, MSG_RESTART_ABORTED, LoggingRelationTypeToString(engineState.command.relationType)); | ||
| 220 | |||
| 221 | fRestart = FALSE; | ||
| 222 | hr = HRESULT_FROM_WIN32(ERROR_SUCCESS_REBOOT_REQUIRED); | ||
| 223 | } | ||
| 224 | |||
| 225 | UninitializeEngineState(&engineState); | ||
| 226 | |||
| 227 | if (fXmlInitialized) | ||
| 228 | { | ||
| 229 | XmlUninitialize(); | ||
| 230 | } | ||
| 231 | |||
| 232 | if (fWiuInitialized) | ||
| 233 | { | ||
| 234 | WiuUninitialize(); | ||
| 235 | } | ||
| 236 | |||
| 237 | if (fRegInitialized) | ||
| 238 | { | ||
| 239 | RegUninitialize(); | ||
| 240 | } | ||
| 241 | |||
| 242 | if (fCrypInitialized) | ||
| 243 | { | ||
| 244 | CrypUninitialize(); | ||
| 245 | } | ||
| 246 | |||
| 247 | if (fComInitialized) | ||
| 248 | { | ||
| 249 | ::CoUninitialize(); | ||
| 250 | } | ||
| 251 | |||
| 252 | if (fRunNormal) | ||
| 253 | { | ||
| 254 | LogId(REPORT_STANDARD, MSG_EXITING, FAILED(hr) ? (int)hr : *pdwExitCode, LoggingBoolToString(fRestart)); | ||
| 255 | |||
| 256 | if (fRestart) | ||
| 257 | { | ||
| 258 | LogId(REPORT_STANDARD, MSG_RESTARTING); | ||
| 259 | } | ||
| 260 | } | ||
| 261 | |||
| 262 | if (fLogInitialized) | ||
| 263 | { | ||
| 264 | LogClose(FALSE); | ||
| 265 | } | ||
| 266 | |||
| 267 | if (fRestart) | ||
| 268 | { | ||
| 269 | Restart(); | ||
| 270 | } | ||
| 271 | |||
| 272 | if (fLogInitialized) | ||
| 273 | { | ||
| 274 | LogUninitialize(FALSE); | ||
| 275 | } | ||
| 276 | |||
| 277 | return hr; | ||
| 278 | } | ||
| 279 | |||
| 280 | |||
| 281 | // internal function definitions | ||
| 282 | |||
| 283 | static HRESULT InitializeEngineState( | ||
| 284 | __in BURN_ENGINE_STATE* pEngineState, | ||
| 285 | __in HANDLE hEngineFile | ||
| 286 | ) | ||
| 287 | { | ||
| 288 | HRESULT hr = S_OK; | ||
| 289 | LPCWSTR wzParam = NULL; | ||
| 290 | HANDLE hSectionFile = hEngineFile; | ||
| 291 | HANDLE hSourceEngineFile = INVALID_HANDLE_VALUE; | ||
| 292 | |||
| 293 | pEngineState->automaticUpdates = BURN_AU_PAUSE_ACTION_IFELEVATED; | ||
| 294 | pEngineState->dwElevatedLoggingTlsId = TLS_OUT_OF_INDEXES; | ||
| 295 | ::InitializeCriticalSection(&pEngineState->csActive); | ||
| 296 | ::InitializeCriticalSection(&pEngineState->userExperience.csEngineActive); | ||
| 297 | PipeConnectionInitialize(&pEngineState->companionConnection); | ||
| 298 | PipeConnectionInitialize(&pEngineState->embeddedConnection); | ||
| 299 | |||
| 300 | for (int i = 0; i < pEngineState->argc; ++i) | ||
| 301 | { | ||
| 302 | if (pEngineState->argv[i][0] == L'-') | ||
| 303 | { | ||
| 304 | if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &pEngineState->argv[i][1], lstrlenW(BURN_COMMANDLINE_SWITCH_FILEHANDLE_ATTACHED), BURN_COMMANDLINE_SWITCH_FILEHANDLE_ATTACHED, lstrlenW(BURN_COMMANDLINE_SWITCH_FILEHANDLE_ATTACHED))) | ||
| 305 | { | ||
| 306 | wzParam = &pEngineState->argv[i][2 + lstrlenW(BURN_COMMANDLINE_SWITCH_FILEHANDLE_ATTACHED)]; | ||
| 307 | if (L'=' != wzParam[-1] || L'\0' == wzParam[0]) | ||
| 308 | { | ||
| 309 | ExitOnRootFailure(hr = E_INVALIDARG, "Missing required parameter for switch: %ls", BURN_COMMANDLINE_SWITCH_FILEHANDLE_ATTACHED); | ||
| 310 | } | ||
| 311 | |||
| 312 | hr = StrStringToUInt32(wzParam, 0, reinterpret_cast<UINT*>(&hSourceEngineFile)); | ||
| 313 | ExitOnFailure(hr, "Failed to parse file handle: '%ls'", (wzParam)); | ||
| 314 | } | ||
| 315 | if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &pEngineState->argv[i][1], lstrlenW(BURN_COMMANDLINE_SWITCH_FILEHANDLE_SELF), BURN_COMMANDLINE_SWITCH_FILEHANDLE_SELF, lstrlenW(BURN_COMMANDLINE_SWITCH_FILEHANDLE_SELF))) | ||
| 316 | { | ||
| 317 | wzParam = &pEngineState->argv[i][2 + lstrlenW(BURN_COMMANDLINE_SWITCH_FILEHANDLE_SELF)]; | ||
| 318 | if (L'=' != wzParam[-1] || L'\0' == wzParam[0]) | ||
| 319 | { | ||
| 320 | ExitOnRootFailure(hr = E_INVALIDARG, "Missing required parameter for switch: %ls", BURN_COMMANDLINE_SWITCH_FILEHANDLE_SELF); | ||
| 321 | } | ||
| 322 | |||
| 323 | hr = StrStringToUInt32(wzParam, 0, reinterpret_cast<UINT*>(&hSectionFile)); | ||
| 324 | ExitOnFailure(hr, "Failed to parse file handle: '%ls'", (wzParam)); | ||
| 325 | } | ||
| 326 | } | ||
| 327 | } | ||
| 328 | |||
| 329 | hr = SectionInitialize(&pEngineState->section, hSectionFile, hSourceEngineFile); | ||
| 330 | ExitOnFailure(hr, "Failed to initialize engine section."); | ||
| 331 | |||
| 332 | LExit: | ||
| 333 | return hr; | ||
| 334 | } | ||
| 335 | |||
| 336 | static void UninitializeEngineState( | ||
| 337 | __in BURN_ENGINE_STATE* pEngineState | ||
| 338 | ) | ||
| 339 | { | ||
| 340 | if (pEngineState->argv) | ||
| 341 | { | ||
| 342 | AppFreeCommandLineArgs(pEngineState->argv); | ||
| 343 | } | ||
| 344 | |||
| 345 | ReleaseStr(pEngineState->sczIgnoreDependencies); | ||
| 346 | |||
| 347 | PipeConnectionUninitialize(&pEngineState->embeddedConnection); | ||
| 348 | PipeConnectionUninitialize(&pEngineState->companionConnection); | ||
| 349 | ReleaseStr(pEngineState->sczBundleEngineWorkingPath) | ||
| 350 | |||
| 351 | ReleaseHandle(pEngineState->hMessageWindowThread); | ||
| 352 | |||
| 353 | ::DeleteCriticalSection(&pEngineState->userExperience.csEngineActive); | ||
| 354 | UserExperienceUninitialize(&pEngineState->userExperience); | ||
| 355 | |||
| 356 | ApprovedExesUninitialize(&pEngineState->approvedExes); | ||
| 357 | UpdateUninitialize(&pEngineState->update); | ||
| 358 | VariablesUninitialize(&pEngineState->variables); | ||
| 359 | SearchesUninitialize(&pEngineState->searches); | ||
| 360 | RegistrationUninitialize(&pEngineState->registration); | ||
| 361 | PayloadsUninitialize(&pEngineState->payloads); | ||
| 362 | PackagesUninitialize(&pEngineState->packages); | ||
| 363 | CatalogUninitialize(&pEngineState->catalogs); | ||
| 364 | SectionUninitialize(&pEngineState->section); | ||
| 365 | ContainersUninitialize(&pEngineState->containers); | ||
| 366 | |||
| 367 | ReleaseStr(pEngineState->command.wzLayoutDirectory); | ||
| 368 | ReleaseStr(pEngineState->command.wzCommandLine); | ||
| 369 | |||
| 370 | ReleaseStr(pEngineState->log.sczExtension); | ||
| 371 | ReleaseStr(pEngineState->log.sczPrefix); | ||
| 372 | ReleaseStr(pEngineState->log.sczPath); | ||
| 373 | ReleaseStr(pEngineState->log.sczPathVariable); | ||
| 374 | |||
| 375 | if (TLS_OUT_OF_INDEXES != pEngineState->dwElevatedLoggingTlsId) | ||
| 376 | { | ||
| 377 | ::TlsFree(pEngineState->dwElevatedLoggingTlsId); | ||
| 378 | } | ||
| 379 | |||
| 380 | ::DeleteCriticalSection(&pEngineState->csActive); | ||
| 381 | |||
| 382 | // clear struct | ||
| 383 | memset(pEngineState, 0, sizeof(BURN_ENGINE_STATE)); | ||
| 384 | } | ||
| 385 | |||
| 386 | static HRESULT RunUntrusted( | ||
| 387 | __in LPCWSTR wzCommandLine, | ||
| 388 | __in BURN_ENGINE_STATE* pEngineState | ||
| 389 | ) | ||
| 390 | { | ||
| 391 | HRESULT hr = S_OK; | ||
| 392 | LPWSTR sczCurrentProcessPath = NULL; | ||
| 393 | LPWSTR wzCleanRoomBundlePath = NULL; | ||
| 394 | LPWSTR sczCachedCleanRoomBundlePath = NULL; | ||
| 395 | LPWSTR sczParameters = NULL; | ||
| 396 | LPWSTR sczFullCommandLine = NULL; | ||
| 397 | STARTUPINFOW si = { }; | ||
| 398 | PROCESS_INFORMATION pi = { }; | ||
| 399 | HANDLE hFileAttached = NULL; | ||
| 400 | HANDLE hFileSelf = NULL; | ||
| 401 | HANDLE hProcess = NULL; | ||
| 402 | |||
| 403 | hr = PathForCurrentProcess(&sczCurrentProcessPath, NULL); | ||
| 404 | ExitOnFailure(hr, "Failed to get path for current process."); | ||
| 405 | |||
| 406 | BOOL fRunningFromCache = CacheBundleRunningFromCache(); | ||
| 407 | |||
| 408 | // If we're running from the package cache, we're in a secure | ||
| 409 | // folder (DLLs cannot be inserted here for hijacking purposes) | ||
| 410 | // so just launch the current process's path as the clean room | ||
| 411 | // process. Technically speaking, we'd be able to skip creating | ||
| 412 | // a clean room process at all (since we're already running from | ||
| 413 | // a secure folder) but it makes the code that only wants to run | ||
| 414 | // in clean room more complicated if we don't launch an explicit | ||
| 415 | // clean room process. | ||
| 416 | if (fRunningFromCache) | ||
| 417 | { | ||
| 418 | wzCleanRoomBundlePath = sczCurrentProcessPath; | ||
| 419 | } | ||
| 420 | else | ||
| 421 | { | ||
| 422 | hr = CacheBundleToCleanRoom(&pEngineState->userExperience.payloads, &pEngineState->section, &sczCachedCleanRoomBundlePath); | ||
| 423 | ExitOnFailure(hr, "Failed to cache to clean room."); | ||
| 424 | |||
| 425 | wzCleanRoomBundlePath = sczCachedCleanRoomBundlePath; | ||
| 426 | } | ||
| 427 | |||
| 428 | // The clean room switch must always be at the front of the command line so | ||
| 429 | // the EngineInCleanRoom function will operate correctly. | ||
| 430 | hr = StrAllocFormatted(&sczParameters, L"-%ls=\"%ls\"", BURN_COMMANDLINE_SWITCH_CLEAN_ROOM, sczCurrentProcessPath); | ||
| 431 | ExitOnFailure(hr, "Failed to allocate parameters for unelevated process."); | ||
| 432 | |||
| 433 | // Send a file handle for the child Burn process to access the attached container. | ||
| 434 | hr = CoreAppendFileHandleAttachedToCommandLine(pEngineState->section.hEngineFile, &hFileAttached, &sczParameters); | ||
| 435 | ExitOnFailure(hr, "Failed to append %ls", BURN_COMMANDLINE_SWITCH_FILEHANDLE_ATTACHED); | ||
| 436 | |||
| 437 | // Grab a file handle for the child Burn process. | ||
| 438 | hr = CoreAppendFileHandleSelfToCommandLine(wzCleanRoomBundlePath, &hFileSelf, &sczParameters, NULL); | ||
| 439 | ExitOnFailure(hr, "Failed to append %ls", BURN_COMMANDLINE_SWITCH_FILEHANDLE_SELF); | ||
| 440 | |||
| 441 | hr = StrAllocFormattedSecure(&sczParameters, L"%ls %ls", sczParameters, wzCommandLine); | ||
| 442 | ExitOnFailure(hr, "Failed to append original command line."); | ||
| 443 | |||
| 444 | #ifdef ENABLE_UNELEVATE | ||
| 445 | // TODO: Pass file handle to unelevated process if this ever gets reenabled. | ||
| 446 | if (!pEngineState->fDisableUnelevate) | ||
| 447 | { | ||
| 448 | // Try to launch unelevated and if that fails for any reason, we'll launch our process normally (even though that may make it elevated). | ||
| 449 | hr = ProcExecuteAsInteractiveUser(wzCleanRoomBundlePath, sczParameters, &hProcess); | ||
| 450 | } | ||
| 451 | #endif | ||
| 452 | |||
| 453 | if (!hProcess) | ||
| 454 | { | ||
| 455 | hr = StrAllocFormattedSecure(&sczFullCommandLine, L"\"%ls\" %ls", wzCleanRoomBundlePath, sczParameters); | ||
| 456 | ExitOnFailure(hr, "Failed to allocate full command-line."); | ||
| 457 | |||
| 458 | si.cb = sizeof(si); | ||
| 459 | si.wShowWindow = static_cast<WORD>(pEngineState->command.nCmdShow); | ||
| 460 | if (!::CreateProcessW(wzCleanRoomBundlePath, sczFullCommandLine, NULL, NULL, TRUE, 0, 0, NULL, &si, &pi)) | ||
| 461 | { | ||
| 462 | ExitWithLastError(hr, "Failed to launch clean room process: %ls", sczFullCommandLine); | ||
| 463 | } | ||
| 464 | |||
| 465 | hProcess = pi.hProcess; | ||
| 466 | pi.hProcess = NULL; | ||
| 467 | } | ||
| 468 | |||
| 469 | hr = ProcWaitForCompletion(hProcess, INFINITE, &pEngineState->userExperience.dwExitCode); | ||
| 470 | ExitOnFailure(hr, "Failed to wait for clean room process: %ls", wzCleanRoomBundlePath); | ||
| 471 | |||
| 472 | LExit: | ||
| 473 | ReleaseHandle(pi.hThread); | ||
| 474 | ReleaseFileHandle(hFileSelf); | ||
| 475 | ReleaseFileHandle(hFileAttached); | ||
| 476 | ReleaseHandle(hProcess); | ||
| 477 | StrSecureZeroFreeString(sczFullCommandLine); | ||
| 478 | StrSecureZeroFreeString(sczParameters); | ||
| 479 | ReleaseStr(sczCachedCleanRoomBundlePath); | ||
| 480 | ReleaseStr(sczCurrentProcessPath); | ||
| 481 | |||
| 482 | return hr; | ||
| 483 | } | ||
| 484 | |||
| 485 | static HRESULT RunNormal( | ||
| 486 | __in HINSTANCE hInstance, | ||
| 487 | __in BURN_ENGINE_STATE* pEngineState | ||
| 488 | ) | ||
| 489 | { | ||
| 490 | HRESULT hr = S_OK; | ||
| 491 | HANDLE hPipesCreatedEvent = NULL; | ||
| 492 | BOOL fContinueExecution = TRUE; | ||
| 493 | BOOL fReloadApp = FALSE; | ||
| 494 | |||
| 495 | // Initialize logging. | ||
| 496 | hr = LoggingOpen(&pEngineState->log, &pEngineState->variables, pEngineState->command.display, pEngineState->registration.sczDisplayName); | ||
| 497 | ExitOnFailure(hr, "Failed to open log."); | ||
| 498 | |||
| 499 | // Ensure we're on a supported operating system. | ||
| 500 | hr = ConditionGlobalCheck(&pEngineState->variables, &pEngineState->condition, pEngineState->command.display, pEngineState->registration.sczDisplayName, &pEngineState->userExperience.dwExitCode, &fContinueExecution); | ||
| 501 | ExitOnFailure(hr, "Failed to check global conditions"); | ||
| 502 | |||
| 503 | if (!fContinueExecution) | ||
| 504 | { | ||
| 505 | LogId(REPORT_STANDARD, MSG_FAILED_CONDITION_CHECK); | ||
| 506 | |||
| 507 | // If the block told us to abort, abort! | ||
| 508 | ExitFunction1(hr = S_OK); | ||
| 509 | } | ||
| 510 | |||
| 511 | if (pEngineState->userExperience.fSplashScreen && BOOTSTRAPPER_DISPLAY_NONE < pEngineState->command.display) | ||
| 512 | { | ||
| 513 | SplashScreenCreate(hInstance, NULL, &pEngineState->command.hwndSplashScreen); | ||
| 514 | } | ||
| 515 | |||
| 516 | // Create a top-level window to handle system messages. | ||
| 517 | hr = UiCreateMessageWindow(hInstance, pEngineState); | ||
| 518 | ExitOnFailure(hr, "Failed to create the message window."); | ||
| 519 | |||
| 520 | // Query registration state. | ||
| 521 | hr = CoreQueryRegistration(pEngineState); | ||
| 522 | ExitOnFailure(hr, "Failed to query registration."); | ||
| 523 | |||
| 524 | // Set some built-in variables before loading the BA. | ||
| 525 | hr = PlanSetVariables(pEngineState->command.action, &pEngineState->variables); | ||
| 526 | ExitOnFailure(hr, "Failed to set action variables."); | ||
| 527 | |||
| 528 | hr = RegistrationSetVariables(&pEngineState->registration, &pEngineState->variables); | ||
| 529 | ExitOnFailure(hr, "Failed to set registration variables."); | ||
| 530 | |||
| 531 | // If a layout directory was specified on the command-line, set it as a well-known variable. | ||
| 532 | if (pEngineState->command.wzLayoutDirectory && *pEngineState->command.wzLayoutDirectory) | ||
| 533 | { | ||
| 534 | hr = VariableSetString(&pEngineState->variables, BURN_BUNDLE_LAYOUT_DIRECTORY, pEngineState->command.wzLayoutDirectory, FALSE); | ||
| 535 | ExitOnFailure(hr, "Failed to set layout directory variable to value provided from command-line."); | ||
| 536 | } | ||
| 537 | |||
| 538 | do | ||
| 539 | { | ||
| 540 | fReloadApp = FALSE; | ||
| 541 | |||
| 542 | hr = RunApplication(pEngineState, &fReloadApp); | ||
| 543 | ExitOnFailure(hr, "Failed while running "); | ||
| 544 | } while (fReloadApp); | ||
| 545 | |||
| 546 | LExit: | ||
| 547 | // If the message window is still around, close it. | ||
| 548 | UiCloseMessageWindow(pEngineState); | ||
| 549 | |||
| 550 | VariablesDump(&pEngineState->variables); | ||
| 551 | |||
| 552 | // end per-machine process if running | ||
| 553 | if (INVALID_HANDLE_VALUE != pEngineState->companionConnection.hPipe) | ||
| 554 | { | ||
| 555 | PipeTerminateChildProcess(&pEngineState->companionConnection, pEngineState->userExperience.dwExitCode, FALSE); | ||
| 556 | } | ||
| 557 | |||
| 558 | // If the splash screen is still around, close it. | ||
| 559 | if (::IsWindow(pEngineState->command.hwndSplashScreen)) | ||
| 560 | { | ||
| 561 | ::PostMessageW(pEngineState->command.hwndSplashScreen, WM_CLOSE, 0, 0); | ||
| 562 | } | ||
| 563 | |||
| 564 | ReleaseHandle(hPipesCreatedEvent); | ||
| 565 | |||
| 566 | return hr; | ||
| 567 | } | ||
| 568 | |||
| 569 | static HRESULT RunElevated( | ||
| 570 | __in HINSTANCE hInstance, | ||
| 571 | __in LPCWSTR /*wzCommandLine*/, | ||
| 572 | __in BURN_ENGINE_STATE* pEngineState | ||
| 573 | ) | ||
| 574 | { | ||
| 575 | HRESULT hr = S_OK; | ||
| 576 | HANDLE hLock = NULL; | ||
| 577 | BOOL fDisabledAutomaticUpdates = FALSE; | ||
| 578 | |||
| 579 | // connect to per-user process | ||
| 580 | hr = PipeChildConnect(&pEngineState->companionConnection, TRUE); | ||
| 581 | ExitOnFailure(hr, "Failed to connect to unelevated process."); | ||
| 582 | |||
| 583 | // Set up the thread local storage to store the correct pipe to communicate logging then | ||
| 584 | // override logging to write over the pipe. | ||
| 585 | pEngineState->dwElevatedLoggingTlsId = ::TlsAlloc(); | ||
| 586 | if (TLS_OUT_OF_INDEXES == pEngineState->dwElevatedLoggingTlsId) | ||
| 587 | { | ||
| 588 | ExitWithLastError(hr, "Failed to allocate thread local storage for logging."); | ||
| 589 | } | ||
| 590 | |||
| 591 | if (!::TlsSetValue(pEngineState->dwElevatedLoggingTlsId, pEngineState->companionConnection.hPipe)) | ||
| 592 | { | ||
| 593 | ExitWithLastError(hr, "Failed to set elevated pipe into thread local storage for logging."); | ||
| 594 | } | ||
| 595 | |||
| 596 | LogRedirect(RedirectLoggingOverPipe, pEngineState); | ||
| 597 | |||
| 598 | // Create a top-level window to prevent shutting down the elevated process. | ||
| 599 | hr = UiCreateMessageWindow(hInstance, pEngineState); | ||
| 600 | ExitOnFailure(hr, "Failed to create the message window."); | ||
| 601 | |||
| 602 | SrpInitialize(TRUE); | ||
| 603 | |||
| 604 | // Pump messages from parent process. | ||
| 605 | hr = ElevationChildPumpMessages(pEngineState->dwElevatedLoggingTlsId, pEngineState->companionConnection.hPipe, pEngineState->companionConnection.hCachePipe, &pEngineState->approvedExes, &pEngineState->containers, &pEngineState->packages, &pEngineState->payloads, &pEngineState->variables, &pEngineState->registration, &pEngineState->userExperience, &hLock, &fDisabledAutomaticUpdates, &pEngineState->userExperience.dwExitCode, &pEngineState->fRestart); | ||
| 606 | LogRedirect(NULL, NULL); // reset logging so the next failure gets written to "log buffer" for the failure log. | ||
| 607 | ExitOnFailure(hr, "Failed to pump messages from parent process."); | ||
| 608 | |||
| 609 | LExit: | ||
| 610 | LogRedirect(NULL, NULL); // we're done talking to the child so always reset logging now. | ||
| 611 | |||
| 612 | // If the message window is still around, close it. | ||
| 613 | UiCloseMessageWindow(pEngineState); | ||
| 614 | |||
| 615 | if (fDisabledAutomaticUpdates) | ||
| 616 | { | ||
| 617 | ElevationChildResumeAutomaticUpdates(); | ||
| 618 | } | ||
| 619 | |||
| 620 | if (hLock) | ||
| 621 | { | ||
| 622 | ::ReleaseMutex(hLock); | ||
| 623 | ::CloseHandle(hLock); | ||
| 624 | } | ||
| 625 | |||
| 626 | return hr; | ||
| 627 | } | ||
| 628 | |||
| 629 | static HRESULT RunEmbedded( | ||
| 630 | __in HINSTANCE hInstance, | ||
| 631 | __in BURN_ENGINE_STATE* pEngineState | ||
| 632 | ) | ||
| 633 | { | ||
| 634 | HRESULT hr = S_OK; | ||
| 635 | |||
| 636 | // Disable system restore since the parent bundle may have done it. | ||
| 637 | pEngineState->fDisableSystemRestore = TRUE; | ||
| 638 | |||
| 639 | // Connect to parent process. | ||
| 640 | hr = PipeChildConnect(&pEngineState->embeddedConnection, FALSE); | ||
| 641 | ExitOnFailure(hr, "Failed to connect to parent of embedded process."); | ||
| 642 | |||
| 643 | // Do not register the bundle to automatically restart if embedded. | ||
| 644 | if (BOOTSTRAPPER_DISPLAY_EMBEDDED == pEngineState->command.display) | ||
| 645 | { | ||
| 646 | pEngineState->registration.fDisableResume = TRUE; | ||
| 647 | } | ||
| 648 | |||
| 649 | // Now run the application like normal. | ||
| 650 | hr = RunNormal(hInstance, pEngineState); | ||
| 651 | ExitOnFailure(hr, "Failed to run bootstrapper application embedded."); | ||
| 652 | |||
| 653 | LExit: | ||
| 654 | return hr; | ||
| 655 | } | ||
| 656 | |||
| 657 | static HRESULT RunRunOnce( | ||
| 658 | __in const BURN_REGISTRATION* pRegistration, | ||
| 659 | __in int nCmdShow | ||
| 660 | ) | ||
| 661 | { | ||
| 662 | HRESULT hr = S_OK; | ||
| 663 | LPWSTR sczNewCommandLine = NULL; | ||
| 664 | LPWSTR sczBurnPath = NULL; | ||
| 665 | HANDLE hProcess = NULL; | ||
| 666 | |||
| 667 | hr = RegistrationGetResumeCommandLine(pRegistration, &sczNewCommandLine); | ||
| 668 | ExitOnFailure(hr, "Unable to get resume command line from the registry"); | ||
| 669 | |||
| 670 | // and re-launch | ||
| 671 | hr = PathForCurrentProcess(&sczBurnPath, NULL); | ||
| 672 | ExitOnFailure(hr, "Failed to get current process path."); | ||
| 673 | |||
| 674 | hr = ProcExec(sczBurnPath, 0 < sczNewCommandLine ? sczNewCommandLine : L"", nCmdShow, &hProcess); | ||
| 675 | ExitOnFailure(hr, "Failed to re-launch bundle process after RunOnce: %ls", sczBurnPath); | ||
| 676 | |||
| 677 | LExit: | ||
| 678 | ReleaseHandle(hProcess); | ||
| 679 | ReleaseStr(sczNewCommandLine); | ||
| 680 | ReleaseStr(sczBurnPath); | ||
| 681 | |||
| 682 | return hr; | ||
| 683 | } | ||
| 684 | |||
| 685 | static HRESULT RunApplication( | ||
| 686 | __in BURN_ENGINE_STATE* pEngineState, | ||
| 687 | __out BOOL* pfReloadApp | ||
| 688 | ) | ||
| 689 | { | ||
| 690 | HRESULT hr = S_OK; | ||
| 691 | BOOTSTRAPPER_ENGINE_CONTEXT engineContext = { }; | ||
| 692 | BOOL fStartupCalled = FALSE; | ||
| 693 | BOOL fRet = FALSE; | ||
| 694 | MSG msg = { }; | ||
| 695 | BOOTSTRAPPER_SHUTDOWN_ACTION shutdownAction = BOOTSTRAPPER_SHUTDOWN_ACTION_NONE; | ||
| 696 | |||
| 697 | ::PeekMessageW(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE); | ||
| 698 | |||
| 699 | // Setup the bootstrapper engine. | ||
| 700 | engineContext.dwThreadId = ::GetCurrentThreadId(); | ||
| 701 | engineContext.pEngineState = pEngineState; | ||
| 702 | |||
| 703 | // Load the bootstrapper application. | ||
| 704 | hr = UserExperienceLoad(&pEngineState->userExperience, &engineContext, &pEngineState->command); | ||
| 705 | ExitOnFailure(hr, "Failed to load BA."); | ||
| 706 | |||
| 707 | fStartupCalled = TRUE; | ||
| 708 | hr = UserExperienceOnStartup(&pEngineState->userExperience); | ||
| 709 | ExitOnFailure(hr, "Failed to start bootstrapper application."); | ||
| 710 | |||
| 711 | // Enter the message pump. | ||
| 712 | while (0 != (fRet = ::GetMessageW(&msg, NULL, 0, 0))) | ||
| 713 | { | ||
| 714 | if (-1 == fRet) | ||
| 715 | { | ||
| 716 | hr = E_UNEXPECTED; | ||
| 717 | ExitOnRootFailure(hr, "Unexpected return value from message pump."); | ||
| 718 | } | ||
| 719 | else | ||
| 720 | { | ||
| 721 | ProcessMessage(pEngineState, &msg); | ||
| 722 | } | ||
| 723 | } | ||
| 724 | |||
| 725 | // Get exit code. | ||
| 726 | pEngineState->userExperience.dwExitCode = (DWORD)msg.wParam; | ||
| 727 | |||
| 728 | LExit: | ||
| 729 | if (fStartupCalled) | ||
| 730 | { | ||
| 731 | UserExperienceOnShutdown(&pEngineState->userExperience, &shutdownAction); | ||
| 732 | if (BOOTSTRAPPER_SHUTDOWN_ACTION_RESTART == shutdownAction) | ||
| 733 | { | ||
| 734 | LogId(REPORT_STANDARD, MSG_BA_REQUESTED_RESTART, LoggingBoolToString(pEngineState->fRestart)); | ||
| 735 | pEngineState->fRestart = TRUE; | ||
| 736 | } | ||
| 737 | else if (BOOTSTRAPPER_SHUTDOWN_ACTION_RELOAD_BOOTSTRAPPER == shutdownAction) | ||
| 738 | { | ||
| 739 | LogId(REPORT_STANDARD, MSG_BA_REQUESTED_RELOAD); | ||
| 740 | *pfReloadApp = TRUE; | ||
| 741 | } | ||
| 742 | } | ||
| 743 | |||
| 744 | // Unload BA. | ||
| 745 | UserExperienceUnload(&pEngineState->userExperience); | ||
| 746 | |||
| 747 | return hr; | ||
| 748 | } | ||
| 749 | |||
| 750 | static HRESULT ProcessMessage( | ||
| 751 | __in BURN_ENGINE_STATE* pEngineState, | ||
| 752 | __in const MSG* pmsg | ||
| 753 | ) | ||
| 754 | { | ||
| 755 | HRESULT hr = S_OK; | ||
| 756 | |||
| 757 | switch (pmsg->message) | ||
| 758 | { | ||
| 759 | case WM_BURN_DETECT: | ||
| 760 | hr = CoreDetect(pEngineState, reinterpret_cast<HWND>(pmsg->lParam)); | ||
| 761 | break; | ||
| 762 | |||
| 763 | case WM_BURN_PLAN: | ||
| 764 | hr = CorePlan(pEngineState, static_cast<BOOTSTRAPPER_ACTION>(pmsg->lParam)); | ||
| 765 | break; | ||
| 766 | |||
| 767 | case WM_BURN_ELEVATE: | ||
| 768 | hr = CoreElevate(pEngineState, reinterpret_cast<HWND>(pmsg->lParam)); | ||
| 769 | break; | ||
| 770 | |||
| 771 | case WM_BURN_APPLY: | ||
| 772 | hr = CoreApply(pEngineState, reinterpret_cast<HWND>(pmsg->lParam)); | ||
| 773 | break; | ||
| 774 | |||
| 775 | case WM_BURN_LAUNCH_APPROVED_EXE: | ||
| 776 | hr = CoreLaunchApprovedExe(pEngineState, reinterpret_cast<BURN_LAUNCH_APPROVED_EXE*>(pmsg->lParam)); | ||
| 777 | break; | ||
| 778 | |||
| 779 | case WM_BURN_QUIT: | ||
| 780 | hr = CoreQuit(pEngineState, static_cast<int>(pmsg->wParam)); | ||
| 781 | break; | ||
| 782 | } | ||
| 783 | |||
| 784 | return hr; | ||
| 785 | } | ||
| 786 | |||
| 787 | static HRESULT DAPI RedirectLoggingOverPipe( | ||
| 788 | __in_z LPCSTR szString, | ||
| 789 | __in_opt LPVOID pvContext | ||
| 790 | ) | ||
| 791 | { | ||
| 792 | static BOOL s_fCurrentlyLoggingToPipe = FALSE; | ||
| 793 | |||
| 794 | HRESULT hr = S_OK; | ||
| 795 | BURN_ENGINE_STATE* pEngineState = static_cast<BURN_ENGINE_STATE*>(pvContext); | ||
| 796 | BOOL fStartedLogging = FALSE; | ||
| 797 | HANDLE hPipe = INVALID_HANDLE_VALUE; | ||
| 798 | BYTE* pbData = NULL; | ||
| 799 | SIZE_T cbData = 0; | ||
| 800 | DWORD dwResult = 0; | ||
| 801 | |||
| 802 | // Prevent this function from being called recursively. | ||
| 803 | if (s_fCurrentlyLoggingToPipe) | ||
| 804 | { | ||
| 805 | ExitFunction(); | ||
| 806 | } | ||
| 807 | |||
| 808 | s_fCurrentlyLoggingToPipe = TRUE; | ||
| 809 | fStartedLogging = TRUE; | ||
| 810 | |||
| 811 | // Make sure the current thread set the pipe in TLS. | ||
| 812 | hPipe = ::TlsGetValue(pEngineState->dwElevatedLoggingTlsId); | ||
| 813 | if (!hPipe || INVALID_HANDLE_VALUE == hPipe) | ||
| 814 | { | ||
| 815 | hr = HRESULT_FROM_WIN32(ERROR_PIPE_NOT_CONNECTED); | ||
| 816 | ExitFunction(); | ||
| 817 | } | ||
| 818 | |||
| 819 | // Do not log or use ExitOnFailure() macro here because they will be discarded | ||
| 820 | // by the recursive block at the top of this function. | ||
| 821 | hr = BuffWriteStringAnsi(&pbData, &cbData, szString); | ||
| 822 | if (SUCCEEDED(hr)) | ||
| 823 | { | ||
| 824 | hr = PipeSendMessage(hPipe, static_cast<DWORD>(BURN_PIPE_MESSAGE_TYPE_LOG), pbData, cbData, NULL, NULL, &dwResult); | ||
| 825 | if (SUCCEEDED(hr)) | ||
| 826 | { | ||
| 827 | hr = (HRESULT)dwResult; | ||
| 828 | } | ||
| 829 | } | ||
| 830 | |||
| 831 | LExit: | ||
| 832 | ReleaseBuffer(pbData); | ||
| 833 | |||
| 834 | // We started logging so remember to say we are no longer logging. | ||
| 835 | if (fStartedLogging) | ||
| 836 | { | ||
| 837 | s_fCurrentlyLoggingToPipe = FALSE; | ||
| 838 | } | ||
| 839 | |||
| 840 | return hr; | ||
| 841 | } | ||
| 842 | |||
| 843 | static HRESULT Restart() | ||
| 844 | { | ||
| 845 | HRESULT hr = S_OK; | ||
| 846 | HANDLE hProcessToken = NULL; | ||
| 847 | TOKEN_PRIVILEGES priv = { }; | ||
| 848 | DWORD dwRetries = 0; | ||
| 849 | |||
| 850 | if (!::OpenProcessToken(::GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hProcessToken)) | ||
| 851 | { | ||
| 852 | ExitWithLastError(hr, "Failed to get process token."); | ||
| 853 | } | ||
| 854 | |||
| 855 | priv.PrivilegeCount = 1; | ||
| 856 | priv.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; | ||
| 857 | if (!::LookupPrivilegeValueW(NULL, L"SeShutdownPrivilege", &priv.Privileges[0].Luid)) | ||
| 858 | { | ||
| 859 | ExitWithLastError(hr, "Failed to get shutdown privilege LUID."); | ||
| 860 | } | ||
| 861 | |||
| 862 | if (!::AdjustTokenPrivileges(hProcessToken, FALSE, &priv, sizeof(TOKEN_PRIVILEGES), NULL, 0)) | ||
| 863 | { | ||
| 864 | ExitWithLastError(hr, "Failed to adjust token to add shutdown privileges."); | ||
| 865 | } | ||
| 866 | |||
| 867 | do | ||
| 868 | { | ||
| 869 | hr = S_OK; | ||
| 870 | |||
| 871 | // Wait a second to let the companion process (assuming we did an elevated install) to get to the | ||
| 872 | // point where it too is thinking about restarting the computer. Only one will schedule the restart | ||
| 873 | // but both will have their log files closed and otherwise be ready to exit. | ||
| 874 | // | ||
| 875 | // On retry, we'll also wait a second to let the OS try to get to a place where the restart can | ||
| 876 | // be initiated. | ||
| 877 | ::Sleep(1000); | ||
| 878 | |||
| 879 | if (!vpfnInitiateSystemShutdownExW(NULL, NULL, 0, FALSE, TRUE, SHTDN_REASON_MAJOR_APPLICATION | SHTDN_REASON_MINOR_INSTALLATION | SHTDN_REASON_FLAG_PLANNED)) | ||
| 880 | { | ||
| 881 | hr = HRESULT_FROM_WIN32(::GetLastError()); | ||
| 882 | } | ||
| 883 | } while (dwRetries++ < RESTART_RETRIES && (HRESULT_FROM_WIN32(ERROR_MACHINE_LOCKED) == hr || HRESULT_FROM_WIN32(ERROR_NOT_READY) == hr)); | ||
| 884 | ExitOnRootFailure(hr, "Failed to schedule restart."); | ||
| 885 | |||
| 886 | LExit: | ||
| 887 | ReleaseHandle(hProcessToken); | ||
| 888 | return hr; | ||
| 889 | } | ||
