mirror of
https://github.com/microsoft/WSL.git
synced 2026-04-09 23:24:37 -05:00
Suppress MSI-initiated reboots during Store updates (#40079)
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: Ben Hillis <benhill@ntdev.microsoft.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -1277,7 +1277,11 @@ int Uninstall()
|
||||
|
||||
const auto exitCode = wsl::windows::common::install::UninstallViaMsi(logFile.c_str(), &wsl::windows::common::install::MsiMessageCallback);
|
||||
|
||||
if (exitCode != 0)
|
||||
if (exitCode == ERROR_SUCCESS_REBOOT_REQUIRED)
|
||||
{
|
||||
wsl::windows::common::wslutil::PrintSystemError(ERROR_SUCCESS_REBOOT_REQUIRED);
|
||||
}
|
||||
else if (exitCode != 0)
|
||||
{
|
||||
clearLogs.release();
|
||||
THROW_HR_WITH_USER_ERROR(
|
||||
|
||||
@@ -104,7 +104,11 @@ int UpdatePackageImpl(bool preRelease, bool repair)
|
||||
|
||||
const auto exitCode = UpgradeViaMsi(downloadPath.c_str(), L"", logFile.c_str(), &MsiMessageCallback);
|
||||
|
||||
if (exitCode != 0)
|
||||
if (exitCode == ERROR_SUCCESS_REBOOT_REQUIRED)
|
||||
{
|
||||
PrintSystemError(ERROR_SUCCESS_REBOOT_REQUIRED);
|
||||
}
|
||||
else if (exitCode != 0)
|
||||
{
|
||||
clearLogs.release();
|
||||
THROW_HR_WITH_USER_ERROR(
|
||||
@@ -358,15 +362,20 @@ int wsl::windows::common::install::UpdatePackage(bool PreRelease, bool Repair)
|
||||
UINT wsl::windows::common::install::UpgradeViaMsi(
|
||||
_In_ LPCWSTR PackageLocation, _In_opt_ LPCWSTR ExtraArgs, _In_opt_ LPCWSTR LogFile, _In_ const std::function<void(INSTALLMESSAGE, LPCWSTR)>& Callback)
|
||||
{
|
||||
WriteInstallLog(std::format("Upgrading via MSI package: {}. Args: {}", PackageLocation, ExtraArgs != nullptr ? ExtraArgs : L""));
|
||||
// Always suppress MSI-initiated reboots. With INSTALLUILEVEL_NONE, Windows Installer
|
||||
// will silently reboot the machine if files are in use and REBOOT is not suppressed.
|
||||
std::wstring args = L"REBOOT=ReallySuppress";
|
||||
if (ExtraArgs != nullptr && *ExtraArgs != L'\0')
|
||||
{
|
||||
args = std::wstring(ExtraArgs) + L" " + args;
|
||||
}
|
||||
|
||||
WriteInstallLog(std::format("Upgrading via MSI package: {}. Args: {}", PackageLocation, args));
|
||||
|
||||
ConfigureMsiLogging(LogFile, Callback);
|
||||
|
||||
auto result = MsiInstallProduct(PackageLocation, ExtraArgs);
|
||||
WSL_LOG(
|
||||
"MsiInstallResult",
|
||||
TraceLoggingValue(result, "result"),
|
||||
TraceLoggingValue(ExtraArgs != nullptr ? ExtraArgs : L"", "ExtraArgs"));
|
||||
auto result = MsiInstallProduct(PackageLocation, args.c_str());
|
||||
WSL_LOG("MsiInstallResult", TraceLoggingValue(result, "result"), TraceLoggingValue(args.c_str(), "ExtraArgs"));
|
||||
|
||||
WriteInstallLog(std::format("MSI upgrade result: {}", result));
|
||||
|
||||
@@ -382,7 +391,7 @@ UINT wsl::windows::common::install::UninstallViaMsi(_In_opt_ LPCWSTR LogFile, _I
|
||||
|
||||
ConfigureMsiLogging(LogFile, Callback);
|
||||
|
||||
auto result = MsiConfigureProduct(productCode.c_str(), 0, INSTALLSTATE_ABSENT);
|
||||
auto result = MsiConfigureProductEx(productCode.c_str(), 0, INSTALLSTATE_ABSENT, L"REBOOT=ReallySuppress");
|
||||
WSL_LOG("MsiUninstallResult", TraceLoggingValue(result, "result"));
|
||||
|
||||
WriteInstallLog(std::format("MSI package uninstall result: {}", result));
|
||||
|
||||
@@ -79,7 +79,20 @@ std::pair<UINT, std::wstring> InstallMsipackageImpl()
|
||||
auto result = wsl::windows::common::install::UpgradeViaMsi(
|
||||
GetMsiPackagePath().c_str(), L"SKIPMSIX=1", logFile.has_value() ? logFile->c_str() : nullptr, messageCallback);
|
||||
|
||||
WSL_LOG("MSIUpgradeResult", TraceLoggingValue(result, "result"), TraceLoggingValue(errors.c_str(), "errorMessage"));
|
||||
// ERROR_SUCCESS_REBOOT_REQUIRED (3010) means the install succeeded but some files
|
||||
// will be replaced on the next reboot. Treat as success since the service runs
|
||||
// silently with no user-facing console.
|
||||
const bool rebootRequired = (result == ERROR_SUCCESS_REBOOT_REQUIRED);
|
||||
if (rebootRequired)
|
||||
{
|
||||
result = ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
WSL_LOG(
|
||||
"MSIUpgradeResult",
|
||||
TraceLoggingValue(result, "result"),
|
||||
TraceLoggingValue(rebootRequired, "rebootRequired"),
|
||||
TraceLoggingValue(errors.c_str(), "errorMessage"));
|
||||
|
||||
return {result, errors};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user