mirror of
https://bitbucket.org/smil3y/kde-playground.git
synced 2025-02-23 10:22:50 +00:00
381 lines
14 KiB
C++
381 lines
14 KiB
C++
/*
|
|
Copyright (c) 2008 Volker Krause <vkrause@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 "servermanager.h"
|
|
#include "servermanager_p.h"
|
|
|
|
#include "agenttype.h"
|
|
#include "agentbase.h"
|
|
#include "agentmanager.h"
|
|
#include "dbusconnectionpool.h"
|
|
#include "selftestdialog_p.h"
|
|
#include "session_p.h"
|
|
#include "firstrun_p.h"
|
|
|
|
#include <KDebug>
|
|
#include <KGlobal>
|
|
#include <KLocale>
|
|
|
|
#include <akonadi/private/protocol_p.h>
|
|
#include <akonadi/private/xdgbasedirs_p.h>
|
|
|
|
#include <QtDBus>
|
|
#include <QPointer>
|
|
#include <QTimer>
|
|
|
|
#include <boost/scoped_ptr.hpp>
|
|
|
|
using namespace Akonadi;
|
|
|
|
class Akonadi::ServerManagerPrivate
|
|
{
|
|
public:
|
|
ServerManagerPrivate()
|
|
: instance(new ServerManager(this))
|
|
, mState(ServerManager::NotRunning)
|
|
, mSafetyTimer(new QTimer)
|
|
, mFirstRunner(0)
|
|
{
|
|
mState = instance->state();
|
|
mSafetyTimer->setSingleShot(true);
|
|
mSafetyTimer->setInterval(30000);
|
|
QObject::connect(mSafetyTimer.get(), SIGNAL(timeout()), instance, SLOT(timeout()));
|
|
KGlobal::locale()->insertCatalog(QString::fromLatin1("libakonadi"));
|
|
if (mState == ServerManager::Running && Internal::clientType() == Internal::User && !ServerManager::hasInstanceIdentifier()) {
|
|
mFirstRunner = new Firstrun(instance);
|
|
}
|
|
}
|
|
|
|
~ServerManagerPrivate()
|
|
{
|
|
delete instance;
|
|
}
|
|
|
|
void serviceOwnerChanged( const QString &name, const QString &oldOwner, const QString &newOwner )
|
|
{
|
|
if (name == ServerManager::serviceName(ServerManager::ControlLock ) && !oldOwner.isEmpty() && newOwner.isEmpty()) {
|
|
// Control.Lock has disappeared during startup, which means that akonadi_control
|
|
// has terminated, most probably because it was not able to start akonadiserver
|
|
// process. Don't wait 30 seconds for sefetyTimeout, but go into Broken state
|
|
// immediately.
|
|
if (mState == ServerManager::Starting) {
|
|
setState(ServerManager::Broken);
|
|
return;
|
|
}
|
|
}
|
|
|
|
serverProtocolVersion = -1,
|
|
checkStatusChanged();
|
|
}
|
|
|
|
void checkStatusChanged()
|
|
{
|
|
setState(instance->state());
|
|
}
|
|
|
|
void setState(ServerManager::State state)
|
|
{
|
|
|
|
if (mState != state) {
|
|
mState = state;
|
|
emit instance->stateChanged(state);
|
|
if (state == ServerManager::Running) {
|
|
emit instance->started();
|
|
if (!mFirstRunner && Internal::clientType() == Internal::User && !ServerManager::hasInstanceIdentifier()) {
|
|
mFirstRunner = new Firstrun(instance);
|
|
}
|
|
} else if (state == ServerManager::NotRunning || state == ServerManager::Broken) {
|
|
emit instance->stopped();
|
|
}
|
|
|
|
if (state == ServerManager::Starting || state == ServerManager::Stopping) {
|
|
QMetaObject::invokeMethod(mSafetyTimer.get(), "start", Qt::QueuedConnection); // in case we are in a different thread
|
|
} else {
|
|
QMetaObject::invokeMethod(mSafetyTimer.get(), "stop", Qt::QueuedConnection); // in case we are in a different thread
|
|
}
|
|
}
|
|
}
|
|
|
|
void timeout()
|
|
{
|
|
if (mState == ServerManager::Starting || mState == ServerManager::Stopping) {
|
|
setState(ServerManager::Broken);
|
|
}
|
|
}
|
|
|
|
ServerManager *instance;
|
|
static int serverProtocolVersion;
|
|
ServerManager::State mState;
|
|
boost::scoped_ptr<QTimer> mSafetyTimer;
|
|
Firstrun *mFirstRunner;
|
|
static Internal::ClientType clientType;
|
|
};
|
|
|
|
int ServerManagerPrivate::serverProtocolVersion = -1;
|
|
Internal::ClientType ServerManagerPrivate::clientType = Internal::User;
|
|
|
|
K_GLOBAL_STATIC(ServerManagerPrivate, sInstance)
|
|
|
|
ServerManager::ServerManager(ServerManagerPrivate *dd)
|
|
: d(dd)
|
|
{
|
|
qRegisterMetaType<Akonadi::ServerManager::State>();
|
|
|
|
QDBusServiceWatcher *watcher = new QDBusServiceWatcher(ServerManager::serviceName(ServerManager::Server),
|
|
DBusConnectionPool::threadConnection(),
|
|
QDBusServiceWatcher::WatchForOwnerChange, this);
|
|
watcher->addWatchedService(ServerManager::serviceName(ServerManager::Control));
|
|
watcher->addWatchedService(ServerManager::serviceName(ServerManager::ControlLock));
|
|
watcher->addWatchedService(ServerManager::serviceName(ServerManager::UpgradeIndicator));
|
|
|
|
// this (and also the two connects below) are queued so that they trigger after AgentManager is done loading
|
|
// the current agent types and instances
|
|
// this ensures the invariant of AgentManager reporting a consistent state if ServerManager::state() == Running
|
|
// that's the case with direct connections as well, but only after you enter the event loop once
|
|
connect(watcher, SIGNAL(serviceOwnerChanged(QString,QString,QString)),
|
|
this, SLOT(serviceOwnerChanged(QString,QString,QString)), Qt::QueuedConnection);
|
|
|
|
// AgentManager is dangerous to use for agents themselves
|
|
if (Internal::clientType() != Internal::User) {
|
|
return;
|
|
}
|
|
connect(AgentManager::self(), SIGNAL(typeAdded(Akonadi::AgentType)), SLOT(checkStatusChanged()), Qt::QueuedConnection);
|
|
connect(AgentManager::self(), SIGNAL(typeRemoved(Akonadi::AgentType)), SLOT(checkStatusChanged()), Qt::QueuedConnection);
|
|
}
|
|
|
|
ServerManager *Akonadi::ServerManager::self()
|
|
{
|
|
return sInstance->instance;
|
|
}
|
|
|
|
bool ServerManager::start()
|
|
{
|
|
const bool controlRegistered = DBusConnectionPool::threadConnection().interface()->isServiceRegistered(ServerManager::serviceName(ServerManager::Control));
|
|
const bool serverRegistered = DBusConnectionPool::threadConnection().interface()->isServiceRegistered(ServerManager::serviceName(ServerManager::Server));
|
|
if (controlRegistered && serverRegistered) {
|
|
return true;
|
|
}
|
|
|
|
const bool controlLockRegistered = DBusConnectionPool::threadConnection().interface()->isServiceRegistered(ServerManager::serviceName(ServerManager::ControlLock));
|
|
if (controlLockRegistered || controlRegistered) {
|
|
kDebug() << "Akonadi server is already starting up";
|
|
sInstance->setState(Starting);
|
|
return true;
|
|
}
|
|
|
|
kDebug() << "executing akonadi_control";
|
|
QStringList args;
|
|
if (hasInstanceIdentifier()) {
|
|
args << QLatin1String("--instance") << instanceIdentifier();
|
|
}
|
|
const bool ok = QProcess::startDetached(QLatin1String("akonadi_control"), args);
|
|
if (!ok) {
|
|
kWarning() << "Unable to execute akonadi_control, falling back to D-Bus auto-launch";
|
|
QDBusReply<void> reply = DBusConnectionPool::threadConnection().interface()->startService(ServerManager::serviceName(ServerManager::Control));
|
|
if (!reply.isValid()) {
|
|
kDebug() << "Akonadi server could not be started via D-Bus either: "
|
|
<< reply.error().message();
|
|
return false;
|
|
}
|
|
}
|
|
sInstance->setState(Starting);
|
|
return true;
|
|
}
|
|
|
|
bool ServerManager::stop()
|
|
{
|
|
QDBusInterface iface(ServerManager::serviceName(ServerManager::Control),
|
|
QString::fromLatin1("/ControlManager"),
|
|
QString::fromLatin1("org.freedesktop.Akonadi.ControlManager"));
|
|
if (!iface.isValid()) {
|
|
return false;
|
|
}
|
|
iface.call(QDBus::NoBlock, QString::fromLatin1("shutdown"));
|
|
sInstance->setState(Stopping);
|
|
return true;
|
|
}
|
|
|
|
void ServerManager::showSelfTestDialog(QWidget *parent)
|
|
{
|
|
QPointer<Akonadi::SelfTestDialog> dlg(new Akonadi::SelfTestDialog(parent));
|
|
dlg->hideIntroduction();
|
|
dlg->exec();
|
|
delete dlg;
|
|
}
|
|
|
|
bool ServerManager::isRunning()
|
|
{
|
|
return state() == Running;
|
|
}
|
|
|
|
ServerManager::State ServerManager::state()
|
|
{
|
|
ServerManager::State previousState = NotRunning;
|
|
if (sInstance.exists()) { // be careful, this is called from the ServerManager::Private ctor, so using sInstance unprotected can cause infinite recursion
|
|
previousState = sInstance->mState;
|
|
}
|
|
|
|
const bool serverUpgrading = DBusConnectionPool::threadConnection().interface()->isServiceRegistered(ServerManager::serviceName(ServerManager::UpgradeIndicator));
|
|
if (serverUpgrading) {
|
|
return Upgrading;
|
|
}
|
|
|
|
const bool controlRegistered = DBusConnectionPool::threadConnection().interface()->isServiceRegistered(ServerManager::serviceName(ServerManager::Control));
|
|
const bool serverRegistered = DBusConnectionPool::threadConnection().interface()->isServiceRegistered(ServerManager::serviceName(ServerManager::Server));
|
|
if (controlRegistered && serverRegistered) {
|
|
// check if the server protocol is recent enough
|
|
if (sInstance.exists()) {
|
|
if (Internal::serverProtocolVersion() >= 0 &&
|
|
Internal::serverProtocolVersion() < SessionPrivate::minimumProtocolVersion()) {
|
|
return Broken;
|
|
}
|
|
}
|
|
|
|
// AgentManager is dangerous to use for agents themselves
|
|
if (Internal::clientType() == Internal::User) {
|
|
// besides the running server processes we also need at least one resource to be operational
|
|
AgentType::List agentTypes = AgentManager::self()->types();
|
|
foreach (const AgentType &type, agentTypes) {
|
|
if (type.capabilities().contains(QLatin1String("Resource"))) {
|
|
return Running;
|
|
}
|
|
}
|
|
return Broken;
|
|
} else {
|
|
return Running;
|
|
}
|
|
}
|
|
|
|
const bool controlLockRegistered = DBusConnectionPool::threadConnection().interface()->isServiceRegistered(ServerManager::serviceName(ServerManager::ControlLock));
|
|
if (controlLockRegistered || controlRegistered) {
|
|
kDebug() << "Akonadi server is already starting up";
|
|
if (previousState == Running) {
|
|
return NotRunning; // we don't know if it's starting or stopping, probably triggered by someone else
|
|
}
|
|
return previousState;
|
|
}
|
|
|
|
if (serverRegistered) {
|
|
kWarning() << "Akonadi server running without control process!";
|
|
return Broken;
|
|
}
|
|
|
|
if (previousState == Starting) { // valid case where nothing is running (yet)
|
|
return previousState;
|
|
}
|
|
return NotRunning;
|
|
}
|
|
|
|
QString ServerManager::instanceIdentifier()
|
|
{
|
|
return QLatin1String(qgetenv("AKONADI_INSTANCE"));
|
|
}
|
|
|
|
bool ServerManager::hasInstanceIdentifier()
|
|
{
|
|
return !instanceIdentifier().isEmpty();
|
|
}
|
|
|
|
static QString makeServiceName(const char *base, const QString &name = QString())
|
|
{
|
|
if (ServerManager::instanceIdentifier().isEmpty()) {
|
|
return QLatin1String(base) % name;
|
|
}
|
|
return QLatin1String(base) % name % QLatin1Literal(".") % ServerManager::instanceIdentifier();
|
|
}
|
|
|
|
// remove once we require Akonadi 1.9
|
|
#ifndef AKONADI_DBUS_SERVER_SERVICE_UPGRADING
|
|
#define AKONADI_DBUS_SERVER_SERVICE_UPGRADING "org.freedesktop.Akonadi.upgrading"
|
|
#endif
|
|
|
|
QString ServerManager::serviceName(ServerManager::ServiceType serviceType)
|
|
{
|
|
switch (serviceType) {
|
|
case Server:
|
|
return makeServiceName(AKONADI_DBUS_SERVER_SERVICE);
|
|
case Control:
|
|
return makeServiceName(AKONADI_DBUS_CONTROL_SERVICE);
|
|
case ControlLock:
|
|
return makeServiceName(AKONADI_DBUS_CONTROL_SERVICE_LOCK);
|
|
case UpgradeIndicator:
|
|
return makeServiceName(AKONADI_DBUS_SERVER_SERVICE_UPGRADING);
|
|
}
|
|
Q_ASSERT(!"WTF?");
|
|
return QString();
|
|
}
|
|
|
|
QString ServerManager::agentServiceName(ServiceAgentType agentType, const QString &identifier)
|
|
{
|
|
switch (agentType) {
|
|
case Agent:
|
|
return makeServiceName(AKONADI_DBUS_SERVER_SERVICE, QString::fromLatin1(".Agent.%1").arg(identifier));
|
|
case Resource:
|
|
return makeServiceName(AKONADI_DBUS_SERVER_SERVICE, QString::fromLatin1(".Resource.%1").arg(identifier));
|
|
case Preprocessor:
|
|
return makeServiceName(AKONADI_DBUS_SERVER_SERVICE, QString::fromLatin1(".Preprocessor.%1").arg(identifier));
|
|
}
|
|
Q_ASSERT(!"WTF?");
|
|
return QString();
|
|
}
|
|
|
|
QString ServerManager::addNamespace(const QString &string)
|
|
{
|
|
if (ServerManager::hasInstanceIdentifier()) {
|
|
return string % QLatin1Char('_') % ServerManager::instanceIdentifier();
|
|
}
|
|
return string;
|
|
}
|
|
|
|
int Internal::serverProtocolVersion()
|
|
{
|
|
return ServerManagerPrivate::serverProtocolVersion;
|
|
}
|
|
|
|
void Internal::setServerProtocolVersion(int version)
|
|
{
|
|
ServerManagerPrivate::serverProtocolVersion = version;
|
|
if (sInstance.exists()) {
|
|
sInstance->checkStatusChanged();
|
|
}
|
|
}
|
|
|
|
Internal::ClientType Internal::clientType()
|
|
{
|
|
return ServerManagerPrivate::clientType;
|
|
}
|
|
|
|
void Internal::setClientType(ClientType type)
|
|
{
|
|
ServerManagerPrivate::clientType = type;
|
|
}
|
|
|
|
QString Internal::xdgSaveDir(const char *resource, const QString &relPath)
|
|
{
|
|
QString fullRelPath = QLatin1String("akonadi");
|
|
if (!ServerManager::instanceIdentifier().isEmpty()) {
|
|
fullRelPath += QLatin1String("/instance/") + ServerManager::instanceIdentifier();
|
|
}
|
|
if (!relPath.isEmpty()) {
|
|
fullRelPath += QLatin1Char('/') + relPath;
|
|
}
|
|
return XdgBaseDirs::saveDir(resource, fullRelPath);
|
|
}
|
|
|
|
#include "moc_servermanager.cpp"
|