diff options
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 | } | ||