X-Git-Url: https://git.quassel-irc.org/?p=quassel.git;a=blobdiff_plain;f=src%2Fcore%2Fcore.cpp;h=35b46b613886dd93f76b8ccb0ae19f0fd382e416;hp=cf943b98e438d1a50fb0e3b377600c42596d3b2c;hb=e2188dc438be6f3eb0d9cdf47d28821aefe9835e;hpb=dc11e24b95e5b5e3595cc0e98eb2a572006912c7 diff --git a/src/core/core.cpp b/src/core/core.cpp index cf943b98..35b46b61 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -26,18 +26,18 @@ #include "coreauthhandler.h" #include "coresession.h" #include "coresettings.h" -#include "logger.h" #include "internalpeer.h" +#include "logmessage.h" #include "network.h" #include "postgresqlstorage.h" #include "quassel.h" #include "sqlauthenticator.h" #include "sqlitestorage.h" +#include "types.h" #include "util.h" -// Currently building with LDAP bindings is optional. #ifdef HAVE_LDAP -#include "ldapauthenticator.h" +# include "ldapauthenticator.h" #endif // migration related @@ -66,21 +66,11 @@ public: // ============================== // Core // ============================== -Core *Core::_instance{nullptr}; - -Core *Core::instance() -{ - return _instance; -} - Core::Core() + : Singleton{this} { - if (_instance) { - qWarning() << "Recreating core instance!"; - delete _instance; - } - _instance = this; + Q_INIT_RESOURCE(sql); // Parent all QObject-derived attributes, so when the Core instance gets moved into another // thread, they get moved with it @@ -92,29 +82,21 @@ Core::Core() Core::~Core() { - saveState(); qDeleteAll(_connectingClients); qDeleteAll(_sessions); syncStorage(); - _instance = nullptr; } -bool Core::init() +void Core::init() { _startTime = QDateTime::currentDateTime().toUTC(); // for uptime :) - if (Quassel::runMode() == Quassel::RunMode::CoreOnly) { - Quassel::loadTranslation(QLocale::system()); - } - // check settings version // 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 @@ -135,7 +117,8 @@ bool Core::init() 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(); @@ -149,12 +132,15 @@ bool Core::init() 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")) { @@ -165,36 +151,36 @@ bool Core::init() 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) { - _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) { - qWarning() << "Cannot configure from environment"; - QCoreApplication::exit(EXIT_FAILURE); - return false; + throw ExitException{EXIT_FAILURE, tr("Cannot configure from environment!")}; } } 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) { - 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."; @@ -203,14 +189,12 @@ bool Core::init() 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")); - QCoreApplication::exit(success ? EXIT_SUCCESS : EXIT_FAILURE); - return success; + throw ExitException{success ? EXIT_SUCCESS : EXIT_FAILURE}; } _strictIdentEnabled = Quassel::isOptionSet("strict-ident"); @@ -222,6 +206,11 @@ bool Core::init() _oidentdConfigGenerator = new OidentdConfigGenerator(this); } + + if (Quassel::isOptionSet("ident-daemon")) { + _identServer = new IdentServer(this); + } + Quassel::registerReloadHandler([]() { // Currently, only reloading SSL certificates and the sysident cache is supported if (Core::instance()) { @@ -240,8 +229,7 @@ bool Core::init() 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")) { @@ -254,10 +242,53 @@ bool Core::init() connectInternalPeer(_pendingInternalConnection); _pendingInternalConnection = {}; } +} - return true; + +void Core::initAsync() +{ + try { + init(); + } + catch (ExitException e) { + emit exitRequested(e.exitCode, e.errorString); + } } + +void Core::shutdown() +{ + quInfo() << "Core shutting down..."; + + saveState(); + + for (auto &&client : _connectingClients) { + client->deleteLater(); + } + _connectingClients.clear(); + + if (_sessions.isEmpty()) { + emit shutdownComplete(); + return; + } + + for (auto &&session : _sessions) { + connect(session, SIGNAL(shutdownComplete(SessionThread*)), this, SLOT(onSessionShutdown(SessionThread*))); + session->shutdown(); + } +} + + +void Core::onSessionShutdown(SessionThread *session) +{ + _sessions.take(_sessions.key(session))->deleteLater(); + if (_sessions.isEmpty()) { + quInfo() << "Core shutdown complete!"; + emit shutdownComplete(); + } +} + + /*** Session Restore ***/ void Core::saveState() @@ -320,14 +351,21 @@ QString Core::setupCore(const QString &adminUser, const QString &adminPassword, 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)) { @@ -390,8 +428,7 @@ DeferredSharedPtr Core::storageBackend(const QString &backendId) const 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) { @@ -417,12 +454,12 @@ bool Core::initStorage(const QString &backend, const QVariantMap &settings, 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: - qCritical() << "FATAL: Selected storage backend is not available:" << backend; 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: @@ -517,12 +554,12 @@ bool Core::initAuthenticator(const QString &backend, const QVariantMap &settings 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: - qCritical() << "FATAL: Selected auth backend is not available:" << backend; 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: @@ -540,7 +577,7 @@ bool Core::initAuthenticator(const QString &backend, const QVariantMap &settings bool Core::sslSupported() { #ifdef HAVE_SSL - SslServer *sslServer = qobject_cast(&instance()->_server); + auto *sslServer = qobject_cast(&instance()->_server); return sslServer && sslServer->isCertValid(); #else return false; @@ -551,10 +588,10 @@ bool Core::sslSupported() bool Core::reloadCerts() { #ifdef HAVE_SSL - SslServer *sslServerv4 = qobject_cast(&_server); + auto *sslServerv4 = qobject_cast(&_server); bool retv4 = sslServerv4->reloadCerts(); - SslServer *sslServerv6 = qobject_cast(&_v6server); + auto *sslServerv6 = qobject_cast(&_v6server); bool retv6 = sslServerv6->reloadCerts(); return retv4 && retv6; @@ -665,12 +702,20 @@ bool Core::startListening() if (!success) quError() << qPrintable(tr("Could not open any network interfaces to listen on!")); + if (_identServer) { + _identServer->startListening(); + } + return success; } void Core::stopListening(const QString &reason) { + if (_identServer) { + _identServer->stopListening(reason); + } + bool wasListening = false; if (_server.isListening()) { wasListening = true; @@ -691,12 +736,12 @@ void Core::stopListening(const QString &reason) void Core::incomingConnection() { - QTcpServer *server = qobject_cast(sender()); + auto *server = qobject_cast(sender()); Q_ASSERT(server); while (server->hasPendingConnections()) { QTcpSocket *socket = server->nextPendingConnection(); - CoreAuthHandler *handler = new CoreAuthHandler(socket, this); + auto *handler = new CoreAuthHandler(socket, this); _connectingClients.insert(handler); connect(handler, SIGNAL(disconnected()), SLOT(clientDisconnected())); @@ -715,7 +760,7 @@ void Core::incomingConnection() // Potentially called during the initialization phase (before handing the connection off to the session) void Core::clientDisconnected() { - CoreAuthHandler *handler = qobject_cast(sender()); + auto *handler = qobject_cast(sender()); Q_ASSERT(handler); quInfo() << qPrintable(tr("Non-authed client disconnected:")) << qPrintable(handler->socket()->peerAddress().toString()); @@ -734,11 +779,11 @@ void Core::clientDisconnected() void Core::setupClientSession(RemotePeer *peer, UserId uid) { - CoreAuthHandler *handler = qobject_cast(sender()); + auto *handler = qobject_cast(sender()); Q_ASSERT(handler); // From now on everything is handled by the client session - disconnect(handler, 0, this, 0); + disconnect(handler, nullptr, this, nullptr); _connectingClients.remove(handler); handler->deleteLater(); @@ -754,7 +799,7 @@ void Core::setupClientSession(RemotePeer *peer, UserId uid) void Core::customEvent(QEvent *event) { if (event->type() == AddClientEventId) { - AddClientEvent *addClientEvent = static_cast(event); + auto *addClientEvent = static_cast(event); addClientHelper(addClientEvent->peer, addClientEvent->userId); return; } @@ -784,7 +829,11 @@ void Core::setupInternalClientSession(QPointer clientPeer) { if (!_configured) { stopListening(); - setupCoreForInternalUsage(); + auto errorString = setupCoreForInternalUsage(); + if (!errorString.isEmpty()) { + emit exitRequested(EXIT_FAILURE, errorString); + return; + } } UserId uid; @@ -793,7 +842,7 @@ void Core::setupInternalClientSession(QPointer clientPeer) } 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; } @@ -802,7 +851,7 @@ void Core::setupInternalClientSession(QPointer clientPeer) return; } - InternalPeer *corePeer = new InternalPeer(this); + auto *corePeer = new InternalPeer(this); corePeer->setPeer(clientPeer); clientPeer->setPeer(corePeer); @@ -817,10 +866,7 @@ SessionThread *Core::sessionForUser(UserId uid, bool restore) if (_sessions.contains(uid)) return _sessions[uid]; - SessionThread *session = new SessionThread(uid, restore, strictIdentEnabled(), this); - _sessions[uid] = session; - session->start(); - return session; + return (_sessions[uid] = new SessionThread(uid, restore, strictIdentEnabled(), this)); } @@ -1135,7 +1181,7 @@ std::unique_ptr Core::getMigrationReader(Storage *st if (!storage) return nullptr; - AbstractSqlStorage *sqlStorage = qobject_cast(storage); + auto *sqlStorage = qobject_cast(storage); if (!sqlStorage) { qDebug() << "Core::migrateDb(): only SQL based backends can be migrated!"; return nullptr; @@ -1150,7 +1196,7 @@ std::unique_ptr Core::getMigrationWriter(Storage *st if (!storage) return nullptr; - AbstractSqlStorage *sqlStorage = qobject_cast(storage); + auto *sqlStorage = qobject_cast(storage); if (!sqlStorage) { qDebug() << "Core::migrateDb(): only SQL based backends can be migrated!"; return nullptr;