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