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