core: Improve handling of core initialization errors
authorManuel Nickschas <sputnick@quassel-irc.org>
Sun, 15 Jul 2018 21:48:32 +0000 (23:48 +0200)
committerManuel Nickschas <sputnick@quassel-irc.org>
Tue, 17 Jul 2018 17:58:05 +0000 (19:58 +0200)
Since the event loop may not be running while the core is being
initialized, calling exit() does not have the desired effect.
Instead, throw an exception with exit code and error message to
be able to handle this correctly. Exceptions must not be thrown
through the event loop, so ensure that they're caught before in
cases where the event loop was started already.

For the monolithic client, introduce an asynchronous init method
that catches potential exceptions and calls exit() instead. Show
an error popup in the UI if an error message is provided, so the
user gets some feedback before the client is terminated.

14 files changed:
src/client/client.cpp
src/client/client.h
src/common/main.cpp
src/common/types.h
src/core/core.cpp
src/core/core.h
src/core/coreapplication.cpp
src/core/coreapplication.h
src/qtui/mainwin.cpp
src/qtui/mainwin.h
src/qtui/monoapplication.cpp
src/qtui/monoapplication.h
src/qtui/qtuiapplication.cpp
src/qtui/qtuiapplication.h

index c3c4385..23b838f 100644 (file)
@@ -214,6 +214,16 @@ void Client::onDbUpgradeInProgress(bool inProgress)
 }
 
 
 }
 
 
+void Client::onExitRequested(int exitCode, const QString &reason)
+{
+    if (!reason.isEmpty()) {
+        qCritical() << reason;
+        emit exitRequested(reason);
+    }
+    QCoreApplication::exit(exitCode);
+}
+
+
 /*** Network handling ***/
 
 QList<NetworkId> Client::networkIds()
 /*** Network handling ***/
 
 QList<NetworkId> Client::networkIds()
index 45880cf..00c9c97 100644 (file)
@@ -256,6 +256,9 @@ signals:
     //! Emitted when database schema upgrade starts or ends (only mono client)
     void dbUpgradeInProgress(bool inProgress);
 
     //! Emitted when database schema upgrade starts or ends (only mono client)
     void dbUpgradeInProgress(bool inProgress);
 
+    //! Emitted before an exit request is handled
+    void exitRequested(const QString &reason);
+
 public slots:
     void disconnectFromCore();
 
 public slots:
     void disconnectFromCore();
 
@@ -266,6 +269,7 @@ public slots:
     void markBufferAsRead(BufferId id);
 
     void onDbUpgradeInProgress(bool inProgress);
     void markBufferAsRead(BufferId id);
 
     void onDbUpgradeInProgress(bool inProgress);
+    void onExitRequested(int exitCode, const QString &reason);
 
 private slots:
     void setSyncedToCore();
 
 private slots:
     void setSyncedToCore();
index 3076eea..6c4b5a4 100644 (file)
@@ -71,6 +71,7 @@ Q_IMPORT_PLUGIN(qgif)
 #endif
 
 #include "quassel.h"
 #endif
 
 #include "quassel.h"
+#include "types.h"
 
 int main(int argc, char **argv)
 {
 
 int main(int argc, char **argv)
 {
@@ -241,8 +242,15 @@ int main(int argc, char **argv)
     AboutData::setQuasselPersons(&aboutData);
     KAboutData::setApplicationData(aboutData.kAboutData());
 #endif
     AboutData::setQuasselPersons(&aboutData);
     KAboutData::setApplicationData(aboutData.kAboutData());
 #endif
-    if (!app.init())
-        return EXIT_FAILURE;
+    try {
+        app.init();
+    }
+    catch (ExitException e) {
+        if (!e.errorString.isEmpty()) {
+            qCritical() << e.errorString;
+        }
+        return e.exitCode;
+    }
 
     return app.exec();
 }
 
     return app.exec();
 }
index d7b4404..72cee26 100644 (file)
@@ -169,3 +169,17 @@ QDataStream &operator>>(QDataStream &in, T &value) {
     value = static_cast<T>(v);
     return in;
 }
     value = static_cast<T>(v);
     return in;
 }
+
+// Exceptions
+
+/**
+ * Thrown during initialization to enforce exiting the application before the event loop is started
+ */
+struct ExitException
+{
+    int exitCode;
+    QString errorString;
+
+    ExitException(int code = EXIT_FAILURE, QString error = {})
+        : exitCode(code), errorString(std::move(error)) {}
+};
index 5818e2f..9b7af58 100644 (file)
@@ -33,6 +33,7 @@
 #include "quassel.h"
 #include "sqlauthenticator.h"
 #include "sqlitestorage.h"
 #include "quassel.h"
 #include "sqlauthenticator.h"
 #include "sqlitestorage.h"
+#include "types.h"
 #include "util.h"
 
 // Currently building with LDAP bindings is optional.
 #include "util.h"
 
 // Currently building with LDAP bindings is optional.
@@ -100,7 +101,7 @@ Core::~Core()
 }
 
 
 }
 
 
-bool Core::init()
+void Core::init()
 {
     _startTime = QDateTime::currentDateTime().toUTC(); // for uptime :)
 
 {
     _startTime = QDateTime::currentDateTime().toUTC(); // for uptime :)
 
@@ -112,9 +113,7 @@ bool Core::init()
     // so far, we only have 1
     CoreSettings s;
     if (s.version() != 1) {
     // so far, we only have 1
     CoreSettings s;
     if (s.version() != 1) {
-        qCritical() << "Invalid core settings version, terminating!";
-        QCoreApplication::exit(EXIT_FAILURE);
-        return false;
+        throw ExitException{EXIT_FAILURE, tr("Invalid core settings version!")};
     }
 
     // Set up storage and authentication backends
     }
 
     // Set up storage and authentication backends
@@ -135,7 +134,8 @@ bool Core::init()
     if (config_from_environment) {
         db_backend = environment.value("DB_BACKEND");
         auth_authenticator = environment.value("AUTH_AUTHENTICATOR");
     if (config_from_environment) {
         db_backend = environment.value("DB_BACKEND");
         auth_authenticator = environment.value("AUTH_AUTHENTICATOR");
-    } else {
+    }
+    else {
         CoreSettings cs;
 
         QVariantMap dbsettings = cs.storageSettings().toMap();
         CoreSettings cs;
 
         QVariantMap dbsettings = cs.storageSettings().toMap();
@@ -149,12 +149,15 @@ bool Core::init()
         writeError = !cs.isWritable();
     }
 
         writeError = !cs.isWritable();
     }
 
-    // legacy
-    _configured = initStorage(db_backend, db_connectionProperties, environment, config_from_environment);
-
-    // Not entirely sure what is 'legacy' about the above, but it seems to be the way things work!
-    if (_configured) {
-        initAuthenticator(auth_authenticator, auth_properties, environment, config_from_environment);
+    try {
+        _configured = initStorage(db_backend, db_connectionProperties, environment, config_from_environment);
+        if (_configured) {
+            _configured = initAuthenticator(auth_authenticator, auth_properties, environment, config_from_environment);
+        }
+    }
+    catch (ExitException) {
+        // Try again later
+        _configured = false;
     }
 
     if (Quassel::isOptionSet("select-backend") || Quassel::isOptionSet("select-authenticator")) {
     }
 
     if (Quassel::isOptionSet("select-backend") || Quassel::isOptionSet("select-authenticator")) {
@@ -165,36 +168,36 @@ bool Core::init()
         if (Quassel::isOptionSet("select-authenticator")) {
             success &= selectAuthenticator(Quassel::optionValue("select-authenticator"));
         }
         if (Quassel::isOptionSet("select-authenticator")) {
             success &= selectAuthenticator(Quassel::optionValue("select-authenticator"));
         }
-        QCoreApplication::exit(success ? EXIT_SUCCESS : EXIT_FAILURE);
-        return success;
+        throw ExitException{success ? EXIT_SUCCESS : EXIT_FAILURE};
     }
 
     if (!_configured) {
         if (config_from_environment) {
     }
 
     if (!_configured) {
         if (config_from_environment) {
-            _configured = initStorage(db_backend, db_connectionProperties, environment, config_from_environment, true);
-            initAuthenticator(auth_authenticator, auth_properties, environment, config_from_environment, true);
+            try {
+                _configured = initStorage(db_backend, db_connectionProperties, environment, config_from_environment, true);
+                if (_configured) {
+                    _configured = initAuthenticator(auth_authenticator, auth_properties, environment, config_from_environment, true);
+                }
+            }
+            catch (ExitException e) {
+                throw ExitException{EXIT_FAILURE, tr("Cannot configure from environment: %1").arg(e.errorString)};
+            }
 
             if (!_configured) {
 
             if (!_configured) {
-                qWarning() << "Cannot configure from environment";
-                QCoreApplication::exit(EXIT_FAILURE);
-                return false;
+                throw ExitException{EXIT_FAILURE, tr("Cannot configure from environment!")};
             }
         }
         else {
             if (_registeredStorageBackends.empty()) {
             }
         }
         else {
             if (_registeredStorageBackends.empty()) {
-                quWarning() << qPrintable(tr("Could not initialize any storage backend! Exiting..."));
-                quWarning()
-                        << qPrintable(tr("Currently, Quassel supports SQLite3 and PostgreSQL. You need to build your\n"
-                                         "Qt library with the sqlite or postgres plugin enabled in order for quasselcore\n"
-                                         "to work."));
-                QCoreApplication::exit(EXIT_FAILURE); // TODO make this less brutal (especially for mono client -> popup)
-                return false;
+                throw ExitException{EXIT_FAILURE,
+                                    tr("Could not initialize any storage backend! Exiting...\n"
+                                       "Currently, Quassel supports SQLite3 and PostgreSQL. You need to build your\n"
+                                       "Qt library with the sqlite or postgres plugin enabled in order for quasselcore\n"
+                                       "to work.")};
             }
 
             if (writeError) {
             }
 
             if (writeError) {
-                qWarning() << "Cannot write quasselcore configuration; probably a permission problem.";
-                QCoreApplication::exit(EXIT_FAILURE);
-                return false;
+                throw ExitException{EXIT_FAILURE, tr("Cannot write quasselcore configuration; probably a permission problem.")};
             }
 
             quInfo() << "Core is currently not configured! Please connect with a Quassel Client for basic setup.";
             }
 
             quInfo() << "Core is currently not configured! Please connect with a Quassel Client for basic setup.";
@@ -203,14 +206,12 @@ bool Core::init()
     else {
         if (Quassel::isOptionSet("add-user")) {
             bool success = createUser();
     else {
         if (Quassel::isOptionSet("add-user")) {
             bool success = createUser();
-            QCoreApplication::exit(success ? EXIT_SUCCESS : EXIT_FAILURE);
-            return success;
+            throw ExitException{success ? EXIT_SUCCESS : EXIT_FAILURE};
         }
 
         if (Quassel::isOptionSet("change-userpass")) {
             bool success = changeUserPass(Quassel::optionValue("change-userpass"));
         }
 
         if (Quassel::isOptionSet("change-userpass")) {
             bool success = changeUserPass(Quassel::optionValue("change-userpass"));
-            QCoreApplication::exit(success ? EXIT_SUCCESS : EXIT_FAILURE);
-            return success;
+            throw ExitException{success ? EXIT_SUCCESS : EXIT_FAILURE};
         }
 
         _strictIdentEnabled = Quassel::isOptionSet("strict-ident");
         }
 
         _strictIdentEnabled = Quassel::isOptionSet("strict-ident");
@@ -245,8 +246,7 @@ bool Core::init()
     connect(&_v6server, SIGNAL(newConnection()), this, SLOT(incomingConnection()));
 
     if (!startListening()) {
     connect(&_v6server, SIGNAL(newConnection()), this, SLOT(incomingConnection()));
 
     if (!startListening()) {
-        QCoreApplication::exit(EXIT_FAILURE);  // TODO make this less brutal
-        return false;
+        throw ExitException{EXIT_FAILURE, tr("Cannot open port for listening!")};
     }
 
     if (_configured && !Quassel::isOptionSet("norestore")) {
     }
 
     if (_configured && !Quassel::isOptionSet("norestore")) {
@@ -259,10 +259,20 @@ bool Core::init()
         connectInternalPeer(_pendingInternalConnection);
         _pendingInternalConnection = {};
     }
         connectInternalPeer(_pendingInternalConnection);
         _pendingInternalConnection = {};
     }
+}
 
 
-    return true;
+
+void Core::initAsync()
+{
+    try {
+        init();
+    }
+    catch (ExitException e) {
+        emit exitRequested(e.exitCode, e.errorString);
+    }
 }
 
 }
 
+
 /*** Session Restore ***/
 
 void Core::saveState()
 /*** Session Restore ***/
 
 void Core::saveState()
@@ -325,14 +335,21 @@ QString Core::setupCore(const QString &adminUser, const QString &adminPassword,
     if (adminUser.isEmpty() || adminPassword.isEmpty()) {
         return tr("Admin user or password not set.");
     }
     if (adminUser.isEmpty() || adminPassword.isEmpty()) {
         return tr("Admin user or password not set.");
     }
-    if (!(_configured = initStorage(backend, setupData, {}, false, true))) {
-        return tr("Could not setup storage!");
-    }
+    try {
+        if (!(_configured = initStorage(backend, setupData, {}, false, true))) {
+            return tr("Could not setup storage!");
+        }
 
 
-    quInfo() << "Selected authenticator:" << authenticator;
-    if (!(_configured = initAuthenticator(authenticator, authSetupData, {}, false, true)))
-    {
-        return tr("Could not setup authenticator!");
+        quInfo() << "Selected authenticator:" << authenticator;
+        if (!(_configured = initAuthenticator(authenticator, authSetupData, {}, false, true)))
+        {
+            return tr("Could not setup authenticator!");
+        }
+    }
+    catch (ExitException e) {
+        // Event loop is running, so trigger an exit rather than throwing an exception
+        QCoreApplication::exit(e.exitCode);
+        return e.errorString.isEmpty() ? tr("Fatal failure while trying to setup, terminating") : e.errorString;
     }
 
     if (!saveBackendSettings(backend, setupData)) {
     }
 
     if (!saveBackendSettings(backend, setupData)) {
@@ -395,8 +412,7 @@ DeferredSharedPtr<Storage> Core::storageBackend(const QString &backendId) const
     return it != _registeredStorageBackends.end() ? *it : nullptr;
 }
 
     return it != _registeredStorageBackends.end() ? *it : nullptr;
 }
 
-// old db settings:
-// "Type" => "sqlite"
+
 bool Core::initStorage(const QString &backend, const QVariantMap &settings,
                        const QProcessEnvironment &environment, bool loadFromEnvironment, bool setup)
 {
 bool Core::initStorage(const QString &backend, const QVariantMap &settings,
                        const QProcessEnvironment &environment, bool loadFromEnvironment, bool setup)
 {
@@ -422,12 +438,12 @@ bool Core::initStorage(const QString &backend, const QVariantMap &settings,
             return initStorage(backend, settings, environment, loadFromEnvironment, false);
         return false;
 
             return initStorage(backend, settings, environment, loadFromEnvironment, false);
         return false;
 
-    // if initialization wasn't successful, we quit to keep from coming up unconfigured
     case Storage::NotAvailable:
     case Storage::NotAvailable:
-        qCritical() << "FATAL: Selected storage backend is not available:" << backend;
         if (!setup) {
         if (!setup) {
-            QCoreApplication::exit(EXIT_FAILURE);
+            // If initialization wasn't successful, we quit to keep from coming up unconfigured
+            throw ExitException{EXIT_FAILURE, tr("Selected storage backend %1 is not available.").arg(backend)};
         }
         }
+        qCritical() << "Selected storage backend is not available:" << backend;
         return false;
 
     case Storage::IsReady:
         return false;
 
     case Storage::IsReady:
@@ -522,12 +538,12 @@ bool Core::initAuthenticator(const QString &backend, const QVariantMap &settings
             return initAuthenticator(backend, settings, environment, loadFromEnvironment, false);
         return false;
 
             return initAuthenticator(backend, settings, environment, loadFromEnvironment, false);
         return false;
 
-    // if initialization wasn't successful, we quit to keep from coming up unconfigured
     case Authenticator::NotAvailable:
     case Authenticator::NotAvailable:
-        qCritical() << "FATAL: Selected auth backend is not available:" << backend;
         if (!setup) {
         if (!setup) {
-            QCoreApplication::exit(EXIT_FAILURE);
+            // If initialization wasn't successful, we quit to keep from coming up unconfigured
+            throw ExitException{EXIT_FAILURE, tr("Selected auth backend %1 is not available.").arg(backend)};
         }
         }
+        qCritical() << "Selected auth backend is not available:" << backend;
         return false;
 
     case Authenticator::IsReady:
         return false;
 
     case Authenticator::IsReady:
@@ -797,7 +813,11 @@ void Core::setupInternalClientSession(QPointer<InternalPeer> clientPeer)
 {
     if (!_configured) {
         stopListening();
 {
     if (!_configured) {
         stopListening();
-        setupCoreForInternalUsage();
+        auto errorString = setupCoreForInternalUsage();
+        if (!errorString.isEmpty()) {
+            emit exitRequested(EXIT_FAILURE, errorString);
+            return;
+        }
     }
 
     UserId uid;
     }
 
     UserId uid;
@@ -806,7 +826,7 @@ void Core::setupInternalClientSession(QPointer<InternalPeer> clientPeer)
     }
     else {
         quWarning() << "Core::setupInternalClientSession(): You're trying to run monolithic Quassel with an unusable Backend! Go fix it!";
     }
     else {
         quWarning() << "Core::setupInternalClientSession(): You're trying to run monolithic Quassel with an unusable Backend! Go fix it!";
-        QCoreApplication::exit(EXIT_FAILURE);
+        emit exitRequested(EXIT_FAILURE, tr("Cannot setup storage backend."));
         return;
     }
 
         return;
     }
 
index e7a8c63..102252b 100644 (file)
@@ -68,6 +68,8 @@ public:
     Core();
     ~Core() override;
 
     Core();
     ~Core() override;
 
+    void init();
+
     /*** Storage access ***/
     // These methods are threadsafe.
 
     /*** Storage access ***/
     // These methods are threadsafe.
 
@@ -707,8 +709,11 @@ signals:
     //! Emitted when database schema upgrade starts or ends
     void dbUpgradeInProgress(bool inProgress);
 
     //! Emitted when database schema upgrade starts or ends
     void dbUpgradeInProgress(bool inProgress);
 
+    //! Emitted when a fatal error was encountered during async initialization
+    void exitRequested(int exitCode, const QString &reason);
+
 public slots:
 public slots:
-    bool init();
+    void initAsync();
 
     /** Persist storage.
      *
 
     /** Persist storage.
      *
index cacd671..e73a09f 100644 (file)
@@ -40,11 +40,12 @@ CoreApplication::~CoreApplication()
 }
 
 
 }
 
 
-bool CoreApplication::init()
+void CoreApplication::init()
 {
 {
-    if (Quassel::init()) {
-        _core.reset(new Core{}); // FIXME C++14: std::make_unique
-        return _core->init();
+    if (!Quassel::init()) {
+        throw ExitException{EXIT_FAILURE, tr("Could not initialize Quassel!")};
     }
     }
-    return false;
+
+    _core.reset(new Core{}); // FIXME C++14: std::make_unique
+    _core->init();
 }
 }
index 3c1e563..f157b54 100644 (file)
@@ -35,7 +35,7 @@ public:
     CoreApplication(int &argc, char **argv);
     ~CoreApplication() override;
 
     CoreApplication(int &argc, char **argv);
     ~CoreApplication() override;
 
-    bool init();
+    void init();
 
 private:
     std::unique_ptr<Core> _core;
 
 private:
     std::unique_ptr<Core> _core;
index 80879a2..3ada47f 100644 (file)
@@ -216,6 +216,7 @@ void MainWin::init()
     connect(GraphicalUi::contextMenuActionProvider(), SIGNAL(showIgnoreList(QString)), SLOT(showIgnoreList(QString)));
     connect(Client::instance(), SIGNAL(showIgnoreList(QString)), SLOT(showIgnoreList(QString)));
     connect(Client::instance(), SIGNAL(dbUpgradeInProgress(bool)), SLOT(showMigrationWarning(bool)));
     connect(GraphicalUi::contextMenuActionProvider(), SIGNAL(showIgnoreList(QString)), SLOT(showIgnoreList(QString)));
     connect(Client::instance(), SIGNAL(showIgnoreList(QString)), SLOT(showIgnoreList(QString)));
     connect(Client::instance(), SIGNAL(dbUpgradeInProgress(bool)), SLOT(showMigrationWarning(bool)));
+    connect(Client::instance(), SIGNAL(exitRequested(QString)), SLOT(onExitRequested(QString)));
 
     connect(Client::coreConnection(), SIGNAL(startCoreSetup(QVariantList, QVariantList)), SLOT(showCoreConfigWizard(QVariantList, QVariantList)));
     connect(Client::coreConnection(), SIGNAL(connectionErrorPopup(QString)), SLOT(handleCoreConnectionError(QString)));
 
     connect(Client::coreConnection(), SIGNAL(startCoreSetup(QVariantList, QVariantList)), SLOT(showCoreConfigWizard(QVariantList, QVariantList)));
     connect(Client::coreConnection(), SIGNAL(connectionErrorPopup(QString)), SLOT(handleCoreConnectionError(QString)));
@@ -858,6 +859,19 @@ void MainWin::showMigrationWarning(bool show)
 }
 
 
 }
 
 
+void MainWin::onExitRequested(const QString &reason)
+{
+    if (!reason.isEmpty()) {
+        QMessageBox box(QMessageBox::Critical,
+                        tr("Fatal error"),
+                        "<b>" + tr("Quassel encountered a fatal error and is terminated.") + "</b>",
+                        QMessageBox::Ok, this);
+        box.setInformativeText("<p>" + tr("Reason:<em>") + " " + reason + "</em>");
+        box.exec();
+    }
+}
+
+
 void MainWin::changeActiveBufferView(bool backwards)
 {
     if (_activeBufferViewIndex >= 0 && _activeBufferViewIndex < _bufferViews.count()) {
 void MainWin::changeActiveBufferView(bool backwards)
 {
     if (_activeBufferViewIndex >= 0 && _activeBufferViewIndex < _bufferViews.count()) {
index 476db51..9dc2694 100644 (file)
@@ -100,6 +100,8 @@ public slots:
 
     void showMigrationWarning(bool show);
 
 
     void showMigrationWarning(bool show);
 
+    void onExitRequested(const QString &reason);
+
     //! Quit application
     void quit();
 
     //! Quit application
     void quit();
 
index d880f33..e92afcc 100644 (file)
@@ -38,10 +38,9 @@ MonolithicApplication::MonolithicApplication(int &argc, char **argv)
 }
 
 
 }
 
 
-bool MonolithicApplication::init()
+void MonolithicApplication::init()
 {
 {
-    if (!QtUiApplication::init())
-        return false;
+    QtUiApplication::init();
 
     connect(Client::coreConnection(), SIGNAL(connectToInternalCore(QPointer<InternalPeer>)), this, SLOT(onConnectionRequest(QPointer<InternalPeer>)));
 
 
     connect(Client::coreConnection(), SIGNAL(connectToInternalCore(QPointer<InternalPeer>)), this, SLOT(onConnectionRequest(QPointer<InternalPeer>)));
 
@@ -51,8 +50,6 @@ bool MonolithicApplication::init()
     if (Quassel::isOptionSet("port")) {
         startInternalCore();
     }
     if (Quassel::isOptionSet("port")) {
         startInternalCore();
     }
-
-    return true;
 }
 
 
 }
 
 
@@ -76,13 +73,14 @@ void MonolithicApplication::startInternalCore()
     // Start internal core in a separate thread, so it doesn't block the UI
     _core = new Core{};
     _core->moveToThread(&_coreThread);
     // Start internal core in a separate thread, so it doesn't block the UI
     _core = new Core{};
     _core->moveToThread(&_coreThread);
-    connect(&_coreThread, SIGNAL(started()), _core, SLOT(init()));
+    connect(&_coreThread, SIGNAL(started()), _core, SLOT(initAsync()));
     connect(&_coreThread, SIGNAL(finished()), _core, SLOT(deleteLater()));
 
     connect(this, SIGNAL(connectInternalPeer(QPointer<InternalPeer>)), _core, SLOT(connectInternalPeer(QPointer<InternalPeer>)));
     connect(_core, SIGNAL(sessionState(Protocol::SessionState)), Client::coreConnection(), SLOT(internalSessionStateReceived(Protocol::SessionState)));
 
     connect(_core, SIGNAL(dbUpgradeInProgress(bool)), Client::instance(), SLOT(onDbUpgradeInProgress(bool)));
     connect(&_coreThread, SIGNAL(finished()), _core, SLOT(deleteLater()));
 
     connect(this, SIGNAL(connectInternalPeer(QPointer<InternalPeer>)), _core, SLOT(connectInternalPeer(QPointer<InternalPeer>)));
     connect(_core, SIGNAL(sessionState(Protocol::SessionState)), Client::coreConnection(), SLOT(internalSessionStateReceived(Protocol::SessionState)));
 
     connect(_core, SIGNAL(dbUpgradeInProgress(bool)), Client::instance(), SLOT(onDbUpgradeInProgress(bool)));
+    connect(_core, SIGNAL(exitRequested(int,QString)), Client::instance(), SLOT(onExitRequested(int,QString)));
 
     _coreThread.start();
 }
 
     _coreThread.start();
 }
index 59e9549..56f4f93 100644 (file)
@@ -36,7 +36,7 @@ public:
     MonolithicApplication(int &, char **);
     ~MonolithicApplication() override;
 
     MonolithicApplication(int &, char **);
     ~MonolithicApplication() override;
 
-    bool init() override;
+    void init() override;
 
 signals:
     void connectInternalPeer(QPointer<InternalPeer> peer);
 
 signals:
     void connectInternalPeer(QPointer<InternalPeer> peer);
index c9475fe..20c1662 100644 (file)
@@ -34,6 +34,7 @@
 #include "mainwin.h"
 #include "qtui.h"
 #include "qtuisettings.h"
 #include "mainwin.h"
 #include "qtui.h"
 #include "qtuisettings.h"
+#include "types.h"
 
 QtUiApplication::QtUiApplication(int &argc, char **argv)
 #ifdef HAVE_KDE4
 
 QtUiApplication::QtUiApplication(int &argc, char **argv)
 #ifdef HAVE_KDE4
@@ -98,28 +99,26 @@ QtUiApplication::QtUiApplication(int &argc, char **argv)
 }
 
 
 }
 
 
-bool QtUiApplication::init()
+void QtUiApplication::init()
 {
 {
-    if (Quassel::init()) {
-        // Settings upgrade/downgrade handling
-        if (!migrateSettings()) {
-            qCritical() << "Could not load or upgrade client settings, terminating!";
-            return false;
-        }
+    if (!Quassel::init()) {
+        throw ExitException{EXIT_FAILURE, tr("Could not initialize Quassel!")};
+    }
 
 
-        Client::init(new QtUi());
+    // Settings upgrade/downgrade handling
+    if (!migrateSettings()) {
+        throw ExitException{EXIT_FAILURE, tr("Could not load or upgrade client settings!")};
+    }
 
 
-        // Init UI only after the event loop has started
-        // TODO Qt5: Make this a lambda
-        QTimer::singleShot(0, this, SLOT(initUi()));
+    Client::init(new QtUi());
 
 
-        Quassel::registerQuitHandler([]() {
-            QtUi::mainWindow()->quit();
-        });
+    // Init UI only after the event loop has started
+    // TODO Qt5: Make this a lambda
+    QTimer::singleShot(0, this, SLOT(initUi()));
 
 
-        return true;
-    }
-    return false;
+    Quassel::registerQuitHandler([]() {
+        QtUi::mainWindow()->quit();
+    });
 }
 
 
 }
 
 
index e5e20b5..508bea5 100644 (file)
@@ -47,7 +47,7 @@ class QtUiApplication : public QApplication
 public:
     QtUiApplication(int &, char **);
     ~QtUiApplication();
 public:
     QtUiApplication(int &, char **);
     ~QtUiApplication();
-    virtual bool init();
+    virtual void init();
 
     void resumeSessionIfPossible();
     inline bool isAboutToQuit() const { return _aboutToQuit; }
 
     void resumeSessionIfPossible();
     inline bool isAboutToQuit() const { return _aboutToQuit; }