summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEike Ziller <eike.ziller@qt.io>2020-01-13 16:12:28 +0100
committerEike Ziller <eike.ziller@qt.io>2020-01-20 10:39:28 +0000
commit392b063fe8c4cf49d225c1327ff38cd8f3c386dc (patch)
tree96dd3fb146d0469bb2326b9ddf9c6b84e2f1cd75
parent0334b6e491a3688f2455e075595afde87b8f76af (diff)
downloadqt-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.cpp1
-rw-r--r--src/libs/extensionsystem/pluginmanager.cpp94
-rw-r--r--src/libs/extensionsystem/pluginmanager.h1
-rw-r--r--src/libs/extensionsystem/pluginmanager_p.h1
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);