main
cpp 433 lines 12.9 KB
Raw
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
13 static HRESULT EnsureWUServiceEnabled(
14 __in BOOL fStopWusaService,
15 __out SC_HANDLE* pschWu,
16 __out BOOL* pfPreviouslyDisabled
17 );
18 static HRESULT SetServiceStartType(
19 __in SC_HANDLE sch,
20 __in DWORD stratType
21 );
22 static HRESULT StopWUService(
23 __in SC_HANDLE schWu
24 );
25
26
27 extern "C" HRESULT MsuEngineParsePackageFromXml(
28 __in IXMLDOMNode* pixnMsuPackage,
29 __in BURN_PACKAGE* pPackage
30 )
31 {
32 HRESULT hr = S_OK;
33
34 // @DetectCondition
35 hr = XmlGetAttributeEx(pixnMsuPackage, L"DetectCondition", &pPackage->Msu.sczDetectCondition);
36 ExitOnRequiredXmlQueryFailure(hr, "Failed to get @DetectCondition.");
37
38 // Uninstalling MSU packages isn't supported because newer OS's don't allow silent uninstallation.
39 pPackage->fPermanent = TRUE;
40
41 LExit:
42 return hr;
43 }
44
45 extern "C" void MsuEnginePackageUninitialize(
46 __in BURN_PACKAGE* pPackage
47 )
48 {
49 ReleaseNullStr(pPackage->Msu.sczDetectCondition);
50 }
51
52 extern "C" HRESULT MsuEngineDetectPackage(
53 __in BURN_PACKAGE* pPackage,
54 __in BURN_REGISTRATION* pRegistration,
55 __in BURN_VARIABLES* pVariables
56 )
57 {
58 HRESULT hr = S_OK;
59 BOOL fDetected = FALSE;
60
61 // evaluate detect condition
62 if (pPackage->Msu.sczDetectCondition && *pPackage->Msu.sczDetectCondition)
63 {
64 hr = ConditionEvaluate(pVariables, pPackage->Msu.sczDetectCondition, &fDetected);
65 ExitOnFailure(hr, "Failed to evaluate MSU package detect condition.");
66 }
67
68 // update detect state
69 pPackage->currentState = fDetected ? BOOTSTRAPPER_PACKAGE_STATE_PRESENT : BOOTSTRAPPER_PACKAGE_STATE_ABSENT;
70
71 if (pPackage->fCanAffectRegistration)
72 {
73 pPackage->installRegistrationState = BOOTSTRAPPER_PACKAGE_STATE_ABSENT < pPackage->currentState ? BURN_PACKAGE_REGISTRATION_STATE_PRESENT : BURN_PACKAGE_REGISTRATION_STATE_ABSENT;
74 }
75
76 hr = DependencyDetectChainPackage(pPackage, pRegistration);
77 ExitOnFailure(hr, "Failed to detect dependencies for MSU package.");
78
79 LExit:
80 return hr;
81 }
82
83 //
84 // PlanCalculate - calculates the execute and rollback state for the requested package state.
85 //
86 extern "C" HRESULT MsuEnginePlanCalculatePackage(
87 __in BURN_PACKAGE* pPackage
88 )
89 {
90 HRESULT hr = S_OK;
91 BOOTSTRAPPER_ACTION_STATE execute = BOOTSTRAPPER_ACTION_STATE_NONE;
92 BOOTSTRAPPER_ACTION_STATE rollback = BOOTSTRAPPER_ACTION_STATE_NONE;
93
94 // execute action
95 switch (pPackage->currentState)
96 {
97 case BOOTSTRAPPER_PACKAGE_STATE_PRESENT:
98 execute = BOOTSTRAPPER_ACTION_STATE_NONE;
99 break;
100
101 case BOOTSTRAPPER_PACKAGE_STATE_ABSENT:
102 switch (pPackage->requested)
103 {
104 case BOOTSTRAPPER_REQUEST_STATE_PRESENT: __fallthrough;
105 case BOOTSTRAPPER_REQUEST_STATE_FORCE_PRESENT: __fallthrough;
106 case BOOTSTRAPPER_REQUEST_STATE_REPAIR:
107 execute = BOOTSTRAPPER_ACTION_STATE_INSTALL;
108 break;
109
110 default:
111 execute = BOOTSTRAPPER_ACTION_STATE_NONE;
112 break;
113 }
114 break;
115
116 default:
117 hr = E_INVALIDARG;
118 ExitOnRootFailure(hr, "Invalid package state.");
119 }
120
121 // Calculate the rollback action if there is an execute action.
122 if (BOOTSTRAPPER_ACTION_STATE_NONE != execute)
123 {
124 rollback = BOOTSTRAPPER_ACTION_STATE_NONE;
125 }
126
127 // return values
128 pPackage->execute = execute;
129 pPackage->rollback = rollback;
130
131 LExit:
132 return hr;
133 }
134
135 //
136 // PlanAdd - adds the calculated execute and rollback actions for the package.
137 //
138 extern "C" HRESULT MsuEnginePlanAddPackage(
139 __in BURN_PACKAGE* pPackage,
140 __in BURN_PLAN* pPlan,
141 __in BURN_LOGGING* pLog,
142 __in BURN_VARIABLES* pVariables
143 )
144 {
145 HRESULT hr = S_OK;
146 BURN_EXECUTE_ACTION* pAction = NULL;
147
148 hr = DependencyPlanPackage(NULL, pPackage, pPlan);
149 ExitOnFailure(hr, "Failed to plan package dependency actions.");
150
151 // add rollback action
152 if (BOOTSTRAPPER_ACTION_STATE_NONE != pPackage->rollback)
153 {
154 hr = PlanAppendRollbackAction(pPlan, &pAction);
155 ExitOnFailure(hr, "Failed to append rollback action.");
156
157 pAction->type = BURN_EXECUTE_ACTION_TYPE_MSU_PACKAGE;
158 pAction->msuPackage.pPackage = pPackage;
159 pAction->msuPackage.action = pPackage->rollback;
160
161 LoggingSetPackageVariable(pPackage, NULL, TRUE, pLog, pVariables, &pAction->msuPackage.sczLogPath); // ignore errors.
162
163 hr = PlanExecuteCheckpoint(pPlan);
164 ExitOnFailure(hr, "Failed to append execute checkpoint.");
165 }
166
167 // add execute action
168 if (BOOTSTRAPPER_ACTION_STATE_NONE != pPackage->execute)
169 {
170 hr = PlanAppendExecuteAction(pPlan, &pAction);
171 ExitOnFailure(hr, "Failed to append execute action.");
172
173 pAction->type = BURN_EXECUTE_ACTION_TYPE_MSU_PACKAGE;
174 pAction->msuPackage.pPackage = pPackage;
175 pAction->msuPackage.action = pPackage->execute;
176
177 LoggingSetPackageVariable(pPackage, NULL, FALSE, pLog, pVariables, &pAction->msuPackage.sczLogPath); // ignore errors.
178 }
179
180 LExit:
181 return hr;
182 }
183
184 extern "C" HRESULT MsuEngineExecutePackage(
185 __in BURN_EXECUTE_ACTION* pExecuteAction,
186 __in BURN_CACHE* pCache,
187 __in BURN_VARIABLES* pVariables,
188 __in BOOL fRollback,
189 __in BOOL fStopWusaService,
190 __in PFN_GENERICMESSAGEHANDLER pfnGenericMessageHandler,
191 __in LPVOID pvContext,
192 __out BOOTSTRAPPER_APPLY_RESTART* pRestart
193 )
194 {
195 HRESULT hr = S_OK;
196 LPWSTR sczCachedDirectory = NULL;
197 LPWSTR sczMsuPath = NULL;
198 LPWSTR sczSystemPath = NULL;
199 LPWSTR sczWusaPath = NULL;
200 LPWSTR sczCommand = NULL;
201 SC_HANDLE schWu = NULL;
202 BOOL fWuWasDisabled = FALSE;
203 STARTUPINFOW si = { };
204 PROCESS_INFORMATION pi = { };
205 GENERIC_EXECUTE_MESSAGE message = { };
206 DWORD dwExitCode = 0;
207 BOOL fUseSysNativePath = FALSE;
208 BURN_PACKAGE* pPackage = pExecuteAction->msuPackage.pPackage;
209 BURN_PAYLOAD* pPackagePayload = pPackage->payloads.rgItems[0].pPayload;
210
211 #if !defined(_WIN64)
212 hr = ProcWow64(::GetCurrentProcess(), &fUseSysNativePath);
213 ExitOnFailure(hr, "Failed to determine WOW64 status.");
214 #endif
215
216 *pRestart = BOOTSTRAPPER_APPLY_RESTART_NONE;
217
218 // get wusa.exe path
219 if (fUseSysNativePath)
220 {
221 hr = PathSystemWindowsSubdirectory(L"SysNative\\", &sczSystemPath);
222 ExitOnFailure(hr, "Failed to append SysNative directory.");
223 }
224 else
225 {
226 hr = PathGetSystemDirectory(&sczSystemPath);
227 ExitOnFailure(hr, "Failed to find System32 directory.");
228 }
229
230 hr = PathConcat(sczSystemPath, L"wusa.exe", &sczWusaPath);
231 ExitOnFailure(hr, "Failed to allocate WUSA.exe path.");
232
233 // build command
234 switch (pExecuteAction->msuPackage.action)
235 {
236 case BOOTSTRAPPER_ACTION_STATE_INSTALL:
237 // get cached MSU path
238 hr = CacheGetCompletedPath(pCache, TRUE, pPackage->sczCacheId, &sczCachedDirectory);
239 ExitOnFailure(hr, "Failed to get cached path for package: %ls", pPackage->sczId);
240
241 // Best effort to set the execute package cache folder variable.
242 VariableSetString(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_CACHE_FOLDER, sczCachedDirectory, TRUE, FALSE);
243
244 hr = PathConcatRelativeToFullyQualifiedBase(sczCachedDirectory, pPackagePayload->sczFilePath, &sczMsuPath);
245 ExitOnFailure(hr, "Failed to build MSU path.");
246
247 // format command
248 hr = StrAllocFormatted(&sczCommand, L"\"%ls\" \"%ls\" /quiet /norestart", sczWusaPath, sczMsuPath);
249 ExitOnFailure(hr, "Failed to format MSU install command.");
250 break;
251
252 default:
253 hr = E_UNEXPECTED;
254 ExitOnFailure(hr, "Failed to get action arguments for MSU package.");
255 }
256
257 if (pExecuteAction->msuPackage.sczLogPath && *pExecuteAction->msuPackage.sczLogPath)
258 {
259 hr = StrAllocConcat(&sczCommand, L" /log:", 0);
260 ExitOnFailure(hr, "Failed to append log switch to MSU command-line.");
261
262 hr = StrAllocConcat(&sczCommand, pExecuteAction->msuPackage.sczLogPath, 0);
263 ExitOnFailure(hr, "Failed to append log path to MSU command-line.");
264 }
265
266 LogId(REPORT_STANDARD, MSG_APPLYING_PACKAGE, LoggingRollbackOrExecute(fRollback), pPackage->sczId, LoggingActionStateToString(pExecuteAction->msuPackage.action), sczMsuPath, sczCommand);
267
268 hr = EnsureWUServiceEnabled(fStopWusaService, &schWu, &fWuWasDisabled);
269 ExitOnFailure(hr, "Failed to ensure WU service was enabled to install MSU package.");
270
271 hr = ExeEngineRunProcess(pfnGenericMessageHandler, pvContext, pPackage, sczWusaPath, sczCommand, NULL, NULL, &dwExitCode);
272 ExitOnFailure(hr, "Failed to run MSU process");
273
274 // We'll normalize the restart required error code from wusa.exe just in case. Most likely
275 // that on reboot we'll actually get WU_S_REBOOT_REQUIRED.
276 if (HRESULT_FROM_WIN32(ERROR_SUCCESS_REBOOT_REQUIRED) == static_cast<HRESULT>(dwExitCode))
277 {
278 dwExitCode = ERROR_SUCCESS_REBOOT_REQUIRED;
279 }
280
281 // handle exit code
282 switch (dwExitCode)
283 {
284 case S_OK: __fallthrough;
285 case S_FALSE: __fallthrough;
286 case WU_S_ALREADY_INSTALLED:
287 hr = S_OK;
288 break;
289
290 case ERROR_SUCCESS_REBOOT_REQUIRED: __fallthrough;
291 case WU_S_REBOOT_REQUIRED:
292 *pRestart = BOOTSTRAPPER_APPLY_RESTART_REQUIRED;
293 hr = S_OK;
294 break;
295
296 default:
297 hr = static_cast<HRESULT>(dwExitCode);
298 break;
299 }
300
301 LExit:
302 ReleaseStr(sczCachedDirectory);
303 ReleaseStr(sczMsuPath);
304 ReleaseStr(sczSystemPath);
305 ReleaseStr(sczWusaPath);
306 ReleaseStr(sczCommand);
307
308 ReleaseHandle(pi.hProcess);
309 ReleaseHandle(pi.hThread);
310
311 if (fWuWasDisabled)
312 {
313 SetServiceStartType(schWu, SERVICE_DISABLED);
314 }
315
316 // Best effort to clear the execute package cache folder variable.
317 VariableSetString(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_CACHE_FOLDER, NULL, TRUE, FALSE);
318
319 return hr;
320 }
321
322 extern "C" void MsuEngineUpdateInstallRegistrationState(
323 __in BURN_EXECUTE_ACTION* pAction,
324 __in HRESULT hrExecute
325 )
326 {
327 BURN_PACKAGE* pPackage = pAction->msuPackage.pPackage;
328
329 if (FAILED(hrExecute) || !pPackage->fCanAffectRegistration)
330 {
331 ExitFunction();
332 }
333
334 if (BOOTSTRAPPER_ACTION_STATE_UNINSTALL == pAction->msuPackage.action)
335 {
336 pPackage->installRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_ABSENT;
337 }
338 else
339 {
340 pPackage->installRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_PRESENT;
341 }
342
343 LExit:
344 return;
345 }
346
347 static HRESULT EnsureWUServiceEnabled(
348 __in BOOL fStopWusaService,
349 __out SC_HANDLE* pschWu,
350 __out BOOL* pfPreviouslyDisabled
351 )
352 {
353 HRESULT hr = S_OK;
354 SC_HANDLE schSCM = NULL;
355 SC_HANDLE schWu = NULL;
356 SERVICE_STATUS serviceStatus = { };
357 QUERY_SERVICE_CONFIGW* pConfig = NULL;
358
359 schSCM = ::OpenSCManagerW(NULL, NULL, SC_MANAGER_ALL_ACCESS);
360 ExitOnNullWithLastError(schSCM, hr, "Failed to open service control manager.");
361
362 schWu = ::OpenServiceW(schSCM, L"wuauserv", SERVICE_QUERY_CONFIG | SERVICE_CHANGE_CONFIG | SERVICE_QUERY_STATUS | SERVICE_STOP );
363 ExitOnNullWithLastError(schWu, hr, "Failed to open WU service.");
364
365 if (!::QueryServiceStatus(schWu, &serviceStatus) )
366 {
367 ExitWithLastError(hr, "Failed to query status of WU service.");
368 }
369
370 // Stop service if requested to.
371 if (SERVICE_STOPPED != serviceStatus.dwCurrentState && fStopWusaService)
372 {
373 hr = StopWUService(schWu);
374 }
375
376 // If the service is not running then it might be disabled so let's check.
377 if (SERVICE_RUNNING != serviceStatus.dwCurrentState)
378 {
379 hr = SvcQueryConfig(schWu, &pConfig);
380 ExitOnFailure(hr, "Failed to read configuration for WU service.");
381
382 // If WU is disabled, change it to a demand start service (but touch nothing else).
383 if (SERVICE_DISABLED == pConfig->dwStartType)
384 {
385 hr = SetServiceStartType(schWu, SERVICE_DEMAND_START);
386 ExitOnFailure(hr, "Failed to mark WU service to start on demand.");
387
388 *pfPreviouslyDisabled = TRUE;
389 }
390 }
391
392 *pschWu = schWu;
393 schWu = NULL;
394
395 LExit:
396 ReleaseMem(pConfig);
397 ReleaseServiceHandle(schWu);
398 ReleaseServiceHandle(schSCM);
399
400 return hr;
401 }
402
403 static HRESULT SetServiceStartType(
404 __in SC_HANDLE sch,
405 __in DWORD startType
406 )
407 {
408 HRESULT hr = S_OK;
409
410 if (!::ChangeServiceConfigW(sch, SERVICE_NO_CHANGE, startType, SERVICE_NO_CHANGE, NULL, NULL, NULL, NULL, NULL, NULL, NULL))
411 {
412 ExitWithLastError(hr, "Failed to set service start type.");
413 }
414
415 LExit:
416 return hr;
417 }
418
419 static HRESULT StopWUService(
420 __in SC_HANDLE schWu
421 )
422 {
423 HRESULT hr = S_OK;
424 SERVICE_STATUS serviceStatus = { };
425
426 if(!::ControlService(schWu, SERVICE_CONTROL_STOP, &serviceStatus))
427 {
428 ExitWithLastError(hr, "Failed to stop wusa service.");
429 }
430
431 LExit:
432 return hr;
433 }