main
cpp 1,081 lines 33.7 KB
Raw
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 #if 0
20 static HRESULT RunUntrusted(
21 __in BURN_ENGINE_STATE* pEngineState
22 );
23 #endif
24 static HRESULT RunNormal(
25 __in HINSTANCE hInstance,
26 __in BURN_ENGINE_STATE* pEngineState
27 );
28 static HRESULT RunElevated(
29 __in HINSTANCE hInstance,
30 __in LPCWSTR wzCommandLine,
31 __in BURN_ENGINE_STATE* pEngineState
32 );
33 static HRESULT RunEmbedded(
34 __in HINSTANCE hInstance,
35 __in BURN_ENGINE_STATE* pEngineState
36 );
37 static HRESULT RunRunOnce(
38 __in BURN_ENGINE_STATE* pEngineState,
39 __in int nCmdShow
40 );
41 static HRESULT RunApplication(
42 __in BURN_ENGINE_STATE* pEngineState,
43 __in BOOL fSecondaryBootstrapperApplication,
44 __out BOOL* pfReloadApp,
45 __out BOOL* pfSkipCleanup
46 );
47 static HRESULT ProcessMessage(
48 __in BAENGINE_CONTEXT* pEngineContext,
49 __in BAENGINE_ACTION* pAction
50 );
51 static HRESULT DAPI RedirectLoggingOverPipe(
52 __in_z LPCSTR szString,
53 __in_opt LPVOID pvContext
54 );
55 static HRESULT LogStringOverPipe(
56 __in_z LPCSTR szString,
57 __in HANDLE hPipe
58 );
59 static DWORD WINAPI ElevatedLoggingThreadProc(
60 __in LPVOID lpThreadParameter
61 );
62 static HRESULT Restart(
63 __in BURN_ENGINE_STATE* pEngineState
64 );
65 static void CALLBACK BurnTraceError(
66 __in_z LPCSTR szFile,
67 __in int iLine,
68 __in REPORT_LEVEL rl,
69 __in UINT source,
70 __in HRESULT hrError,
71 __in_z __format_string LPCSTR szFormat,
72 __in va_list args
73 );
74
75
76 // function definitions
77
78 extern "C" HRESULT EngineRun(
79 __in HINSTANCE hInstance,
80 __in HANDLE hEngineFile,
81 __in_z_opt LPCWSTR wzCommandLine,
82 __in int nCmdShow,
83 __out DWORD* pdwExitCode
84 )
85 {
86 HRESULT hr = S_OK;
87 BOOL fComInitialized = FALSE;
88 BOOL fLogInitialized = FALSE;
89 BOOL fCrypInitialized = FALSE;
90 BOOL fDpiuInitialized = FALSE;
91 BOOL fRegInitialized = FALSE;
92 BOOL fWiuInitialized = FALSE;
93 BOOL fXmlInitialized = FALSE;
94 SYSTEM_INFO si = { };
95 RTL_OSVERSIONINFOEXW ovix = { };
96 LPWSTR sczExePath = NULL;
97 BOOL fRunNormal = FALSE;
98 BOOL fRunElevated = FALSE;
99 BOOL fRunRunOnce = FALSE;
100
101 BURN_ENGINE_STATE engineState = { };
102 engineState.command.cbSize = sizeof(BOOTSTRAPPER_COMMAND);
103
104 // Always initialize logging first
105 LogInitialize(::GetModuleHandleW(NULL));
106 DutilInitialize(&BurnTraceError);
107 fLogInitialized = TRUE;
108
109 // Ensure that log contains approriate level of information
110 #ifdef _DEBUG
111 LogSetLevel(REPORT_DEBUG, FALSE);
112 #else
113 LogSetLevel(REPORT_VERBOSE, FALSE); // FALSE means don't write an additional text line to the log saying the level changed
114 #endif
115
116 hr = AppParseCommandLine(wzCommandLine, &engineState.internalCommand.argc, &engineState.internalCommand.argv);
117 ExitOnFailure(hr, "Failed to parse command line.");
118
119 hr = InitializeEngineState(&engineState, hEngineFile);
120 ExitOnFailure(hr, "Failed to initialize engine state.");
121
122 engineState.command.nCmdShow = nCmdShow;
123
124 if (BURN_MODE_ELEVATED != engineState.internalCommand.mode && BOOTSTRAPPER_DISPLAY_NONE < engineState.command.display)
125 {
126 SplashScreenCreate(hInstance, NULL, &engineState.command.hwndSplashScreen);
127 }
128
129 // initialize platform layer
130 PlatformInitialize();
131
132 // initialize COM
133 hr = ::CoInitializeEx(NULL, COINIT_MULTITHREADED);
134 ExitOnFailure(hr, "Failed to initialize COM.");
135 fComInitialized = TRUE;
136
137 // Initialize dutil.
138 hr = CrypInitialize();
139 ExitOnFailure(hr, "Failed to initialize Cryputil.");
140 fCrypInitialized = TRUE;
141
142 DpiuInitialize();
143 fDpiuInitialized = TRUE;
144
145 hr = RegInitialize();
146 ExitOnFailure(hr, "Failed to initialize Regutil.");
147 fRegInitialized = TRUE;
148
149 hr = WiuInitialize();
150 ExitOnFailure(hr, "Failed to initialize Wiutil.");
151 fWiuInitialized = TRUE;
152
153 hr = XmlInitialize();
154 ExitOnFailure(hr, "Failed to initialize XML util.");
155 fXmlInitialized = TRUE;
156
157 hr = OsRtlGetVersion(&ovix);
158 ExitOnFailure(hr, "Failed to get OS info.");
159
160 #if defined(_M_ARM64)
161 LPCSTR szBurnPlatform = "ARM64";
162 #elif defined(_M_AMD64)
163 LPCSTR szBurnPlatform = "x64";
164 #else
165 LPCSTR szBurnPlatform = "x86";
166 #endif
167
168 LPCSTR szMachinePlatform = "unknown architecture";
169 ::GetNativeSystemInfo(&si);
170 switch (si.wProcessorArchitecture)
171 {
172 case PROCESSOR_ARCHITECTURE_AMD64:
173 szMachinePlatform = "x64";
174 break;
175 case PROCESSOR_ARCHITECTURE_ARM:
176 szMachinePlatform = "ARM";
177 break;
178 case PROCESSOR_ARCHITECTURE_ARM64:
179 szMachinePlatform = "ARM64";
180 break;
181 case PROCESSOR_ARCHITECTURE_INTEL:
182 szMachinePlatform = "x86";
183 break;
184 }
185
186 PathForCurrentProcess(&sczExePath, NULL); // Ignore failure.
187 LogId(REPORT_STANDARD, MSG_BURN_INFO, szInformationalVersion, ovix.dwMajorVersion, ovix.dwMinorVersion, ovix.dwBuildNumber, ovix.wServicePackMajor, sczExePath, szBurnPlatform, szMachinePlatform);
188 ReleaseNullStr(sczExePath);
189
190 // initialize core
191 hr = CoreInitialize(&engineState);
192 ExitOnFailure(hr, "Failed to initialize core.");
193
194 // Select run mode.
195 switch (engineState.internalCommand.mode)
196 {
197 case BURN_MODE_NORMAL:
198 fRunNormal = TRUE;
199
200 hr = RunNormal(hInstance, &engineState);
201 ExitOnFailure(hr, "Failed to run normal mode.");
202 break;
203
204 case BURN_MODE_ELEVATED:
205 fRunElevated = TRUE;
206
207 hr = RunElevated(hInstance, wzCommandLine, &engineState);
208 ExitOnFailure(hr, "Failed to run elevated mode.");
209 break;
210
211 case BURN_MODE_EMBEDDED:
212 fRunNormal = TRUE;
213
214 hr = RunEmbedded(hInstance, &engineState);
215 ExitOnFailure(hr, "Failed to run embedded mode.");
216 break;
217
218 case BURN_MODE_RUNONCE:
219 fRunRunOnce = TRUE;
220
221 hr = RunRunOnce(&engineState, nCmdShow);
222 ExitOnFailure(hr, "Failed to run RunOnce mode.");
223 break;
224
225 default:
226 hr = E_UNEXPECTED;
227 ExitOnFailure(hr, "Invalid run mode.");
228 }
229
230 *pdwExitCode = engineState.userExperience.dwExitCode;
231
232 LExit:
233 ReleaseStr(sczExePath);
234
235 // If anything went wrong but the log was never open, try to open a "failure" log
236 // and that will dump anything captured in the log memory buffer to the log.
237 if (FAILED(hr) && BURN_LOGGING_STATE_CLOSED == engineState.log.state)
238 {
239 LoggingOpenFailed();
240 }
241
242 BootstrapperApplicationRemove(&engineState.userExperience);
243
244 CacheRemoveBaseWorkingFolder(&engineState.cache);
245 CacheUninitialize(&engineState.cache);
246
247 // If this is a related bundle (but not an update) suppress restart and return the standard restart error code.
248 if (engineState.fRestart && BOOTSTRAPPER_RELATION_NONE != engineState.command.relationType && BOOTSTRAPPER_RELATION_UPDATE != engineState.command.relationType)
249 {
250 LogId(REPORT_STANDARD, MSG_RESTART_ABORTED, LoggingRelationTypeToString(engineState.command.relationType));
251
252 engineState.fRestart = FALSE;
253 hr = SUCCEEDED(hr) ? HRESULT_FROM_WIN32(ERROR_SUCCESS_REBOOT_REQUIRED) : HRESULT_FROM_WIN32(ERROR_FAIL_REBOOT_REQUIRED);
254 }
255
256 if (fRunNormal)
257 {
258 LogId(REPORT_STANDARD, MSG_EXITING, FAILED(hr) ? (int)hr : *pdwExitCode, LoggingBoolToString(engineState.fRestart));
259 }
260 else if (fRunRunOnce)
261 {
262 LogId(REPORT_STANDARD, MSG_EXITING_RUN_ONCE, FAILED(hr) ? (int)hr : *pdwExitCode);
263 }
264
265 if (fLogInitialized)
266 {
267 // Leave the log open before calling restart so messages can be logged from there.
268 // Best effort to make sure all previous messages are written to disk in case the restart causes messages to be lost in buffers.
269 LogFlush();
270 }
271
272 // end per-machine process if running
273 if (!fRunElevated && INVALID_HANDLE_VALUE != engineState.companionConnection.hPipe)
274 {
275 BurnPipeTerminateChildProcess(&engineState.companionConnection, *pdwExitCode, engineState.fRestart);
276 }
277 else if (engineState.fRestart)
278 {
279 LogId(REPORT_STANDARD, MSG_RESTARTING);
280
281 HRESULT hrRestart = Restart(&engineState);
282 if (FAILED(hrRestart))
283 {
284 LogErrorId(hrRestart, MSG_RESTART_FAILED);
285 }
286 }
287
288 // If the message window is still around, close it.
289 UiCloseMessageWindow(&engineState);
290
291 // If the logging thread is still around, close it.
292 if (!fRunElevated)
293 {
294 CoreWaitForUnelevatedLoggingThread(engineState.hUnelevatedLoggingThread);
295 }
296 else if (fRunElevated)
297 {
298 CoreCloseElevatedLoggingThread(&engineState);
299
300 // We're done talking to the child so always reset logging now.
301 LogRedirect(NULL, NULL);
302
303 // If there was a log message left, try to log it locally.
304 if (engineState.elevatedLoggingContext.sczBuffer)
305 {
306 LogStringWorkRaw(engineState.elevatedLoggingContext.sczBuffer);
307
308 ReleaseStr(engineState.elevatedLoggingContext.sczBuffer);
309 }
310
311 // Log the exit code here to make sure it gets in the elevated log.
312 LogId(REPORT_STANDARD, MSG_EXITING_ELEVATED, FAILED(hr) ? (int)hr : *pdwExitCode);
313 }
314
315 UninitializeEngineState(&engineState);
316
317 if (fXmlInitialized)
318 {
319 XmlUninitialize();
320 }
321
322 if (fWiuInitialized)
323 {
324 WiuUninitialize();
325 }
326
327 if (fRegInitialized)
328 {
329 RegUninitialize();
330 }
331
332 if (fDpiuInitialized)
333 {
334 DpiuUninitialize();
335 }
336
337 if (fCrypInitialized)
338 {
339 CrypUninitialize();
340 }
341
342 if (fComInitialized)
343 {
344 ::CoUninitialize();
345 }
346
347 if (fLogInitialized)
348 {
349 DutilUninitialize();
350 LogUninitialize(FALSE);
351 }
352
353 return hr;
354 }
355
356
357 // internal function definitions
358
359 static HRESULT InitializeEngineState(
360 __in BURN_ENGINE_STATE* pEngineState,
361 __in HANDLE hEngineFile
362 )
363 {
364 HRESULT hr = S_OK;
365 HANDLE hSectionFile = hEngineFile;
366 HANDLE hSourceEngineFile = INVALID_HANDLE_VALUE;
367
368 pEngineState->hUnelevatedLoggingThread = INVALID_HANDLE_VALUE;
369 pEngineState->elevatedLoggingContext.hPipe = INVALID_HANDLE_VALUE;
370 pEngineState->elevatedLoggingContext.hThread = INVALID_HANDLE_VALUE;
371
372 ::InitializeCriticalSection(&pEngineState->csRestartState);
373 ::InitializeCriticalSection(&pEngineState->elevatedLoggingContext.csBuffer);
374
375 pEngineState->internalCommand.automaticUpdates = BURN_AU_PAUSE_ACTION_IFELEVATED;
376 ::InitializeCriticalSection(&pEngineState->userExperience.csEngineActive);
377 BurnPipeConnectionInitialize(&pEngineState->companionConnection);
378 BurnPipeConnectionInitialize(&pEngineState->embeddedConnection);
379
380 // Retain whether bundle was initially run elevated.
381 ProcElevated(::GetCurrentProcess(), &pEngineState->internalCommand.fInitiallyElevated);
382
383 // Parse command line.
384 hr = CoreParseCommandLine(&pEngineState->internalCommand, &pEngineState->command, &pEngineState->companionConnection, &pEngineState->embeddedConnection, &hSectionFile, &hSourceEngineFile);
385 ExitOnFailure(hr, "Fatal error while parsing command line.");
386
387 hr = SectionInitialize(&pEngineState->section, hSectionFile, hSourceEngineFile);
388 ExitOnFailure(hr, "Failed to initialize engine section.");
389
390 hr = CacheInitialize(&pEngineState->cache, &pEngineState->internalCommand);
391 ExitOnFailure(hr, "Failed to initialize internal cache functionality.");
392
393 LExit:
394 return hr;
395 }
396
397 static void UninitializeEngineState(
398 __in BURN_ENGINE_STATE* pEngineState
399 )
400 {
401 if (pEngineState->internalCommand.argv)
402 {
403 AppFreeCommandLineArgs(pEngineState->internalCommand.argv);
404 }
405
406 ReleaseMem(pEngineState->internalCommand.rgSecretArgs);
407 ReleaseMem(pEngineState->internalCommand.rgUnknownArgs);
408
409 ReleaseFileHandle(pEngineState->hUnelevatedLoggingThread);
410 ReleaseFileHandle(pEngineState->elevatedLoggingContext.hThread);
411 ::DeleteCriticalSection(&pEngineState->elevatedLoggingContext.csBuffer);
412 ReleaseHandle(pEngineState->elevatedLoggingContext.hLogEvent);
413 ReleaseHandle(pEngineState->elevatedLoggingContext.hFinishedEvent);
414
415 BurnPipeConnectionUninitialize(&pEngineState->embeddedConnection);
416 BurnPipeConnectionUninitialize(&pEngineState->companionConnection);
417 ReleaseStr(pEngineState->sczBundleEngineWorkingPath)
418
419 ReleaseHandle(pEngineState->hMessageWindowThread);
420
421 BurnExtensionUninitialize(&pEngineState->extensions);
422
423 ::DeleteCriticalSection(&pEngineState->userExperience.csEngineActive);
424 BootstrapperApplicationUninitialize(&pEngineState->userExperience);
425
426 ApprovedExesUninitialize(&pEngineState->approvedExes);
427 DependencyUninitialize(&pEngineState->dependencies);
428 UpdateUninitialize(&pEngineState->update);
429 VariablesUninitialize(&pEngineState->variables);
430 SearchesUninitialize(&pEngineState->searches);
431 RegistrationUninitialize(&pEngineState->registration);
432 PayloadsUninitialize(&pEngineState->payloads);
433 PackagesUninitialize(&pEngineState->packages);
434 SectionUninitialize(&pEngineState->section);
435 ContainersUninitialize(&pEngineState->containers);
436
437 ReleaseStr(pEngineState->command.wzBootstrapperApplicationDataPath);
438 ReleaseStr(pEngineState->command.wzBootstrapperWorkingFolder);
439 ReleaseStr(pEngineState->command.wzLayoutDirectory);
440 ReleaseStr(pEngineState->command.wzCommandLine);
441
442 ReleaseStr(pEngineState->internalCommand.sczActiveParent);
443 ReleaseStr(pEngineState->internalCommand.sczAncestors);
444 ReleaseStr(pEngineState->internalCommand.sczIgnoreDependencies);
445 ReleaseStr(pEngineState->internalCommand.sczLogFile);
446 ReleaseStr(pEngineState->internalCommand.sczOriginalSource);
447 ReleaseStr(pEngineState->internalCommand.sczEngineWorkingDirectory);
448
449 ReleaseStr(pEngineState->log.sczExtension);
450 ReleaseStr(pEngineState->log.sczPrefix);
451 ReleaseStr(pEngineState->log.sczPath);
452 ReleaseStr(pEngineState->log.sczPathVariable);
453
454 ::DeleteCriticalSection(&pEngineState->csRestartState);
455
456 // clear struct
457 memset(pEngineState, 0, sizeof(BURN_ENGINE_STATE));
458 }
459
460 static HRESULT RunNormal(
461 __in HINSTANCE hInstance,
462 __in BURN_ENGINE_STATE* pEngineState
463 )
464 {
465 HRESULT hr = S_OK;
466 LPWSTR sczOriginalSource = NULL;
467 LPWSTR sczCopiedOriginalSource = NULL;
468 BOOL fContinueExecution = TRUE;
469 BOOL fReloadApp = TRUE;
470 BOOL fSkipCleanup = FALSE;
471 BURN_EXTENSION_ENGINE_CONTEXT extensionEngineContext = { };
472 BOOL fRunSecondaryBootstrapperApplication = FALSE;
473
474 // Initialize logging.
475 hr = LoggingOpen(&pEngineState->log, &pEngineState->internalCommand, &pEngineState->command, &pEngineState->variables, pEngineState->registration.sczDisplayName);
476 ExitOnFailure(hr, "Failed to open log.");
477
478 // Ensure we're on a supported operating system.
479 hr = ConditionGlobalCheck(&pEngineState->variables, &pEngineState->condition, pEngineState->command.display, pEngineState->registration.sczDisplayName, &pEngineState->userExperience.dwExitCode, &fContinueExecution);
480 ExitOnFailure(hr, "Failed to check global conditions");
481
482 if (!fContinueExecution)
483 {
484 LogId(REPORT_STANDARD, MSG_FAILED_CONDITION_CHECK);
485
486 // If the block told us to abort, abort!
487 ExitFunction1(hr = S_OK);
488 }
489
490 // Create a top-level window to handle system messages.
491 hr = UiCreateMessageWindow(hInstance, pEngineState);
492 ExitOnFailure(hr, "Failed to create the message window.");
493
494 // Query registration state.
495 hr = CoreQueryRegistration(pEngineState);
496 ExitOnFailure(hr, "Failed to query registration.");
497
498 // Best effort to set the source of attached containers to BURN_BUNDLE_ORIGINAL_SOURCE.
499 hr = VariableGetString(&pEngineState->variables, BURN_BUNDLE_ORIGINAL_SOURCE, &sczOriginalSource);
500 if (SUCCEEDED(hr))
501 {
502 for (DWORD i = 0; i < pEngineState->containers.cContainers; ++i)
503 {
504 BURN_CONTAINER* pContainer = pEngineState->containers.rgContainers + i;
505 if (pContainer->fAttached)
506 {
507 hr = StrAllocString(&sczCopiedOriginalSource, sczOriginalSource, 0);
508 if (SUCCEEDED(hr))
509 {
510 ReleaseNullStr(pContainer->sczSourcePath);
511 pContainer->sczSourcePath = sczCopiedOriginalSource;
512 sczCopiedOriginalSource = NULL;
513 }
514 }
515 }
516 }
517 hr = S_OK;
518
519 // Set some built-in variables before loading the BA.
520 hr = VariableSetNumeric(&pEngineState->variables, BURN_BUNDLE_COMMAND_LINE_ACTION, pEngineState->command.action, TRUE);
521 ExitOnFailure(hr, "Failed to set command line action variable.");
522
523 hr = RegistrationSetVariables(&pEngineState->registration, &pEngineState->variables);
524 ExitOnFailure(hr, "Failed to set registration variables.");
525
526 // If a layout directory was specified on the command-line, set it as a well-known variable.
527 if (pEngineState->command.wzLayoutDirectory && *pEngineState->command.wzLayoutDirectory)
528 {
529 hr = VariableSetString(&pEngineState->variables, BURN_BUNDLE_LAYOUT_DIRECTORY, pEngineState->command.wzLayoutDirectory, FALSE, FALSE);
530 ExitOnFailure(hr, "Failed to set layout directory variable to value provided from command-line.");
531 }
532
533 // Setup the extension engine.
534 extensionEngineContext.pEngineState = pEngineState;
535
536 // Load the extensions.
537 hr = BurnExtensionLoad(&pEngineState->extensions, &extensionEngineContext);
538 ExitOnFailure(hr, "Failed to load BootstrapperExtensions.");
539
540 // The secondary bootstrapper application only gets one chance to execute. That means
541 // first time through we run the primary bootstrapper application and on reload we run
542 // the secondary bootstrapper application, and if the secondary bootstrapper application
543 // requests a reload, we load the primary bootstrapper application one last time.
544 for (DWORD i = 0; i < 3 && fReloadApp; i++)
545 {
546 fReloadApp = FALSE;
547 pEngineState->fQuit = FALSE;
548
549 hr = RunApplication(pEngineState, fRunSecondaryBootstrapperApplication, &fReloadApp, &fSkipCleanup);
550
551 // If reloading, switch to the other bootstrapper application.
552 if (fReloadApp)
553 {
554 fRunSecondaryBootstrapperApplication = !fRunSecondaryBootstrapperApplication;
555 }
556 else if (FAILED(hr))
557 {
558 break;
559 }
560 }
561
562 LExit:
563 if (!fSkipCleanup)
564 {
565 CoreCleanup(pEngineState);
566 }
567
568 BurnExtensionUnload(&pEngineState->extensions);
569
570 VariablesDump(&pEngineState->variables);
571
572 // If the splash screen is still around, close it.
573 if (::IsWindow(pEngineState->command.hwndSplashScreen))
574 {
575 ::PostMessageW(pEngineState->command.hwndSplashScreen, WM_CLOSE, 0, 0);
576 }
577
578 ReleaseStr(sczOriginalSource);
579 ReleaseStr(sczCopiedOriginalSource);
580
581 return hr;
582 }
583
584 static HRESULT RunElevated(
585 __in HINSTANCE hInstance,
586 __in LPCWSTR /*wzCommandLine*/,
587 __in BURN_ENGINE_STATE* pEngineState
588 )
589 {
590 HRESULT hr = S_OK;
591 HANDLE hLock = NULL;
592 BURN_REDIRECTED_LOGGING_CONTEXT* pLoggingContext = &pEngineState->elevatedLoggingContext;
593
594 // Initialize logging.
595 hr = LoggingOpen(&pEngineState->log, &pEngineState->internalCommand, &pEngineState->command, &pEngineState->variables, pEngineState->registration.sczDisplayName);
596 ExitOnFailure(hr, "Failed to open elevated log.");
597
598 // connect to per-user process
599 hr = BurnPipeChildConnect(&pEngineState->companionConnection, TRUE);
600 ExitOnFailure(hr, "Failed to connect to unelevated process.");
601
602 // Set up the context for the logging thread then
603 // override logging to write over the pipe.
604 pLoggingContext->hLogEvent = ::CreateEventW(NULL, TRUE, FALSE, NULL);
605 ExitOnNullWithLastError(pLoggingContext->hLogEvent, hr, "Failed to create log event for logging thread.");
606
607 pLoggingContext->hFinishedEvent = ::CreateEventW(NULL, TRUE, FALSE, NULL);
608 ExitOnNullWithLastError(pLoggingContext->hFinishedEvent, hr, "Failed to create finished event for logging thread.");
609
610 pLoggingContext->hPipe = pEngineState->companionConnection.hLoggingPipe;
611
612 pLoggingContext->hThread = ::CreateThread(NULL, 0, ElevatedLoggingThreadProc, pLoggingContext, 0, NULL);
613 ExitOnNullWithLastError(pLoggingContext->hThread, hr, "Failed to create elevated logging thread.");
614
615 LogRedirect(RedirectLoggingOverPipe, pLoggingContext);
616
617 if (!pEngineState->internalCommand.fInitiallyElevated)
618 {
619 LogId(REPORT_ERROR, MSG_ELEVATED_ENGINE_NOT_ELEVATED);
620 }
621
622 // Create a top-level window to prevent shutting down the elevated process.
623 hr = UiCreateMessageWindow(hInstance, pEngineState);
624 ExitOnFailure(hr, "Failed to create the message window.");
625
626 SrpInitialize(TRUE);
627
628 // Pump messages from parent process.
629 hr = ElevationChildPumpMessages(pEngineState->companionConnection.hPipe, pEngineState->companionConnection.hCachePipe, &pEngineState->approvedExes, &pEngineState->cache, &pEngineState->containers, &pEngineState->packages, &pEngineState->payloads, &pEngineState->variables, &pEngineState->registration, &pEngineState->userExperience, &hLock, &pEngineState->userExperience.dwExitCode, &pEngineState->fRestart, &pEngineState->plan.fApplying);
630 ExitOnFailure(hr, "Failed to pump messages from parent process.");
631
632 LExit:
633 if (hLock)
634 {
635 ::ReleaseMutex(hLock);
636 ::CloseHandle(hLock);
637 }
638
639 return hr;
640 }
641
642 static HRESULT RunEmbedded(
643 __in HINSTANCE hInstance,
644 __in BURN_ENGINE_STATE* pEngineState
645 )
646 {
647 HRESULT hr = S_OK;
648
649 // Connect to parent process.
650 hr = BurnPipeChildConnect(&pEngineState->embeddedConnection, FALSE);
651 ExitOnFailure(hr, "Failed to connect to parent of embedded process.");
652
653 // Do not register the bundle to automatically restart if embedded.
654 if (BOOTSTRAPPER_DISPLAY_EMBEDDED == pEngineState->command.display)
655 {
656 pEngineState->registration.fDisableResume = TRUE;
657 }
658
659 // Now run the application like normal.
660 hr = RunNormal(hInstance, pEngineState);
661 ExitOnFailure(hr, "Failed to run bootstrapper application embedded.");
662
663 LExit:
664 return hr;
665 }
666
667 static HRESULT RunRunOnce(
668 __in BURN_ENGINE_STATE* pEngineState,
669 __in int nCmdShow
670 )
671 {
672 HRESULT hr = S_OK;
673 LPWSTR sczNewCommandLine = NULL;
674 LPWSTR sczBurnPath = NULL;
675 HANDLE hProcess = NULL;
676
677 // Initialize logging.
678 hr = LoggingOpen(&pEngineState->log, &pEngineState->internalCommand, &pEngineState->command, &pEngineState->variables, pEngineState->registration.sczDisplayName);
679 ExitOnFailure(hr, "Failed to open run once log.");
680
681 hr = RegistrationGetResumeCommandLine(&pEngineState->registration, &sczNewCommandLine);
682 ExitOnFailure(hr, "Unable to get resume command line from the registry");
683
684 // and re-launch
685 hr = PathForCurrentProcess(&sczBurnPath, NULL);
686 ExitOnFailure(hr, "Failed to get current process path.");
687
688 hr = ProcExec(sczBurnPath, 0 < sczNewCommandLine ? sczNewCommandLine : L"", nCmdShow, &hProcess);
689 ExitOnFailure(hr, "Failed to re-launch bundle process after RunOnce: %ls", sczBurnPath);
690
691 LExit:
692 ReleaseHandle(hProcess);
693 ReleaseStr(sczNewCommandLine);
694 ReleaseStr(sczBurnPath);
695
696 return hr;
697 }
698
699 static HRESULT RunApplication(
700 __in BURN_ENGINE_STATE* pEngineState,
701 __in BOOL fSecondaryBootstrapperApplication,
702 __out BOOL* pfReloadApp,
703 __out BOOL* pfSkipCleanup
704 )
705 {
706 HRESULT hr = S_OK;
707 BOOL fStartupCalled = FALSE;
708 BAENGINE_CONTEXT* pEngineContext = NULL;
709 HANDLE rghWait[2] = { };
710 DWORD dwSignaled = 0;
711 BAENGINE_ACTION* pAction = NULL;
712 BOOTSTRAPPER_SHUTDOWN_ACTION shutdownAction = BOOTSTRAPPER_SHUTDOWN_ACTION_NONE;
713
714 // Start the bootstrapper application.
715 hr = BootstrapperApplicationStart(pEngineState, fSecondaryBootstrapperApplication);
716 ExitOnFailure(hr, "Failed to start bootstrapper application.");
717
718 pEngineContext = pEngineState->userExperience.pEngineContext;
719
720 fStartupCalled = TRUE;
721 hr = BACallbackOnStartup(&pEngineState->userExperience);
722 ExitOnFailure(hr, "Failed to start bootstrapper application.");
723
724 rghWait[0] = pEngineState->userExperience.hBAProcess;
725 rghWait[1] = pEngineContext->hQueueSemaphore;
726
727 while (!pEngineState->fQuit)
728 {
729 hr = AppWaitForMultipleObjects(countof(rghWait), rghWait, FALSE, INFINITE, &dwSignaled);
730 ExitOnFailure(hr, "Failed to wait on queue event.");
731
732 // If the bootstrapper application process exited, bail.
733 if (0 == dwSignaled)
734 {
735 pEngineState->fQuit = TRUE;
736 break;
737 }
738
739 ::EnterCriticalSection(&pEngineContext->csQueue);
740
741 hr = QueDequeue(pEngineContext->hQueue, reinterpret_cast<void**>(&pAction));
742
743 ::LeaveCriticalSection(&pEngineContext->csQueue);
744
745 ExitOnFailure(hr, "Failed to dequeue action.");
746
747 ProcessMessage(pEngineContext, pAction);
748
749 BAEngineFreeAction(pAction);
750 pAction = NULL;
751 }
752
753 LExit:
754 if (fStartupCalled)
755 {
756 BACallbackOnShutdown(&pEngineState->userExperience, &shutdownAction);
757 if (BOOTSTRAPPER_SHUTDOWN_ACTION_RESTART == shutdownAction)
758 {
759 LogId(REPORT_STANDARD, MSG_BA_REQUESTED_RESTART, LoggingBoolToString(pEngineState->fRestart));
760 pEngineState->fRestart = TRUE;
761 }
762 else if (BOOTSTRAPPER_SHUTDOWN_ACTION_RELOAD_BOOTSTRAPPER == shutdownAction)
763 {
764 LogId(REPORT_STANDARD, MSG_BA_REQUESTED_RELOAD);
765 *pfReloadApp = SUCCEEDED(hr);
766 }
767 else if (BOOTSTRAPPER_SHUTDOWN_ACTION_SKIP_CLEANUP == shutdownAction)
768 {
769 LogId(REPORT_STANDARD, MSG_BA_REQUESTED_SKIP_CLEANUP);
770 *pfSkipCleanup = TRUE;
771 }
772 }
773 else // if the bootstrapper application did not start, there won't be anything to clean up.
774 {
775 *pfSkipCleanup = TRUE;
776 }
777
778 // Stop the BA.
779 BootstrapperApplicationStop(&pEngineState->userExperience, pfReloadApp);
780
781 if (*pfReloadApp && !pEngineState->userExperience.pSecondaryExePayload)
782 {
783 // If the BA requested a reload but we do not have a secondary EXE,
784 // then log a message and do not reload.
785 LogId(REPORT_STANDARD, MSG_BA_NO_SECONDARY_BOOTSTRAPPER_SO_RELOAD_NOT_SUPPORTED);
786 *pfReloadApp = FALSE;
787 }
788
789 return hr;
790 }
791
792 static HRESULT ProcessMessage(
793 __in BAENGINE_CONTEXT* pEngineContext,
794 __in BAENGINE_ACTION* pAction
795 )
796 {
797 HRESULT hr = S_OK;
798 BURN_ENGINE_STATE* pEngineState = pEngineContext->pEngineState;
799
800 BootstrapperApplicationActivateEngine(&pEngineState->userExperience);
801
802 switch (pAction->dwMessage)
803 {
804 case WM_BURN_DETECT:
805 hr = CoreDetect(pEngineState, pAction->detect.hwndParent);
806 break;
807
808 case WM_BURN_PLAN:
809 hr = CorePlan(pEngineState, pAction->plan.action);
810 break;
811
812 case WM_BURN_ELEVATE:
813 hr = CoreElevate(pEngineState, WM_BURN_ELEVATE, pAction->elevate.hwndParent);
814 break;
815
816 case WM_BURN_APPLY:
817 hr = CoreApply(pEngineState, pAction->apply.hwndParent);
818 break;
819
820 case WM_BURN_LAUNCH_APPROVED_EXE:
821 hr = CoreLaunchApprovedExe(pEngineState, &pAction->launchApprovedExe);
822 break;
823
824 case WM_BURN_QUIT:
825 CoreQuit(pEngineContext, pAction->quit.dwExitCode);
826 break;
827 }
828
829 BootstrapperApplicationDeactivateEngine(&pEngineState->userExperience);
830
831 return hr;
832 }
833
834 static HRESULT DAPI RedirectLoggingOverPipe(
835 __in_z LPCSTR szString,
836 __in_opt LPVOID pvContext
837 )
838 {
839 HRESULT hr = S_OK;
840 BURN_REDIRECTED_LOGGING_CONTEXT* pContext = static_cast<BURN_REDIRECTED_LOGGING_CONTEXT*>(pvContext);
841
842 ::EnterCriticalSection(&pContext->csBuffer);
843
844 hr = StrAnsiAllocConcat(&pContext->sczBuffer, szString, 0);
845
846 if (SUCCEEDED(hr) && !::SetEvent(pContext->hLogEvent))
847 {
848 HRESULT hrSet = HRESULT_FROM_WIN32(::GetLastError());
849 if (FAILED(hrSet))
850 {
851 TraceError(hrSet, "Failed to set log event.");
852 }
853 }
854
855 ::LeaveCriticalSection(&pContext->csBuffer);
856
857 return hr;
858 }
859
860 static HRESULT LogStringOverPipe(
861 __in_z LPCSTR szString,
862 __in HANDLE hPipe
863 )
864 {
865 HRESULT hr = S_OK;
866 BYTE* pbData = NULL;
867 SIZE_T cbData = 0;
868 DWORD dwResult = 0;
869
870 hr = BuffWriteStringAnsi(&pbData, &cbData, szString);
871 ExitOnFailure(hr, "Failed to prepare logging pipe message.");
872
873 hr = BurnPipeSendMessage(hPipe, static_cast<DWORD>(BURN_PIPE_MESSAGE_TYPE_LOG), pbData, cbData, NULL, NULL, &dwResult);
874 ExitOnFailure(hr, "Failed to send logging message over the pipe.");
875
876 hr = (HRESULT)dwResult;
877
878 LExit:
879 ReleaseMem(pbData);
880
881 return hr;
882 }
883
884 static DWORD WINAPI ElevatedLoggingThreadProc(
885 __in LPVOID lpThreadParameter
886 )
887 {
888 HRESULT hr = S_OK;
889 DWORD dwLastError = ERROR_SUCCESS;
890 BURN_REDIRECTED_LOGGING_CONTEXT* pContext = static_cast<BURN_REDIRECTED_LOGGING_CONTEXT*>(lpThreadParameter);
891 DWORD dwSignaledIndex = 0;
892 LPSTR sczBuffer = NULL;
893 BURN_PIPE_RESULT result = { };
894 HANDLE rghEvents[2] =
895 {
896 pContext->hLogEvent,
897 pContext->hFinishedEvent,
898 };
899
900 for (;;)
901 {
902 hr = AppWaitForMultipleObjects(countof(rghEvents), rghEvents, FALSE, INFINITE, &dwSignaledIndex);
903 if (FAILED(hr))
904 {
905 LogRedirect(NULL, NULL); // reset logging so the next failure gets written locally.
906 ExitOnFailure(hr, "Failed to wait for log thread events, signaled: %u.", dwSignaledIndex);
907 }
908
909 if (1 == dwSignaledIndex)
910 {
911 LogRedirect(NULL, NULL); // No more messages will be logged over the pipe.
912 }
913
914 dwLastError = ERROR_SUCCESS;
915
916 ::EnterCriticalSection(&pContext->csBuffer);
917
918 sczBuffer = pContext->sczBuffer;
919 pContext->sczBuffer = NULL;
920
921 if (0 == dwSignaledIndex && !::ResetEvent(rghEvents[0]))
922 {
923 dwLastError = ::GetLastError();
924 }
925
926 ::LeaveCriticalSection(&pContext->csBuffer);
927
928 if (ERROR_SUCCESS != dwLastError)
929 {
930 LogRedirect(NULL, NULL); // reset logging so the next failure gets written locally.
931 ExitOnWin32Error(dwLastError, hr, "Failed to reset log event.");
932 }
933
934 if (sczBuffer)
935 {
936 hr = LogStringOverPipe(sczBuffer, pContext->hPipe);
937 if (FAILED(hr))
938 {
939 LogRedirect(NULL, NULL); // reset logging so the next failure gets written locally.
940 ExitOnFailure(hr, "Failed to wait log message over pipe.");
941 }
942
943 ReleaseStr(sczBuffer);
944 }
945
946 if (1 == dwSignaledIndex)
947 {
948 break;
949 }
950 }
951
952 LExit:
953 LogRedirect(NULL, NULL); // No more messages will be logged over the pipe.
954
955 {
956 HRESULT hrTerminate = BurnPipeTerminateLoggingPipe(pContext->hPipe, hr);
957 if (FAILED(hrTerminate))
958 {
959 TraceError(hrTerminate, "Failed to terminate logging pipe.");
960 }
961 }
962
963 // Log the message locally if it failed to go over the pipe.
964 if (sczBuffer)
965 {
966 LogStringWorkRaw(sczBuffer);
967
968 ReleaseStr(sczBuffer);
969 }
970
971 // Log any remaining message locally.
972 if (pContext->sczBuffer)
973 {
974 AssertSz(FAILED(hr), "Exiting logging thread on success even though there was a leftover message");
975 LogStringWorkRaw(pContext->sczBuffer);
976
977 ReleaseStr(pContext->sczBuffer);
978 }
979
980 return (DWORD)hr;
981 }
982
983 static HRESULT Restart(
984 __in BURN_ENGINE_STATE* pEngineState
985 )
986 {
987 HRESULT hr = S_OK;
988 DWORD dwRetries = 0;
989
990 hr = ProcEnablePrivilege(::GetCurrentProcess(), SE_SHUTDOWN_NAME);
991 ExitOnFailure(hr, "Failed to enable shutdown privilege in process token.");
992
993 pEngineState->fRestarting = TRUE;
994 CoreUpdateRestartState(pEngineState, BURN_RESTART_STATE_REQUESTING);
995
996 do
997 {
998 hr = S_OK;
999
1000 if (dwRetries)
1001 {
1002 // On retry, wait a second to let the OS try to get to a place where the restart can
1003 // be initiated.
1004 ::Sleep(1000);
1005 }
1006
1007 if (!vpfnInitiateSystemShutdownExW(NULL, NULL, 0, FALSE, TRUE, SHTDN_REASON_MAJOR_APPLICATION | SHTDN_REASON_MINOR_INSTALLATION | SHTDN_REASON_FLAG_PLANNED))
1008 {
1009 hr = HRESULT_FROM_WIN32(::GetLastError());
1010 }
1011 } while (dwRetries++ < RESTART_RETRIES && (HRESULT_FROM_WIN32(ERROR_MACHINE_LOCKED) == hr || HRESULT_FROM_WIN32(ERROR_NOT_READY) == hr));
1012 ExitOnRootFailure(hr, "Failed to schedule restart.");
1013
1014 CoreUpdateRestartState(pEngineState, BURN_RESTART_STATE_REQUESTED);
1015
1016 // Give the UI thread approximately 15 seconds to get the WM_QUERYENDSESSION message.
1017 for (DWORD i = 0; i < 60; ++i)
1018 {
1019 if (!::IsWindow(pEngineState->hMessageWindow))
1020 {
1021 ExitFunction();
1022 }
1023
1024 if (BURN_RESTART_STATE_REQUESTED < pEngineState->restartState)
1025 {
1026 break;
1027 }
1028
1029 ::Sleep(250);
1030 }
1031
1032 if (BURN_RESTART_STATE_INITIATING > pEngineState->restartState)
1033 {
1034 LogId(REPORT_WARNING, MSG_RESTART_BLOCKED);
1035 ExitFunction();
1036 }
1037
1038 // Give the UI thread the chance to process the WM_ENDSESSION message.
1039 for (;;)
1040 {
1041 if (!::IsWindow(pEngineState->hMessageWindow) || BURN_RESTART_STATE_INITIATING < pEngineState->restartState)
1042 {
1043 break;
1044 }
1045
1046 ::Sleep(250);
1047 }
1048
1049 LExit:
1050 return hr;
1051 }
1052
1053 static void CALLBACK BurnTraceError(
1054 __in_z LPCSTR /*szFile*/,
1055 __in int /*iLine*/,
1056 __in REPORT_LEVEL /*rl*/,
1057 __in UINT source,
1058 __in HRESULT hrError,
1059 __in_z __format_string LPCSTR szFormat,
1060 __in va_list args
1061 )
1062 {
1063 BOOL fLog = FALSE;
1064
1065 switch (source)
1066 {
1067 case DUTIL_SOURCE_DEFAULT:
1068 fLog = TRUE;
1069 break;
1070 default:
1071 fLog = REPORT_VERBOSE < LogGetLevel();
1072 break;
1073 }
1074
1075 if (fLog)
1076 {
1077 DutilSuppressTraceErrorSource();
1078 LogErrorStringArgs(hrError, szFormat, args);
1079 DutilUnsuppressTraceErrorSource();
1080 }
1081 }