summaryrefslogtreecommitdiffstats
path: root/src/libs/installer
diff options
context:
space:
mode:
authorArttu Tarkiainen <arttu.tarkiainen@qt.io>2022-08-18 12:51:36 +0300
committerArttu Tarkiainen <arttu.tarkiainen@qt.io>2022-09-07 14:09:46 +0300
commit86c68384cbf197dae3c6583c7b752eb21c75e34c (patch)
tree416a6ab40627a192631fe004a50f1d3261ad65e4 /src/libs/installer
parent1b702ac2801de72c67224c42e6456db235ee6b8c (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.cpp10
-rw-r--r--src/libs/installer/commandlineparser.h2
-rw-r--r--src/libs/installer/constants.h2
-rw-r--r--src/libs/installer/packagemanagercore_p.cpp65
-rw-r--r--src/libs/installer/protocol.h1
-rw-r--r--src/libs/installer/qprocesswrapper.cpp53
-rw-r--r--src/libs/installer/qprocesswrapper.h5
-rw-r--r--src/libs/installer/remoteserverconnection.cpp11
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;