core: Simplify core initialization logic
authorManuel Nickschas <sputnick@quassel-irc.org>
Tue, 29 May 2018 21:11:07 +0000 (23:11 +0200)
committerManuel Nickschas <sputnick@quassel-irc.org>
Wed, 6 Jun 2018 19:12:17 +0000 (21:12 +0200)
Move all core initialization into Core::init(), including things
that were previously done in CoreApplication and MonoApplication.
Make several functions private and non-static that no longer need
to be called from the outside.

Allow to construct and destroy the core directly, rather than
relying on Core::instance() and Core::destroy(). Explicitly
create and destroy the instance in the application to better control
lifetime.

Ensure core state is saved and storage synced on destruction, instead
of relying on explicit calls (which were partially missing for mono).

src/core/core.cpp
src/core/core.h
src/core/coreapplication.cpp
src/core/coreapplication.h
src/qtui/monoapplication.cpp
src/qtui/monoapplication.h

index 80646c0..7d20fa7 100644 (file)
@@ -66,25 +66,35 @@ public:
 // ==============================
 //  Core
 // ==============================
 // ==============================
 //  Core
 // ==============================
-Core *Core::instanceptr = 0;
+Core *Core::_instance{nullptr};
 
 Core *Core::instance()
 {
 
 Core *Core::instance()
 {
-    if (instanceptr) return instanceptr;
-    instanceptr = new Core();
-    instanceptr->init();
-    return instanceptr;
+    return _instance;
 }
 
 
 }
 
 
-void Core::destroy()
+Core::Core()
 {
 {
-    delete instanceptr;
-    instanceptr = 0;
+    if (_instance) {
+        qWarning() << "Recreating core instance!";
+        delete _instance;
+    }
+    _instance = this;
 }
 
 
 }
 
 
-Core::Core()
+Core::~Core()
+{
+    saveState();
+    qDeleteAll(_connectingClients);
+    qDeleteAll(_sessions);
+    syncStorage();
+    _instance = nullptr;
+}
+
+
+bool Core::init()
 {
     _startTime = QDateTime::currentDateTime().toUTC(); // for uptime :)
 
 {
     _startTime = QDateTime::currentDateTime().toUTC(); // for uptime :)
 
@@ -102,13 +112,6 @@ Core::Core()
     registerStorageBackends();
     registerAuthenticators();
 
     registerStorageBackends();
     registerAuthenticators();
 
-    connect(&_storageSyncTimer, SIGNAL(timeout()), this, SLOT(syncStorage()));
-    _storageSyncTimer.start(10 * 60 * 1000); // 10 minutes
-}
-
-
-void Core::init()
-{
     QProcessEnvironment environment = QProcessEnvironment::systemEnvironment();
     bool config_from_environment = Quassel::isOptionSet("config-from-environment");
 
     QProcessEnvironment environment = QProcessEnvironment::systemEnvironment();
     bool config_from_environment = Quassel::isOptionSet("config-from-environment");
 
@@ -164,7 +167,8 @@ void Core::init()
                 qWarning() << "Cannot configure from environment";
                 exit(EXIT_FAILURE);
             }
                 qWarning() << "Cannot configure from environment";
                 exit(EXIT_FAILURE);
             }
-        } else {
+        }
+        else {
             if (_registeredStorageBackends.empty()) {
                 quWarning() << qPrintable(tr("Could not initialize any storage backend! Exiting..."));
                 quWarning()
             if (_registeredStorageBackends.empty()) {
                 quWarning() << qPrintable(tr("Could not initialize any storage backend! Exiting..."));
                 quWarning()
@@ -173,71 +177,85 @@ void Core::init()
                                          "to work."));
                 exit(EXIT_FAILURE); // TODO make this less brutal (especially for mono client -> popup)
             }
                                          "to work."));
                 exit(EXIT_FAILURE); // TODO make this less brutal (especially for mono client -> popup)
             }
-            quWarning() << "Core is currently not configured! Please connect with a Quassel Client for basic setup.";
 
             if (writeError) {
                 qWarning() << "Cannot write quasselcore configuration; probably a permission problem.";
                 exit(EXIT_FAILURE);
             }
 
             if (writeError) {
                 qWarning() << "Cannot write quasselcore configuration; probably a permission problem.";
                 exit(EXIT_FAILURE);
             }
+
+            quInfo() << "Core is currently not configured! Please connect with a Quassel Client for basic setup.";
         }
     }
         }
     }
+    else {
+        if (Quassel::isOptionSet("add-user")) {
+            exit(createUser() ? EXIT_SUCCESS : EXIT_FAILURE);
+        }
 
 
-    if (Quassel::isOptionSet("add-user")) {
-        exit(createUser() ? EXIT_SUCCESS : EXIT_FAILURE);
+        if (Quassel::isOptionSet("change-userpass")) {
+            exit(changeUserPass(Quassel::optionValue("change-userpass")) ? EXIT_SUCCESS : EXIT_FAILURE);
+        }
 
 
-    }
+        _strictIdentEnabled = Quassel::isOptionSet("strict-ident");
+        if (_strictIdentEnabled) {
+            cacheSysIdent();
+        }
+
+        if (Quassel::isOptionSet("oidentd")) {
+            _oidentdConfigGenerator = new OidentdConfigGenerator(this);
+        }
+
+        Quassel::registerReloadHandler([]() {
+            // Currently, only reloading SSL certificates and the sysident cache is supported
+            if (Core::instance()) {
+                Core::instance()->cacheSysIdent();
+                Core::instance()->reloadCerts();
+                return true;
+            }
+            return false;
+        });
 
 
-    if (Quassel::isOptionSet("change-userpass")) {
-        exit(changeUserPass(Quassel::optionValue("change-userpass")) ?
-                       EXIT_SUCCESS : EXIT_FAILURE);
+        connect(&_storageSyncTimer, SIGNAL(timeout()), this, SLOT(syncStorage()));
+        _storageSyncTimer.start(10 * 60 * 1000); // 10 minutes
     }
 
     connect(&_server, SIGNAL(newConnection()), this, SLOT(incomingConnection()));
     connect(&_v6server, SIGNAL(newConnection()), this, SLOT(incomingConnection()));
     }
 
     connect(&_server, SIGNAL(newConnection()), this, SLOT(incomingConnection()));
     connect(&_v6server, SIGNAL(newConnection()), this, SLOT(incomingConnection()));
-    if (!startListening()) exit(1);  // TODO make this less brutal
 
 
-    _strictIdentEnabled = Quassel::isOptionSet("strict-ident");
-    if (_strictIdentEnabled) {
-        cacheSysIdent();
+    if (!startListening()) {
+        exit(EXIT_FAILURE);  // TODO make this less brutal
     }
 
     }
 
-    if (Quassel::isOptionSet("oidentd")) {
-        _oidentdConfigGenerator = new OidentdConfigGenerator(this);
+    if (_configured && !Quassel::isOptionSet("norestore")) {
+        Core::restoreState();
     }
     }
-}
-
 
 
-Core::~Core()
-{
-    // FIXME do we need more cleanup for handlers?
-    foreach(CoreAuthHandler *handler, _connectingClients) {
-        handler->deleteLater(); // disconnect non authed clients
-    }
-    qDeleteAll(_sessions);
+    return true;
 }
 
 }
 
-
 /*** Session Restore ***/
 
 void Core::saveState()
 {
 /*** Session Restore ***/
 
 void Core::saveState()
 {
-    QVariantList activeSessions;
-    foreach(UserId user, instance()->_sessions.keys())
-        activeSessions << QVariant::fromValue<UserId>(user);
-    instance()->_storage->setCoreState(activeSessions);
+    if (_storage) {
+        QVariantList activeSessions;
+        for (auto &&user : instance()->_sessions.keys())
+            activeSessions << QVariant::fromValue<UserId>(user);
+        _storage->setCoreState(activeSessions);
+    }
 }
 
 
 void Core::restoreState()
 {
 }
 
 
 void Core::restoreState()
 {
-    if (!instance()->_configured) {
-        // quWarning() << qPrintable(tr("Cannot restore a state for an unconfigured core!"));
+    if (!_configured) {
+        quWarning() << qPrintable(tr("Cannot restore a state for an unconfigured core!"));
         return;
     }
         return;
     }
-    if (instance()->_sessions.count()) {
+    if (_sessions.count()) {
         quWarning() << qPrintable(tr("Calling restoreState() even though active sessions exist!"));
         return;
     }
         quWarning() << qPrintable(tr("Calling restoreState() even though active sessions exist!"));
         return;
     }
+
     CoreSettings s;
     /* We don't check, since we are at the first version since switching to Git
     uint statever = s.coreState().toMap()["CoreStateVersion"].toUInt();
     CoreSettings s;
     /* We don't check, since we are at the first version since switching to Git
     uint statever = s.coreState().toMap()["CoreStateVersion"].toUInt();
@@ -252,9 +270,9 @@ void Core::restoreState()
 
     if (activeSessions.count() > 0) {
         quInfo() << "Restoring previous core state...";
 
     if (activeSessions.count() > 0) {
         quInfo() << "Restoring previous core state...";
-        foreach(QVariant v, activeSessions) {
+        for(auto &&v : activeSessions) {
             UserId user = v.value<UserId>();
             UserId user = v.value<UserId>();
-            instance()->sessionForUser(user, true);
+            sessionForUser(user, true);
         }
     }
 }
         }
     }
 }
@@ -503,10 +521,10 @@ bool Core::sslSupported()
 bool Core::reloadCerts()
 {
 #ifdef HAVE_SSL
 bool Core::reloadCerts()
 {
 #ifdef HAVE_SSL
-    SslServer *sslServerv4 = qobject_cast<SslServer *>(&instance()->_server);
+    SslServer *sslServerv4 = qobject_cast<SslServer *>(&_server);
     bool retv4 = sslServerv4->reloadCerts();
 
     bool retv4 = sslServerv4->reloadCerts();
 
-    SslServer *sslServerv6 = qobject_cast<SslServer *>(&instance()->_v6server);
+    SslServer *sslServerv6 = qobject_cast<SslServer *>(&_v6server);
     bool retv6 = sslServerv6->reloadCerts();
 
     return retv4 && retv6;
     bool retv6 = sslServerv6->reloadCerts();
 
     return retv4 && retv6;
@@ -520,7 +538,7 @@ bool Core::reloadCerts()
 void Core::cacheSysIdent()
 {
     if (isConfigured()) {
 void Core::cacheSysIdent()
 {
     if (isConfigured()) {
-        instance()->_authUserNames = instance()->_storage->getAllAuthUserNames();
+        _authUserNames = _storage->getAllAuthUserNames();
     }
 }
 
     }
 }
 
@@ -533,7 +551,7 @@ QString Core::strictSysIdent(UserId user) const
 
     // A new user got added since we last pulled our cache from the database.
     // There's no way to avoid a database hit - we don't even know the authname!
 
     // A new user got added since we last pulled our cache from the database.
     // There's no way to avoid a database hit - we don't even know the authname!
-    cacheSysIdent();
+    instance()->cacheSysIdent();
 
     if (_authUserNames.contains(user)) {
         return _authUserNames[user];
 
     if (_authUserNames.contains(user)) {
         return _authUserNames[user];
index a9ede2e..dad8ee2 100644 (file)
@@ -60,10 +60,9 @@ class Core : public QObject
 
 public:
     static Core *instance();
 
 public:
     static Core *instance();
-    static void destroy();
 
 
-    static void saveState();
-    static void restoreState();
+    Core();
+    ~Core() override;
 
     /*** Storage access ***/
     // These methods are threadsafe.
 
     /*** Storage access ***/
     // These methods are threadsafe.
@@ -682,15 +681,6 @@ public:
 
     static bool sslSupported();
 
 
     static bool sslSupported();
 
-    /**
-     * Reloads SSL certificates used for connection with clients
-     *
-     * @return True if certificates reloaded successfully, otherwise false.
-     */
-    static bool reloadCerts();
-
-    static void cacheSysIdent();
-
     static QVariantList backendInfo();
     static QVariantList authenticatorInfo();
 
     static QVariantList backendInfo();
     static QVariantList authenticatorInfo();
 
@@ -703,10 +693,23 @@ public:
     static const int AddClientEventId;
 
 public slots:
     static const int AddClientEventId;
 
 public slots:
-    //! Make storage data persistent
-    /** \note This method is threadsafe.
+    bool init();
+
+    /** Persist storage.
+     *
+     * @note This method is threadsafe.
      */
     void syncStorage();
      */
     void syncStorage();
+
+    /**
+     * Reload SSL certificates used for connection with clients.
+     *
+     * @return True if certificates reloaded successfully, otherwise false.
+     */
+    bool reloadCerts();
+
+    void cacheSysIdent();
+
     void setupInternalClientSession(InternalPeer *clientConnection);
     QString setupCore(const QString &adminUser, const QString &adminPassword, const QString &backend, const QVariantMap &setupData, const QString &authenticator, const QVariantMap &authSetupMap);
 
     void setupInternalClientSession(InternalPeer *clientConnection);
     QString setupCore(const QString &adminUser, const QString &adminPassword, const QString &backend, const QVariantMap &setupData, const QString &authenticator, const QVariantMap &authSetupMap);
 
@@ -718,7 +721,7 @@ signals:
     void sessionState(const Protocol::SessionState &sessionState);
 
 protected:
     void sessionState(const Protocol::SessionState &sessionState);
 
 protected:
-    virtual void customEvent(QEvent *event);
+    void customEvent(QEvent *event) override;
 
 private slots:
     bool startListening();
 
 private slots:
     bool startListening();
@@ -739,11 +742,6 @@ private slots:
     bool changeUserPass(const QString &username);
 
 private:
     bool changeUserPass(const QString &username);
 
 private:
-    Core();
-    ~Core();
-    void init();
-    static Core *instanceptr;
-
     SessionThread *sessionForUser(UserId userId, bool restoreState = false);
     void addClientHelper(RemotePeer *peer, UserId uid);
     //void processCoreSetup(QTcpSocket *socket, QVariantMap &msg);
     SessionThread *sessionForUser(UserId userId, bool restoreState = false);
     void addClientHelper(RemotePeer *peer, UserId uid);
     //void processCoreSetup(QTcpSocket *socket, QVariantMap &msg);
@@ -769,10 +767,14 @@ private:
     bool saveBackendSettings(const QString &backend, const QVariantMap &settings);
     void saveAuthenticatorSettings(const QString &backend, const QVariantMap &settings);
 
     bool saveBackendSettings(const QString &backend, const QVariantMap &settings);
     void saveAuthenticatorSettings(const QString &backend, const QVariantMap &settings);
 
+    void saveState();
+    void restoreState();
+
     template<typename Backend>
     QVariantMap promptForSettings(const Backend *backend);
 
 private:
     template<typename Backend>
     QVariantMap promptForSettings(const Backend *backend);
 
 private:
+    static Core *_instance;
     QSet<CoreAuthHandler *> _connectingClients;
     QHash<UserId, SessionThread *> _sessions;
     DeferredSharedPtr<Storage>       _storage;        ///< Active storage backend
     QSet<CoreAuthHandler *> _connectingClients;
     QHash<UserId, SessionThread *> _sessions;
     DeferredSharedPtr<Storage>       _storage;        ///< Active storage backend
index b31d937..cacd671 100644 (file)
 #include "core.h"
 #include "coreapplication.h"
 
 #include "core.h"
 #include "coreapplication.h"
 
-CoreApplicationInternal::CoreApplicationInternal()
-    : _coreCreated(false)
-{
-}
-
-
-CoreApplicationInternal::~CoreApplicationInternal()
-{
-    if (_coreCreated) {
-        Core::saveState();
-        Core::destroy();
-    }
-}
-
-
-bool CoreApplicationInternal::init()
-{
-    Core::instance(); // create and init the core
-    _coreCreated = true;
-
-    Quassel::registerReloadHandler([]() {
-        // Currently, only reloading SSL certificates and the sysident cache is supported
-        Core::cacheSysIdent();
-        return Core::reloadCerts();
-    });
-
-    if (!Quassel::isOptionSet("norestore"))
-        Core::restoreState();
-
-    return true;
-}
-
-
-/*****************************************************************************/
 
 CoreApplication::CoreApplication(int &argc, char **argv)
     : QCoreApplication(argc, argv)
 
 CoreApplication::CoreApplication(int &argc, char **argv)
     : QCoreApplication(argc, argv)
@@ -64,18 +30,21 @@ CoreApplication::CoreApplication(int &argc, char **argv)
 #endif /* Q_OS_MAC */
 
     Quassel::setRunMode(Quassel::CoreOnly);
 #endif /* Q_OS_MAC */
 
     Quassel::setRunMode(Quassel::CoreOnly);
-    _internal = new CoreApplicationInternal();
 }
 
 
 CoreApplication::~CoreApplication()
 {
 }
 
 
 CoreApplication::~CoreApplication()
 {
-    delete _internal;
+    _core.reset();
     Quassel::destroy();
 }
 
 
 bool CoreApplication::init()
 {
     Quassel::destroy();
 }
 
 
 bool CoreApplication::init()
 {
-    return Quassel::init() && _internal->init();
+    if (Quassel::init()) {
+        _core.reset(new Core{}); // FIXME C++14: std::make_unique
+        return _core->init();
+    }
+    return false;
 }
 }
index af53d88..3c1e563 100644 (file)
 
 #pragma once
 
 
 #pragma once
 
+#include <memory>
+
 #include <QCoreApplication>
 
 #include "quassel.h"
 
 #include <QCoreApplication>
 
 #include "quassel.h"
 
-/// Encapsulates CoreApplication's logic.
-/** This allows it to be reused within MonolithicApplication as well.
- */
-class CoreApplicationInternal
-{
-    Q_DECLARE_TR_FUNCTIONS(CoreApplicationInternal)
-
-public:
-    CoreApplicationInternal();
-    ~CoreApplicationInternal();
-
-    bool init();
-
-private:
-    bool _coreCreated;
-};
-
+class Core;
 
 class CoreApplication : public QCoreApplication
 {
     Q_OBJECT
 public:
     CoreApplication(int &argc, char **argv);
 
 class CoreApplication : public QCoreApplication
 {
     Q_OBJECT
 public:
     CoreApplication(int &argc, char **argv);
-    ~CoreApplication();
+    ~CoreApplication() override;
 
     bool init();
 
 private:
 
     bool init();
 
 private:
-    CoreApplicationInternal *_internal;
+    std::unique_ptr<Core> _core;
 };
 };
index 3d1a060..c2ae9e5 100644 (file)
 class InternalPeer;
 
 MonolithicApplication::MonolithicApplication(int &argc, char **argv)
 class InternalPeer;
 
 MonolithicApplication::MonolithicApplication(int &argc, char **argv)
-    : QtUiApplication(argc, argv),
-    _internalInitDone(false)
+    : QtUiApplication(argc, argv)
 {
 {
-    _internal = new CoreApplicationInternal(); // needed for parser options
 #if defined(HAVE_KDE4) || defined(Q_OS_MAC)
     Quassel::disableCrashHandler();
 #endif /* HAVE_KDE4 || Q_OS_MAC */
 #if defined(HAVE_KDE4) || defined(Q_OS_MAC)
     Quassel::disableCrashHandler();
 #endif /* HAVE_KDE4 || Q_OS_MAC */
@@ -46,11 +44,6 @@ bool MonolithicApplication::init()
 
     connect(Client::coreConnection(), SIGNAL(startInternalCore()), SLOT(startInternalCore()));
 
 
     connect(Client::coreConnection(), SIGNAL(startInternalCore()), SLOT(startInternalCore()));
 
-    // If port is given, start core so it can listen to incoming connections
-    if (Quassel::isOptionSet("port")) {
-        startInternalCore();
-    }
-
     return true;
 }
 
     return true;
 }
 
@@ -59,18 +52,17 @@ MonolithicApplication::~MonolithicApplication()
 {
     // Client needs to be destroyed first
     Client::destroy();
 {
     // Client needs to be destroyed first
     Client::destroy();
-    delete _internal;
+    _core.reset();
+    Quassel::destroy();
 }
 
 
 void MonolithicApplication::startInternalCore()
 {
 }
 
 
 void MonolithicApplication::startInternalCore()
 {
-    if (!_internalInitDone) {
-        _internal->init();
-        _internalInitDone = true;
+    if (!_core) {
+        _core.reset(new Core{});  // FIXME C++14: std::make_unique
+        Core::instance()->init();
     }
     }
-    Core *core = Core::instance();
-    CoreConnection *connection = Client::coreConnection();
-    connect(connection, SIGNAL(connectToInternalCore(InternalPeer*)), core, SLOT(setupInternalClientSession(InternalPeer*)));
-    connect(core, SIGNAL(sessionState(Protocol::SessionState)), connection, SLOT(internalSessionStateReceived(Protocol::SessionState)));
+    connect(Client::coreConnection(), SIGNAL(connectToInternalCore(InternalPeer*)), Core::instance(), SLOT(setupInternalClientSession(InternalPeer*)));
+    connect(Core::instance(), SIGNAL(sessionState(Protocol::SessionState)), Client::coreConnection(), SLOT(internalSessionStateReceived(Protocol::SessionState)));
 }
 }
index 4b51e9d..34363bb 100644 (file)
 
 #pragma once
 
 
 #pragma once
 
+#include <memory>
+
 #include "qtuiapplication.h"
 
 #include "qtuiapplication.h"
 
-class CoreApplicationInternal;
+class Core;
 
 class MonolithicApplication : public QtUiApplication
 {
     Q_OBJECT
 public:
     MonolithicApplication(int &, char **);
 
 class MonolithicApplication : public QtUiApplication
 {
     Q_OBJECT
 public:
     MonolithicApplication(int &, char **);
-    ~MonolithicApplication();
+    ~MonolithicApplication() override;
 
     bool init() override;
 
 
     bool init() override;
 
@@ -37,6 +39,5 @@ private slots:
     void startInternalCore();
 
 private:
     void startInternalCore();
 
 private:
-    CoreApplicationInternal *_internal;
-    bool _internalInitDone;
+    std::unique_ptr<Core> _core;
 };
 };