aboutsummaryrefslogtreecommitdiff
path: root/src/engine/exeengine.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/engine/exeengine.cpp')
-rw-r--r--src/engine/exeengine.cpp820
1 files changed, 820 insertions, 0 deletions
diff --git a/src/engine/exeengine.cpp b/src/engine/exeengine.cpp
new file mode 100644
index 00000000..71540d5d
--- /dev/null
+++ b/src/engine/exeengine.cpp
@@ -0,0 +1,820 @@
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// internal function declarations
7
8static HRESULT HandleExitCode(
9 __in BURN_PACKAGE* pPackage,
10 __in DWORD dwExitCode,
11 __out BOOTSTRAPPER_APPLY_RESTART* pRestart
12 );
13static HRESULT ParseCommandLineArgumentsFromXml(
14 __in IXMLDOMNode* pixnExePackage,
15 __in BURN_PACKAGE* pPackage
16 );
17static HRESULT ParseExitCodesFromXml(
18 __in IXMLDOMNode* pixnExePackage,
19 __in BURN_PACKAGE* pPackage
20 );
21
22
23// function definitions
24
25extern "C" HRESULT ExeEngineParsePackageFromXml(
26 __in IXMLDOMNode* pixnExePackage,
27 __in BURN_PACKAGE* pPackage
28 )
29{
30 HRESULT hr = S_OK;
31 IXMLDOMNodeList* pixnNodes = NULL;
32 IXMLDOMNode* pixnNode = NULL;
33 LPWSTR scz = NULL;
34
35 // @DetectCondition
36 hr = XmlGetAttributeEx(pixnExePackage, L"DetectCondition", &pPackage->Exe.sczDetectCondition);
37 ExitOnFailure(hr, "Failed to get @DetectCondition.");
38
39 // @InstallArguments
40 hr = XmlGetAttributeEx(pixnExePackage, L"InstallArguments", &pPackage->Exe.sczInstallArguments);
41 ExitOnFailure(hr, "Failed to get @InstallArguments.");
42
43 // @UninstallArguments
44 hr = XmlGetAttributeEx(pixnExePackage, L"UninstallArguments", &pPackage->Exe.sczUninstallArguments);
45 ExitOnFailure(hr, "Failed to get @UninstallArguments.");
46
47 // @RepairArguments
48 hr = XmlGetAttributeEx(pixnExePackage, L"RepairArguments", &pPackage->Exe.sczRepairArguments);
49 ExitOnFailure(hr, "Failed to get @RepairArguments.");
50
51 // @Repairable
52 hr = XmlGetYesNoAttribute(pixnExePackage, L"Repairable", &pPackage->Exe.fRepairable);
53 if (E_NOTFOUND != hr)
54 {
55 ExitOnFailure(hr, "Failed to get @Repairable.");
56 }
57
58 // @Protocol
59 hr = XmlGetAttributeEx(pixnExePackage, L"Protocol", &scz);
60 if (SUCCEEDED(hr))
61 {
62 if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"burn", -1))
63 {
64 pPackage->Exe.protocol = BURN_EXE_PROTOCOL_TYPE_BURN;
65 }
66 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"netfx4", -1))
67 {
68 pPackage->Exe.protocol = BURN_EXE_PROTOCOL_TYPE_NETFX4;
69 }
70 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"none", -1))
71 {
72 pPackage->Exe.protocol = BURN_EXE_PROTOCOL_TYPE_NONE;
73 }
74 else
75 {
76 hr = E_UNEXPECTED;
77 ExitOnFailure(hr, "Invalid protocol type: %ls", scz);
78 }
79 }
80 else if (E_NOTFOUND != hr)
81 {
82 ExitOnFailure(hr, "Failed to get @Protocol.");
83 }
84
85 hr = ParseExitCodesFromXml(pixnExePackage, pPackage);
86 ExitOnFailure(hr, "Failed to parse exit codes.");
87
88 hr = ParseCommandLineArgumentsFromXml(pixnExePackage, pPackage);
89 ExitOnFailure(hr, "Failed to parse command lines.");
90
91LExit:
92 ReleaseObject(pixnNodes);
93 ReleaseObject(pixnNode);
94 ReleaseStr(scz);
95
96 return hr;
97}
98
99extern "C" void ExeEnginePackageUninitialize(
100 __in BURN_PACKAGE* pPackage
101 )
102{
103 ReleaseStr(pPackage->Exe.sczDetectCondition);
104 ReleaseStr(pPackage->Exe.sczInstallArguments);
105 ReleaseStr(pPackage->Exe.sczRepairArguments);
106 ReleaseStr(pPackage->Exe.sczUninstallArguments);
107 ReleaseStr(pPackage->Exe.sczIgnoreDependencies);
108 ReleaseStr(pPackage->Exe.sczAncestors);
109 //ReleaseStr(pPackage->Exe.sczProgressSwitch);
110 ReleaseMem(pPackage->Exe.rgExitCodes);
111
112 // free command-line arguments
113 if (pPackage->Exe.rgCommandLineArguments)
114 {
115 for (DWORD i = 0; i < pPackage->Exe.cCommandLineArguments; ++i)
116 {
117 BURN_EXE_COMMAND_LINE_ARGUMENT* pCommandLineArgument = &pPackage->Exe.rgCommandLineArguments[i];
118 ReleaseStr(pCommandLineArgument->sczInstallArgument);
119 ReleaseStr(pCommandLineArgument->sczUninstallArgument);
120 ReleaseStr(pCommandLineArgument->sczRepairArgument);
121 ReleaseStr(pCommandLineArgument->sczCondition);
122 }
123 MemFree(pPackage->Exe.rgCommandLineArguments);
124 }
125
126 // clear struct
127 memset(&pPackage->Exe, 0, sizeof(pPackage->Exe));
128}
129
130extern "C" HRESULT ExeEngineDetectPackage(
131 __in BURN_PACKAGE* pPackage,
132 __in BURN_VARIABLES* pVariables
133 )
134{
135 HRESULT hr = S_OK;
136 BOOL fDetected = FALSE;
137
138 // evaluate detect condition
139 if (pPackage->Exe.sczDetectCondition && *pPackage->Exe.sczDetectCondition)
140 {
141 hr = ConditionEvaluate(pVariables, pPackage->Exe.sczDetectCondition, &fDetected);
142 ExitOnFailure(hr, "Failed to evaluate executable package detect condition.");
143 }
144
145 // update detect state
146 pPackage->currentState = fDetected ? BOOTSTRAPPER_PACKAGE_STATE_PRESENT : BOOTSTRAPPER_PACKAGE_STATE_ABSENT;
147
148LExit:
149 return hr;
150}
151
152//
153// PlanCalculate - calculates the execute and rollback state for the requested package state.
154//
155extern "C" HRESULT ExeEnginePlanCalculatePackage(
156 __in BURN_PACKAGE* pPackage,
157 __out_opt BOOL* pfBARequestedCache
158 )
159{
160 HRESULT hr = S_OK;
161 //BOOL fCondition = FALSE;
162 //BOOTSTRAPPER_PACKAGE_STATE expected = BOOTSTRAPPER_PACKAGE_STATE_UNKNOWN;
163 BOOTSTRAPPER_ACTION_STATE execute = BOOTSTRAPPER_ACTION_STATE_NONE;
164 BOOTSTRAPPER_ACTION_STATE rollback = BOOTSTRAPPER_ACTION_STATE_NONE;
165 BOOL fBARequestedCache = FALSE;
166
167 //// evaluate rollback install condition
168 //if (pPackage->sczRollbackInstallCondition)
169 //{
170 // hr = ConditionEvaluate(pVariables, pPackage->sczRollbackInstallCondition, &fCondition);
171 // ExitOnFailure(hr, "Failed to evaluate rollback install condition.");
172
173 // expected = fCondition ? BOOTSTRAPPER_PACKAGE_STATE_PRESENT : BOOTSTRAPPER_PACKAGE_STATE_ABSENT;
174 //}
175
176 // execute action
177 switch (pPackage->currentState)
178 {
179 case BOOTSTRAPPER_PACKAGE_STATE_PRESENT:
180 switch (pPackage->requested)
181 {
182 case BOOTSTRAPPER_REQUEST_STATE_PRESENT:
183 execute = pPackage->Exe.fPseudoBundle ? BOOTSTRAPPER_ACTION_STATE_INSTALL : BOOTSTRAPPER_ACTION_STATE_NONE;
184 break;
185 case BOOTSTRAPPER_REQUEST_STATE_REPAIR:
186 execute = pPackage->Exe.fRepairable ? BOOTSTRAPPER_ACTION_STATE_REPAIR : BOOTSTRAPPER_ACTION_STATE_NONE;
187 break;
188 case BOOTSTRAPPER_REQUEST_STATE_ABSENT: __fallthrough;
189 case BOOTSTRAPPER_REQUEST_STATE_CACHE:
190 execute = pPackage->fUninstallable ? BOOTSTRAPPER_ACTION_STATE_UNINSTALL : BOOTSTRAPPER_ACTION_STATE_NONE;
191 break;
192 case BOOTSTRAPPER_REQUEST_STATE_FORCE_ABSENT:
193 execute = BOOTSTRAPPER_ACTION_STATE_UNINSTALL;
194 break;
195 default:
196 execute = BOOTSTRAPPER_ACTION_STATE_NONE;
197 break;
198 }
199 break;
200
201 case BOOTSTRAPPER_PACKAGE_STATE_ABSENT:
202 switch (pPackage->requested)
203 {
204 case BOOTSTRAPPER_REQUEST_STATE_PRESENT: __fallthrough;
205 case BOOTSTRAPPER_REQUEST_STATE_REPAIR:
206 execute = BOOTSTRAPPER_ACTION_STATE_INSTALL;
207 break;
208 case BOOTSTRAPPER_REQUEST_STATE_CACHE:
209 execute = BOOTSTRAPPER_ACTION_STATE_NONE;
210 fBARequestedCache = TRUE;
211 break;
212 default:
213 execute = BOOTSTRAPPER_ACTION_STATE_NONE;
214 break;
215 }
216 break;
217
218 default:
219 hr = E_INVALIDARG;
220 ExitOnRootFailure(hr, "Invalid package current state: %d.", pPackage->currentState);
221 }
222
223 // Calculate the rollback action if there is an execute action.
224 if (BOOTSTRAPPER_ACTION_STATE_NONE != execute)
225 {
226 switch (BOOTSTRAPPER_PACKAGE_STATE_UNKNOWN != pPackage->expected ? pPackage->expected : pPackage->currentState)
227 {
228 case BOOTSTRAPPER_PACKAGE_STATE_PRESENT:
229 switch (pPackage->requested)
230 {
231 case BOOTSTRAPPER_REQUEST_STATE_PRESENT: __fallthrough;
232 case BOOTSTRAPPER_REQUEST_STATE_REPAIR:
233 rollback = BOOTSTRAPPER_ACTION_STATE_NONE;
234 break;
235 case BOOTSTRAPPER_REQUEST_STATE_FORCE_ABSENT: __fallthrough;
236 case BOOTSTRAPPER_REQUEST_STATE_ABSENT:
237 rollback = BOOTSTRAPPER_ACTION_STATE_INSTALL;
238 break;
239 default:
240 rollback = BOOTSTRAPPER_ACTION_STATE_NONE;
241 break;
242 }
243 break;
244
245 case BOOTSTRAPPER_PACKAGE_STATE_ABSENT:
246 switch (pPackage->requested)
247 {
248 case BOOTSTRAPPER_REQUEST_STATE_PRESENT: __fallthrough;
249 case BOOTSTRAPPER_REQUEST_STATE_REPAIR:
250 rollback = pPackage->fUninstallable ? BOOTSTRAPPER_ACTION_STATE_UNINSTALL : BOOTSTRAPPER_ACTION_STATE_NONE;
251 break;
252 case BOOTSTRAPPER_REQUEST_STATE_FORCE_ABSENT: __fallthrough;
253 case BOOTSTRAPPER_REQUEST_STATE_ABSENT:
254 rollback = BOOTSTRAPPER_ACTION_STATE_NONE;
255 break;
256 default:
257 rollback = BOOTSTRAPPER_ACTION_STATE_NONE;
258 break;
259 }
260 break;
261
262 default:
263 hr = E_INVALIDARG;
264 ExitOnRootFailure(hr, "Invalid package expected state.");
265 }
266 }
267
268 // return values
269 pPackage->execute = execute;
270 pPackage->rollback = rollback;
271
272 if (pfBARequestedCache)
273 {
274 *pfBARequestedCache = fBARequestedCache;
275 }
276
277LExit:
278 return hr;
279}
280
281//
282// PlanAdd - adds the calculated execute and rollback actions for the package.
283//
284extern "C" HRESULT ExeEnginePlanAddPackage(
285 __in_opt DWORD *pdwInsertSequence,
286 __in BURN_PACKAGE* pPackage,
287 __in BURN_PLAN* pPlan,
288 __in BURN_LOGGING* pLog,
289 __in BURN_VARIABLES* pVariables,
290 __in_opt HANDLE hCacheEvent,
291 __in BOOL fPlanPackageCacheRollback
292 )
293{
294 HRESULT hr = S_OK;
295 BURN_EXECUTE_ACTION* pAction = NULL;
296
297 // add wait for cache
298 if (hCacheEvent)
299 {
300 hr = PlanExecuteCacheSyncAndRollback(pPlan, pPackage, hCacheEvent, fPlanPackageCacheRollback);
301 ExitOnFailure(hr, "Failed to plan package cache syncpoint");
302 }
303
304 hr = DependencyPlanPackage(pdwInsertSequence, pPackage, pPlan);
305 ExitOnFailure(hr, "Failed to plan package dependency actions.");
306
307 // add execute action
308 if (BOOTSTRAPPER_ACTION_STATE_NONE != pPackage->execute)
309 {
310 if (NULL != pdwInsertSequence)
311 {
312 hr = PlanInsertExecuteAction(*pdwInsertSequence, pPlan, &pAction);
313 ExitOnFailure(hr, "Failed to insert execute action.");
314 }
315 else
316 {
317 hr = PlanAppendExecuteAction(pPlan, &pAction);
318 ExitOnFailure(hr, "Failed to append execute action.");
319 }
320
321 pAction->type = BURN_EXECUTE_ACTION_TYPE_EXE_PACKAGE;
322 pAction->exePackage.pPackage = pPackage;
323 pAction->exePackage.fFireAndForget = (BOOTSTRAPPER_ACTION_UPDATE_REPLACE == pPlan->action);
324 pAction->exePackage.action = pPackage->execute;
325
326 if (pPackage->Exe.sczIgnoreDependencies)
327 {
328 hr = StrAllocString(&pAction->exePackage.sczIgnoreDependencies, pPackage->Exe.sczIgnoreDependencies, 0);
329 ExitOnFailure(hr, "Failed to allocate the list of dependencies to ignore.");
330 }
331
332 if (pPackage->Exe.sczAncestors)
333 {
334 hr = StrAllocString(&pAction->exePackage.sczAncestors, pPackage->Exe.sczAncestors, 0);
335 ExitOnFailure(hr, "Failed to allocate the list of ancestors.");
336 }
337
338 LoggingSetPackageVariable(pPackage, NULL, FALSE, pLog, pVariables, NULL); // ignore errors.
339 }
340
341 // add rollback action
342 if (BOOTSTRAPPER_ACTION_STATE_NONE != pPackage->rollback)
343 {
344 hr = PlanAppendRollbackAction(pPlan, &pAction);
345 ExitOnFailure(hr, "Failed to append rollback action.");
346
347 pAction->type = BURN_EXECUTE_ACTION_TYPE_EXE_PACKAGE;
348 pAction->exePackage.pPackage = pPackage;
349 pAction->exePackage.action = pPackage->rollback;
350
351 if (pPackage->Exe.sczIgnoreDependencies)
352 {
353 hr = StrAllocString(&pAction->exePackage.sczIgnoreDependencies, pPackage->Exe.sczIgnoreDependencies, 0);
354 ExitOnFailure(hr, "Failed to allocate the list of dependencies to ignore.");
355 }
356
357 if (pPackage->Exe.sczAncestors)
358 {
359 hr = StrAllocString(&pAction->exePackage.sczAncestors, pPackage->Exe.sczAncestors, 0);
360 ExitOnFailure(hr, "Failed to allocate the list of ancestors.");
361 }
362
363 LoggingSetPackageVariable(pPackage, NULL, TRUE, pLog, pVariables, NULL); // ignore errors.
364 }
365
366LExit:
367 return hr;
368}
369
370extern "C" HRESULT ExeEngineExecutePackage(
371 __in BURN_EXECUTE_ACTION* pExecuteAction,
372 __in BURN_VARIABLES* pVariables,
373 __in BOOL fRollback,
374 __in PFN_GENERICMESSAGEHANDLER pfnGenericMessageHandler,
375 __in LPVOID pvContext,
376 __out BOOTSTRAPPER_APPLY_RESTART* pRestart
377 )
378{
379 HRESULT hr = S_OK;
380 WCHAR wzCurrentDirectory[MAX_PATH] = { };
381 BOOL fChangedCurrentDirectory = FALSE;
382 int nResult = IDNOACTION;
383 LPCWSTR wzArguments = NULL;
384 LPWSTR sczArguments = NULL;
385 LPWSTR sczArgumentsFormatted = NULL;
386 LPWSTR sczArgumentsObfuscated = NULL;
387 LPWSTR sczCachedDirectory = NULL;
388 LPWSTR sczExecutablePath = NULL;
389 LPWSTR sczCommand = NULL;
390 LPWSTR sczCommandObfuscated = NULL;
391 HANDLE hExecutableFile = INVALID_HANDLE_VALUE;
392 STARTUPINFOW si = { };
393 PROCESS_INFORMATION pi = { };
394 DWORD dwExitCode = 0;
395 GENERIC_EXECUTE_MESSAGE message = { };
396
397 // get cached executable path
398 hr = CacheGetCompletedPath(pExecuteAction->exePackage.pPackage->fPerMachine, pExecuteAction->exePackage.pPackage->sczCacheId, &sczCachedDirectory);
399 ExitOnFailure(hr, "Failed to get cached path for package: %ls", pExecuteAction->exePackage.pPackage->sczId);
400
401 // Best effort to set the execute package cache folder and action variables.
402 VariableSetString(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_CACHE_FOLDER, sczCachedDirectory, TRUE);
403 VariableSetNumeric(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_ACTION, pExecuteAction->exePackage.action, TRUE);
404
405 hr = PathConcat(sczCachedDirectory, pExecuteAction->exePackage.pPackage->rgPayloads[0].pPayload->sczFilePath, &sczExecutablePath);
406 ExitOnFailure(hr, "Failed to build executable path.");
407
408 // pick arguments
409 switch (pExecuteAction->exePackage.action)
410 {
411 case BOOTSTRAPPER_ACTION_STATE_INSTALL:
412 wzArguments = pExecuteAction->exePackage.pPackage->Exe.sczInstallArguments;
413 break;
414
415 case BOOTSTRAPPER_ACTION_STATE_UNINSTALL:
416 wzArguments = pExecuteAction->exePackage.pPackage->Exe.sczUninstallArguments;
417 break;
418
419 case BOOTSTRAPPER_ACTION_STATE_REPAIR:
420 wzArguments = pExecuteAction->exePackage.pPackage->Exe.sczRepairArguments;
421 break;
422
423 default:
424 hr = E_INVALIDARG;
425 ExitOnFailure(hr, "Invalid Exe package action: %d.", pExecuteAction->exePackage.action);
426 }
427
428 // now add optional arguments
429 hr = StrAllocString(&sczArguments, wzArguments && *wzArguments ? wzArguments : L"", 0);
430 ExitOnFailure(hr, "Failed to copy package arguments.");
431
432 for (DWORD i = 0; i < pExecuteAction->exePackage.pPackage->Exe.cCommandLineArguments; ++i)
433 {
434 BURN_EXE_COMMAND_LINE_ARGUMENT* commandLineArgument = &pExecuteAction->exePackage.pPackage->Exe.rgCommandLineArguments[i];
435 BOOL fCondition = FALSE;
436
437 hr = ConditionEvaluate(pVariables, commandLineArgument->sczCondition, &fCondition);
438 ExitOnFailure(hr, "Failed to evaluate executable package command-line condition.");
439
440 if (fCondition)
441 {
442 hr = StrAllocConcat(&sczArguments, L" ", 0);
443 ExitOnFailure(hr, "Failed to separate command-line arguments.");
444
445 switch (pExecuteAction->exePackage.action)
446 {
447 case BOOTSTRAPPER_ACTION_STATE_INSTALL:
448 hr = StrAllocConcat(&sczArguments, commandLineArgument->sczInstallArgument, 0);
449 ExitOnFailure(hr, "Failed to get command-line argument for install.");
450 break;
451
452 case BOOTSTRAPPER_ACTION_STATE_UNINSTALL:
453 hr = StrAllocConcat(&sczArguments, commandLineArgument->sczUninstallArgument, 0);
454 ExitOnFailure(hr, "Failed to get command-line argument for uninstall.");
455 break;
456
457 case BOOTSTRAPPER_ACTION_STATE_REPAIR:
458 hr = StrAllocConcat(&sczArguments, commandLineArgument->sczRepairArgument, 0);
459 ExitOnFailure(hr, "Failed to get command-line argument for repair.");
460 break;
461
462 default:
463 hr = E_INVALIDARG;
464 ExitOnFailure(hr, "Invalid Exe package action: %d.", pExecuteAction->exePackage.action);
465 }
466 }
467 }
468
469 // build command
470 if (0 < lstrlenW(sczArguments))
471 {
472 hr = VariableFormatString(pVariables, sczArguments, &sczArgumentsFormatted, NULL);
473 ExitOnFailure(hr, "Failed to format argument string.");
474
475 hr = StrAllocFormattedSecure(&sczCommand, L"\"%ls\" %s", sczExecutablePath, sczArgumentsFormatted);
476 ExitOnFailure(hr, "Failed to create executable command.");
477
478 hr = VariableFormatStringObfuscated(pVariables, sczArguments, &sczArgumentsObfuscated, NULL);
479 ExitOnFailure(hr, "Failed to format obfuscated argument string.");
480
481 hr = StrAllocFormatted(&sczCommandObfuscated, L"\"%ls\" %s", sczExecutablePath, sczArgumentsObfuscated);
482 }
483 else
484 {
485 hr = StrAllocFormatted(&sczCommand, L"\"%ls\"", sczExecutablePath);
486 ExitOnFailure(hr, "Failed to create executable command.");
487
488 hr = StrAllocFormatted(&sczCommandObfuscated, L"\"%ls\"", sczExecutablePath);
489 }
490 ExitOnFailure(hr, "Failed to create obfuscated executable command.");
491
492 if (pExecuteAction->exePackage.pPackage->Exe.fSupportsAncestors)
493 {
494 // Add the list of dependencies to ignore, if any, to the burn command line.
495 if (pExecuteAction->exePackage.sczIgnoreDependencies && BURN_EXE_PROTOCOL_TYPE_BURN == pExecuteAction->exePackage.pPackage->Exe.protocol)
496 {
497 hr = StrAllocFormattedSecure(&sczCommand, L"%ls -%ls=%ls", sczCommand, BURN_COMMANDLINE_SWITCH_IGNOREDEPENDENCIES, pExecuteAction->exePackage.sczIgnoreDependencies);
498 ExitOnFailure(hr, "Failed to append the list of dependencies to ignore to the command line.");
499
500 hr = StrAllocFormatted(&sczCommandObfuscated, L"%ls -%ls=%ls", sczCommandObfuscated, BURN_COMMANDLINE_SWITCH_IGNOREDEPENDENCIES, pExecuteAction->exePackage.sczIgnoreDependencies);
501 ExitOnFailure(hr, "Failed to append the list of dependencies to ignore to the obfuscated command line.");
502 }
503
504 // Add the list of ancestors, if any, to the burn command line.
505 if (pExecuteAction->exePackage.sczAncestors)
506 {
507 hr = StrAllocFormattedSecure(&sczCommand, L"%ls -%ls=%ls", sczCommand, BURN_COMMANDLINE_SWITCH_ANCESTORS, pExecuteAction->exePackage.sczAncestors);
508 ExitOnFailure(hr, "Failed to append the list of ancestors to the command line.");
509
510 hr = StrAllocFormatted(&sczCommandObfuscated, L"%ls -%ls=%ls", sczCommandObfuscated, BURN_COMMANDLINE_SWITCH_ANCESTORS, pExecuteAction->exePackage.sczAncestors);
511 ExitOnFailure(hr, "Failed to append the list of ancestors to the obfuscated command line.");
512 }
513 }
514
515 if (BURN_EXE_PROTOCOL_TYPE_BURN == pExecuteAction->exePackage.pPackage->Exe.protocol)
516 {
517 hr = CoreAppendFileHandleSelfToCommandLine(sczExecutablePath, &hExecutableFile, &sczCommand, &sczCommandObfuscated);
518 ExitOnFailure(hr, "Failed to append %ls", BURN_COMMANDLINE_SWITCH_FILEHANDLE_SELF);
519 }
520
521 // Log before we add the secret pipe name and client token for embedded processes.
522 LogId(REPORT_STANDARD, MSG_APPLYING_PACKAGE, LoggingRollbackOrExecute(fRollback), pExecuteAction->exePackage.pPackage->sczId, LoggingActionStateToString(pExecuteAction->exePackage.action), sczExecutablePath, sczCommandObfuscated);
523
524 if (!pExecuteAction->exePackage.fFireAndForget && BURN_EXE_PROTOCOL_TYPE_BURN == pExecuteAction->exePackage.pPackage->Exe.protocol)
525 {
526 hr = EmbeddedRunBundle(sczExecutablePath, sczCommand, pfnGenericMessageHandler, pvContext, &dwExitCode);
527 ExitOnFailure(hr, "Failed to run bundle as embedded from path: %ls", sczExecutablePath);
528 }
529 else if (!pExecuteAction->exePackage.fFireAndForget && BURN_EXE_PROTOCOL_TYPE_NETFX4 == pExecuteAction->exePackage.pPackage->Exe.protocol)
530 {
531 hr = NetFxRunChainer(sczExecutablePath, sczCommand, pfnGenericMessageHandler, pvContext, &dwExitCode);
532 ExitOnFailure(hr, "Failed to run netfx chainer: %ls", sczExecutablePath);
533 }
534 else // create and wait for the executable process while sending fake progress to allow cancel.
535 {
536 // Make the cache location of the executable the current directory to help those executables
537 // that expect stuff to be relative to them.
538 if (::GetCurrentDirectoryW(countof(wzCurrentDirectory), wzCurrentDirectory))
539 {
540 fChangedCurrentDirectory = ::SetCurrentDirectoryW(sczCachedDirectory);
541 }
542
543 si.cb = sizeof(si); // TODO: hookup the stdin/stdout/stderr pipes for logging purposes?
544 if (!::CreateProcessW(sczExecutablePath, sczCommand, NULL, NULL, TRUE, CREATE_NO_WINDOW, NULL, NULL, &si, &pi))
545 {
546 ExitWithLastError(hr, "Failed to CreateProcess on path: %ls", sczExecutablePath);
547 }
548
549 if (pExecuteAction->exePackage.fFireAndForget)
550 {
551 ::WaitForInputIdle(pi.hProcess, 5000);
552 ExitFunction();
553 }
554
555 do
556 {
557 message.type = GENERIC_EXECUTE_MESSAGE_PROGRESS;
558 message.dwAllowedResults = MB_OKCANCEL;
559 message.progress.dwPercentage = 50;
560 nResult = pfnGenericMessageHandler(&message, pvContext);
561 hr = (IDOK == nResult || IDNOACTION == nResult) ? S_OK : IDCANCEL == nResult ? HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT) : HRESULT_FROM_WIN32(ERROR_INSTALL_FAILURE);
562 ExitOnRootFailure(hr, "Bootstrapper application aborted during EXE progress.");
563
564 hr = ProcWaitForCompletion(pi.hProcess, 500, &dwExitCode);
565 if (HRESULT_FROM_WIN32(WAIT_TIMEOUT) != hr)
566 {
567 ExitOnFailure(hr, "Failed to wait for executable to complete: %ls", sczExecutablePath);
568 }
569 } while (HRESULT_FROM_WIN32(WAIT_TIMEOUT) == hr);
570 }
571
572 hr = HandleExitCode(pExecuteAction->exePackage.pPackage, dwExitCode, pRestart);
573 ExitOnRootFailure(hr, "Process returned error: 0x%x", dwExitCode);
574
575LExit:
576 if (fChangedCurrentDirectory)
577 {
578 ::SetCurrentDirectoryW(wzCurrentDirectory);
579 }
580
581 StrSecureZeroFreeString(sczArguments);
582 StrSecureZeroFreeString(sczArgumentsFormatted);
583 ReleaseStr(sczArgumentsObfuscated);
584 ReleaseStr(sczCachedDirectory);
585 ReleaseStr(sczExecutablePath);
586 StrSecureZeroFreeString(sczCommand);
587 ReleaseStr(sczCommandObfuscated);
588
589 ReleaseHandle(pi.hThread);
590 ReleaseHandle(pi.hProcess);
591 ReleaseFileHandle(hExecutableFile);
592
593 // Best effort to clear the execute package cache folder and action variables.
594 VariableSetString(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_CACHE_FOLDER, NULL, TRUE);
595 VariableSetString(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_ACTION, NULL, TRUE);
596
597 return hr;
598}
599
600
601// internal helper functions
602
603static HRESULT ParseExitCodesFromXml(
604 __in IXMLDOMNode* pixnExePackage,
605 __in BURN_PACKAGE* pPackage
606 )
607{
608 HRESULT hr = S_OK;
609 IXMLDOMNodeList* pixnNodes = NULL;
610 IXMLDOMNode* pixnNode = NULL;
611 DWORD cNodes = 0;
612 LPWSTR scz = NULL;
613
614 // select exit code nodes
615 hr = XmlSelectNodes(pixnExePackage, L"ExitCode", &pixnNodes);
616 ExitOnFailure(hr, "Failed to select exit code nodes.");
617
618 // get exit code node count
619 hr = pixnNodes->get_length((long*) &cNodes);
620 ExitOnFailure(hr, "Failed to get exit code node count.");
621
622 if (cNodes)
623 {
624 // allocate memory for exit codes
625 pPackage->Exe.rgExitCodes = (BURN_EXE_EXIT_CODE*) MemAlloc(sizeof(BURN_EXE_EXIT_CODE) * cNodes, TRUE);
626 ExitOnNull(pPackage->Exe.rgExitCodes, hr, E_OUTOFMEMORY, "Failed to allocate memory for exit code structs.");
627
628 pPackage->Exe.cExitCodes = cNodes;
629
630 // parse package elements
631 for (DWORD i = 0; i < cNodes; ++i)
632 {
633 BURN_EXE_EXIT_CODE* pExitCode = &pPackage->Exe.rgExitCodes[i];
634
635 hr = XmlNextElement(pixnNodes, &pixnNode, NULL);
636 ExitOnFailure(hr, "Failed to get next node.");
637
638 // @Type
639 hr = XmlGetAttributeNumber(pixnNode, L"Type", (DWORD*)&pExitCode->type);
640 ExitOnFailure(hr, "Failed to get @Type.");
641
642 // @Code
643 hr = XmlGetAttributeEx(pixnNode, L"Code", &scz);
644 ExitOnFailure(hr, "Failed to get @Code.");
645
646 if (L'*' == scz[0])
647 {
648 pExitCode->fWildcard = TRUE;
649 }
650 else
651 {
652 hr = StrStringToUInt32(scz, 0, (UINT*) &pExitCode->dwCode);
653 ExitOnFailure(hr, "Failed to parse @Code value: %ls", scz);
654 }
655
656 // prepare next iteration
657 ReleaseNullObject(pixnNode);
658 }
659 }
660
661 hr = S_OK;
662
663LExit:
664 ReleaseObject(pixnNodes);
665 ReleaseObject(pixnNode);
666 ReleaseStr(scz);
667
668 return hr;
669}
670
671static HRESULT ParseCommandLineArgumentsFromXml(
672 __in IXMLDOMNode* pixnExePackage,
673 __in BURN_PACKAGE* pPackage
674 )
675{
676 HRESULT hr = S_OK;
677 IXMLDOMNodeList* pixnNodes = NULL;
678 IXMLDOMNode* pixnNode = NULL;
679 DWORD cNodes = 0;
680 LPWSTR scz = NULL;
681
682 // Select command-line argument nodes.
683 hr = XmlSelectNodes(pixnExePackage, L"CommandLine", &pixnNodes);
684 ExitOnFailure(hr, "Failed to select command-line argument nodes.");
685
686 // Get command-line argument node count.
687 hr = pixnNodes->get_length((long*) &cNodes);
688 ExitOnFailure(hr, "Failed to get command-line argument count.");
689
690 if (cNodes)
691 {
692 pPackage->Exe.rgCommandLineArguments = (BURN_EXE_COMMAND_LINE_ARGUMENT*) MemAlloc(sizeof(BURN_EXE_COMMAND_LINE_ARGUMENT) * cNodes, TRUE);
693 ExitOnNull(pPackage->Exe.rgCommandLineArguments, hr, E_OUTOFMEMORY, "Failed to allocate memory for command-line argument structs.");
694
695 pPackage->Exe.cCommandLineArguments = cNodes;
696
697 // Parse command-line argument elements.
698 for (DWORD i = 0; i < cNodes; ++i)
699 {
700 BURN_EXE_COMMAND_LINE_ARGUMENT* pCommandLineArgument = &pPackage->Exe.rgCommandLineArguments[i];
701
702 hr = XmlNextElement(pixnNodes, &pixnNode, NULL);
703 ExitOnFailure(hr, "Failed to get next command-line argument node.");
704
705 // @InstallArgument
706 hr = XmlGetAttributeEx(pixnNode, L"InstallArgument", &pCommandLineArgument->sczInstallArgument);
707 ExitOnFailure(hr, "Failed to get @InstallArgument.");
708
709 // @UninstallArgument
710 hr = XmlGetAttributeEx(pixnNode, L"UninstallArgument", &pCommandLineArgument->sczUninstallArgument);
711 ExitOnFailure(hr, "Failed to get @UninstallArgument.");
712
713 // @RepairArgument
714 hr = XmlGetAttributeEx(pixnNode, L"RepairArgument", &pCommandLineArgument->sczRepairArgument);
715 ExitOnFailure(hr, "Failed to get @RepairArgument.");
716
717 // @Condition
718 hr = XmlGetAttributeEx(pixnNode, L"Condition", &pCommandLineArgument->sczCondition);
719 ExitOnFailure(hr, "Failed to get @Condition.");
720
721 // Prepare next iteration.
722 ReleaseNullObject(pixnNode);
723 }
724 }
725
726 hr = S_OK;
727
728LExit:
729 ReleaseObject(pixnNodes);
730 ReleaseObject(pixnNode);
731 ReleaseStr(scz);
732
733 return hr;
734}
735
736static HRESULT HandleExitCode(
737 __in BURN_PACKAGE* pPackage,
738 __in DWORD dwExitCode,
739 __out BOOTSTRAPPER_APPLY_RESTART* pRestart
740 )
741{
742 HRESULT hr = S_OK;
743 BURN_EXE_EXIT_CODE_TYPE typeCode = BURN_EXE_EXIT_CODE_TYPE_NONE;
744
745 for (DWORD i = 0; i < pPackage->Exe.cExitCodes; ++i)
746 {
747 BURN_EXE_EXIT_CODE* pExitCode = &pPackage->Exe.rgExitCodes[i];
748
749 // If this is a wildcard, use the last one we come across.
750 if (pExitCode->fWildcard)
751 {
752 typeCode = pExitCode->type;
753 }
754 else if (dwExitCode == pExitCode->dwCode) // If we have an exact match on the error code use that and stop looking.
755 {
756 typeCode = pExitCode->type;
757 break;
758 }
759 }
760
761 // If we didn't find a matching code then treat 0 as success, the standard restarts codes as restarts
762 // and everything else as an error.
763 if (BURN_EXE_EXIT_CODE_TYPE_NONE == typeCode)
764 {
765 if (0 == dwExitCode)
766 {
767 typeCode = BURN_EXE_EXIT_CODE_TYPE_SUCCESS;
768 }
769 else if (ERROR_SUCCESS_REBOOT_REQUIRED == dwExitCode ||
770 HRESULT_FROM_WIN32(ERROR_SUCCESS_REBOOT_REQUIRED) == static_cast<HRESULT>(dwExitCode) ||
771 ERROR_SUCCESS_RESTART_REQUIRED == dwExitCode ||
772 HRESULT_FROM_WIN32(ERROR_SUCCESS_RESTART_REQUIRED) == static_cast<HRESULT>(dwExitCode))
773 {
774 typeCode = BURN_EXE_EXIT_CODE_TYPE_SCHEDULE_REBOOT;
775 }
776 else if (ERROR_SUCCESS_REBOOT_INITIATED == dwExitCode ||
777 HRESULT_FROM_WIN32(ERROR_SUCCESS_REBOOT_INITIATED) == static_cast<HRESULT>(dwExitCode))
778 {
779 typeCode = BURN_EXE_EXIT_CODE_TYPE_FORCE_REBOOT;
780 }
781 else
782 {
783 typeCode = BURN_EXE_EXIT_CODE_TYPE_ERROR;
784 }
785 }
786
787 switch (typeCode)
788 {
789 case BURN_EXE_EXIT_CODE_TYPE_SUCCESS:
790 *pRestart = BOOTSTRAPPER_APPLY_RESTART_NONE;
791 hr = S_OK;
792 break;
793
794 case BURN_EXE_EXIT_CODE_TYPE_ERROR:
795 *pRestart = BOOTSTRAPPER_APPLY_RESTART_NONE;
796 hr = HRESULT_FROM_WIN32(dwExitCode);
797 if (SUCCEEDED(hr))
798 {
799 hr = E_FAIL;
800 }
801 break;
802
803 case BURN_EXE_EXIT_CODE_TYPE_SCHEDULE_REBOOT:
804 *pRestart = BOOTSTRAPPER_APPLY_RESTART_REQUIRED;
805 hr = S_OK;
806 break;
807
808 case BURN_EXE_EXIT_CODE_TYPE_FORCE_REBOOT:
809 *pRestart = BOOTSTRAPPER_APPLY_RESTART_INITIATED;
810 hr = S_OK;
811 break;
812
813 default:
814 hr = E_UNEXPECTED;
815 break;
816 }
817
818//LExit:
819 return hr;
820}