core: Properly parent QObject-derived attributes
[quassel.git] / src / core / core.cpp
index 80646c0..b103b42 100644 (file)
@@ -66,25 +66,41 @@ 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;
+
+    // Parent all QObject-derived attributes, so when the Core instance gets moved into another
+    // thread, they get moved with it
+    _server.setParent(this);
+    _v6server.setParent(this);
+    _storageSyncTimer.setParent(this);
 }
 
 
-Core::Core()
+Core::~Core()
+{
+    saveState();
+    qDeleteAll(_connectingClients);
+    qDeleteAll(_sessions);
+    syncStorage();
+    _instance = nullptr;
+}
+
+
+bool Core::init()
 {
     _startTime = QDateTime::currentDateTime().toUTC(); // for uptime :)
 
@@ -95,20 +111,14 @@ Core::Core()
     CoreSettings s;
     if (s.version() != 1) {
         qCritical() << "Invalid core settings version, terminating!";
-        exit(EXIT_FAILURE);
+        QCoreApplication::exit(EXIT_FAILURE);
+        return false;
     }
 
     // Set up storage and authentication backends
     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");
 
@@ -146,13 +156,15 @@ void Core::init()
     }
 
     if (Quassel::isOptionSet("select-backend") || Quassel::isOptionSet("select-authenticator")) {
+        bool success{true};
         if (Quassel::isOptionSet("select-backend")) {
-            selectBackend(Quassel::optionValue("select-backend"));
+            success &= selectBackend(Quassel::optionValue("select-backend"));
         }
         if (Quassel::isOptionSet("select-authenticator")) {
-            selectAuthenticator(Quassel::optionValue("select-authenticator"));
+            success &= selectAuthenticator(Quassel::optionValue("select-authenticator"));
         }
-        exit(EXIT_SUCCESS);
+        QCoreApplication::exit(success ? EXIT_SUCCESS : EXIT_FAILURE);
+        return success;
     }
 
     if (!_configured) {
@@ -162,82 +174,105 @@ void Core::init()
 
             if (!_configured) {
                 qWarning() << "Cannot configure from environment";
-                exit(EXIT_FAILURE);
+                QCoreApplication::exit(EXIT_FAILURE);
+                return false;
             }
-        } else {
+        }
+        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."));
-                exit(EXIT_FAILURE); // TODO make this less brutal (especially for mono client -> popup)
+                QCoreApplication::exit(EXIT_FAILURE); // TODO make this less brutal (especially for mono client -> popup)
+                return false;
             }
-            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);
+                QCoreApplication::exit(EXIT_FAILURE);
+                return false;
             }
+
+            quInfo() << "Core is currently not configured! Please connect with a Quassel Client for basic setup.";
         }
     }
+    else {
+        if (Quassel::isOptionSet("add-user")) {
+            bool success = createUser();
+            QCoreApplication::exit(success ? EXIT_SUCCESS : EXIT_FAILURE);
+            return success;
+        }
 
-    if (Quassel::isOptionSet("add-user")) {
-        exit(createUser() ? EXIT_SUCCESS : EXIT_FAILURE);
+        if (Quassel::isOptionSet("change-userpass")) {
+            bool success = changeUserPass(Quassel::optionValue("change-userpass"));
+            QCoreApplication::exit(success ? EXIT_SUCCESS : EXIT_FAILURE);
+            return success;
+        }
 
-    }
+        _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()) {
+        QCoreApplication::exit(EXIT_FAILURE);  // TODO make this less brutal
+        return false;
     }
 
-    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 +287,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);
         }
     }
 }
@@ -374,8 +409,9 @@ bool Core::initStorage(const QString &backend, const QVariantMap &settings,
     // if initialization wasn't successful, we quit to keep from coming up unconfigured
     case Storage::NotAvailable:
         qCritical() << "FATAL: Selected storage backend is not available:" << backend;
-        if (!setup)
-            exit(EXIT_FAILURE);
+        if (!setup) {
+            QCoreApplication::exit(EXIT_FAILURE);
+        }
         return false;
 
     case Storage::IsReady:
@@ -473,8 +509,9 @@ bool Core::initAuthenticator(const QString &backend, const QVariantMap &settings
     // if initialization wasn't successful, we quit to keep from coming up unconfigured
     case Authenticator::NotAvailable:
         qCritical() << "FATAL: Selected auth backend is not available:" << backend;
-        if (!setup)
-            exit(EXIT_FAILURE);
+        if (!setup) {
+            QCoreApplication::exit(EXIT_FAILURE);
+        }
         return false;
 
     case Authenticator::IsReady:
@@ -503,10 +540,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 +557,7 @@ bool Core::reloadCerts()
 void Core::cacheSysIdent()
 {
     if (isConfigured()) {
-        instance()->_authUserNames = instance()->_storage->getAllAuthUserNames();
+        _authUserNames = _storage->getAllAuthUserNames();
     }
 }
 
@@ -533,7 +570,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];