summaryrefslogtreecommitdiffstats
path: root/src/libs
diff options
context:
space:
mode:
authorKatja Marttila <katja.marttila@qt.io>2021-01-22 14:50:16 +0200
committerKatja Marttila <katja.marttila@qt.io>2021-02-05 09:07:04 +0200
commit5aee36b74eb1d7613ea0108971e8a22f8dca8101 (patch)
tree30842cc3b88483c18facd2510ed7c5e3d0a0f138 /src/libs
parenta7b0eead83379d9343429a37efa8e39f924e8525 (diff)
Try rerunning execute operation
Execute operation can have hard coded paths to program which is executed. In case the program is relocated, UNDO operation will fail as it will not find the program. Implemented new XXXX_OLD value which can be used for overwriting the hardcoded value. In case the program execution fails, program is tried to launch again with the replaced value. Task-number: QTIFW-2125 Change-Id: I446a4c423e53cc4ffc6e5e25617d2400945ac3d9 Reviewed-by: Arttu Tarkiainen <arttu.tarkiainen@qt.io>
Diffstat (limited to 'src/libs')
-rw-r--r--src/libs/installer/elevatedexecuteoperation.cpp85
-rw-r--r--src/libs/installer/elevatedexecuteoperation.h8
-rw-r--r--src/libs/installer/extractarchiveoperation.cpp4
-rw-r--r--src/libs/installer/packagemanagercore.cpp15
-rw-r--r--src/libs/installer/packagemanagercore.h1
-rw-r--r--src/libs/installer/packagemanagercore_p.cpp28
-rw-r--r--src/libs/installer/packagemanagercore_p.h12
-rw-r--r--src/libs/installer/packagemanagercoredata.cpp7
-rw-r--r--src/libs/installer/packagemanagercoredata.h3
-rw-r--r--src/libs/kdtools/updateoperation.h6
10 files changed, 118 insertions, 51 deletions
diff --git a/src/libs/installer/elevatedexecuteoperation.cpp b/src/libs/installer/elevatedexecuteoperation.cpp
index 39be98e6f..f81377ac4 100644
--- a/src/libs/installer/elevatedexecuteoperation.cpp
+++ b/src/libs/installer/elevatedexecuteoperation.cpp
@@ -61,7 +61,11 @@ private:
public:
void readProcessOutput();
- bool run(const QStringList &arguments);
+ int run(QStringList &arguments, const OperationType type);
+
+private:
+ bool needsRerunWithReplacedVariables(QStringList &arguments, const OperationType type);
+
QProcessWrapper *process;
bool showStandardError;
@@ -100,10 +104,10 @@ bool ElevatedExecuteOperation::performOperation()
PackageManagerCore *const core = packageManager();
args = core->replaceVariables(args);
}
- return d->run(args);
+ return d->run(args, Operation::Perform) ? false : true;
}
-bool ElevatedExecuteOperation::Private::run(const QStringList &arguments)
+int ElevatedExecuteOperation::Private::run(QStringList &arguments, const OperationType type)
{
QStringList args = arguments;
QString workingDirectory;
@@ -192,13 +196,17 @@ bool ElevatedExecuteOperation::Private::run(const QStringList &arguments)
success = process->waitForFinished(-1);
}
- bool returnValue = true;
+ int returnValue = NoError;
if (!success) {
q->setError(UserDefinedError);
//TODO: pass errorString() through the wrapper */
q->setErrorString(tr("Cannot start: \"%1\": %2").arg(callstr,
process->errorString()));
- returnValue = false;
+ if (!needsRerunWithReplacedVariables(arguments, type)) {
+ returnValue = Error;
+ } else {
+ returnValue = NeedsRerun;
+ }
}
if (QThread::currentThread() == qApp->thread()) {
@@ -213,26 +221,29 @@ bool ElevatedExecuteOperation::Private::run(const QStringList &arguments)
if (process->exitStatus() == QProcessWrapper::CrashExit) {
q->setError(UserDefinedError);
q->setErrorString(tr("Program crashed: \"%1\"").arg(callstr));
- returnValue = false;
+ returnValue = Error;
}
- if (!allowedExitCodes.contains(process->exitCode())) {
- q->setError(UserDefinedError);
- if (customErrorMessage.isEmpty()) {
- q->setErrorString(tr("Execution failed (Unexpected exit code: %1): \"%2\"")
- .arg(QString::number(process->exitCode()), callstr));
+ if (!allowedExitCodes.contains(process->exitCode()) && returnValue != NeedsRerun) {
+ if (!needsRerunWithReplacedVariables(arguments, type)) {
+ q->setError(UserDefinedError);
+ if (customErrorMessage.isEmpty()) {
+ q->setErrorString(tr("Execution failed (Unexpected exit code: %1): \"%2\"")
+ .arg(QString::number(process->exitCode()), callstr));
+ } else {
+ q->setErrorString(customErrorMessage);
+ }
+
+ QByteArray standardErrorOutput = process->readAllStandardError();
+ // in error case it would be useful to see something in verbose output
+ if (!standardErrorOutput.isEmpty())
+ qCWarning(QInstaller::lcInstallerInstallLog).noquote() << standardErrorOutput;
+
+ returnValue = Error;
} else {
- q->setErrorString(customErrorMessage);
+ returnValue = NeedsRerun;
}
-
- QByteArray standardErrorOutput = process->readAllStandardError();
- // in error case it would be useful to see something in verbose output
- if (!standardErrorOutput.isEmpty())
- qCWarning(QInstaller::lcInstallerInstallLog).noquote() << standardErrorOutput;
-
- returnValue = false;
}
-
Q_ASSERT(process);
Q_ASSERT(process->state() == QProcessWrapper::NotRunning);
delete process;
@@ -241,6 +252,28 @@ bool ElevatedExecuteOperation::Private::run(const QStringList &arguments)
return returnValue;
}
+bool ElevatedExecuteOperation::Private::needsRerunWithReplacedVariables(QStringList &arguments, const OperationType type)
+{
+ if (type != Operation::Undo)
+ return false;
+ bool rerun = false;
+ PackageManagerCore *const core = q->packageManager();
+ for (int i = 0; i < arguments.count(); i++) {
+ QString key = core->key(arguments.at(i));
+ if (!key.isEmpty() && key.endsWith(QLatin1String("_OLD"))) {
+ key.remove(key.length() - 4, 4);
+ if (core->containsValue(key)) {
+ key.prepend(QLatin1String("@"));
+ key.append(QLatin1String("@"));
+ QString value = core->replaceVariables(key);
+ arguments.replace(i, value);
+ rerun = true;
+ }
+ }
+ }
+ return rerun;
+}
+
/*!
Cancels the ElevatedExecuteOperation. This methods tries to terminate the process
gracefully by calling QProcessWrapper::terminate. After 10 seconds, the process gets killed.
@@ -285,7 +318,17 @@ bool ElevatedExecuteOperation::undoOperation()
PackageManagerCore *const core = packageManager();
args = core->replaceVariables(args);
}
- return d->run(args);
+
+ int returnValue = d->run(args, Operation::Undo);
+ if (returnValue == NeedsRerun) {
+ qCDebug(QInstaller::lcInstallerInstallLog).noquote() << QString::fromLatin1("Failed to run "
+ "undo operation \"%1\" for component %2. Trying again with arguments %3").arg(name(),
+ value(QLatin1String("component")).toString(), args.join(QLatin1String(", ")));
+ setError(NoError);
+ setErrorString(QString());
+ returnValue = d->run(args, Operation::Undo);
+ }
+ return returnValue ? false : true;
}
bool ElevatedExecuteOperation::testOperation()
diff --git a/src/libs/installer/elevatedexecuteoperation.h b/src/libs/installer/elevatedexecuteoperation.h
index 5585e77a8..470d3e506 100644
--- a/src/libs/installer/elevatedexecuteoperation.h
+++ b/src/libs/installer/elevatedexecuteoperation.h
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2021 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -38,6 +38,12 @@ class INSTALLER_EXPORT ElevatedExecuteOperation : public QObject, public Operati
Q_OBJECT
public:
+ enum Error {
+ NoError = 0,
+ Error,
+ NeedsRerun
+ };
+
explicit ElevatedExecuteOperation(PackageManagerCore *core);
~ElevatedExecuteOperation();
diff --git a/src/libs/installer/extractarchiveoperation.cpp b/src/libs/installer/extractarchiveoperation.cpp
index 5b13b1f25..c2d541929 100644
--- a/src/libs/installer/extractarchiveoperation.cpp
+++ b/src/libs/installer/extractarchiveoperation.cpp
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2020 The Qt Company Ltd.
+** Copyright (C) 2021 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -163,7 +163,7 @@ bool ExtractArchiveOperation::performOperation()
// TODO: Use backups for rollback, too? Doesn't work for uninstallation though.
// delete all backups we can delete right now, remember the rest
- foreach (const Backup &i, callback.backupFiles())
+ foreach (const QInstaller::Backup &i, callback.backupFiles())
deleteFileNowOrLater(i.second);
if (!receiver.success()) {
diff --git a/src/libs/installer/packagemanagercore.cpp b/src/libs/installer/packagemanagercore.cpp
index ed513fb69..7eaae7b33 100644
--- a/src/libs/installer/packagemanagercore.cpp
+++ b/src/libs/installer/packagemanagercore.cpp
@@ -875,7 +875,7 @@ void PackageManagerCore::rollBackInstallation()
operation->setValue(QLatin1String("forceremoval"), false);
}
- PackageManagerCorePrivate::performOperationThreaded(operation, PackageManagerCorePrivate::Undo);
+ PackageManagerCorePrivate::performOperationThreaded(operation, Operation::Undo);
const QString componentName = operation->value(QLatin1String("component")).toString();
if (!componentName.isEmpty()) {
@@ -2963,7 +2963,7 @@ bool PackageManagerCore::performOperation(const QString &name, const QStringList
op->setArguments(replaceVariables(arguments));
op->backup();
if (!PackageManagerCorePrivate::performOperationThreaded(op.data())) {
- PackageManagerCorePrivate::performOperationThreaded(op.data(), PackageManagerCorePrivate::Undo);
+ PackageManagerCorePrivate::performOperationThreaded(op.data(), Operation::Undo);
return false;
}
return true;
@@ -3121,6 +3121,17 @@ QStringList PackageManagerCore::values(const QString &key, const QStringList &de
}
/*!
+ Returns the installer key for \a value. If \a value is not known, empty string is
+ returned.
+
+ \sa {installer::key}{installer.key}
+*/
+QString PackageManagerCore::key(const QString &value) const
+{
+ return d->m_data.key(value);
+}
+
+/*!
Sets the installer value for \a key to \a value.
\sa {installer::setValue}{installer.setValue}
diff --git a/src/libs/installer/packagemanagercore.h b/src/libs/installer/packagemanagercore.h
index b83d8f05a..5c480e1a9 100644
--- a/src/libs/installer/packagemanagercore.h
+++ b/src/libs/installer/packagemanagercore.h
@@ -169,6 +169,7 @@ public:
Q_INVOKABLE void setValue(const QString &key, const QString &value);
Q_INVOKABLE QString value(const QString &key, const QString &defaultValue = QString()) const;
Q_INVOKABLE QStringList values(const QString &key, const QStringList &defaultValue = QStringList()) const;
+ Q_INVOKABLE QString key(const QString &value) const;
QString replaceVariables(const QString &str) const;
QByteArray replaceVariables(const QByteArray &str) const;
diff --git a/src/libs/installer/packagemanagercore_p.cpp b/src/libs/installer/packagemanagercore_p.cpp
index 274ea017b..94e08018a 100644
--- a/src/libs/installer/packagemanagercore_p.cpp
+++ b/src/libs/installer/packagemanagercore_p.cpp
@@ -117,18 +117,18 @@ private:
Operation *m_operation;
};
-static bool runOperation(Operation *operation, PackageManagerCorePrivate::OperationType type)
+static bool runOperation(Operation *operation, Operation::OperationType type)
{
OperationTracer tracer(operation);
switch (type) {
- case PackageManagerCorePrivate::Backup:
+ case Operation::Backup:
tracer.trace(QLatin1String("backup"));
operation->backup();
return true;
- case PackageManagerCorePrivate::Perform:
+ case Operation::Perform:
tracer.trace(QLatin1String("perform"));
return operation->performOperation();
- case PackageManagerCorePrivate::Undo:
+ case Operation::Undo:
tracer.trace(QLatin1String("undo"));
return operation->undoOperation();
default:
@@ -368,7 +368,7 @@ bool PackageManagerCorePrivate::isProcessRunning(const QString &name,
}
/* static */
-bool PackageManagerCorePrivate::performOperationThreaded(Operation *operation, OperationType type)
+bool PackageManagerCorePrivate::performOperationThreaded(Operation *operation, Operation::OperationType type)
{
QFutureWatcher<bool> futureWatcher;
const QFuture<bool> future = QtConcurrent::run(runOperation, operation, type);
@@ -1254,7 +1254,7 @@ void PackageManagerCorePrivate::writeMaintenanceTool(OperationList performedOper
// create the directory containing the maintenance tool (like a bundle structure on macOS...)
Operation *op = createOwnedOperation(QLatin1String("Mkdir"));
op->setArguments(QStringList() << targetAppDirPath);
- performOperationThreaded(op, Backup);
+ performOperationThreaded(op, Operation::Backup);
performOperationThreaded(op);
performedOperations.append(takeOwnedOperation(op));
}
@@ -1266,14 +1266,14 @@ void PackageManagerCorePrivate::writeMaintenanceTool(OperationList performedOper
Operation *op = createOwnedOperation(QLatin1String("Copy"));
op->setArguments(QStringList() << (sourceAppDirPath + QLatin1String("/../PkgInfo"))
<< (targetAppDirPath + QLatin1String("/../PkgInfo")));
- performOperationThreaded(op, Backup);
+ performOperationThreaded(op, Operation::Backup);
performOperationThreaded(op);
// copy Info.plist to target directory
op = createOwnedOperation(QLatin1String("Copy"));
op->setArguments(QStringList() << (sourceAppDirPath + QLatin1String("/../Info.plist"))
<< (targetAppDirPath + QLatin1String("/../Info.plist")));
- performOperationThreaded(op, Backup);
+ performOperationThreaded(op, Operation::Backup);
performOperationThreaded(op);
// patch the Info.plist after copying it
@@ -1306,7 +1306,7 @@ void PackageManagerCorePrivate::writeMaintenanceTool(OperationList performedOper
op = createOwnedOperation(QLatin1String("Mkdir"));
op->setArguments(QStringList() << (QFileInfo(targetAppDirPath).path() + QLatin1String("/Resources")));
- performOperationThreaded(op, Backup);
+ performOperationThreaded(op, Operation::Backup);
performOperationThreaded(op);
// copy application icons if it exists.
@@ -1315,7 +1315,7 @@ void PackageManagerCorePrivate::writeMaintenanceTool(OperationList performedOper
op = createOwnedOperation(QLatin1String("Copy"));
op->setArguments(QStringList() << (sourceAppDirPath + QLatin1String("/../Resources/") + icon)
<< (targetAppDirPath + QLatin1String("/../Resources/") + icon));
- performOperationThreaded(op, Backup);
+ performOperationThreaded(op, Operation::Backup);
performOperationThreaded(op);
// finally, copy everything within Frameworks and plugins
@@ -1617,7 +1617,7 @@ bool PackageManagerCorePrivate::runInstaller()
mkdirOp->setValue(QLatin1String("forceremoval"), true);
mkdirOp->setValue(QLatin1String("uninstall-only"), true);
- performOperationThreaded(mkdirOp, Backup);
+ performOperationThreaded(mkdirOp, Operation::Backup);
if (!performOperationThreaded(mkdirOp)) {
// if we cannot create the target dir, we try to activate the admin rights
adminRightsGained = m_core->gainAdminRights();
@@ -2177,7 +2177,7 @@ void PackageManagerCorePrivate::installComponent(Component *component, double pr
connectOperationCallMethodRequest(operation);
// allow the operation to backup stuff before performing the operation
- performOperationThreaded(operation, PackageManagerCorePrivate::Backup);
+ performOperationThreaded(operation, Operation::Backup);
bool ignoreError = false;
bool ok = performOperationThreaded(operation);
@@ -2412,7 +2412,7 @@ void PackageManagerCorePrivate::runUndoOperations(const OperationList &undoOpera
qCDebug(QInstaller::lcInstallerInstallLog) << "undo operation=" << undoOperation->name();
bool ignoreError = false;
- bool ok = performOperationThreaded(undoOperation, PackageManagerCorePrivate::Undo);
+ bool ok = performOperationThreaded(undoOperation, Operation::Undo);
const QString componentName = undoOperation->value(QLatin1String("component")).toString();
@@ -2425,7 +2425,7 @@ void PackageManagerCorePrivate::runUndoOperations(const OperationList &undoOpera
QMessageBox::Retry | QMessageBox::Ignore, QMessageBox::Ignore);
if (button == QMessageBox::Retry)
- ok = performOperationThreaded(undoOperation, Undo);
+ ok = performOperationThreaded(undoOperation, Operation::Undo);
else if (button == QMessageBox::Ignore)
ignoreError = true;
}
diff --git a/src/libs/installer/packagemanagercore_p.h b/src/libs/installer/packagemanagercore_p.h
index 8983a95c2..858baf9eb 100644
--- a/src/libs/installer/packagemanagercore_p.h
+++ b/src/libs/installer/packagemanagercore_p.h
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2020 The Qt Company Ltd.
+** Copyright (C) 2021 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -67,12 +67,6 @@ class PackageManagerCorePrivate : public QObject
Q_DISABLE_COPY(PackageManagerCorePrivate)
public:
- enum OperationType {
- Backup,
- Perform,
- Undo
- };
-
explicit PackageManagerCorePrivate(PackageManagerCore *core);
explicit PackageManagerCorePrivate(PackageManagerCore *core, qint64 magicInstallerMaker,
const QList<OperationBlob> &performedOperations);
@@ -80,8 +74,8 @@ public:
static bool isProcessRunning(const QString &name, const QList<ProcessInfo> &processes);
- static bool performOperationThreaded(Operation *op, PackageManagerCorePrivate::OperationType type
- = PackageManagerCorePrivate::Perform);
+ static bool performOperationThreaded(Operation *op, UpdateOperation::OperationType type
+ = UpdateOperation::Perform);
void initialize(const QHash<QString, QString> &params);
bool isOfflineOnly() const;
diff --git a/src/libs/installer/packagemanagercoredata.cpp b/src/libs/installer/packagemanagercoredata.cpp
index d488731df..2f3dd9204 100644
--- a/src/libs/installer/packagemanagercoredata.cpp
+++ b/src/libs/installer/packagemanagercoredata.cpp
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2021 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -244,6 +244,11 @@ QVariant PackageManagerCoreData::value(const QString &key, const QVariant &_defa
return m_settings.value(key, _default);
}
+QString PackageManagerCoreData::key(const QString &value) const
+{
+ return m_variables.key(value, QString());
+}
+
QString PackageManagerCoreData::replaceVariables(const QString &str) const
{
static const QChar at = QLatin1Char('@');
diff --git a/src/libs/installer/packagemanagercoredata.h b/src/libs/installer/packagemanagercoredata.h
index e112fea1c..4c954d943 100644
--- a/src/libs/installer/packagemanagercoredata.h
+++ b/src/libs/installer/packagemanagercoredata.h
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2021 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -52,6 +52,7 @@ public:
bool contains(const QString &key) const;
bool setValue(const QString &key, const QString &normalizedValue);
QVariant value(const QString &key, const QVariant &_default = QVariant()) const;
+ QString key(const QString &value) const;
QString replaceVariables(const QString &str) const;
QByteArray replaceVariables(const QByteArray &ba) const;
diff --git a/src/libs/kdtools/updateoperation.h b/src/libs/kdtools/updateoperation.h
index e05fc34d2..a8110791c 100644
--- a/src/libs/kdtools/updateoperation.h
+++ b/src/libs/kdtools/updateoperation.h
@@ -53,6 +53,12 @@ public:
UserDefinedError = 128
};
+ enum OperationType {
+ Backup,
+ Perform,
+ Undo
+ };
+
explicit UpdateOperation(QInstaller::PackageManagerCore *core);
virtual ~UpdateOperation();