summaryrefslogtreecommitdiff
path: root/setup_native
diff options
context:
space:
mode:
authorMike Kaganski <mike.kaganski@collabora.com>2019-04-21 22:39:43 +0300
committerCaolán McNamara <caolanm@redhat.com>2019-04-25 13:00:55 +0200
commit1151e8fe6c8061f8b9c4777fba9665464ab5b5b8 (patch)
tree1e9b82372406e370cfe6e4197b0c21ee50192e11 /setup_native
parent086701be50e9d95c67f6fdee97b4080419b55e61 (diff)
tdf#124794: Wait for WU service stopped before launching wusa.exe
It seems that wusa.exe would fail with error code 0x80080005 if launched too soon after WU service was sent a stop control (which was added in tdf#123832). Change-Id: I470a8a8e933e8a0cd6208c095ed63c1761b3b434 Reviewed-on: https://gerrit.libreoffice.org/71045 Tested-by: Jenkins Reviewed-by: Mike Kaganski <mike.kaganski@collabora.com> Reviewed-on: https://gerrit.libreoffice.org/71062 Reviewed-by: Caolán McNamara <caolanm@redhat.com> Tested-by: Caolán McNamara <caolanm@redhat.com>
Diffstat (limited to 'setup_native')
-rw-r--r--setup_native/source/win32/customactions/inst_msu/inst_msu.cxx120
1 files changed, 86 insertions, 34 deletions
diff --git a/setup_native/source/win32/customactions/inst_msu/inst_msu.cxx b/setup_native/source/win32/customactions/inst_msu/inst_msu.cxx
index d81532020cce..d8c196ccd1a8 100644
--- a/setup_native/source/win32/customactions/inst_msu/inst_msu.cxx
+++ b/setup_native/source/win32/customactions/inst_msu/inst_msu.cxx
@@ -181,6 +181,46 @@ public:
}
};
+// This class uses MsiProcessMessage to check for user input: it returns IDCANCEL when user cancels
+// installation. It throws a special exception, to be intercepted in main action function to return
+// corresponding exit code.
+class UserInputChecker
+{
+public:
+ class eUserCancelled
+ {
+ };
+
+ UserInputChecker(MSIHANDLE hInstall)
+ : m_hInstall(hInstall)
+ , m_hProgressRec(MsiCreateRecord(3))
+ {
+ // Use explicit progress messages
+ MsiRecordSetInteger(m_hProgressRec, 1, 1);
+ MsiRecordSetInteger(m_hProgressRec, 2, 1);
+ MsiRecordSetInteger(m_hProgressRec, 3, 0);
+ int nResult = MsiProcessMessage(m_hInstall, INSTALLMESSAGE_PROGRESS, m_hProgressRec);
+ if (nResult == IDCANCEL)
+ throw eUserCancelled();
+ // Prepare the record to following progress update calls
+ MsiRecordSetInteger(m_hProgressRec, 1, 2);
+ MsiRecordSetInteger(m_hProgressRec, 2, 0); // step by 0 - don't move progress
+ MsiRecordSetInteger(m_hProgressRec, 3, 0);
+ }
+
+ void ThrowIfUserCancelled()
+ {
+ // Check if user has cancelled
+ int nResult = MsiProcessMessage(m_hInstall, INSTALLMESSAGE_PROGRESS, m_hProgressRec);
+ if (nResult == IDCANCEL)
+ throw eUserCancelled();
+ }
+
+private:
+ MSIHANDLE m_hInstall;
+ PMSIHANDLE m_hProgressRec;
+};
+
// Checks if Windows Update service is disabled, and if it is, enables it temporarily.
// Also stops the service if it's currently running, because it seems that wusa.exe
// does not freeze when it starts the service itself.
@@ -200,7 +240,7 @@ public:
if (mhService)
{
EnsureServiceEnabled(mhInstall, mhService.get(), false);
- StopService(mhInstall, mhService.get());
+ StopService(mhInstall, mhService.get(), false);
}
}
catch (std::exception& e)
@@ -230,8 +270,9 @@ private:
// Stop currently running service to prevent wusa.exe from hanging trying to detect if the
// update is applicable (sometimes this freezes it ~indefinitely; it seems that it doesn't
// happen if wusa.exe starts the service itself: https://superuser.com/questions/1044528/).
+ // tdf#124794: Wait for service to stop.
if (nCurrentStatus == SERVICE_RUNNING)
- StopService(hInstall, hService.get());
+ StopService(hInstall, hService.get(), true);
if (nCurrentStatus == SERVICE_RUNNING
|| !EnsureServiceEnabled(hInstall, hService.get(), true))
@@ -260,9 +301,8 @@ private:
DWORD nCbRequired = 0;
if (!QueryServiceConfigW(hService, nullptr, 0, &nCbRequired))
{
- DWORD nError = GetLastError();
- if (nError != ERROR_INSUFFICIENT_BUFFER)
- ThrowLastError("QueryServiceConfigW");
+ if (DWORD nError = GetLastError(); nError != ERROR_INSUFFICIENT_BUFFER)
+ ThrowWin32Error("QueryServiceConfigW", nError);
}
std::unique_ptr<char[]> pBuf(new char[nCbRequired]);
LPQUERY_SERVICE_CONFIGW pConfig = reinterpret_cast<LPQUERY_SERVICE_CONFIGW>(pBuf.get());
@@ -336,7 +376,7 @@ private:
return aServiceStatus.dwCurrentState;
}
- static void StopService(MSIHANDLE hInstall, SC_HANDLE hService)
+ static void StopService(MSIHANDLE hInstall, SC_HANDLE hService, bool bWait)
{
try
{
@@ -344,12 +384,34 @@ private:
{
SERVICE_STATUS aServiceStatus{};
if (!ControlService(hService, SERVICE_CONTROL_STOP, &aServiceStatus))
- WriteLog(hInstall, Win32ErrorMessage("ControlService", GetLastError()));
- else
- WriteLog(
- hInstall,
- "Successfully sent SERVICE_CONTROL_STOP code to Windows Update service");
- // No need to wait for the service stopped
+ ThrowLastError("ControlService");
+ WriteLog(hInstall,
+ "Successfully sent SERVICE_CONTROL_STOP code to Windows Update service");
+ if (aServiceStatus.dwCurrentState != SERVICE_STOPPED && bWait)
+ {
+ // Let user cancel too long wait
+ UserInputChecker aInputChecker(hInstall);
+ // aServiceStatus.dwWaitHint is unreasonably high for Windows Update (30000),
+ // so don't use it, but simply poll service status each second
+ for (int nWait = 0; nWait < 30; ++nWait) // arbitrary limit of 30 s
+ {
+ for (int i = 0; i < 2; ++i) // check user input twice a second
+ {
+ Sleep(500);
+ aInputChecker.ThrowIfUserCancelled();
+ }
+
+ if (!QueryServiceStatus(hService, &aServiceStatus))
+ ThrowLastError("QueryServiceStatus");
+
+ if (aServiceStatus.dwCurrentState == SERVICE_STOPPED)
+ break;
+ }
+ }
+ if (aServiceStatus.dwCurrentState == SERVICE_STOPPED)
+ WriteLog(hInstall, "Successfully stopped Windows Update service");
+ else if (bWait)
+ WriteLog(hInstall, "Wait for Windows Update stop timed out - proceeding");
}
else
WriteLog(hInstall, "Windows Update service is not running");
@@ -496,33 +558,16 @@ extern "C" __declspec(dllexport) UINT __stdcall InstallMSU(MSIHANDLE hInstall)
{
// This block waits when the started wusa.exe process finishes. Since it's possible
- // for wusa.exe in some circumstances to wait really long (indefinitely?), we use
- // MsiProcessMessage to check for user input: it returns IDCANCEL when user cancels
- // installation.
- PMSIHANDLE hProgressRec = MsiCreateRecord(3);
- // Use explicit progress messages
- MsiRecordSetInteger(hProgressRec, 1, 1);
- MsiRecordSetInteger(hProgressRec, 2, 1);
- MsiRecordSetInteger(hProgressRec, 3, 0);
- int nResult = MsiProcessMessage(hInstall, INSTALLMESSAGE_PROGRESS, hProgressRec);
- if (nResult == IDCANCEL)
- return ERROR_INSTALL_USEREXIT;
- // Prepare the record to following progress update calls
- MsiRecordSetInteger(hProgressRec, 1, 2);
- MsiRecordSetInteger(hProgressRec, 2, 0); // step by 0 - don't move progress
- MsiRecordSetInteger(hProgressRec, 3, 0);
+ // for wusa.exe in some circumstances to wait really long (indefinitely?), we check
+ // for user input here.
+ UserInputChecker aInputChecker(hInstall);
for (;;)
{
DWORD nWaitResult = WaitForSingleObject(pi.hProcess, 500);
if (nWaitResult == WAIT_OBJECT_0)
break; // wusa.exe finished
else if (nWaitResult == WAIT_TIMEOUT)
- {
- // Check if user has cancelled
- nResult = MsiProcessMessage(hInstall, INSTALLMESSAGE_PROGRESS, hProgressRec);
- if (nResult == IDCANCEL)
- return ERROR_INSTALL_USEREXIT;
- }
+ aInputChecker.ThrowIfUserCancelled();
else
ThrowWin32Error("WaitForSingleObject", nWaitResult);
}
@@ -558,6 +603,10 @@ extern "C" __declspec(dllexport) UINT __stdcall InstallMSU(MSIHANDLE hInstall)
"continue. You may need to install the required update manually");
return ERROR_SUCCESS;
}
+ catch (const UserInputChecker::eUserCancelled&)
+ {
+ return ERROR_INSTALL_USEREXIT;
+ }
catch (std::exception& e)
{
WriteLog(hInstall, e.what());
@@ -583,7 +632,10 @@ extern "C" __declspec(dllexport) UINT __stdcall CleanupMSU(MSIHANDLE hInstall)
WriteLog(hInstall, "Got CustomActionData value:", sBinaryName);
if (!DeleteFileW(sBinaryName))
- ThrowLastError("DeleteFileW");
+ {
+ if (DWORD nError = GetLastError(); nError != ERROR_FILE_NOT_FOUND)
+ ThrowWin32Error("DeleteFileW", nError);
+ }
WriteLog(hInstall, "File successfully removed");
}
catch (std::exception& e)