diff options
author | Arttu Tarkiainen <arttu.tarkiainen@qt.io> | 2022-08-18 12:51:36 +0300 |
---|---|---|
committer | Arttu Tarkiainen <arttu.tarkiainen@qt.io> | 2022-09-07 14:09:46 +0300 |
commit | 86c68384cbf197dae3c6583c7b752eb21c75e34c (patch) | |
tree | 416a6ab40627a192631fe004a50f1d3261ad65e4 /src/libs/installer | |
parent | 1b702ac2801de72c67224c42e6456db235ee6b8c (diff) |
Replace .vbs hack to update maintenance tool binary on Windows
Do the replacing of the executable instead by:
- The original MT renames itself to a temporary name
- The new MT is renamed to the destination file name
- The new MT is started as a detached process, it polls to delete the
temporary MT (requires that the parent process exited)
Introduce hidden options, cleanup-update and cleanup-update-only to
start the maintenance tool in a mode for deleting the old binary.
Also fix missing verbose messages on hard restart of maintenance tool
after update. Due to QProcessWrapper::startDetached using a modified
implementation of the wrapped class (QProcess) method, the detached
process was started with a new console instead of inheriting the
console of the parent process. Fix by adding a second variant of the
QProcessWrapper::startDetached functions using the QProcess
implementation.
Task-number: QTIFW-2625
Change-Id: Iebc5b04befa1e79765217f93723f977556e03d90
Reviewed-by: Leena Miettinen <riitta-leena.miettinen@qt.io>
Reviewed-by: Katja Marttila <katja.marttila@qt.io>
Diffstat (limited to 'src/libs/installer')
-rw-r--r-- | src/libs/installer/commandlineparser.cpp | 10 | ||||
-rw-r--r-- | src/libs/installer/commandlineparser.h | 2 | ||||
-rw-r--r-- | src/libs/installer/constants.h | 2 | ||||
-rw-r--r-- | src/libs/installer/packagemanagercore_p.cpp | 65 | ||||
-rw-r--r-- | src/libs/installer/protocol.h | 1 | ||||
-rw-r--r-- | src/libs/installer/qprocesswrapper.cpp | 53 | ||||
-rw-r--r-- | src/libs/installer/qprocesswrapper.h | 5 | ||||
-rw-r--r-- | src/libs/installer/remoteserverconnection.cpp | 11 |
8 files changed, 104 insertions, 45 deletions
diff --git a/src/libs/installer/commandlineparser.cpp b/src/libs/installer/commandlineparser.cpp index a04891303..8585379ad 100644 --- a/src/libs/installer/commandlineparser.cpp +++ b/src/libs/installer/commandlineparser.cpp @@ -233,6 +233,16 @@ CommandLineParser::CommandLineParser() "processor cores in the system."), QLatin1String("threads"))); + QCommandLineOption cleanupUpdate(CommandLineOptions::scCleanupUpdate); + cleanupUpdate.setValueName(QLatin1String("path")); + cleanupUpdate.setFlags(QCommandLineOption::HiddenFromHelp); + addOption(cleanupUpdate); + + QCommandLineOption cleanupUpdateOnly(CommandLineOptions::scCleanupUpdateOnly); + cleanupUpdateOnly.setValueName(QLatin1String("path")); + cleanupUpdateOnly.setFlags(QCommandLineOption::HiddenFromHelp); + addOption(cleanupUpdateOnly); + // Deprecated options QCommandLineOption deprecatedUpdater(CommandLineOptions::scDeprecatedUpdater); deprecatedUpdater.setHidden(true); diff --git a/src/libs/installer/commandlineparser.h b/src/libs/installer/commandlineparser.h index f1bedf92e..4511969cd 100644 --- a/src/libs/installer/commandlineparser.h +++ b/src/libs/installer/commandlineparser.h @@ -48,7 +48,7 @@ public: bool addOptionWithContext(const QCommandLineOption &option, OptionContextFlags flags); QString helpText() const { return m_parser.helpText(); } - bool isSet(const QString &option) { return m_parser.isSet(option); } + bool isSet(const QString &option) const { return m_parser.isSet(option); } QStringList unknownOptionNames() const { return m_parser.unknownOptionNames(); } QStringList positionalArguments() const { return m_parser.positionalArguments(); } bool parse(const QStringList &argumens) { return m_parser.parse(argumens); } diff --git a/src/libs/installer/constants.h b/src/libs/installer/constants.h index 7fa54c4ec..0973e8c3e 100644 --- a/src/libs/installer/constants.h +++ b/src/libs/installer/constants.h @@ -220,6 +220,8 @@ static const QLatin1String scSquishPortShort("q"); static const QLatin1String scSquishPortLong("squish-port"); static const QLatin1String scMaxConcurrentOperationsShort("mco"); static const QLatin1String scMaxConcurrentOperationsLong("max-concurrent-operations"); +static const QLatin1String scCleanupUpdate("cleanup-update"); +static const QLatin1String scCleanupUpdateOnly("cleanup-update-only"); // Deprecated options, provided only for backward compatibility static const QLatin1String scDeprecatedUpdater("updater"); diff --git a/src/libs/installer/packagemanagercore_p.cpp b/src/libs/installer/packagemanagercore_p.cpp index de093db82..eddb44555 100644 --- a/src/libs/installer/packagemanagercore_p.cpp +++ b/src/libs/installer/packagemanagercore_p.cpp @@ -123,54 +123,27 @@ static QStringList checkRunningProcessesFromList(const QStringList &processList) static void deferredRename(const QString &oldName, const QString &newName, bool restart = false) { #ifdef Q_OS_WIN - QStringList arguments; - - // Check if .vbs extension can be used for running renaming script. If not, create own extension - QString extension = QLatin1String(".vbs"); - QSettingsWrapper settingRoot(QLatin1String("HKEY_CLASSES_ROOT\\.vbs"), QSettings::NativeFormat); - if (settingRoot.value(QLatin1String(".")).toString() != QLatin1String("VBSFile")) { - extension = QLatin1String(".qtInstaller"); - QSettingsWrapper settingsUser(QLatin1String("HKEY_CURRENT_USER\\Software\\Classes"), QSettings::NativeFormat); - QString value = settingsUser.value(extension).toString(); - if (value != QLatin1String("VBSFile")) - settingsUser.setValue(extension, QLatin1String("VBSFile")); - } - QTemporaryFile f(QDir::temp().absoluteFilePath(QLatin1String("deferredrenameXXXXXX%1")).arg(extension)); + const QString currentExecutable = QCoreApplication::applicationFilePath(); + const QString tmpExecutable = generateTemporaryFileName(currentExecutable); - QInstaller::openForWrite(&f); - f.setAutoRemove(false); + QFile::rename(currentExecutable, tmpExecutable); + QFile::rename(oldName, newName); - arguments << QDir::toNativeSeparators(f.fileName()) << QDir::toNativeSeparators(oldName) - << QDir::toNativeSeparators(QFileInfo(oldName).dir().absoluteFilePath(QFileInfo(newName) - .fileName())); - - QTextStream batch(&f); - batch.setCodec("UTF-16"); - batch << "Set fso = WScript.CreateObject(\"Scripting.FileSystemObject\")\n"; - batch << "Set tmp = WScript.CreateObject(\"WScript.Shell\")\n"; - batch << QString::fromLatin1("file = \"%1\"\n").arg(arguments[2]); - batch << "on error resume next\n"; - - batch << "while fso.FileExists(file)\n"; - batch << " fso.DeleteFile(file)\n"; - batch << " WScript.Sleep(1000)\n"; - batch << "wend\n"; - batch << QString::fromLatin1("fso.MoveFile \"%1\", file\n").arg(arguments[1]); + QStringList arguments; if (restart) { - //Restart with same command line arguments as first executable - QStringList commandLineArguments = QCoreApplication::arguments(); - batch << QString::fromLatin1("tmp.exec \"%1 --%2") - .arg(arguments[2]).arg(CommandLineOptions::scStartUpdaterLong); - //Skip the first argument as that is executable itself - for (int i = 1; i < commandLineArguments.count(); i++) { - batch << QString::fromLatin1(" %1").arg(commandLineArguments.at(i)); - } - batch << QString::fromLatin1("\"\n"); + // Restart with same command line arguments as first executable + arguments = QCoreApplication::arguments(); + arguments.removeFirst(); // Remove program name + arguments.prepend(tmpExecutable); + arguments.prepend(QLatin1String("--") + + CommandLineOptions::scCleanupUpdate); + } else { + arguments.append(QLatin1String("--") + + CommandLineOptions::scCleanupUpdateOnly); + arguments.append(tmpExecutable); } - batch << "fso.DeleteFile(WScript.ScriptFullName)\n"; + QProcessWrapper::startDetached2(newName, arguments); - QProcessWrapper::startDetached(QLatin1String("cscript"), QStringList() << QLatin1String("//Nologo") - << arguments[0]); #else QFile::remove(newName); QFile::rename(oldName, newName); @@ -1555,7 +1528,11 @@ void PackageManagerCorePrivate::writeMaintenanceTool(OperationList performedOper << (restart ? "true." : "false."); if (newBinaryWritten) { - deferredRename(maintenanceToolName() + QLatin1String(".new"), maintenanceToolName(), restart); + if (isInstaller()) + QFile::rename(maintenanceToolName() + QLatin1String(".new"), maintenanceToolName()); + else + deferredRename(maintenanceToolName() + QLatin1String(".new"), maintenanceToolName(), restart); + writeMaintenanceToolAlias(); } else if (restart) { SelfRestarter::setRestartOnQuit(true); diff --git a/src/libs/installer/protocol.h b/src/libs/installer/protocol.h index c2d6ccaa6..2dd1a5b1c 100644 --- a/src/libs/installer/protocol.h +++ b/src/libs/installer/protocol.h @@ -67,6 +67,7 @@ const char QProcessReadAll[] = "QProcess::readAll"; const char QProcessReadAllStandardOutput[] = "QProcess::readAllStandardOutput"; const char QProcessReadAllStandardError[] = "QProcess::readAllStandardError"; const char QProcessStartDetached[] = "QProcess::startDetached"; +const char QProcessStartDetached2[] = "QProcess::startDetached2"; const char QProcessSetWorkingDirectory[] = "QProcess::setWorkingDirectory"; const char QProcessSetEnvironment[] = "QProcess::setEnvironment"; const char QProcessEnvironment[] = "QProcess::environment"; diff --git a/src/libs/installer/qprocesswrapper.cpp b/src/libs/installer/qprocesswrapper.cpp index b33e7d43c..f37d65b29 100644 --- a/src/libs/installer/qprocesswrapper.cpp +++ b/src/libs/installer/qprocesswrapper.cpp @@ -107,6 +107,11 @@ void QProcessWrapper::processSignals() m_lock.unlock(); } +/*! + Starts the \a program with \a arguments in the working directory \a workingDirectory as a detached + process. The process id can be retrieved with the \a pid parameter. Compared to the QProcess + implementation of the same method this does not show a window for the started process on Windows. +*/ bool QProcessWrapper::startDetached(const QString &program, const QStringList &arguments, const QString &workingDirectory, qint64 *pid) { @@ -123,16 +128,64 @@ bool QProcessWrapper::startDetached(const QString &program, const QStringList &a return QInstaller::startDetached(program, arguments, workingDirectory, pid); } +/*! + Starts the \a program with \a arguments as a detached process. Compared to the QProcess + implementation of the same method this does not show a window for the started process on Windows. +*/ bool QProcessWrapper::startDetached(const QString &program, const QStringList &arguments) { return startDetached(program, arguments, QDir::currentPath()); } +/*! + Starts the \a program as a detached process. Compared to the QProcess implementation of the same + method this does not show a window for the started process on Windows. +*/ bool QProcessWrapper::startDetached(const QString &program) { return startDetached(program, QStringList()); } +/*! + Starts the \a program as a detached process. The variants of the function suffixed with \c 2 + use the base \c QProcess::startDetached implementation internally to start the process. +*/ +bool QProcessWrapper::startDetached2(const QString &program) +{ + return startDetached2(program, QStringList()); +} + +/*! + Starts the \a program with \a arguments as a detached process. The variants of the function + suffixed with \c 2 use the base \c QProcess::startDetached implementation internally to + start the process. +*/ +bool QProcessWrapper::startDetached2(const QString &program, const QStringList &arguments) +{ + return startDetached2(program, arguments, QDir::currentPath()); +} + +/*! + Starts the \a program with \a arguments in the working directory \a workingDirectory as a detached + process. The process id can be retrieved with the \a pid parameter. The variants + of the function suffixed with \c 2 use the base \c QProcess::startDetached implementation + internally to start the process. +*/ +bool QProcessWrapper::startDetached2(const QString &program, const QStringList &arguments, const QString &workingDirectory, qint64 *pid) +{ + QProcessWrapper w; + if (w.connectToServer()) { + const QPair<bool, qint64> result = + w.callRemoteMethod<QPair<bool, qint64> >(QLatin1String(Protocol::QProcessStartDetached2), + program, arguments, workingDirectory); + if (pid != nullptr) + *pid = result.second; + w.processSignals(); + return result.first; + } + return QProcess::startDetached(program, arguments, workingDirectory, pid); +} + void QProcessWrapper::setProcessChannelMode(QProcessWrapper::ProcessChannelMode mode) { if (connectToServer()) { diff --git a/src/libs/installer/qprocesswrapper.h b/src/libs/installer/qprocesswrapper.h index e76217eda..1a68bbf92 100644 --- a/src/libs/installer/qprocesswrapper.h +++ b/src/libs/installer/qprocesswrapper.h @@ -104,6 +104,11 @@ public: static bool startDetached(const QString &program, const QStringList &arguments, const QString &workingDirectory, qint64 *pid = 0); + static bool startDetached2(const QString &program); + static bool startDetached2(const QString &program, const QStringList &arguments); + static bool startDetached2(const QString &program, const QStringList &arguments, + const QString &workingDirectory, qint64 *pid = 0); + QString errorString() const; qint64 write(const QByteArray &byteArray); #ifdef Q_OS_WIN diff --git a/src/libs/installer/remoteserverconnection.cpp b/src/libs/installer/remoteserverconnection.cpp index 0868dbe0a..b7dcb22a4 100644 --- a/src/libs/installer/remoteserverconnection.cpp +++ b/src/libs/installer/remoteserverconnection.cpp @@ -277,6 +277,17 @@ void RemoteServerConnection::handleQProcess(RemoteServerReply *reply, const QStr qint64 pid = -1; bool success = QInstaller::startDetached(program, arguments, workingDirectory, &pid); reply->send(qMakePair< bool, qint64>(success, pid)); + } else if (command == QLatin1String(Protocol::QProcessStartDetached2)) { + QString program; + QStringList arguments; + QString workingDirectory; + data >> program; + data >> arguments; + data >> workingDirectory; + + qint64 pid = -1; + bool success = QProcess::startDetached(program, arguments, workingDirectory, &pid); + reply->send(qMakePair< bool, qint64>(success, pid)); } else if (command == QLatin1String(Protocol::QProcessSetWorkingDirectory)) { QString dir; data >> dir; |