Kaydet (Commit) 0b6884b9 authored tarafından Mike Kaganski's avatar Mike Kaganski

tdf#121987 follow-up: never fail MSU install; warn instead.

This replaces commit 53058090. Now
not only failure to start WU service, but also other errors during
MSU installation won't break installation. E.g., running WU service
as Guest does not prevent the service from starting; but installing
updates fail, which makes previous solution incomplete.

Instead, show a warning in those rare cases when an error happens,
and continue. It will allow users to see the reason if they see
"api-ms-win-*.dll missing" message later after installation. Of
course, some small percentage of these warnings will be false, e.g.
those on Windows 10. But those false messages must be really small
minority, because only when (1) the installation fails (2) on a
system with the component already present, such a message would be
false. And it will not prevent the installation.

This will not block unattended installations, since in those cases,
MsiProcessMessage() will do nothing.

Change-Id: I3a9e681e9d6701d092bd5c18bb4b68b4f77170f3
Reviewed-on: https://gerrit.libreoffice.org/64874
Tested-by: Jenkins
Reviewed-by: 's avatarMike Kaganski <mike.kaganski@collabora.com>
üst 7e7e203c
......@@ -406,4 +406,7 @@ en-US = "This setup requires Internet Information Server 4.0 or higher for confi
[OOO_ERROR_130]
en-US = "This setup requires Administrator privileges for configuring IIS Virtual Roots."
[OOO_ERROR_131]
en-US = "Installing a pre-requisite KB2999226 failed. You might need to manually install it from Microsoft site to be able to run the product. [2]"
......@@ -131,3 +131,4 @@ i2 L0
1932 OOO_ERROR_128
1933 OOO_ERROR_129
1934 OOO_ERROR_130
25000 OOO_ERROR_131
......@@ -85,11 +85,14 @@ WindowsCustomAction gid_Customaction_cleanup_msu
Assignment1 = ("InstallExecuteSequence", "Not Installed And cleanup_msu", "inst_msu");
End
/* The "InstMSUBinary" property contains an error message number and a binary name, separated by "|".
The former is used when installing the MSU fails.
*/
WindowsCustomAction gid_Customaction_check_win7x64_ucrt
Name = "check_win7x64_ucrt";
Typ = "51";
Source = "InstMSUBinary";
Target = "Windows61-KB2999226-x64msu";
Target = "25000|Windows61-KB2999226-x64msu";
Inbinarytable = 0;
Assignment1 = ("InstallExecuteSequence", "Not Installed And VersionNT = 601 And VersionNT64", "FileCost");
Styles = "NO_FILE";
......@@ -99,7 +102,7 @@ WindowsCustomAction gid_Customaction_check_win8x64_ucrt
Name = "check_win8x64_ucrt";
Typ = "51";
Source = "InstMSUBinary";
Target = "Windows8-RT-KB2999226-x64msu";
Target = "25000|Windows8-RT-KB2999226-x64msu";
Inbinarytable = 0;
Assignment1 = ("InstallExecuteSequence", "Not Installed And VersionNT = 602 And VersionNT64", "check_win7x64_ucrt");
Styles = "NO_FILE";
......@@ -109,7 +112,7 @@ WindowsCustomAction gid_Customaction_check_win81x64_ucrt
Name = "check_win81x64_ucrt";
Typ = "51";
Source = "InstMSUBinary";
Target = "Windows81-KB2999226-x64msu";
Target = "25000|Windows81-KB2999226-x64msu";
Inbinarytable = 0;
Assignment1 = ("InstallExecuteSequence", "Not Installed And VersionNT = 603 And VersionNT64", "check_win8x64_ucrt");
Styles = "NO_FILE";
......@@ -125,7 +128,7 @@ WindowsCustomAction gid_Customaction_check_win7x32_ucrt
Name = "check_win7x32_ucrt";
Typ = "51";
Source = "InstMSUBinary";
Target = "Windows61-KB2999226-x86msu";
Target = "25000|Windows61-KB2999226-x86msu";
Inbinarytable = 0;
Assignment1 = ("InstallExecuteSequence", "Not Installed And VersionNT = 601 And Not VersionNT64", "check_win81x64_ucrt");
Styles = "NO_FILE";
......@@ -135,7 +138,7 @@ WindowsCustomAction gid_Customaction_check_win8x32_ucrt
Name = "check_win8x32_ucrt";
Typ = "51";
Source = "InstMSUBinary";
Target = "Windows8-RT-KB2999226-x86msu";
Target = "25000|Windows8-RT-KB2999226-x86msu";
Inbinarytable = 0;
Assignment1 = ("InstallExecuteSequence", "Not Installed And VersionNT = 602 And Not VersionNT64", "check_win7x32_ucrt");
Styles = "NO_FILE";
......@@ -145,7 +148,7 @@ WindowsCustomAction gid_Customaction_check_win81x32_ucrt
Name = "check_win81x32_ucrt";
Typ = "51";
Source = "InstMSUBinary";
Target = "Windows81-KB2999226-x86msu";
Target = "25000|Windows81-KB2999226-x86msu";
Inbinarytable = 0;
Assignment1 = ("InstallExecuteSequence", "Not Installed And VersionNT = 603 And Not VersionNT64", "check_win8x32_ucrt");
Styles = "NO_FILE";
......
......@@ -120,6 +120,17 @@ template <class... StrType> void WriteLog(MSIHANDLE hInst, const StrType&... ele
WriteLogElem(hInst, hRec, sTemplate, 1, elements...);
}
void ShowWarning(MSIHANDLE hInst, const std::wstring& sErrNo, const char* sMessage)
{
PMSIHANDLE hRec = MsiCreateRecord(2);
// To show a message from Error table, record's Field 0 must be null
MsiRecordSetStringW(hRec, 1, sErrNo.c_str());
std::string s("\n");
s += sMessage;
MsiRecordSetStringA(hRec, 2, s.c_str());
MsiProcessMessage(hInst, INSTALLMESSAGE_WARNING, hRec);
}
typedef std::unique_ptr<void, decltype(&CloseHandle)> CloseHandleGuard;
CloseHandleGuard Guard(HANDLE h) { return CloseHandleGuard(h, CloseHandle); }
......@@ -166,16 +177,6 @@ bool IsWow64Process()
#endif
}
// An exception class to differentiate a non-fatal exception
class nonfatal_exception : public std::exception
{
public:
nonfatal_exception(const std::exception& e)
: std::exception(e)
{
}
};
// Checks if Windows Update service is disabled, and if it is, enables it temporarily.
class WUServiceEnabler
{
......@@ -205,37 +206,27 @@ public:
private:
static CloseServiceHandleGuard EnableWUService(MSIHANDLE hInstall)
{
try
{
auto hSCM = Guard(OpenSCManagerW(nullptr, nullptr, SC_MANAGER_ALL_ACCESS));
if (!hSCM)
ThrowLastError("OpenSCManagerW");
WriteLog(hInstall, "Opened service control manager");
auto hService = Guard(OpenServiceW(hSCM.get(), L"wuauserv",
SERVICE_QUERY_CONFIG | SERVICE_CHANGE_CONFIG
| SERVICE_QUERY_STATUS | SERVICE_STOP));
if (!hService)
ThrowLastError("OpenServiceW");
WriteLog(hInstall, "Obtained WU service handle");
if (ServiceStatus(hInstall, hService.get()) == SERVICE_RUNNING
|| !EnsureServiceEnabled(hInstall, hService.get(), true))
{
// No need to restore anything back, since we didn't change config
hService.reset();
WriteLog(hInstall, "Service configuration is unchanged");
}
return hService;
}
catch (const std::exception& e)
auto hSCM = Guard(OpenSCManagerW(nullptr, nullptr, SC_MANAGER_ALL_ACCESS));
if (!hSCM)
ThrowLastError("OpenSCManagerW");
WriteLog(hInstall, "Opened service control manager");
auto hService = Guard(OpenServiceW(hSCM.get(), L"wuauserv",
SERVICE_QUERY_CONFIG | SERVICE_CHANGE_CONFIG
| SERVICE_QUERY_STATUS | SERVICE_STOP));
if (!hService)
ThrowLastError("OpenServiceW");
WriteLog(hInstall, "Obtained WU service handle");
if (ServiceStatus(hInstall, hService.get()) == SERVICE_RUNNING
|| !EnsureServiceEnabled(hInstall, hService.get(), true))
{
// Allow errors opening service to be logged, but not interrupt installation.
// They are likely to happen in situations where people hard-disable WU service,
// and for these cases, let people deal with install logs instead of failing.
throw nonfatal_exception(e);
// No need to restore anything back, since we didn't change config
hService.reset();
WriteLog(hInstall, "Service configuration is unchanged");
}
return hService;
}
// Returns if the service configuration was actually changed
......@@ -354,11 +345,18 @@ extern "C" __declspec(dllexport) UINT __stdcall UnpackMSUForInstall(MSIHANDLE hI
WriteLog(hInstall, "started");
WriteLog(hInstall, "Checking value of InstMSUBinary");
wchar_t sBinaryName[MAX_PATH + 1];
DWORD nCCh = sizeof(sBinaryName) / sizeof(*sBinaryName);
wchar_t sInstMSUBinary[MAX_PATH + 10];
DWORD nCCh = sizeof(sInstMSUBinary) / sizeof(*sInstMSUBinary);
CheckWin32Error("MsiGetPropertyW",
MsiGetPropertyW(hInstall, L"InstMSUBinary", sBinaryName, &nCCh));
WriteLog(hInstall, "Got InstMSUBinary value:", sBinaryName);
MsiGetPropertyW(hInstall, L"InstMSUBinary", sInstMSUBinary, &nCCh));
WriteLog(hInstall,
"Got InstMSUBinary value:", sInstMSUBinary); // 123|Windows61-KB2999226-x64msu
const wchar_t* sBinaryName = wcschr(sInstMSUBinary, L'|');
if (!sBinaryName)
throw std::exception("No error code in InstMSUBinary!");
// "123" - # of the message in Error table to be shown on failure
const std::wstring sErrNo(sInstMSUBinary, sBinaryName - sInstMSUBinary);
++sBinaryName;
PMSIHANDLE hDatabase = MsiGetActiveDatabase(hInstall);
if (!hDatabase)
......@@ -415,8 +413,9 @@ extern "C" __declspec(dllexport) UINT __stdcall UnpackMSUForInstall(MSIHANDLE hI
WriteLog(hInstall, "Successfully wrote", Num2Dec(nTotal), "bytes");
}
CheckWin32Error("MsiSetPropertyW", MsiSetPropertyW(hInstall, L"inst_msu", sBinary.c_str()));
const std::wstring s_inst_msu = sErrNo + L"|" + sBinary;
CheckWin32Error("MsiSetPropertyW",
MsiSetPropertyW(hInstall, L"inst_msu", s_inst_msu.c_str()));
// Don't delete the file: it will be done by following actions (inst_msu or cleanup_msu)
(void)aDeleteFileGuard.release();
......@@ -433,17 +432,23 @@ extern "C" __declspec(dllexport) UINT __stdcall UnpackMSUForInstall(MSIHANDLE hI
// "CustomActionData" property, and runs wusa.exe to install it. Waits for it and checks exit code.
extern "C" __declspec(dllexport) UINT __stdcall InstallMSU(MSIHANDLE hInstall)
{
std::wstring sErrNo; // "123" - # of the message in Error table to be shown on failure
try
{
sLogPrefix = "InstallMSU:";
WriteLog(hInstall, "started");
WriteLog(hInstall, "Checking value of CustomActionData");
wchar_t sBinaryName[MAX_PATH + 1];
DWORD nCCh = sizeof(sBinaryName) / sizeof(*sBinaryName);
wchar_t sCustomActionData[MAX_PATH + 10]; // "123|C:\Temp\binary.tmp"
DWORD nCCh = sizeof(sCustomActionData) / sizeof(*sCustomActionData);
CheckWin32Error("MsiGetPropertyW",
MsiGetPropertyW(hInstall, L"CustomActionData", sBinaryName, &nCCh));
WriteLog(hInstall, "Got CustomActionData value:", sBinaryName);
MsiGetPropertyW(hInstall, L"CustomActionData", sCustomActionData, &nCCh));
WriteLog(hInstall, "Got CustomActionData value:", sCustomActionData);
const wchar_t* sBinaryName = wcschr(sCustomActionData, L'|');
if (!sBinaryName)
throw std::exception("No error code in CustomActionData!");
sErrNo = std::wstring(sCustomActionData, sBinaryName - sCustomActionData);
++sBinaryName;
auto aDeleteFileGuard(Guard(sBinaryName));
// In case the Windows Update service is disabled, we temporarily enable it here
......@@ -496,19 +501,12 @@ extern "C" __declspec(dllexport) UINT __stdcall InstallMSU(MSIHANDLE hInstall)
ThrowWin32Error("Execution of wusa.exe", nExitCode);
}
}
catch (nonfatal_exception& e)
{
// An error that should not interrupt installation
WriteLog(hInstall, e.what());
WriteLog(hInstall, "Installation of MSU package failed, but installation of product will "
"continue. You may need to install the required update manually");
return ERROR_SUCCESS;
}
catch (std::exception& e)
{
WriteLog(hInstall, e.what());
ShowWarning(hInstall, sErrNo, e.what());
}
return ERROR_INSTALL_FAILURE;
return ERROR_SUCCESS; // Do not break on MSU installation errors
}
// Rollback deferred action "cleanup_msu" that is executed on error or cancel.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment