diff options
| author | Eike Ziller <eike.ziller@qt.io> | 2020-01-13 16:12:28 +0100 |
|---|---|---|
| committer | Eike Ziller <eike.ziller@qt.io> | 2020-01-20 10:39:28 +0000 |
| commit | 392b063fe8c4cf49d225c1327ff38cd8f3c386dc (patch) | |
| tree | 96dd3fb146d0469bb2326b9ddf9c6b84e2f1cd75 | |
| parent | 0334b6e491a3688f2455e075595afde87b8f76af (diff) | |
| download | qt-creator-392b063fe8c4cf49d225c1327ff38cd8f3c386dc.tar.gz | |
Guard against crashing plugins
If a plugin crashes, ask the user if that plugin should be disabled.
For this track which plugin currently is changing its state. Remove that
information if the state change was successful.
At startup check if there is a plugin for which we wrote, but not
removed that information.
This is especially interesting if the user installed 3rdparty plugins.
Change-Id: I5729aa5c786b653d5bd53304f4fbeaca35ec9e71
Reviewed-by: Leena Miettinen <riitta-leena.miettinen@qt.io>
Reviewed-by: Alessandro Portale <alessandro.portale@qt.io>
| -rw-r--r-- | src/app/main.cpp | 1 | ||||
| -rw-r--r-- | src/libs/extensionsystem/pluginmanager.cpp | 94 | ||||
| -rw-r--r-- | src/libs/extensionsystem/pluginmanager.h | 1 | ||||
| -rw-r--r-- | src/libs/extensionsystem/pluginmanager_p.h | 1 |
4 files changed, 97 insertions, 0 deletions
diff --git a/src/app/main.cpp b/src/app/main.cpp index a8da6f67be..59ad381b83 100644 --- a/src/app/main.cpp +++ b/src/app/main.cpp @@ -680,6 +680,7 @@ int main(int argc, char **argv) } } + PluginManager::checkForProblematicPlugins(); PluginManager::loadPlugins(); if (coreplugin->hasError()) { displayError(msgCoreLoadFailure(coreplugin->errorString())); diff --git a/src/libs/extensionsystem/pluginmanager.cpp b/src/libs/extensionsystem/pluginmanager.cpp index 8173a5e0a2..a8badcdddc 100644 --- a/src/libs/extensionsystem/pluginmanager.cpp +++ b/src/libs/extensionsystem/pluginmanager.cpp @@ -31,14 +31,18 @@ #include "iplugin.h" #include <QCoreApplication> +#include <QCryptographicHash> #include <QDateTime> #include <QDebug> #include <QDir> #include <QEventLoop> #include <QFile> +#include <QGuiApplication> #include <QLibrary> #include <QLibraryInfo> +#include <QMessageBox> #include <QMetaProperty> +#include <QPushButton> #include <QSettings> #include <QSysInfo> #include <QTextStream> @@ -1353,6 +1357,94 @@ bool PluginManagerPrivate::loadQueue(PluginSpec *spec, return true; } +class LockFile +{ +public: + static QString filePath(PluginManagerPrivate *pm) + { + return QFileInfo(pm->settings->fileName()).absolutePath() + '/' + + QCoreApplication::applicationName() + '.' + + QCryptographicHash::hash(QCoreApplication::applicationDirPath().toUtf8(), + QCryptographicHash::Sha1) + .left(8) + .toHex() + + ".lock"; + } + + static Utils::optional<QString> lockedPluginName(PluginManagerPrivate *pm) + { + const QString lockFilePath = LockFile::filePath(pm); + if (QFile::exists(lockFilePath)) { + QFile f(lockFilePath); + f.open(QIODevice::ReadOnly); + const auto pluginName = QString::fromUtf8(f.readLine()).trimmed(); + f.close(); + return pluginName; + } + return {}; + } + + LockFile(PluginManagerPrivate *pm, PluginSpec *spec) + : m_filePath(filePath(pm)) + { + QFile f(m_filePath); + f.open(QIODevice::WriteOnly); + f.write(spec->name().toUtf8()); + f.write("\n"); + f.close(); + } + + ~LockFile() { QFile::remove(m_filePath); } + +private: + QString m_filePath; +}; + +void PluginManagerPrivate::checkForProblematicPlugins() +{ + const Utils::optional<QString> pluginName = LockFile::lockedPluginName(this); + if (pluginName) { + PluginSpec *spec = pluginByName(*pluginName); + if (spec && !spec->isRequired()) { + const QSet<PluginSpec *> dependents = PluginManager::pluginsRequiringPlugin(spec); + auto dependentsNames = Utils::transform<QStringList>(dependents, &PluginSpec::name); + std::sort(dependentsNames.begin(), dependentsNames.end()); + const QString dependentsList = dependentsNames.join(", "); + const QString pluginsMenu = HostOsInfo::isMacHost() + ? tr("%1 > About Plugins") + .arg(QGuiApplication::applicationDisplayName()) + : tr("Help > About Plugins"); + const QString otherPluginsText = tr("The following plugins depend on " + "%1 and are also disabled: %2.\n\n") + .arg(spec->name(), dependentsList); + const QString detailsText = (dependents.isEmpty() ? QString() : otherPluginsText) + + tr("Disable plugins permanently in %1.").arg(pluginsMenu); + const QString text = tr("It looks like %1 closed because of a problem with the \"%2\" " + "plugin. Temporarily disable the plugin?") + .arg(QGuiApplication::applicationDisplayName(), spec->name()); + QMessageBox dialog; + dialog.setIcon(QMessageBox::Question); + dialog.setText(text); + dialog.setDetailedText(detailsText); + QPushButton *disableButton = dialog.addButton(tr("Disable Plugin"), + QMessageBox::AcceptRole); + dialog.addButton(tr("Continue"), QMessageBox::RejectRole); + dialog.exec(); + if (dialog.clickedButton() == disableButton) { + spec->d->setForceDisabled(true); + for (PluginSpec *other : dependents) + other->d->setForceDisabled(true); + enableDependenciesIndirectly(); + } + } + } +} + +void PluginManager::checkForProblematicPlugins() +{ + d->checkForProblematicPlugins(); +} + /*! \internal */ @@ -1365,6 +1457,8 @@ void PluginManagerPrivate::loadPlugin(PluginSpec *spec, PluginSpec::State destSt if (!spec->isEffectivelyEnabled() && destState == PluginSpec::Loaded) return; + LockFile f(this, spec); + switch (destState) { case PluginSpec::Running: profilingReport(">initializeExtensions", spec); diff --git a/src/libs/extensionsystem/pluginmanager.h b/src/libs/extensionsystem/pluginmanager.h index d90723b986..cc1a461536 100644 --- a/src/libs/extensionsystem/pluginmanager.h +++ b/src/libs/extensionsystem/pluginmanager.h @@ -96,6 +96,7 @@ public: static const QStringList allErrors(); static QSet<PluginSpec *> pluginsRequiringPlugin(PluginSpec *spec); static QSet<PluginSpec *> pluginsRequiredByPlugin(PluginSpec *spec); + static void checkForProblematicPlugins(); // Settings static void setSettings(QSettings *settings); diff --git a/src/libs/extensionsystem/pluginmanager_p.h b/src/libs/extensionsystem/pluginmanager_p.h index 83dd521b85..08c1eaa1ed 100644 --- a/src/libs/extensionsystem/pluginmanager_p.h +++ b/src/libs/extensionsystem/pluginmanager_p.h @@ -65,6 +65,7 @@ public: void removeObject(QObject *obj); // Plugin operations + void checkForProblematicPlugins(); void loadPlugins(); void shutdown(); void setPluginPaths(const QStringList &paths); |
