Commit 0e478e2b authored by Daniel Vrátil's avatar Daniel Vrátil 🤖
Browse files

Make AkonadiServer QObject, manage multiple QLocalServers for different purposes

This creates so called CommandServer and NotificationServer, both
QLocalServer listening using named pipes (this is a move away from
a socket file to avoid having multiple files).

Each new connection on CommandServer makes AkonadiServer to instantiate
new Connection object as normally. For NotificationServer each new
connection is handed over to NotificationManager, which will deal with
it.
parent d7a64c88
......@@ -37,6 +37,7 @@
#include <private/standarddirs_p.h>
#include <shared/akapplication.h>
#include "aklocalserver.h"
#include "storage/dbconfig.h"
#include "storage/datastore.h"
#include "preprocessormanager.h"
......@@ -127,9 +128,9 @@ QString FakeAkonadiServer::basePath()
return QString::fromLatin1("/tmp/akonadiserver-test-%1").arg(QCoreApplication::instance()->applicationPid());
}
QString FakeAkonadiServer::socketFile()
QString FakeAkonadiServer::namedPipe()
{
return basePath() % QLatin1String("/local/share/akonadi/akonadiserver.socket");
return QStringLiteral("FakeAkonadiServer-%1").arg(qApp->applicationPid());
}
QString FakeAkonadiServer::instanceName()
......@@ -249,7 +250,9 @@ bool FakeAkonadiServer::quit()
qDebug() << "==== Fake Akonadi Server shutting down ====";
// Stop listening for connections
close();
if (mCmdServer) {
mCmdServer->close();
}
const QCommandLineParser &args = AkApplicationBase::instance()->commandLineArguments();
if (!args.isSet(QLatin1String("no-cleanup"))) {
......@@ -275,7 +278,7 @@ void FakeAkonadiServer::setScenarios(const TestScenario::List &scenarios)
mClient->setScenarios(scenarios);
}
void FakeAkonadiServer::incomingConnection(quintptr socketDescriptor)
void FakeAkonadiServer::newCmdConnection(quintptr socketDescriptor)
{
mConnection = new FakeConnection(socketDescriptor);
NotificationCollector *ntfCollector = Q_NULLPTR;
......@@ -291,7 +294,10 @@ void FakeAkonadiServer::incomingConnection(quintptr socketDescriptor)
void FakeAkonadiServer::runTest()
{
QVERIFY(listen(socketFile()));
mCmdServer = new AkLocalServer(this);
connect(mCmdServer, static_cast<void(AkLocalServer::*)(quintptr)>(&AkLocalServer::newConnection),
this, &FakeAkonadiServer::newCmdConnection);
QVERIFY(mCmdServer->listen(namedPipe()));
mServerLoop = new QEventLoop(this);
connect(mClient, &QThread::finished, mServerLoop, &QEventLoop::quit);
......@@ -306,7 +312,7 @@ void FakeAkonadiServer::runTest()
mServerLoop->deleteLater();
mServerLoop = 0;
close();
mCmdServer->close();
}
QSignalSpy *FakeAkonadiServer::notificationSpy() const
......
......@@ -94,7 +94,7 @@ public:
bool quit() Q_DECL_OVERRIDE;
static QString basePath();
static QString socketFile();
static QString namedPipe();
static QString instanceName();
static TestScenario::List loginScenario(const QByteArray &sessionId = QByteArray());
......@@ -110,8 +110,7 @@ public:
void setPopulateDb(bool populate);
protected:
/* Reimpl */
void incomingConnection(quintptr socketDescriptor) Q_DECL_OVERRIDE;
void newCmdConnection(quintptr socketDescriptor) Q_DECL_OVERRIDE;
private:
explicit FakeAkonadiServer();
......
......@@ -159,7 +159,7 @@ void FakeClient::writeClientPart()
void FakeClient::run()
{
mSocket = new QLocalSocket();
mSocket->connectToServer(FakeAkonadiServer::socketFile());
mSocket->connectToServer(FakeAkonadiServer::namedPipe());
connect(mSocket, &QIODevice::readyRead, this, &FakeClient::dataAvailable);
connect(mSocket, &QLocalSocket::disconnected, this, &FakeClient::connectionLost);
if (!mSocket->waitForConnected()) {
......
......@@ -39,6 +39,7 @@ akonadi_generate_schema(${AKONADI_DB_SCHEME} AkonadiSchema akonadischema)
set(libakonadiserver_SRCS
akonadi.cpp
aklocalserver.cpp
akthread.cpp
commandcontext.cpp
connection.cpp
......
/*
Copyright (c) 2016 Daniel Vrátil <dvratil@kde.org>
This library is free software; you can redistribute it and/or modify it
under the terms of the GNU Library General Public License as published by
the Free Software Foundation; either version 2 of the License, or (at your
option) any later version.
This library is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public
License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to the
Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301, USA.
*/
#include "aklocalserver.h"
using namespace Akonadi::Server;
AkLocalServer::AkLocalServer(QObject* parent)
: QLocalServer(parent)
{
}
void AkLocalServer::incomingConnection(quintptr socketDescriptor)
{
Q_EMIT newConnection(socketDescriptor);
}
/*
Copyright (c) 2016 Daniel Vrátil <dvratil@kde.org>
This library is free software; you can redistribute it and/or modify it
under the terms of the GNU Library General Public License as published by
the Free Software Foundation; either version 2 of the License, or (at your
option) any later version.
This library is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public
License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to the
Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301, USA.
*/
#ifndef AKLOCALSERVER_H
#define AKLOCALSERVER_H
#include <QLocalServer>
namespace Akonadi {
namespace Server {
class AkLocalServer : public QLocalServer
{
Q_OBJECT
public:
AkLocalServer(QObject *parent = Q_NULLPTR);
Q_SIGNALS:
void newConnection(quintptr socketDescriptor);
protected:
void incomingConnection(quintptr socketDescriptor) Q_DECL_OVERRIDE;
};
} // namespace Server
} // namespace Akonadi
#endif
......@@ -37,6 +37,7 @@
#include "preprocessormanager.h"
#include "search/searchmanager.h"
#include "search/searchtaskmanager.h"
#include "aklocalserver.h"
#include "collectionreferencemanager.h"
......@@ -44,6 +45,7 @@
#include <private/standarddirs_p.h>
#include <private/protocol_p.h>
#include <private/dbus_p.h>
#include <private/instance_p.h>
#include <QtSql/QSqlQuery>
#include <QtSql/QSqlError>
......@@ -53,6 +55,7 @@
#include <QtCore/QSettings>
#include <QtCore/QTimer>
#include <QtDBus/QDBusServiceWatcher>
#include <QtNetwork/QLocalServer>
#ifdef HAVE_UNISTD_H
# include <unistd.h>
......@@ -70,7 +73,10 @@ using namespace Akonadi::Server;
AkonadiServer *AkonadiServer::s_instance = 0;
AkonadiServer::AkonadiServer(QObject *parent)
: QLocalServer(parent)
: QObject(parent)
, mCmdServer(Q_NULLPTR)
, mNtfServer(Q_NULLPTR)
, mNotificationManager(Q_NULLPTR)
, mCacheCleaner(Q_NULLPTR)
, mIntervalCheck(Q_NULLPTR)
, mStorageJanitor(Q_NULLPTR)
......@@ -117,9 +123,23 @@ bool AkonadiServer::init()
s_instance = this;
mCmdServer = new AkLocalServer(this);
connect(mCmdServer, static_cast<void(AkLocalServer::*)(quintptr)>(&AkLocalServer::newConnection),
this, &AkonadiServer::newCmdConnection);
mNotificationManager = new NotificationManager();
mNtfServer = new AkLocalServer(this);
// Note: this is a queued connection, as NotificationManager lives in its
// own thread
connect(mNtfServer, static_cast<void(AkLocalServer::*)(quintptr)>(&AkLocalServer::newConnection),
mNotificationManager, &NotificationManager::registerConnection);
const QString connectionSettingsFile = StandardDirs::connectionConfigFile(XdgBaseDirs::WriteOnly);
QSettings connectionSettings(connectionSettingsFile, QSettings::IniFormat);
QString pipeName;
#ifdef Q_OS_WIN
HANDLE hToken = NULL;
PSID sid;
......@@ -152,30 +172,37 @@ bool AkonadiServer::init()
}
free(sid);
}
QString defaultPipe = QLatin1String("Akonadi-") + userID;
QString namedPipe = settings.value(QLatin1String("Connection/NamedPipe"), defaultPipe).toString();
if (!listen(namedPipe)) {
qCCritical(AKONADISERVER_LOG) << "Unable to listen on Named Pipe" << namedPipe;
pipeName = QStringLiteral("Akonadi-%1").arg(userID);
#else
uid_t uid = getuid();
pipeName = QStringLiteral("Akonadi-%1").arg(uid);
#endif
if (Instance::hasIdentifier()) {
pipeName += QStringLiteral("-") % Instance::identifier();
}
const QString cmdPipeName = pipeName + QStringLiteral("-Cmd");
if (!mCmdServer->listen(cmdPipeName)) {
qCCritical(AKONADISERVER_LOG) << "Unable to listen on named pipe" << cmdPipeName;
quit();
return false;
}
connectionSettings.setValue(QLatin1String("Data/Method"), QLatin1String("NamedPipe"));
connectionSettings.setValue(QLatin1String("Data/NamedPipe"), namedPipe);
#else
const QString socketDir = Utils::preferredSocketDirectory(StandardDirs::saveDir("data"));
const QString socketFile = socketDir + QLatin1String("/akonadiserver.socket");
QFile::remove(socketFile);
if (!listen(socketFile)) {
qCCritical(AKONADISERVER_LOG) << "Unable to listen on Unix socket" << socketFile;
const QString ntfPipeName = pipeName + QStringLiteral("-Ntf");
if (!mNtfServer->listen(ntfPipeName)) {
qCCritical(AKONADISERVER_LOG) << "Unable to listen on named pipe" << ntfPipeName;
quit();
return false;
}
connectionSettings.setValue(QStringLiteral("Data/Method"), QLatin1String("UnixPath"));
connectionSettings.setValue(QStringLiteral("Data/UnixPath"), socketFile);
#endif
connectionSettings.setValue(QStringLiteral("Data/Method"), QStringLiteral("NamedPipe"));
connectionSettings.setValue(QStringLiteral("Data/NamedPipe"), cmdPipeName);
connectionSettings.setValue(QStringLiteral("Notifications/Method"), QStringLiteral("NamedPipe"));
connectionSettings.setValue(QStringLiteral("Nottifications/NamedPipe"), ntfPipeName);
// initialize the database
DataStore *db = DataStore::self();
......@@ -190,7 +217,6 @@ bool AkonadiServer::init()
return false;
}
NotificationManager::self();
Tracer::self();
new DebugInterface(this);
ResourceManager::self();
......@@ -276,6 +302,12 @@ bool AkonadiServer::quit()
}
mAlreadyShutdown = true;
qCDebug(AKONADISERVER_LOG) << "terminating connection threads";
for (int i = 0; i < mConnections.count(); ++i) {
delete mConnections[i];
}
mConnections.clear();
qCDebug(AKONADISERVER_LOG) << "terminating service threads";
delete mCacheCleaner;
delete mIntervalCheck;
......@@ -283,12 +315,7 @@ bool AkonadiServer::quit()
delete mItemRetrieval;
delete mAgentSearchManager;
delete mSearchManager;
qCDebug(AKONADISERVER_LOG) << "terminating connection threads";
for (int i = 0; i < mConnections.count(); ++i) {
delete mConnections[i];
}
mConnections.clear();
delete mNotificationManager;
// Terminate the preprocessor manager before the database but after all connections are gone
PreprocessorManager::done();
......@@ -325,11 +352,12 @@ void AkonadiServer::doQuit()
QCoreApplication::exit();
}
void AkonadiServer::incomingConnection(quintptr socketDescriptor)
void AkonadiServer::newCmdConnection(quintptr socketDescriptor)
{
if (mAlreadyShutdown) {
return;
}
QPointer<Connection> connection = new Connection(socketDescriptor);
connect(connection.data(), &Connection::disconnected,
this, [connection]() {
......@@ -422,23 +450,22 @@ void AkonadiServer::serviceOwnerChanged(const QString &name, const QString &oldO
CacheCleaner *AkonadiServer::cacheCleaner()
{
if (mCacheCleaner) {
return mCacheCleaner;
}
return Q_NULLPTR;
return mCacheCleaner;
}
IntervalCheck *AkonadiServer::intervalChecker()
{
if (mIntervalCheck) {
return mIntervalCheck;
}
return mIntervalCheck;
}
return Q_NULLPTR;
NotificationManager *AkonadiServer::notificationManager()
{
return mNotificationManager;
}
QString AkonadiServer::serverPath() const
{
return XdgBaseDirs::homePath("config");
}
#include "akonadi.moc"
......@@ -20,11 +20,10 @@
#ifndef AKONADISERVER_H
#define AKONADISERVER_H
#include <QtCore/QObject>
#include <QtCore/QPointer>
#include <QtCore/QVector>
#include <QtNetwork/QLocalServer>
class QProcess;
namespace Akonadi {
......@@ -38,8 +37,10 @@ class SearchManager;
class StorageJanitor;
class CacheCleaner;
class IntervalCheck;
class AkLocalServer;
class NotificationManager;
class AkonadiServer : public QLocalServer
class AkonadiServer : public QObject
{
Q_OBJECT
......@@ -59,6 +60,11 @@ public:
QString serverPath() const;
/**
* Can return a nullptr
*/
NotificationManager *notificationManager();
public Q_SLOTS:
/**
* Triggers a clean server shutdown.
......@@ -67,14 +73,13 @@ public Q_SLOTS:
virtual bool init();
protected Q_SLOTS:
virtual void newCmdConnection(quintptr socketDescriptor);
private Q_SLOTS:
void doQuit();
void serviceOwnerChanged(const QString &name, const QString &oldOwner, const QString &newOwner);
protected:
/** reimpl */
void incomingConnection(quintptr socketDescriptor) Q_DECL_OVERRIDE;
private:
bool startDatabaseProcess();
bool createDatabase();
......@@ -83,6 +88,10 @@ private:
protected:
AkonadiServer(QObject *parent = Q_NULLPTR);
AkLocalServer *mCmdServer;
AkLocalServer *mNtfServer;
NotificationManager *mNotificationManager;
CacheCleaner *mCacheCleaner;
IntervalCheck *mIntervalCheck;
StorageJanitor *mStorageJanitor;
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment