Hello community, here is the log from the commit of package akonadi-server for openSUSE:Factory checked in at 2019-07-16 08:29:33 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/akonadi-server (Old) and /work/SRC/openSUSE:Factory/.akonadi-server.new.1887 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "akonadi-server" Tue Jul 16 08:29:33 2019 rev:59 rq:715034 version:19.04.3 Changes: -------- --- /work/SRC/openSUSE:Factory/akonadi-server/akonadi-server.changes 2019-06-13 22:40:38.796237348 +0200 +++ /work/SRC/openSUSE:Factory/.akonadi-server.new.1887/akonadi-server.changes 2019-07-16 08:29:34.531395422 +0200 @@ -1,0 +2,14 @@ +Sat Jul 13 07:26:32 UTC 2019 - [email protected] + +- Update to 19.04.3 + * New bugfix release + * For more details please see: + * https://www.kde.org/announcements/announce-applications-19.04.3.php +- Changes since 19.04.2: + * Fix race-condition on akonadi_control start (kde#392092) + * Adapt DBInitializer to behavior change of QPSQL driver in Qt 5.13 (kde#409234) + * Check Postgres status by checking return code of pg_ctl (kde#386173) + * Shorten sockets file path + * Show error string from QLocalServer on failure + +------------------------------------------------------------------- Old: ---- akonadi-19.04.2.tar.xz New: ---- akonadi-19.04.3.tar.xz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ akonadi-server.spec ++++++ --- /var/tmp/diff_new_pack.XE4R2D/_old 2019-07-16 08:29:35.135395438 +0200 +++ /var/tmp/diff_new_pack.XE4R2D/_new 2019-07-16 08:29:35.139395436 +0200 @@ -22,7 +22,7 @@ %{!?_kapp_version: %define _kapp_version %(echo %{version}| awk -F. '{print $1"."$2}')} %bcond_without lang Name: akonadi-server -Version: 19.04.2 +Version: 19.04.3 Release: 0 Summary: PIM Storage Service License: LGPL-2.1-or-later ++++++ akonadi-19.04.2.tar.xz -> akonadi-19.04.3.tar.xz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/akonadi-19.04.2/CMakeLists.txt new/akonadi-19.04.3/CMakeLists.txt --- old/akonadi-19.04.2/CMakeLists.txt 2019-06-04 02:15:05.000000000 +0200 +++ new/akonadi-19.04.3/CMakeLists.txt 2019-07-09 02:18:16.000000000 +0200 @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.5) -set(PIM_VERSION "5.11.2") +set(PIM_VERSION "5.11.3") project(Akonadi VERSION ${PIM_VERSION}) if (MSVC) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/akonadi-19.04.2/src/akonadicontrol/main.cpp new/akonadi-19.04.3/src/akonadicontrol/main.cpp --- old/akonadi-19.04.2/src/akonadicontrol/main.cpp 2019-06-02 08:43:52.000000000 +0200 +++ new/akonadi-19.04.3/src/akonadicontrol/main.cpp 2019-06-28 17:13:51.000000000 +0200 @@ -52,7 +52,7 @@ int main(int argc, char **argv) { - AkGuiApplication app(argc, argv, AKONADICONTROL_LOG()); + AkUniqueGuiApplication app(argc, argv, Akonadi::DBus::serviceName(Akonadi::DBus::ControlLock), AKONADICONTROL_LOG()); app.setDescription(QStringLiteral("Akonadi Control Process\nDo not run this manually, use 'akonadictl' instead to start/stop Akonadi.")); KAboutData aboutData(QStringLiteral("akonadi_control"), @@ -64,20 +64,8 @@ app.parseCommandLine(); - // try to acquire the lock first, that means there is no second instance trying to start up at the same time - // registering the real service name happens in AgentManager::continueStartup(), when everything is in fact up and running - if (!QDBusConnection::sessionBus().registerService(Akonadi::DBus::serviceName(Akonadi::DBus::ControlLock))) { - // We couldn't register. Most likely, it's already running. - const QString lastError = QDBusConnection::sessionBus().lastError().message(); - if (lastError.isEmpty()) { - qCWarning(AKONADICONTROL_LOG) << "Unable to register service as" << Akonadi::DBus::serviceName(Akonadi::DBus::ControlLock) << "Maybe it's already running?"; - } else { - qCWarning(AKONADICONTROL_LOG) << "Unable to register service as" << Akonadi::DBus::serviceName(Akonadi::DBus::ControlLock) << "Error was:" << lastError; - } - return -1; - } - // older Akonadi server versions don't use the lock service yet, so check if one is already running before we try to start another one + // TODO: Remove this legacy check? if (QDBusConnection::sessionBus().interface()->isServiceRegistered(Akonadi::DBus::serviceName(Akonadi::DBus::Control))) { qCWarning(AKONADICONTROL_LOG) << "Another Akonadi control process is already running."; return -1; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/akonadi-19.04.2/src/private/standarddirs.cpp new/akonadi-19.04.3/src/private/standarddirs.cpp --- old/akonadi-19.04.2/src/private/standarddirs.cpp 2019-06-02 08:43:52.000000000 +0200 +++ new/akonadi-19.04.3/src/private/standarddirs.cpp 2019-06-28 17:13:51.000000000 +0200 @@ -110,6 +110,8 @@ fullPath = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation) + fullRelPath; } else if (qstrncmp(resource, "data", 4) == 0) { fullPath = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + fullRelPath; + } else if (qstrncmp(resource, "runtime", 7) == 0) { + fullPath = QStandardPaths::writableLocation(QStandardPaths::RuntimeLocation) + fullRelPath; } else { qt_assert_x(__FUNCTION__, "Invalid resource type", __FILE__, __LINE__); return {}; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/akonadi-19.04.2/src/server/akonadi.cpp new/akonadi-19.04.3/src/server/akonadi.cpp --- old/akonadi-19.04.2/src/server/akonadi.cpp 2019-06-02 08:43:52.000000000 +0200 +++ new/akonadi-19.04.3/src/server/akonadi.cpp 2019-06-28 17:13:51.000000000 +0200 @@ -124,7 +124,7 @@ const QString defaultCmdPipe = QStringLiteral("Akonadi-Cmd-") % suffix; const QString cmdPipe = settings.value(QStringLiteral("Connection/NamedPipe"), defaultCmdPipe).toString(); if (!mCmdServer->listen(cmdPipe)) { - qCCritical(AKONADISERVER_LOG) << "Unable to listen on Named Pipe" << cmdPipe; + qCCritical(AKONADISERVER_LOG) << "Unable to listen on Named Pipe" << cmdPipe << ":" << mCmdServer->errorString(); quit(); return false; } @@ -132,7 +132,7 @@ const QString defaultNtfPipe = QStringLiteral("Akonadi-Ntf-") % suffix; const QString ntfPipe = settings.value(QStringLiteral("Connection/NtfNamedPipe"), defaultNtfPipe).toString(); if (!mNtfServer->listen(ntfPipe)) { - qCCritical(AKONADISERVER_LOG) << "Unable to listen on Named Pipe" << ntfPipe; + qCCritical(AKONADISERVER_LOG) << "Unable to listen on Named Pipe" << ntfPipe << ":" << mNtfServer->errorString(); quit(); return false; } @@ -142,19 +142,22 @@ connectionSettings.setValue(QStringLiteral("Notifications/Method"), QStringLiteral("NamedPipe")); connectionSettings.setValue(QStringLiteral("Notifications/NamedPipe"), ntfPipe); #else - const QString socketDir = Utils::preferredSocketDirectory(StandardDirs::saveDir("data")); - const QString cmdSocketFile = socketDir % QStringLiteral("/akonadiserver-cmd.socket"); + const QString cmdSocketName = QStringLiteral("akonadiserver-cmd.socket"); + const QString ntfSocketName = QStringLiteral("akonadiserver-ntf.socket"); + const QString socketDir = Utils::preferredSocketDirectory(StandardDirs::saveDir("data"), + qMax(cmdSocketName.length(), ntfSocketName.length())); + const QString cmdSocketFile = socketDir % QLatin1Char('/') % cmdSocketName; QFile::remove(cmdSocketFile); if (!mCmdServer->listen(cmdSocketFile)) { - qCCritical(AKONADISERVER_LOG) << "Unable to listen on Unix socket" << cmdSocketFile; + qCCritical(AKONADISERVER_LOG) << "Unable to listen on Unix socket" << cmdSocketFile << ":" << mCmdServer->errorString(); quit(); return false; } - const QString ntfSocketFile = socketDir % QStringLiteral("/akonadiserver-ntf.socket"); + const QString ntfSocketFile = socketDir % QLatin1Char('/') % ntfSocketName; QFile::remove(ntfSocketFile); if (!mNtfServer->listen(ntfSocketFile)) { - qCCritical(AKONADISERVER_LOG) << "Unable to listen on Unix socket" << ntfSocketFile; + qCCritical(AKONADISERVER_LOG) << "Unable to listen on Unix socket" << ntfSocketFile << ":" << mNtfServer->errorString(); quit(); return false; } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/akonadi-19.04.2/src/server/storage/dbconfigmysql.cpp new/akonadi-19.04.3/src/server/storage/dbconfigmysql.cpp --- old/akonadi-19.04.2/src/server/storage/dbconfigmysql.cpp 2019-06-02 08:43:52.000000000 +0200 +++ new/akonadi-19.04.3/src/server/storage/dbconfigmysql.cpp 2019-06-28 17:13:51.000000000 +0200 @@ -42,6 +42,8 @@ #define MYSQL_VERSION_CHECK(major, minor, patch) ((major << 16) | (minor << 8) | patch) +static const QString s_mysqlSocketFileName = QStringLiteral("mysql.socket"); + DbConfigMysql::DbConfigMysql() : mInternalServer(true) , mDatabaseProcess(nullptr) @@ -86,7 +88,8 @@ QString defaultCleanShutdownCommand; #ifndef Q_OS_WIN - const QString socketDirectory = Utils::preferredSocketDirectory(StandardDirs::saveDir("data", QStringLiteral("db_misc"))); + const QString socketDirectory = Utils::preferredSocketDirectory(StandardDirs::saveDir("data", QStringLiteral("db_misc")), + s_mysqlSocketFileName.length()); #endif const bool defaultInternalServer = true; @@ -102,8 +105,8 @@ const QString mysqladminPath = findExecutable(QStringLiteral("mysqladmin")); if (!mysqladminPath.isEmpty()) { #ifndef Q_OS_WIN - defaultCleanShutdownCommand = QStringLiteral("%1 --defaults-file=%2/mysql.conf --socket=%3/mysql.socket shutdown") - .arg(mysqladminPath, StandardDirs::saveDir("data"), socketDirectory); + defaultCleanShutdownCommand = QStringLiteral("%1 --defaults-file=%2/mysql.conf --socket=%3/%4 shutdown") + .arg(mysqladminPath, StandardDirs::saveDir("data"), socketDirectory, s_mysqlSocketFileName); #else defaultCleanShutdownCommand = QString::fromLatin1("%1 shutdown --shared-memory").arg(mysqladminPath); #endif @@ -118,7 +121,7 @@ mInternalServer = settings.value(QStringLiteral("QMYSQL/StartServer"), defaultInternalServer).toBool(); #ifndef Q_OS_WIN if (mInternalServer) { - defaultOptions = QStringLiteral("UNIX_SOCKET=%1/mysql.socket").arg(socketDirectory); + defaultOptions = QStringLiteral("UNIX_SOCKET=%1/%2").arg(socketDirectory, s_mysqlSocketFileName); } #endif @@ -200,8 +203,9 @@ const QString akDir = StandardDirs::saveDir("data"); const QString dataDir = StandardDirs::saveDir("data", QStringLiteral("db_data")); #ifndef Q_OS_WIN - const QString socketDirectory = Utils::preferredSocketDirectory(StandardDirs::saveDir("data", QStringLiteral("db_misc"))); - const QString socketFile = QStringLiteral("%1/mysql.socket").arg(socketDirectory); + const QString socketDirectory = Utils::preferredSocketDirectory(StandardDirs::saveDir("data", QStringLiteral("db_misc")), + s_mysqlSocketFileName.length()); + const QString socketFile = QStringLiteral("%1/%2").arg(socketDirectory, s_mysqlSocketFileName); const QString pidFileName = QStringLiteral("%1/mysql.pid").arg(socketDirectory); #endif @@ -302,7 +306,7 @@ return false; } - // If mysql.socket file exists, check if also the server process is still running, + // If mysql socket file exists, check if also the server process is still running, // else we can safely remove the socket file (cleanup after a system crash, etc.) QFile pidFile(pidFileName); if (QFile::exists(socketFile) && pidFile.open(QIODevice::ReadOnly)) { @@ -348,7 +352,7 @@ #endif #ifndef Q_OS_WIN - // If mysql.socket file does not exists, then we must start the server, + // If mysql socket file does not exists, then we must start the server, // otherwise we reconnect to it if (!QFile::exists(socketFile)) { // move mysql error log file out of the way @@ -403,7 +407,7 @@ QThread::msleep(100); } } else { - qCDebug(AKONADISERVER_LOG) << "Found mysql.socket file, reconnecting to the database"; + qCDebug(AKONADISERVER_LOG) << "Found " << qPrintable(s_mysqlSocketFileName) << " file, reconnecting to the database"; } #endif @@ -442,7 +446,7 @@ QStringLiteral("--check-upgrade"), QStringLiteral("--auto-repair"), #ifndef Q_OS_WIN - QStringLiteral("--socket=%1/mysql.socket").arg(socketDirectory), + QStringLiteral("--socket=%1/%2").arg(socketDirectory, s_mysqlSocketFileName), #endif mDatabaseName }); @@ -514,8 +518,9 @@ #ifndef Q_OS_WIN // when the server stopped unexpectedly, make sure to remove the stale socket file since otherwise // it can not be started again - const QString socketDirectory = Utils::preferredSocketDirectory(StandardDirs::saveDir("data", QStringLiteral("db_misc"))); - const QString socketFile = QStringLiteral("%1/mysql.socket").arg(socketDirectory); + const QString socketDirectory = Utils::preferredSocketDirectory(StandardDirs::saveDir("data", QStringLiteral("db_misc")), + s_mysqlSocketFileName.length()); + const QString socketFile = QStringLiteral("%1/%2").arg(socketDirectory, s_mysqlSocketFileName); QFile::remove(socketFile); #endif diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/akonadi-19.04.2/src/server/storage/dbconfigpostgresql.cpp new/akonadi-19.04.3/src/server/storage/dbconfigpostgresql.cpp --- old/akonadi-19.04.2/src/server/storage/dbconfigpostgresql.cpp 2019-06-02 08:43:52.000000000 +0200 +++ new/akonadi-19.04.3/src/server/storage/dbconfigpostgresql.cpp 2019-06-28 17:13:51.000000000 +0200 @@ -382,6 +382,6 @@ return false; } - const QByteArray output = pgCtl.readAllStandardOutput(); - return output.startsWith("pg_ctl: server is running"); + // "pg_ctl status" exits with 0 when server is running and a non-zero code when not. + return pgCtl.exitCode() == 0; } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/akonadi-19.04.2/src/server/storage/dbintrospector.cpp new/akonadi-19.04.3/src/server/storage/dbintrospector.cpp --- old/akonadi-19.04.2/src/server/storage/dbintrospector.cpp 2019-06-02 08:43:52.000000000 +0200 +++ new/akonadi-19.04.3/src/server/storage/dbintrospector.cpp 2019-06-28 17:13:51.000000000 +0200 @@ -76,7 +76,7 @@ QStringList columns = m_columnCache.value(tableName); if (columns.isEmpty()) { - const QSqlRecord table = m_database.record(tableName); + const QSqlRecord table = m_database.record(tableName.toLower()); const int numTables = table.count(); columns.reserve(numTables); for (int i = 0; i < numTables; ++i) { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/akonadi-19.04.2/src/server/utils.cpp new/akonadi-19.04.3/src/server/utils.cpp --- old/akonadi-19.04.2/src/server/utils.cpp 2019-06-02 08:43:52.000000000 +0200 +++ new/akonadi-19.04.3/src/server/utils.cpp 2019-06-28 17:13:51.000000000 +0200 @@ -21,6 +21,7 @@ #include "utils.h" #include "akonadiserver_debug.h" +#include "instance_p.h" #include <private/standarddirs_p.h> @@ -32,13 +33,13 @@ #if !defined(Q_OS_WIN) #include <cstdlib> #include <sys/types.h> +#include <sys/un.h> #include <cerrno> -#include <pwd.h> #include <unistd.h> static QString akonadiSocketDirectory(); static bool checkSocketDirectory(const QString &path); -static bool createSocketDirectory(const QString &link, const QString &tmpl); +static bool createSocketDirectory(const QString &link, const QString &identifier); #endif #ifdef Q_OS_LINUX @@ -51,7 +52,7 @@ using namespace Akonadi; using namespace Akonadi::Server; -QString Utils::preferredSocketDirectory(const QString &defaultDirectory) +QString Utils::preferredSocketDirectory(const QString &defaultDirectory, int fnLengthHint) { const QString serverConfigFile = StandardDirs::serverConfigFile(StandardDirs::ReadWrite); const QSettings serverSettings(serverConfigFile, QSettings::IniFormat); @@ -87,6 +88,10 @@ if (!dirInfo.exists()) { QDir::home().mkpath(dirInfo.absoluteFilePath()); } + + if (socketDir.length() + 1 + fnLengthHint >= static_cast<int>(sizeof(sockaddr_un::sun_path))) { + qCCritical(AKONADISERVER_LOG) << "akonadiSocketDirectory() length is too long to be used by the system."; + } #endif return socketDir; } @@ -101,25 +106,14 @@ return QString(); } - const uid_t uid = getuid(); - const struct passwd *pw_ent = getpwuid(uid); - if (!pw_ent) { - qCCritical(AKONADISERVER_LOG) << "Could not get passwd entry for user id" << uid; - return QString(); - } - - const QString link = StandardDirs::saveDir("data") + QLatin1Char('/') + QLatin1String("socket-") + hostname; - QString tmpl = QLatin1String("akonadi-") + QString::fromLocal8Bit(pw_ent->pw_name) + QLatin1String(".XXXXXX"); - - // Workaround for QLocalServer encoding bug - // basically replace non-latin characters - tmpl = QString::fromLatin1(tmpl.toLatin1()); + const QString identifier = Instance::hasIdentifier() ? Instance::identifier() : QLatin1String("default"); + const QString link = StandardDirs::saveDir("data") + QStringLiteral("/socket-%1-%2").arg(hostname, identifier); if (checkSocketDirectory(link)) { return QFileInfo(link).symLinkTarget(); } - if (createSocketDirectory(link, tmpl)) { + if (createSocketDirectory(link, identifier)) { return QFileInfo(link).symLinkTarget(); } @@ -150,19 +144,15 @@ return true; } -static bool createSocketDirectory(const QString &link, const QString &tmpl) +static bool createSocketDirectory(const QString &link, const QString &identifier) { - QString directory = QStringLiteral("%1%2%3").arg(QDir::tempPath()).arg(QDir::separator()).arg(tmpl); + const QString directory = QStringLiteral("%1/%2").arg(StandardDirs::saveDir("runtime"), identifier); - QByteArray directoryString = directory.toLocal8Bit(); - - if (!mkdtemp(directoryString.data())) { - qCCritical(AKONADISERVER_LOG) << "Creating socket directory with template" << directoryString << "failed:" << strerror(errno); + if (!QDir().mkpath(directory)) { + qCCritical(AKONADISERVER_LOG) << "Creating socket directory with name" << directory << "failed:" << strerror(errno); return false; } - directory = QString::fromLocal8Bit(directoryString); - QFile::remove(link); if (!QFile::link(directory, link)) { @@ -177,6 +167,7 @@ QString Utils::getDirectoryFileSystem(const QString &directory) { #ifndef Q_OS_LINUX + Q_UNUSED(directory); return QString(); #else QString bestMatchPath; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/akonadi-19.04.2/src/server/utils.h new/akonadi-19.04.3/src/server/utils.h --- old/akonadi-19.04.2/src/server/utils.h 2019-06-02 08:43:52.000000000 +0200 +++ new/akonadi-19.04.3/src/server/utils.h 2019-06-28 17:13:51.000000000 +0200 @@ -99,8 +99,10 @@ /** * Returns the socket @p directory that is passed to this method or the one * the user has overwritten via the config file. + * The passed @p fnLengthHint will also ensure the absolute file path length of the + * directory + separator + hint would not overflow the system limitation. */ -QString preferredSocketDirectory(const QString &directory); +QString preferredSocketDirectory(const QString &directory, int fnLengthHint = -1); /** * Returns name of filesystem that @p directory is stored on. This diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/akonadi-19.04.2/src/shared/akapplication.cpp new/akonadi-19.04.3/src/shared/akapplication.cpp --- old/akonadi-19.04.2/src/shared/akapplication.cpp 2019-06-02 08:43:52.000000000 +0200 +++ new/akonadi-19.04.3/src/shared/akapplication.cpp 2019-06-28 17:13:51.000000000 +0200 @@ -32,10 +32,9 @@ AkApplicationBase *AkApplicationBase::sInstance = nullptr; -AkApplicationBase::AkApplicationBase(int &argc, char **argv, const QLoggingCategory &loggingCategory) +AkApplicationBase::AkApplicationBase(std::unique_ptr<QCoreApplication> app, const QLoggingCategory &loggingCategory) : QObject(nullptr) - , mArgc(argc) - , mArgv(argv) + , mApp(std::move(app)) , mLoggingCategory(loggingCategory) { Q_ASSERT(!sInstance); @@ -59,7 +58,7 @@ void AkApplicationBase::init() { - akInit(QString::fromLatin1(mArgv[0])); + akInit(mApp->applicationName()); akInitRemoteLog(); if (!QDBusConnection::sessionBus().isConnected()) { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/akonadi-19.04.2/src/shared/akapplication.h new/akonadi-19.04.3/src/shared/akapplication.h --- old/akonadi-19.04.2/src/shared/akapplication.h 2019-06-02 08:43:52.000000000 +0200 +++ new/akonadi-19.04.3/src/shared/akapplication.h 2019-06-28 17:13:51.000000000 +0200 @@ -23,6 +23,10 @@ #include <QObject> #include <QCommandLineParser> #include <QLoggingCategory> +#include <QDBusConnection> +#include <QDBusError> + +#include <memory> class QCoreApplication; class QApplication; @@ -55,16 +59,15 @@ int exec(); protected: - AkApplicationBase(int &argc, char **argv, const QLoggingCategory &loggingCategory); + AkApplicationBase(std::unique_ptr<QCoreApplication> app, const QLoggingCategory &loggingCategory); void init(); - QScopedPointer<QCoreApplication> mApp; + + std::unique_ptr<QCoreApplication> mApp; private Q_SLOTS: void pollSessionBus() const; private: - int mArgc; - char **mArgv; QString mInstanceId; const QLoggingCategory &mLoggingCategory; static AkApplicationBase *sInstance; @@ -77,13 +80,46 @@ { public: AkApplicationImpl(int &argc, char **argv, const QLoggingCategory &loggingCategory = *QLoggingCategory::defaultCategory()) - : AkApplicationBase(argc, argv, loggingCategory) + : AkApplicationBase(std::make_unique<T>(argc, argv), loggingCategory) { - mApp.reset(new T(argc, argv)); init(); } }; +template<typename T> +class AkUniqueApplicationImpl : public AkApplicationBase +{ +public: + AkUniqueApplicationImpl(int &argc, char **argv, const QString &serviceName, const QLoggingCategory &loggingCategory = *QLoggingCategory::defaultCategory()) + : AkApplicationBase(std::make_unique<T>(argc, argv), loggingCategory) + { + registerUniqueServiceOrTerminate(serviceName, loggingCategory); + init(); + } + +private: + void registerUniqueServiceOrTerminate(const QString &serviceName, const QLoggingCategory &log) + { + auto bus = QDBusConnection::sessionBus(); + if (!bus.isConnected()) { + qCCritical(log, "Session bus not found. Is DBus running?"); + exit(1); + } + + if (!bus.registerService(serviceName)) { + // We couldn't register. Most likely, it's already running. + const QString lastError = bus.lastError().message(); + if (lastError.isEmpty()) { + qCInfo(log, "Service %s already registered, terminating now.", qUtf8Printable(serviceName)); + exit(0); // already running, so it's OK. Terminate now. + } else { + qCCritical(log, "Unable to register service as %s due to an error: %s", qUtf8Printable(serviceName), qUtf8Printable(lastError)); + exit(1); // :( + } + } + } +}; + /** * Returns the contents of @p name environment variable if it is defined, * or @p defaultValue otherwise. @@ -93,5 +129,6 @@ typedef AkApplicationImpl<QCoreApplication> AkCoreApplication; typedef AkApplicationImpl<QApplication> AkApplication; typedef AkApplicationImpl<QGuiApplication> AkGuiApplication; +typedef AkUniqueApplicationImpl<QGuiApplication> AkUniqueGuiApplication; #endif
