Skip to content

Commit da73f0e

Browse files
Ben HillisCopilot
andcommitted
Suppress MSI-initiated reboots during Store updates
When the WSL MSIX package is updated via the Microsoft Store, the WslInstaller service automatically upgrades the MSI package by calling MsiInstallProduct. This call was made with INSTALLUILEVEL_NONE (silent install) but without setting the REBOOT=ReallySuppress property. Per Windows Installer documentation, when a silent install encounters files in use and REBOOT is not suppressed, the system reboots automatically without any user prompt. This could cause unexpected machine restarts after a Store update when WSL binaries (e.g. wslservice.exe) were in use during the upgrade. Every deployment script in the repo already passes /norestart to msiexec (deploy-to-host.ps1, deploy-to-vm.ps1, install-latest-wsl.ps1, test-setup.ps1), but the programmatic MsiInstallProduct path used by the WslInstaller service lacked the equivalent property. This change: - Always appends REBOOT=ReallySuppress to MsiInstallProduct arguments in UpgradeViaMsi, preventing Windows Installer from ever initiating a system restart during install/upgrade. - Switches UninstallViaMsi from MsiConfigureProduct to MsiConfigureProductEx so we can pass REBOOT=ReallySuppress during uninstall as well. - Propagates ERROR_SUCCESS_REBOOT_REQUIRED (3010) to callers instead of swallowing it. User-facing paths (wsl --update, wsl --uninstall) print a reboot-needed message to stderr. The background WslInstaller service silently treats 3010 as success since it has no console. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent f8e579a commit da73f0e

3 files changed

Lines changed: 36 additions & 10 deletions

File tree

src/windows/common/WslClient.cpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1277,7 +1277,11 @@ int Uninstall()
12771277

12781278
const auto exitCode = wsl::windows::common::install::UninstallViaMsi(logFile.c_str(), &wsl::windows::common::install::MsiMessageCallback);
12791279

1280-
if (exitCode != 0)
1280+
if (exitCode == ERROR_SUCCESS_REBOOT_REQUIRED)
1281+
{
1282+
wsl::windows::common::wslutil::PrintSystemError(ERROR_SUCCESS_REBOOT_REQUIRED);
1283+
}
1284+
else if (exitCode != 0)
12811285
{
12821286
clearLogs.release();
12831287
THROW_HR_WITH_USER_ERROR(

src/windows/common/install.cpp

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,11 @@ int UpdatePackageImpl(bool preRelease, bool repair)
104104

105105
const auto exitCode = UpgradeViaMsi(downloadPath.c_str(), L"", logFile.c_str(), &MsiMessageCallback);
106106

107-
if (exitCode != 0)
107+
if (exitCode == ERROR_SUCCESS_REBOOT_REQUIRED)
108+
{
109+
PrintSystemError(ERROR_SUCCESS_REBOOT_REQUIRED);
110+
}
111+
else if (exitCode != 0)
108112
{
109113
clearLogs.release();
110114
THROW_HR_WITH_USER_ERROR(
@@ -358,15 +362,20 @@ int wsl::windows::common::install::UpdatePackage(bool PreRelease, bool Repair)
358362
UINT wsl::windows::common::install::UpgradeViaMsi(
359363
_In_ LPCWSTR PackageLocation, _In_opt_ LPCWSTR ExtraArgs, _In_opt_ LPCWSTR LogFile, _In_ const std::function<void(INSTALLMESSAGE, LPCWSTR)>& Callback)
360364
{
361-
WriteInstallLog(std::format("Upgrading via MSI package: {}. Args: {}", PackageLocation, ExtraArgs != nullptr ? ExtraArgs : L""));
365+
// Always suppress MSI-initiated reboots. With INSTALLUILEVEL_NONE, Windows Installer
366+
// will silently reboot the machine if files are in use and REBOOT is not suppressed.
367+
std::wstring args = L"REBOOT=ReallySuppress";
368+
if (ExtraArgs != nullptr && *ExtraArgs != L'\0')
369+
{
370+
args = std::wstring(ExtraArgs) + L" " + args;
371+
}
372+
373+
WriteInstallLog(std::format("Upgrading via MSI package: {}. Args: {}", PackageLocation, args));
362374

363375
ConfigureMsiLogging(LogFile, Callback);
364376

365-
auto result = MsiInstallProduct(PackageLocation, ExtraArgs);
366-
WSL_LOG(
367-
"MsiInstallResult",
368-
TraceLoggingValue(result, "result"),
369-
TraceLoggingValue(ExtraArgs != nullptr ? ExtraArgs : L"", "ExtraArgs"));
377+
auto result = MsiInstallProduct(PackageLocation, args.c_str());
378+
WSL_LOG("MsiInstallResult", TraceLoggingValue(result, "result"), TraceLoggingValue(args.c_str(), "ExtraArgs"));
370379

371380
WriteInstallLog(std::format("MSI upgrade result: {}", result));
372381

@@ -382,7 +391,7 @@ UINT wsl::windows::common::install::UninstallViaMsi(_In_opt_ LPCWSTR LogFile, _I
382391

383392
ConfigureMsiLogging(LogFile, Callback);
384393

385-
auto result = MsiConfigureProduct(productCode.c_str(), 0, INSTALLSTATE_ABSENT);
394+
auto result = MsiConfigureProductEx(productCode.c_str(), 0, INSTALLSTATE_ABSENT, L"REBOOT=ReallySuppress");
386395
WSL_LOG("MsiUninstallResult", TraceLoggingValue(result, "result"));
387396

388397
WriteInstallLog(std::format("MSI package uninstall result: {}", result));

src/windows/wslinstaller/exe/WslInstaller.cpp

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,20 @@ std::pair<UINT, std::wstring> InstallMsipackageImpl()
7979
auto result = wsl::windows::common::install::UpgradeViaMsi(
8080
GetMsiPackagePath().c_str(), L"SKIPMSIX=1", logFile.has_value() ? logFile->c_str() : nullptr, messageCallback);
8181

82-
WSL_LOG("MSIUpgradeResult", TraceLoggingValue(result, "result"), TraceLoggingValue(errors.c_str(), "errorMessage"));
82+
// ERROR_SUCCESS_REBOOT_REQUIRED (3010) means the install succeeded but some files
83+
// will be replaced on the next reboot. Treat as success since the service runs
84+
// silently with no user-facing console.
85+
const bool rebootRequired = (result == ERROR_SUCCESS_REBOOT_REQUIRED);
86+
if (rebootRequired)
87+
{
88+
result = ERROR_SUCCESS;
89+
}
90+
91+
WSL_LOG(
92+
"MSIUpgradeResult",
93+
TraceLoggingValue(result, "result"),
94+
TraceLoggingValue(rebootRequired, "rebootRequired"),
95+
TraceLoggingValue(errors.c_str(), "errorMessage"));
8396

8497
return {result, errors};
8598
}

0 commit comments

Comments
 (0)