File indexing completed on 2024-06-16 04:50:17

0001 /*
0002     SPDX-FileCopyrightText: 2008 Volker Krause <vkrause@kde.org>
0003 
0004     SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 
0007 #include "servermanager.h"
0008 #include "servermanager_p.h"
0009 
0010 #include "agentmanager.h"
0011 #include "agenttype.h"
0012 #include "firstrun_p.h"
0013 #include "session_p.h"
0014 
0015 #include "akonadicore_debug.h"
0016 
0017 #include <KLocalizedString>
0018 
0019 #include "private/dbus_p.h"
0020 #include "private/instance_p.h"
0021 #include "private/protocol_p.h"
0022 #include "private/standarddirs_p.h"
0023 
0024 #include <QDBusConnection>
0025 #include <QDBusConnectionInterface>
0026 #include <QDBusInterface>
0027 #include <QDBusReply>
0028 #include <QDBusServiceWatcher>
0029 #include <QProcess>
0030 #include <QScopedPointer>
0031 #include <QStandardPaths>
0032 #include <QTimer>
0033 #include <qnamespace.h>
0034 
0035 using namespace Akonadi;
0036 
0037 class Akonadi::ServerManagerPrivate
0038 {
0039 public:
0040     ServerManagerPrivate()
0041         : instance(new ServerManager(this))
0042         , mState(ServerManager::NotRunning)
0043         , mSafetyTimer(new QTimer)
0044     {
0045         mState = instance->state();
0046         mSafetyTimer->setSingleShot(true);
0047         mSafetyTimer->setInterval(30000);
0048         QObject::connect(mSafetyTimer.data(), &QTimer::timeout, instance, [this]() {
0049             timeout();
0050         });
0051         if (mState == ServerManager::Running && Internal::clientType() == Internal::User && !ServerManager::hasInstanceIdentifier()) {
0052             mFirstRunner = new Firstrun(instance);
0053         }
0054     }
0055 
0056     ~ServerManagerPrivate()
0057     {
0058         delete instance;
0059     }
0060 
0061     void checkStatusChanged()
0062     {
0063         setState(instance->state());
0064     }
0065 
0066     void setState(ServerManager::State state)
0067     {
0068         if (mState != state) {
0069             mState = state;
0070             Q_EMIT instance->stateChanged(state);
0071             if (state == ServerManager::Running) {
0072                 Q_EMIT instance->started();
0073                 if (!mFirstRunner && Internal::clientType() == Internal::User && !ServerManager::hasInstanceIdentifier()) {
0074                     mFirstRunner = new Firstrun(instance);
0075                 }
0076             } else if (state == ServerManager::NotRunning || state == ServerManager::Broken) {
0077                 Q_EMIT instance->stopped();
0078             }
0079             if (state == ServerManager::Starting || state == ServerManager::Stopping) {
0080                 QMetaObject::invokeMethod(mSafetyTimer.data(), qOverload<>(&QTimer::start), Qt::QueuedConnection); // in case we are in a different thread
0081             } else {
0082                 QMetaObject::invokeMethod(mSafetyTimer.data(), &QTimer::stop, Qt::QueuedConnection); // in case we are in a different thread
0083             }
0084         }
0085     }
0086 
0087     void timeout()
0088     {
0089         if (mState == ServerManager::Starting || mState == ServerManager::Stopping) {
0090             setState(ServerManager::Broken);
0091         }
0092     }
0093 
0094     ServerManager *instance = nullptr;
0095     static int serverProtocolVersion;
0096     static uint generation;
0097     ServerManager::State mState;
0098     QScopedPointer<QTimer> mSafetyTimer;
0099     Firstrun *mFirstRunner = nullptr;
0100     static Internal::ClientType clientType;
0101     QString mBrokenReason;
0102     std::unique_ptr<QDBusServiceWatcher> serviceWatcher;
0103 };
0104 
0105 int ServerManagerPrivate::serverProtocolVersion = -1;
0106 uint ServerManagerPrivate::generation = 0;
0107 Internal::ClientType ServerManagerPrivate::clientType = Internal::User;
0108 
0109 Q_GLOBAL_STATIC(ServerManagerPrivate, sInstance) // NOLINT(readability-redundant-member-init)
0110 
0111 ServerManager::ServerManager(ServerManagerPrivate *dd)
0112     : d(dd)
0113 {
0114     qRegisterMetaType<Akonadi::ServerManager::State>();
0115 
0116     d->serviceWatcher = std::make_unique<QDBusServiceWatcher>(ServerManager::serviceName(ServerManager::Server),
0117                                                               QDBusConnection::sessionBus(),
0118                                                               QDBusServiceWatcher::WatchForRegistration | QDBusServiceWatcher::WatchForUnregistration);
0119     d->serviceWatcher->addWatchedService(ServerManager::serviceName(ServerManager::Control));
0120     d->serviceWatcher->addWatchedService(ServerManager::serviceName(ServerManager::ControlLock));
0121     d->serviceWatcher->addWatchedService(ServerManager::serviceName(ServerManager::UpgradeIndicator));
0122 
0123     // this (and also the two connects below) are queued so that they trigger after AgentManager is done loading
0124     // the current agent types and instances
0125     // this ensures the invariant of AgentManager reporting a consistent state if ServerManager::state() == Running
0126     // that's the case with direct connections as well, but only after you enter the event loop once
0127     connect(
0128         d->serviceWatcher.get(),
0129         &QDBusServiceWatcher::serviceRegistered,
0130         this,
0131         [this]() {
0132             d->serverProtocolVersion = -1;
0133             d->checkStatusChanged();
0134         },
0135         Qt::QueuedConnection);
0136     connect(
0137         d->serviceWatcher.get(),
0138         &QDBusServiceWatcher::serviceUnregistered,
0139         this,
0140         [this](const QString &name) {
0141             if (name == ServerManager::serviceName(ServerManager::ControlLock) && d->mState == ServerManager::Starting) {
0142                 // Control.Lock has disappeared during startup, which means that akonadi_control
0143                 // has terminated, most probably because it was not able to start akonadiserver
0144                 // process. Don't wait 30 seconds for sefetyTimeout, but go into Broken state
0145                 // immediately.
0146                 d->setState(ServerManager::Broken);
0147                 return;
0148             }
0149 
0150             d->serverProtocolVersion = -1;
0151             d->checkStatusChanged();
0152         },
0153         Qt::QueuedConnection);
0154 
0155     // AgentManager is dangerous to use for agents themselves
0156     if (Internal::clientType() != Internal::User) {
0157         return;
0158     }
0159 
0160     connect(
0161         AgentManager::self(),
0162         &AgentManager::typeAdded,
0163         this,
0164         [this]() {
0165             d->checkStatusChanged();
0166         },
0167         Qt::QueuedConnection);
0168     connect(
0169         AgentManager::self(),
0170         &AgentManager::typeRemoved,
0171         this,
0172         [this]() {
0173             d->checkStatusChanged();
0174         },
0175         Qt::QueuedConnection);
0176 }
0177 
0178 ServerManager *Akonadi::ServerManager::self()
0179 {
0180     return sInstance->instance;
0181 }
0182 
0183 bool ServerManager::start()
0184 {
0185     const bool controlRegistered = QDBusConnection::sessionBus().interface()->isServiceRegistered(ServerManager::serviceName(ServerManager::Control));
0186     const bool serverRegistered = QDBusConnection::sessionBus().interface()->isServiceRegistered(ServerManager::serviceName(ServerManager::Server));
0187     if (controlRegistered && serverRegistered) {
0188         return true;
0189     }
0190 
0191     const bool controlLockRegistered = QDBusConnection::sessionBus().interface()->isServiceRegistered(ServerManager::serviceName(ServerManager::ControlLock));
0192     if (controlLockRegistered || controlRegistered) {
0193         qCDebug(AKONADICORE_LOG) << "Akonadi server is already starting up";
0194         sInstance->setState(Starting);
0195         return true;
0196     }
0197 
0198     qCDebug(AKONADICORE_LOG) << "executing akonadi_control";
0199     QStringList args;
0200     if (hasInstanceIdentifier()) {
0201         args << QStringLiteral("--instance") << instanceIdentifier();
0202     }
0203     const QString exec = QStandardPaths::findExecutable(QStringLiteral("akonadi_control"));
0204     if (exec.isEmpty() || !QProcess::startDetached(exec, args)) {
0205         qCWarning(AKONADICORE_LOG) << "Unable to execute akonadi_control, falling back to D-Bus auto-launch";
0206         QDBusReply<void> reply = QDBusConnection::sessionBus().interface()->startService(ServerManager::serviceName(ServerManager::Control));
0207         if (!reply.isValid()) {
0208             qCDebug(AKONADICORE_LOG) << "Akonadi server could not be started via D-Bus either: " << reply.error().message();
0209             return false;
0210         }
0211     }
0212     sInstance->setState(Starting);
0213     return true;
0214 }
0215 
0216 bool ServerManager::stop()
0217 {
0218     QDBusInterface iface(ServerManager::serviceName(ServerManager::Control),
0219                          QStringLiteral("/ControlManager"),
0220                          QStringLiteral("org.freedesktop.Akonadi.ControlManager"));
0221     if (!iface.isValid()) {
0222         return false;
0223     }
0224     iface.call(QDBus::NoBlock, QStringLiteral("shutdown"));
0225     sInstance->setState(Stopping);
0226     return true;
0227 }
0228 
0229 void ServerManager::showSelfTestDialog(QWidget *parent)
0230 {
0231     Q_UNUSED(parent)
0232     const QString exec = QStandardPaths::findExecutable(QStringLiteral("akonadiselftest"));
0233     if (exec.isEmpty() || !QProcess::startDetached(exec, QStringList())) {
0234         qCWarning(AKONADICORE_LOG) << "Could not find akonadiselftest in PATH.";
0235     }
0236 }
0237 
0238 bool ServerManager::isRunning()
0239 {
0240     return state() == Running;
0241 }
0242 
0243 ServerManager::State ServerManager::state()
0244 {
0245     ServerManager::State previousState = NotRunning;
0246     if (sInstance.exists()) { // be careful, this is called from the ServerManager::Private ctor, so using sInstance unprotected can cause infinite recursion
0247         previousState = sInstance->mState;
0248         sInstance->mBrokenReason.clear();
0249     }
0250 
0251     const bool serverUpgrading = QDBusConnection::sessionBus().interface()->isServiceRegistered(ServerManager::serviceName(ServerManager::UpgradeIndicator));
0252     if (serverUpgrading) {
0253         return Upgrading;
0254     }
0255 
0256     const bool controlRegistered = QDBusConnection::sessionBus().interface()->isServiceRegistered(ServerManager::serviceName(ServerManager::Control));
0257     const bool serverRegistered = QDBusConnection::sessionBus().interface()->isServiceRegistered(ServerManager::serviceName(ServerManager::Server));
0258     if (controlRegistered && serverRegistered) {
0259         // check if the server protocol is recent enough
0260         if (sInstance.exists()) {
0261             if (Internal::serverProtocolVersion() >= 0 && Internal::serverProtocolVersion() != Protocol::version()) {
0262                 sInstance->mBrokenReason = i18n(
0263                     "The Akonadi server protocol version differs from the protocol version used by this application.\n"
0264                     "If you recently updated your system please log out and back in to make sure all applications use the "
0265                     "correct protocol version.");
0266                 return Broken;
0267             }
0268         }
0269 
0270         // AgentManager is dangerous to use for agents themselves
0271         if (Internal::clientType() == Internal::User) {
0272             // besides the running server processes we also need at least one resource to be operational
0273             const AgentType::List agentTypes = AgentManager::self()->types();
0274             for (const AgentType &type : agentTypes) {
0275                 if (type.capabilities().contains(QLatin1StringView("Resource"))) {
0276                     return Running;
0277                 }
0278             }
0279             if (sInstance.exists()) {
0280                 sInstance->mBrokenReason = i18n("There are no Akonadi Agents available. Please verify your KDE PIM installation.");
0281             }
0282             return Broken;
0283         } else {
0284             return Running;
0285         }
0286     }
0287 
0288     const bool controlLockRegistered = QDBusConnection::sessionBus().interface()->isServiceRegistered(ServerManager::serviceName(ServerManager::ControlLock));
0289     if (controlLockRegistered || controlRegistered) {
0290         qCDebug(AKONADICORE_LOG) << "Akonadi server is only partially running. Server:" << serverRegistered << "ControlLock:" << controlLockRegistered
0291                                  << "Control:" << controlRegistered;
0292         if (previousState == Running) {
0293             return NotRunning; // we don't know if it's starting or stopping, probably triggered by someone else
0294         }
0295         return previousState;
0296     }
0297 
0298     if (serverRegistered) {
0299         qCWarning(AKONADICORE_LOG) << "Akonadi server running without control process!";
0300         return Broken;
0301     }
0302 
0303     if (previousState == Starting) { // valid case where nothing is running (yet)
0304         return previousState;
0305     }
0306     return NotRunning;
0307 }
0308 
0309 QString ServerManager::brokenReason()
0310 {
0311     if (sInstance.exists()) {
0312         return sInstance->mBrokenReason;
0313     }
0314     return QString();
0315 }
0316 
0317 QString ServerManager::instanceIdentifier()
0318 {
0319     return Instance::identifier();
0320 }
0321 
0322 bool ServerManager::hasInstanceIdentifier()
0323 {
0324     return Instance::hasIdentifier();
0325 }
0326 
0327 QString ServerManager::serviceName(ServerManager::ServiceType serviceType)
0328 {
0329     switch (serviceType) {
0330     case Server:
0331         return DBus::serviceName(DBus::Server);
0332     case Control:
0333         return DBus::serviceName(DBus::Control);
0334     case ControlLock:
0335         return DBus::serviceName(DBus::ControlLock);
0336     case UpgradeIndicator:
0337         return DBus::serviceName(DBus::UpgradeIndicator);
0338     }
0339     Q_ASSERT(!"WTF?");
0340     return QString();
0341 }
0342 
0343 QString ServerManager::agentServiceName(ServiceAgentType agentType, const QString &identifier)
0344 {
0345     switch (agentType) {
0346     case Agent:
0347         return DBus::agentServiceName(identifier, DBus::Agent);
0348     case Resource:
0349         return DBus::agentServiceName(identifier, DBus::Resource);
0350     case Preprocessor:
0351         return DBus::agentServiceName(identifier, DBus::Preprocessor);
0352     }
0353     Q_ASSERT(!"WTF?");
0354     return QString();
0355 }
0356 
0357 QString ServerManager::serverConfigFilePath(OpenMode openMode)
0358 {
0359     return StandardDirs::serverConfigFile(openMode == Akonadi::ServerManager::ReadOnly ? StandardDirs::ReadOnly : StandardDirs::ReadWrite);
0360 }
0361 
0362 QString ServerManager::agentConfigFilePath(const QString &identifier)
0363 {
0364     return StandardDirs::agentConfigFile(identifier);
0365 }
0366 
0367 QString ServerManager::addNamespace(const QString &string)
0368 {
0369     if (Instance::hasIdentifier()) {
0370         return string % QLatin1Char('_') % Instance::identifier();
0371     }
0372     return string;
0373 }
0374 
0375 uint ServerManager::generation()
0376 {
0377     return Internal::generation();
0378 }
0379 
0380 int Internal::serverProtocolVersion()
0381 {
0382     return ServerManagerPrivate::serverProtocolVersion;
0383 }
0384 
0385 void Internal::setServerProtocolVersion(int version)
0386 {
0387     ServerManagerPrivate::serverProtocolVersion = version;
0388     if (sInstance.exists()) {
0389         sInstance->checkStatusChanged();
0390     }
0391 }
0392 
0393 uint Internal::generation()
0394 {
0395     return ServerManagerPrivate::generation;
0396 }
0397 
0398 void Internal::setGeneration(uint generation)
0399 {
0400     ServerManagerPrivate::generation = generation;
0401 }
0402 
0403 Internal::ClientType Internal::clientType()
0404 {
0405     return ServerManagerPrivate::clientType;
0406 }
0407 
0408 void Internal::setClientType(ClientType type)
0409 {
0410     ServerManagerPrivate::clientType = type;
0411 }
0412 
0413 #include "moc_servermanager.cpp"