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::instanceptr = 0;
+Core *Core::_instance{nullptr};
 
 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 :)
 
@@ -102,13 +112,6 @@ Core::Core()
     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");
 
@@ -164,7 +167,8 @@ void Core::init()
                 qWarning() << "Cannot configure from environment";
                 exit(EXIT_FAILURE);
             }
-        } else {
+        }
+        else {
             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)
             }
-            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);
             }
+
+            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()));
-    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()
 {
-    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()
 {
-    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;
     }
-    if (instance()->_sessions.count()) {
+    if (_sessions.count()) {
         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();
@@ -252,9 +270,9 @@ void Core::restoreState()
 
     if (activeSessions.count() > 0) {
         quInfo() << "Restoring previous core state...";
-        foreach(QVariant v, activeSessions) {
+        for(auto &&v : activeSessions) {
             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
-    SslServer *sslServerv4 = qobject_cast<SslServer *>(&instance()->_server);
+    SslServer *sslServerv4 = qobject_cast<SslServer *>(&_server);
     bool retv4 = sslServerv4->reloadCerts();
 
-    SslServer *sslServerv6 = qobject_cast<SslServer *>(&instance()->_v6server);
+    SslServer *sslServerv6 = qobject_cast<SslServer *>(&_v6server);
     bool retv6 = sslServerv6->reloadCerts();
 
     return retv4 && retv6;
@@ -520,7 +538,7 @@ bool Core::reloadCerts()
 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!
-    cacheSysIdent();
+    instance()->cacheSysIdent();
 
     if (_authUserNames.contains(user)) {
         return _authUserNames[user];
index a9ede2e..dad8ee2 100644 (file)
@@ -60,10 +60,9 @@ class Core : public QObject
 
 public:
     static Core *instance();
-    static void destroy();
 
-    static void saveState();
-    static void restoreState();
+    Core();
+    ~Core() override;
 
     /*** Storage access ***/
     // These methods are threadsafe.
@@ -682,15 +681,6 @@ public:
 
     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();
 
@@ -703,10 +693,23 @@ public:
     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();
+
+    /**
+     * 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);
 
@@ -718,7 +721,7 @@ signals:
     void sessionState(const Protocol::SessionState &sessionState);
 
 protected:
-    virtual void customEvent(QEvent *event);
+    void customEvent(QEvent *event) override;
 
 private slots:
     bool startListening();
@@ -739,11 +742,6 @@ private slots:
     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);
@@ -769,10 +767,14 @@ private:
     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:
+    static Core *_instance;
     QSet<CoreAuthHandler *> _connectingClients;
     QHash<UserId, SessionThread *> _sessions;
     DeferredSharedPtr<Storage>       _storage;        ///< Active storage backend
index b31d937..cacd671 100644 (file)
 #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)
@@ -64,18 +30,21 @@ CoreApplication::CoreApplication(int &argc, char **argv)
 #endif /* Q_OS_MAC */
 
     Quassel::setRunMode(Quassel::CoreOnly);
-    _internal = new CoreApplicationInternal();
 }
 
 
 CoreApplication::~CoreApplication()
 {
-    delete _internal;
+    _core.reset();
     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
 
+#include <memory>
+
 #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);
-    ~CoreApplication();
+    ~CoreApplication() override;
 
     bool init();
 
 private:
-    CoreApplicationInternal *_internal;
+    std::unique_ptr<Core> _core;
 };
index 3d1a060..c2ae9e5 100644 (file)
 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 */
@@ -46,11 +44,6 @@ bool MonolithicApplication::init()
 
     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;
 }
 
@@ -59,18 +52,17 @@ MonolithicApplication::~MonolithicApplication()
 {
     // Client needs to be destroyed first
     Client::destroy();
-    delete _internal;
+    _core.reset();
+    Quassel::destroy();
 }
 
 
 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
 
+#include <memory>
+
 #include "qtuiapplication.h"
 
-class CoreApplicationInternal;
+class Core;
 
 class MonolithicApplication : public QtUiApplication
 {
     Q_OBJECT
 public:
     MonolithicApplication(int &, char **);
-    ~MonolithicApplication();
+    ~MonolithicApplication() override;
 
     bool init() override;
 
@@ -37,6 +39,5 @@ private slots:
     void startInternalCore();
 
 private:
-    CoreApplicationInternal *_internal;
-    bool _internalInitDone;
+    std::unique_ptr<Core> _core;
 };