#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.
// ==============================
// Core
// ==============================
-Core *Core::_instance{nullptr};
-
-Core *Core::instance()
-{
- return _instance;
-}
-
Core::Core()
+ : Singleton<Core>{this}
{
- 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);
Core::~Core()
{
- saveState();
qDeleteAll(_connectingClients);
qDeleteAll(_sessions);
syncStorage();
- _instance = nullptr;
}
-bool Core::init()
+void Core::init()
{
_startTime = QDateTime::currentDateTime().toUTC(); // for uptime :)
// 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
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();
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-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.";
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");
if (Quassel::isOptionSet("ident-daemon")) {
- _identServer = new IdentServer(_strictIdentEnabled, this);
+ _identServer = new IdentServer(this);
}
Quassel::registerReloadHandler([]() {
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")) {
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()
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)) {
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)
{
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:
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:
{
if (!_configured) {
stopListening();
- setupCoreForInternalUsage();
+ auto errorString = setupCoreForInternalUsage();
+ if (!errorString.isEmpty()) {
+ emit exitRequested(EXIT_FAILURE, errorString);
+ return;
+ }
}
UserId uid;
}
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;
}
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));
}