aboutsummaryrefslogtreecommitdiff
path: root/src/burn/engine/msuengine.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/msuengine.cpp
parent9c2aed97299fb96aeee3f1471ce40225437aaecf (diff)
downloadwix-af10c45d7b3a44af0b461a557847fe03263dcc10.tar.gz
wix-af10c45d7b3a44af0b461a557847fe03263dcc10.tar.bz2
wix-af10c45d7b3a44af0b461a557847fe03263dcc10.zip
Move burn into burn
Diffstat (limited to 'src/burn/engine/msuengine.cpp')
-rw-r--r--src/burn/engine/msuengine.cpp529
1 files changed, 529 insertions, 0 deletions
diff --git a/src/burn/engine/msuengine.cpp b/src/burn/engine/msuengine.cpp
new file mode 100644
index 00000000..6003123b
--- /dev/null
+++ b/src/burn/engine/msuengine.cpp
@@ -0,0 +1,529 @@
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#define WU_S_REBOOT_REQUIRED 0x00240005L
9#define WU_S_ALREADY_INSTALLED 0x00240006L
10
11
12// function definitions
13static HRESULT EnsureWUServiceEnabled(
14 __in BOOL fStopWusaService,
15 __out SC_HANDLE* pschWu,
16 __out BOOL* pfPreviouslyDisabled
17 );
18static HRESULT SetServiceStartType(
19 __in SC_HANDLE sch,
20 __in DWORD stratType
21 );
22static HRESULT StopWUService(
23 __in SC_HANDLE schWu
24 );
25
26
27extern "C" HRESULT MsuEngineParsePackageFromXml(
28 __in IXMLDOMNode* pixnMsuPackage,
29 __in BURN_PACKAGE* pPackage
30 )
31{
32 HRESULT hr = S_OK;
33
34 // @KB
35 hr = XmlGetAttributeEx(pixnMsuPackage, L"KB", &pPackage->Msu.sczKB);
36 ExitOnFailure(hr, "Failed to get @KB.");
37
38 // @DetectCondition
39 hr = XmlGetAttributeEx(pixnMsuPackage, L"DetectCondition", &pPackage->Msu.sczDetectCondition);
40 ExitOnFailure(hr, "Failed to get @DetectCondition.");
41
42LExit:
43 return hr;
44}
45
46extern "C" void MsuEnginePackageUninitialize(
47 __in BURN_PACKAGE* pPackage
48 )
49{
50 ReleaseNullStr(pPackage->Msu.sczKB);
51 ReleaseNullStr(pPackage->Msu.sczDetectCondition);
52}
53
54extern "C" HRESULT MsuEngineDetectPackage(
55 __in BURN_PACKAGE* pPackage,
56 __in BURN_VARIABLES* pVariables
57 )
58{
59 HRESULT hr = S_OK;
60 BOOL fDetected = FALSE;
61
62 // evaluate detect condition
63 if (pPackage->Msu.sczDetectCondition && *pPackage->Msu.sczDetectCondition)
64 {
65 hr = ConditionEvaluate(pVariables, pPackage->Msu.sczDetectCondition, &fDetected);
66 ExitOnFailure(hr, "Failed to evaluate MSU package detect condition.");
67 }
68
69 // update detect state
70 pPackage->currentState = fDetected ? BOOTSTRAPPER_PACKAGE_STATE_PRESENT : BOOTSTRAPPER_PACKAGE_STATE_ABSENT;
71
72 if (pPackage->fCanAffectRegistration)
73 {
74 pPackage->installRegistrationState = BOOTSTRAPPER_PACKAGE_STATE_ABSENT < pPackage->currentState ? BURN_PACKAGE_REGISTRATION_STATE_PRESENT : BURN_PACKAGE_REGISTRATION_STATE_ABSENT;
75 }
76
77LExit:
78 return hr;
79}
80
81//
82// PlanCalculate - calculates the execute and rollback state for the requested package state.
83//
84extern "C" HRESULT MsuEnginePlanCalculatePackage(
85 __in BURN_PACKAGE* pPackage
86 )
87{
88 HRESULT hr = S_OK;
89 BOOTSTRAPPER_ACTION_STATE execute = BOOTSTRAPPER_ACTION_STATE_NONE;
90 BOOTSTRAPPER_ACTION_STATE rollback = BOOTSTRAPPER_ACTION_STATE_NONE;
91 BOOL fAllowUninstall = FALSE;
92
93 // We can only uninstall MSU packages if they have a KB and we are on Win7 or newer.
94 fAllowUninstall = pPackage->Msu.sczKB && *pPackage->Msu.sczKB && ::IsWindows7OrGreater();
95
96 // execute action
97 switch (pPackage->currentState)
98 {
99 case BOOTSTRAPPER_PACKAGE_STATE_PRESENT:
100 switch (pPackage->requested)
101 {
102 case BOOTSTRAPPER_REQUEST_STATE_PRESENT: __fallthrough;
103 case BOOTSTRAPPER_REQUEST_STATE_REPAIR:
104 execute = BOOTSTRAPPER_ACTION_STATE_NONE;
105 break;
106
107 case BOOTSTRAPPER_REQUEST_STATE_ABSENT: __fallthrough;
108 case BOOTSTRAPPER_REQUEST_STATE_CACHE:
109 execute = fAllowUninstall && pPackage->fUninstallable ? BOOTSTRAPPER_ACTION_STATE_UNINSTALL : BOOTSTRAPPER_ACTION_STATE_NONE;
110 break;
111
112 case BOOTSTRAPPER_REQUEST_STATE_FORCE_ABSENT:
113 execute = fAllowUninstall ? BOOTSTRAPPER_ACTION_STATE_UNINSTALL : BOOTSTRAPPER_ACTION_STATE_NONE;
114 break;
115
116 default:
117 execute = BOOTSTRAPPER_ACTION_STATE_NONE;
118 break;
119 }
120 break;
121
122 case BOOTSTRAPPER_PACKAGE_STATE_ABSENT:
123 switch (pPackage->requested)
124 {
125 case BOOTSTRAPPER_REQUEST_STATE_PRESENT: __fallthrough;
126 case BOOTSTRAPPER_REQUEST_STATE_REPAIR:
127 execute = BOOTSTRAPPER_ACTION_STATE_INSTALL;
128 break;
129
130 default:
131 execute = BOOTSTRAPPER_ACTION_STATE_NONE;
132 break;
133 }
134 break;
135
136 default:
137 hr = E_INVALIDARG;
138 ExitOnRootFailure(hr, "Invalid package state.");
139 }
140
141 // Calculate the rollback action if there is an execute action.
142 if (BOOTSTRAPPER_ACTION_STATE_NONE != execute)
143 {
144 switch (pPackage->currentState)
145 {
146 case BOOTSTRAPPER_PACKAGE_STATE_PRESENT:
147 switch (pPackage->requested)
148 {
149 case BOOTSTRAPPER_REQUEST_STATE_FORCE_ABSENT: __fallthrough;
150 case BOOTSTRAPPER_REQUEST_STATE_ABSENT:
151 rollback = BOOTSTRAPPER_ACTION_STATE_INSTALL;
152 break;
153
154 default:
155 rollback = BOOTSTRAPPER_ACTION_STATE_NONE;
156 break;
157 }
158 break;
159
160 case BOOTSTRAPPER_PACKAGE_STATE_ABSENT:
161 switch (pPackage->requested)
162 {
163 case BOOTSTRAPPER_REQUEST_STATE_PRESENT: __fallthrough;
164 case BOOTSTRAPPER_REQUEST_STATE_REPAIR:
165 rollback = fAllowUninstall ? BOOTSTRAPPER_ACTION_STATE_UNINSTALL : BOOTSTRAPPER_ACTION_STATE_NONE;
166 break;
167
168 default:
169 rollback = BOOTSTRAPPER_ACTION_STATE_NONE;
170 break;
171 }
172 break;
173
174 default:
175 hr = E_INVALIDARG;
176 ExitOnRootFailure(hr, "Invalid package expected state.");
177 }
178 }
179
180 // return values
181 pPackage->execute = execute;
182 pPackage->rollback = rollback;
183
184LExit:
185 return hr;
186}
187
188//
189// PlanAdd - adds the calculated execute and rollback actions for the package.
190//
191extern "C" HRESULT MsuEnginePlanAddPackage(
192 __in BURN_PACKAGE* pPackage,
193 __in BURN_PLAN* pPlan,
194 __in BURN_LOGGING* pLog,
195 __in BURN_VARIABLES* pVariables,
196 __in HANDLE hCacheEvent
197 )
198{
199 HRESULT hr = S_OK;
200 BURN_EXECUTE_ACTION* pAction = NULL;
201
202 // add wait for cache
203 if (hCacheEvent)
204 {
205 hr = PlanExecuteCacheSyncAndRollback(pPlan, pPackage, hCacheEvent);
206 ExitOnFailure(hr, "Failed to plan package cache syncpoint");
207 }
208
209 hr = DependencyPlanPackage(NULL, pPackage, pPlan);
210 ExitOnFailure(hr, "Failed to plan package dependency actions.");
211
212 // add execute action
213 if (BOOTSTRAPPER_ACTION_STATE_NONE != pPackage->execute)
214 {
215 hr = PlanAppendExecuteAction(pPlan, &pAction);
216 ExitOnFailure(hr, "Failed to append execute action.");
217
218 pAction->type = BURN_EXECUTE_ACTION_TYPE_MSU_PACKAGE;
219 pAction->msuPackage.pPackage = pPackage;
220 pAction->msuPackage.action = pPackage->execute;
221
222 LoggingSetPackageVariable(pPackage, NULL, FALSE, pLog, pVariables, &pAction->msuPackage.sczLogPath); // ignore errors.
223 }
224
225 // add rollback action
226 if (BOOTSTRAPPER_ACTION_STATE_NONE != pPackage->rollback)
227 {
228 hr = PlanAppendRollbackAction(pPlan, &pAction);
229 ExitOnFailure(hr, "Failed to append rollback action.");
230
231 pAction->type = BURN_EXECUTE_ACTION_TYPE_MSU_PACKAGE;
232 pAction->msuPackage.pPackage = pPackage;
233 pAction->msuPackage.action = pPackage->rollback;
234
235 LoggingSetPackageVariable(pPackage, NULL, TRUE, pLog, pVariables, &pAction->msuPackage.sczLogPath); // ignore errors.
236 }
237
238LExit:
239 return hr;
240}
241
242extern "C" HRESULT MsuEngineExecutePackage(
243 __in BURN_EXECUTE_ACTION* pExecuteAction,
244 __in BURN_VARIABLES* pVariables,
245 __in BOOL fRollback,
246 __in BOOL fStopWusaService,
247 __in PFN_GENERICMESSAGEHANDLER pfnGenericMessageHandler,
248 __in LPVOID pvContext,
249 __out BOOTSTRAPPER_APPLY_RESTART* pRestart
250 )
251{
252 HRESULT hr = S_OK;
253 int nResult = IDNOACTION;
254 LPWSTR sczCachedDirectory = NULL;
255 LPWSTR sczMsuPath = NULL;
256 LPWSTR sczWindowsPath = NULL;
257 LPWSTR sczSystemPath = NULL;
258 LPWSTR sczWusaPath = NULL;
259 LPWSTR sczCommand = NULL;
260 SC_HANDLE schWu = NULL;
261 BOOL fWuWasDisabled = FALSE;
262 STARTUPINFOW si = { };
263 PROCESS_INFORMATION pi = { };
264 GENERIC_EXECUTE_MESSAGE message = { };
265 DWORD dwExitCode = 0;
266 BOOL fUseSysNativePath = FALSE;
267 BURN_PACKAGE* pPackage = pExecuteAction->msuPackage.pPackage;
268 BURN_PAYLOAD* pPackagePayload = pPackage->payloads.rgItems[0].pPayload;
269
270#if !defined(_WIN64)
271 hr = ProcWow64(::GetCurrentProcess(), &fUseSysNativePath);
272 ExitOnFailure(hr, "Failed to determine WOW64 status.");
273#endif
274
275 *pRestart = BOOTSTRAPPER_APPLY_RESTART_NONE;
276
277 // get wusa.exe path
278 if (fUseSysNativePath)
279 {
280 hr = PathGetKnownFolder(CSIDL_WINDOWS, &sczWindowsPath);
281 ExitOnFailure(hr, "Failed to find Windows directory.");
282
283 hr = PathConcat(sczWindowsPath, L"SysNative\\", &sczSystemPath);
284 ExitOnFailure(hr, "Failed to append SysNative directory.");
285 }
286 else
287 {
288 hr = PathGetKnownFolder(CSIDL_SYSTEM, &sczSystemPath);
289 ExitOnFailure(hr, "Failed to find System32 directory.");
290 }
291
292 hr = PathConcat(sczSystemPath, L"wusa.exe", &sczWusaPath);
293 ExitOnFailure(hr, "Failed to allocate WUSA.exe path.");
294
295 // build command
296 switch (pExecuteAction->msuPackage.action)
297 {
298 case BOOTSTRAPPER_ACTION_STATE_INSTALL:
299 // get cached MSU path
300 hr = CacheGetCompletedPath(TRUE, pPackage->sczCacheId, &sczCachedDirectory);
301 ExitOnFailure(hr, "Failed to get cached path for package: %ls", pPackage->sczId);
302
303 // Best effort to set the execute package cache folder variable.
304 VariableSetString(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_CACHE_FOLDER, sczCachedDirectory, TRUE, FALSE);
305
306 hr = PathConcat(sczCachedDirectory, pPackagePayload->sczFilePath, &sczMsuPath);
307 ExitOnFailure(hr, "Failed to build MSU path.");
308
309 // format command
310 hr = StrAllocFormatted(&sczCommand, L"\"%ls\" \"%ls\" /quiet /norestart", sczWusaPath, sczMsuPath);
311 ExitOnFailure(hr, "Failed to format MSU install command.");
312 break;
313
314 case BOOTSTRAPPER_ACTION_STATE_UNINSTALL:
315 // format command
316 hr = StrAllocFormatted(&sczCommand, L"\"%ls\" /uninstall /kb:%ls /quiet /norestart", sczWusaPath, pPackage->Msu.sczKB);
317 ExitOnFailure(hr, "Failed to format MSU uninstall command.");
318 break;
319
320 default:
321 hr = E_UNEXPECTED;
322 ExitOnFailure(hr, "Failed to get action arguments for MSU package.");
323 }
324
325 if (pExecuteAction->msuPackage.sczLogPath && *pExecuteAction->msuPackage.sczLogPath)
326 {
327 hr = StrAllocConcat(&sczCommand, L" /log:", 0);
328 ExitOnFailure(hr, "Failed to append log switch to MSU command-line.");
329
330 hr = StrAllocConcat(&sczCommand, pExecuteAction->msuPackage.sczLogPath, 0);
331 ExitOnFailure(hr, "Failed to append log path to MSU command-line.");
332 }
333
334 LogId(REPORT_STANDARD, MSG_APPLYING_PACKAGE, LoggingRollbackOrExecute(fRollback), pPackage->sczId, LoggingActionStateToString(pExecuteAction->msuPackage.action), sczMsuPath ? sczMsuPath : pPackage->Msu.sczKB, sczCommand);
335
336 hr = EnsureWUServiceEnabled(fStopWusaService, &schWu, &fWuWasDisabled);
337 ExitOnFailure(hr, "Failed to ensure WU service was enabled to install MSU package.");
338
339 // create process
340 si.cb = sizeof(si);
341 if (!::CreateProcessW(sczWusaPath, sczCommand, NULL, NULL, FALSE, CREATE_NO_WINDOW, NULL, NULL, &si, &pi))
342 {
343 ExitWithLastError(hr, "Failed to CreateProcess on path: %ls", sczWusaPath);
344 }
345
346 do
347 {
348 message.type = GENERIC_EXECUTE_MESSAGE_PROGRESS;
349 message.dwAllowedResults = MB_OKCANCEL;
350 message.progress.dwPercentage = 50;
351 nResult = pfnGenericMessageHandler(&message, pvContext);
352 hr = (IDOK == nResult || IDNOACTION == nResult) ? S_OK : IDCANCEL == nResult ? HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT) : HRESULT_FROM_WIN32(ERROR_INSTALL_FAILURE);
353 ExitOnRootFailure(hr, "Bootstrapper application aborted during MSU progress.");
354
355 // wait for process to terminate
356 hr = ProcWaitForCompletion(pi.hProcess, 500, &dwExitCode);
357 if (HRESULT_FROM_WIN32(WAIT_TIMEOUT) != hr)
358 {
359 ExitOnFailure(hr, "Failed to wait for executable to complete: %ls", sczWusaPath);
360 }
361 } while (HRESULT_FROM_WIN32(WAIT_TIMEOUT) == hr);
362
363 // get process exit code
364 if (!::GetExitCodeProcess(pi.hProcess, &dwExitCode))
365 {
366 ExitWithLastError(hr, "Failed to get process exit code.");
367 }
368
369 // We'll normalize the restart required error code from wusa.exe just in case. Most likely
370 // that on reboot we'll actually get WU_S_REBOOT_REQUIRED.
371 if (HRESULT_FROM_WIN32(ERROR_SUCCESS_REBOOT_REQUIRED) == static_cast<HRESULT>(dwExitCode))
372 {
373 dwExitCode = ERROR_SUCCESS_REBOOT_REQUIRED;
374 }
375
376 // handle exit code
377 switch (dwExitCode)
378 {
379 case S_OK: __fallthrough;
380 case S_FALSE: __fallthrough;
381 case WU_S_ALREADY_INSTALLED:
382 hr = S_OK;
383 break;
384
385 case ERROR_SUCCESS_REBOOT_REQUIRED: __fallthrough;
386 case WU_S_REBOOT_REQUIRED:
387 *pRestart = BOOTSTRAPPER_APPLY_RESTART_REQUIRED;
388 hr = S_OK;
389 break;
390
391 default:
392 hr = static_cast<HRESULT>(dwExitCode);
393 break;
394 }
395
396LExit:
397 ReleaseStr(sczCachedDirectory);
398 ReleaseStr(sczMsuPath);
399 ReleaseStr(sczSystemPath);
400 ReleaseStr(sczWindowsPath);
401 ReleaseStr(sczWusaPath);
402 ReleaseStr(sczCommand);
403
404 ReleaseHandle(pi.hProcess);
405 ReleaseHandle(pi.hThread);
406
407 if (fWuWasDisabled)
408 {
409 SetServiceStartType(schWu, SERVICE_DISABLED);
410 }
411
412 // Best effort to clear the execute package cache folder variable.
413 VariableSetString(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_CACHE_FOLDER, NULL, TRUE, FALSE);
414
415 return hr;
416}
417
418extern "C" void MsuEngineUpdateInstallRegistrationState(
419 __in BURN_EXECUTE_ACTION* pAction,
420 __in HRESULT hrExecute
421 )
422{
423 BURN_PACKAGE* pPackage = pAction->msuPackage.pPackage;
424
425 if (FAILED(hrExecute) || !pPackage->fCanAffectRegistration)
426 {
427 ExitFunction();
428 }
429
430 if (BOOTSTRAPPER_ACTION_STATE_UNINSTALL == pAction->msuPackage.action)
431 {
432 pPackage->installRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_ABSENT;
433 }
434 else
435 {
436 pPackage->installRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_PRESENT;
437 }
438
439LExit:
440 return;
441}
442
443static HRESULT EnsureWUServiceEnabled(
444 __in BOOL fStopWusaService,
445 __out SC_HANDLE* pschWu,
446 __out BOOL* pfPreviouslyDisabled
447 )
448{
449 HRESULT hr = S_OK;
450 SC_HANDLE schSCM = NULL;
451 SC_HANDLE schWu = NULL;
452 SERVICE_STATUS serviceStatus = { };
453 QUERY_SERVICE_CONFIGW* pConfig = NULL;
454
455 schSCM = ::OpenSCManagerW(NULL, NULL, SC_MANAGER_ALL_ACCESS);
456 ExitOnNullWithLastError(schSCM, hr, "Failed to open service control manager.");
457
458 schWu = ::OpenServiceW(schSCM, L"wuauserv", SERVICE_QUERY_CONFIG | SERVICE_CHANGE_CONFIG | SERVICE_QUERY_STATUS | SERVICE_STOP );
459 ExitOnNullWithLastError(schWu, hr, "Failed to open WU service.");
460
461 if (!::QueryServiceStatus(schWu, &serviceStatus) )
462 {
463 ExitWithLastError(hr, "Failed to query status of WU service.");
464 }
465
466 // Stop service if requested to.
467 if (SERVICE_STOPPED != serviceStatus.dwCurrentState && fStopWusaService)
468 {
469 hr = StopWUService(schWu);
470 }
471
472 // If the service is not running then it might be disabled so let's check.
473 if (SERVICE_RUNNING != serviceStatus.dwCurrentState)
474 {
475 hr = SvcQueryConfig(schWu, &pConfig);
476 ExitOnFailure(hr, "Failed to read configuration for WU service.");
477
478 // If WU is disabled, change it to a demand start service (but touch nothing else).
479 if (SERVICE_DISABLED == pConfig->dwStartType)
480 {
481 hr = SetServiceStartType(schWu, SERVICE_DEMAND_START);
482 ExitOnFailure(hr, "Failed to mark WU service to start on demand.");
483
484 *pfPreviouslyDisabled = TRUE;
485 }
486 }
487
488 *pschWu = schWu;
489 schWu = NULL;
490
491LExit:
492 ReleaseMem(pConfig);
493 ReleaseServiceHandle(schWu);
494 ReleaseServiceHandle(schSCM);
495
496 return hr;
497}
498
499static HRESULT SetServiceStartType(
500 __in SC_HANDLE sch,
501 __in DWORD startType
502 )
503{
504 HRESULT hr = S_OK;
505
506 if (!::ChangeServiceConfigW(sch, SERVICE_NO_CHANGE, startType, SERVICE_NO_CHANGE, NULL, NULL, NULL, NULL, NULL, NULL, NULL))
507 {
508 ExitWithLastError(hr, "Failed to set service start type.");
509 }
510
511LExit:
512 return hr;
513}
514
515static HRESULT StopWUService(
516 __in SC_HANDLE schWu
517 )
518{
519 HRESULT hr = S_OK;
520 SERVICE_STATUS serviceStatus = { };
521
522 if(!::ControlService(schWu, SERVICE_CONTROL_STOP, &serviceStatus))
523 {
524 ExitWithLastError(hr, "Failed to stop wusa service.");
525 }
526
527LExit:
528 return hr;
529}