File indexing completed on 2025-01-05 04:46:27
0001 /*************************************************************************** 0002 * SPDX-FileCopyrightText: 2006 Tobias Koenig <tokoe@kde.org> * 0003 * SPDX-FileCopyrightText: 2007 Volker Krause <vkrause@kde.org> * 0004 * * 0005 * SPDX-License-Identifier: LGPL-2.0-or-later * 0006 ***************************************************************************/ 0007 0008 #include "agentmanager.h" 0009 0010 #include "agentbrokeninstance.h" 0011 #include "agentmanageradaptor.h" 0012 #include "agentmanagerinternaladaptor.h" 0013 #include "agentprocessinstance.h" 0014 #include "agentserverinterface.h" 0015 #include "agentthreadinstance.h" 0016 #include "akonadicontrol_debug.h" 0017 #include "preprocessor_manager.h" 0018 #include "processcontrol.h" 0019 #include "resource_manager.h" 0020 #include "serverinterface.h" 0021 0022 #include "private/dbus_p.h" 0023 #include "private/instance_p.h" 0024 #include "private/protocol_p.h" 0025 #include "private/standarddirs_p.h" 0026 0027 #include "shared/akapplication.h" 0028 0029 #include <QCoreApplication> 0030 #include <QDBusConnection> 0031 #include <QDir> 0032 #include <QScopedPointer> 0033 #include <QSettings> 0034 0035 using Akonadi::ProcessControl; 0036 using namespace std::chrono_literals; 0037 0038 static const bool enableAgentServerDefault = false; 0039 0040 class StorageProcessControl : public Akonadi::ProcessControl 0041 { 0042 Q_OBJECT 0043 public: 0044 explicit StorageProcessControl(const QStringList &args) 0045 { 0046 setShutdownTimeout(15s); 0047 connect(this, &Akonadi::ProcessControl::unableToStart, this, []() { 0048 QCoreApplication::instance()->exit(255); 0049 }); 0050 start(QStringLiteral("akonadiserver"), args, RestartOnCrash); 0051 } 0052 0053 ~StorageProcessControl() override 0054 { 0055 setCrashPolicy(ProcessControl::StopOnCrash); 0056 org::freedesktop::Akonadi::Server serverIface(Akonadi::DBus::serviceName(Akonadi::DBus::Server), 0057 QStringLiteral("/Server"), 0058 QDBusConnection::sessionBus()); 0059 serverIface.quit(); 0060 } 0061 }; 0062 0063 class AgentServerProcessControl : public Akonadi::ProcessControl 0064 { 0065 Q_OBJECT 0066 public: 0067 explicit AgentServerProcessControl(const QStringList &args) 0068 { 0069 connect(this, &Akonadi::ProcessControl::unableToStart, this, []() { 0070 qCCritical(AKONADICONTROL_LOG) << "Failed to start AgentServer!"; 0071 }); 0072 start(QStringLiteral("akonadi_agent_server"), args, RestartOnCrash); 0073 } 0074 0075 ~AgentServerProcessControl() override 0076 { 0077 setCrashPolicy(ProcessControl::StopOnCrash); 0078 org::freedesktop::Akonadi::AgentServer agentServerIface(Akonadi::DBus::serviceName(Akonadi::DBus::AgentServer), 0079 QStringLiteral("/AgentServer"), 0080 QDBusConnection::sessionBus(), 0081 this); 0082 agentServerIface.quit(); 0083 } 0084 }; 0085 0086 AgentManager::AgentManager(bool verbose, QObject *parent) 0087 : QObject(parent) 0088 , mAgentServer(nullptr) 0089 , mVerbose(verbose) 0090 { 0091 new AgentManagerAdaptor(this); 0092 new AgentManagerInternalAdaptor(this); 0093 QDBusConnection::sessionBus().registerObject(QStringLiteral("/AgentManager"), this); 0094 0095 connect(QDBusConnection::sessionBus().interface(), &QDBusConnectionInterface::serviceOwnerChanged, this, &AgentManager::serviceOwnerChanged); 0096 0097 if (QDBusConnection::sessionBus().interface()->isServiceRegistered(Akonadi::DBus::serviceName(Akonadi::DBus::Server))) { 0098 qFatal("akonadiserver already running!"); 0099 } 0100 0101 const QSettings settings(Akonadi::StandardDirs::agentsConfigFile(Akonadi::StandardDirs::ReadOnly), QSettings::IniFormat); 0102 mAgentServerEnabled = settings.value(QStringLiteral("AgentServer/Enabled"), enableAgentServerDefault).toBool(); 0103 0104 QStringList serviceArgs; 0105 if (Akonadi::Instance::hasIdentifier()) { 0106 serviceArgs << QStringLiteral("--instance") << Akonadi::Instance::identifier(); 0107 } 0108 if (verbose) { 0109 serviceArgs << QStringLiteral("--verbose"); 0110 } 0111 0112 mStorageController = std::unique_ptr<Akonadi::ProcessControl>(new StorageProcessControl(serviceArgs)); 0113 0114 if (mAgentServerEnabled) { 0115 mAgentServer = std::unique_ptr<Akonadi::ProcessControl>(new AgentServerProcessControl(serviceArgs)); 0116 } 0117 } 0118 0119 void AgentManager::continueStartup() 0120 { 0121 // prevent multiple calls in case the server has to be restarted 0122 static bool first = true; 0123 if (!first) { 0124 return; 0125 } 0126 0127 first = false; 0128 0129 readPluginInfos(); 0130 for (const AgentType &info : std::as_const(mAgents)) { 0131 Q_EMIT agentTypeAdded(info.identifier); 0132 } 0133 0134 load(); 0135 for (const AgentType &info : std::as_const(mAgents)) { 0136 ensureAutoStart(info); 0137 } 0138 0139 // register the real service name once everything is up an running 0140 if (!QDBusConnection::sessionBus().registerService(Akonadi::DBus::serviceName(Akonadi::DBus::Control))) { 0141 // besides a race with an older Akonadi server I have no idea how we could possibly get here... 0142 qFatal("Unable to register service as %s despite having the lock. Error was: %s", 0143 qPrintable(Akonadi::DBus::serviceName(Akonadi::DBus::Control)), 0144 qPrintable(QDBusConnection::sessionBus().lastError().message())); 0145 } 0146 qCInfo(AKONADICONTROL_LOG) << "Akonadi server is now operational."; 0147 } 0148 0149 AgentManager::~AgentManager() 0150 { 0151 cleanup(); 0152 } 0153 0154 void AgentManager::cleanup() 0155 { 0156 for (const AgentInstance::Ptr &instance : std::as_const(mAgentInstances)) { 0157 instance->quit(); 0158 } 0159 mAgentInstances.clear(); 0160 0161 mStorageController.reset(); 0162 mStorageController.reset(); 0163 } 0164 0165 QStringList AgentManager::agentTypes() const 0166 { 0167 return mAgents.keys(); 0168 } 0169 0170 QString AgentManager::agentName(const QString &identifier) const 0171 { 0172 if (!checkAgentExists(identifier)) { 0173 return QString(); 0174 } 0175 0176 return mAgents.value(identifier).name; 0177 } 0178 0179 QString AgentManager::agentComment(const QString &identifier) const 0180 { 0181 if (!checkAgentExists(identifier)) { 0182 return QString(); 0183 } 0184 0185 return mAgents.value(identifier).comment; 0186 } 0187 0188 QString AgentManager::agentIcon(const QString &identifier) const 0189 { 0190 if (!checkAgentExists(identifier)) { 0191 return QString(); 0192 } 0193 0194 const AgentType info = mAgents.value(identifier); 0195 if (!info.icon.isEmpty()) { 0196 return info.icon; 0197 } 0198 0199 return QStringLiteral("application-x-executable"); 0200 } 0201 0202 QStringList AgentManager::agentMimeTypes(const QString &identifier) const 0203 { 0204 if (!checkAgentExists(identifier)) { 0205 return QStringList(); 0206 } 0207 0208 return mAgents.value(identifier).mimeTypes; 0209 } 0210 0211 QStringList AgentManager::agentCapabilities(const QString &identifier) const 0212 { 0213 if (!checkAgentExists(identifier)) { 0214 return QStringList(); 0215 } 0216 return mAgents.value(identifier).capabilities; 0217 } 0218 0219 QVariantMap AgentManager::agentCustomProperties(const QString &identifier) const 0220 { 0221 if (!checkAgentExists(identifier)) { 0222 return QVariantMap(); 0223 } 0224 0225 return mAgents.value(identifier).custom; 0226 } 0227 0228 AgentInstance::Ptr AgentManager::createAgentInstance(const AgentType &info) 0229 { 0230 switch (info.launchMethod) { 0231 case AgentType::Server: 0232 return QSharedPointer<Akonadi::AgentThreadInstance>::create(*this); 0233 case AgentType::Launcher: // Fall through 0234 case AgentType::Process: 0235 return QSharedPointer<Akonadi::AgentProcessInstance>::create(*this); 0236 default: 0237 Q_ASSERT_X(false, "AgentManger::createAgentInstance", "Unhandled AgentType::LaunchMethod case"); 0238 } 0239 0240 return AgentInstance::Ptr(); 0241 } 0242 0243 QString AgentManager::createAgentInstance(const QString &identifier) 0244 { 0245 if (!checkAgentExists(identifier)) { 0246 return QString(); 0247 } 0248 0249 const AgentType agentInfo = mAgents[identifier]; 0250 mAgents[identifier].instanceCounter++; 0251 0252 const auto instance = createAgentInstance(agentInfo); 0253 if (agentInfo.capabilities.contains(AgentType::CapabilityUnique)) { 0254 instance->setIdentifier(identifier); 0255 } else { 0256 instance->setIdentifier(QStringLiteral("%1_%2").arg(identifier, QString::number(agentInfo.instanceCounter))); 0257 } 0258 0259 const QString instanceIdentifier = instance->identifier(); 0260 if (mAgentInstances.contains(instanceIdentifier)) { 0261 qCWarning(AKONADICONTROL_LOG) << "Cannot create another instance of agent" << identifier; 0262 return QString(); 0263 } 0264 0265 // Return from this dbus call before we do the next. Otherwise dbus brakes for 0266 // this process. 0267 if (calledFromDBus()) { 0268 connection().send(message().createReply(instanceIdentifier)); 0269 } 0270 0271 if (!instance->start(agentInfo)) { 0272 return QString(); 0273 } 0274 0275 mAgentInstances.insert(instanceIdentifier, instance); 0276 registerAgentAtServer(instanceIdentifier, agentInfo); 0277 save(); 0278 0279 return instanceIdentifier; 0280 } 0281 0282 void AgentManager::removeAgentInstance(const QString &identifier) 0283 { 0284 const auto instance = mAgentInstances.value(identifier); 0285 if (!instance) { 0286 qCWarning(AKONADICONTROL_LOG) << Q_FUNC_INFO << "Agent instance with identifier" << identifier << "does not exist"; 0287 return; 0288 } 0289 0290 if (instance->hasAgentInterface()) { 0291 instance->cleanup(); 0292 } else { 0293 qCWarning(AKONADICONTROL_LOG) << "Agent instance" << identifier << "has no interface!"; 0294 } 0295 0296 mAgentInstances.remove(identifier); 0297 0298 save(); 0299 0300 org::freedesktop::Akonadi::ResourceManager resmanager(Akonadi::DBus::serviceName(Akonadi::DBus::Server), 0301 QStringLiteral("/ResourceManager"), 0302 QDBusConnection::sessionBus(), 0303 this); 0304 resmanager.removeResourceInstance(instance->identifier()); 0305 0306 // Kill the preprocessor instance, if any. 0307 org::freedesktop::Akonadi::PreprocessorManager preProcessorManager(Akonadi::DBus::serviceName(Akonadi::DBus::Server), 0308 QStringLiteral("/PreprocessorManager"), 0309 QDBusConnection::sessionBus(), 0310 this); 0311 0312 preProcessorManager.unregisterInstance(instance->identifier()); 0313 0314 if (instance->hasAgentInterface()) { 0315 qCDebug(AKONADICONTROL_LOG) << "AgentManager::removeAgentInstance: calling instance->quit()"; 0316 instance->quit(); 0317 } else { 0318 qCWarning(AKONADICONTROL_LOG) << "Agent instance" << identifier << "has no interface!"; 0319 } 0320 0321 Q_EMIT agentInstanceRemoved(identifier); 0322 } 0323 0324 QString AgentManager::agentInstanceType(const QString &identifier) 0325 { 0326 const AgentInstance::Ptr agent = mAgentInstances.value(identifier); 0327 if (!agent) { 0328 qCWarning(AKONADICONTROL_LOG) << "Agent instance with identifier" << identifier << "does not exist"; 0329 return QString(); 0330 } 0331 0332 return agent->agentType(); 0333 } 0334 0335 QStringList AgentManager::agentInstances() const 0336 { 0337 return mAgentInstances.keys(); 0338 } 0339 0340 int AgentManager::agentInstanceStatus(const QString &identifier) const 0341 { 0342 if (!checkInstance(identifier)) { 0343 return 2; 0344 } 0345 0346 return mAgentInstances.value(identifier)->status(); 0347 } 0348 0349 QString AgentManager::agentInstanceStatusMessage(const QString &identifier) const 0350 { 0351 if (!checkInstance(identifier)) { 0352 return QString(); 0353 } 0354 0355 return mAgentInstances.value(identifier)->statusMessage(); 0356 } 0357 0358 uint AgentManager::agentInstanceProgress(const QString &identifier) const 0359 { 0360 if (!checkInstance(identifier)) { 0361 return 0; 0362 } 0363 0364 return mAgentInstances.value(identifier)->progress(); 0365 } 0366 0367 QString AgentManager::agentInstanceProgressMessage(const QString &identifier) const 0368 { 0369 Q_UNUSED(identifier) 0370 0371 return QString(); 0372 } 0373 0374 void AgentManager::agentInstanceConfigure(const QString &identifier, qlonglong windowId) 0375 { 0376 if (!checkAgentInterfaces(identifier, QStringLiteral("agentInstanceConfigure"))) { 0377 return; 0378 } 0379 0380 mAgentInstances.value(identifier)->configure(windowId); 0381 } 0382 0383 bool AgentManager::agentInstanceOnline(const QString &identifier) 0384 { 0385 if (!checkInstance(identifier)) { 0386 return false; 0387 } 0388 0389 return mAgentInstances.value(identifier)->isOnline(); 0390 } 0391 0392 void AgentManager::setAgentInstanceOnline(const QString &identifier, bool state) 0393 { 0394 if (!checkAgentInterfaces(identifier, QStringLiteral("setAgentInstanceOnline"))) { 0395 return; 0396 } 0397 0398 mAgentInstances.value(identifier)->statusInterface()->setOnline(state); 0399 } 0400 0401 // resource specific methods // 0402 void AgentManager::setAgentInstanceName(const QString &identifier, const QString &name) 0403 { 0404 if (!checkResourceInterface(identifier, QStringLiteral("setAgentInstanceName"))) { 0405 return; 0406 } 0407 0408 mAgentInstances.value(identifier)->resourceInterface()->setName(name); 0409 } 0410 0411 QString AgentManager::agentInstanceName(const QString &identifier) const 0412 { 0413 if (!checkInstance(identifier)) { 0414 return QString(); 0415 } 0416 0417 const AgentInstance::Ptr instance = mAgentInstances.value(identifier); 0418 if (!instance->resourceName().isEmpty()) { 0419 return instance->resourceName(); 0420 } 0421 0422 if (!checkAgentExists(instance->agentType())) { 0423 return QString(); 0424 } 0425 0426 return mAgents.value(instance->agentType()).name; 0427 } 0428 0429 void AgentManager::agentInstanceSynchronize(const QString &identifier) 0430 { 0431 if (!checkResourceInterface(identifier, QStringLiteral("agentInstanceSynchronize"))) { 0432 return; 0433 } 0434 0435 mAgentInstances.value(identifier)->resourceInterface()->synchronize(); 0436 } 0437 0438 void AgentManager::agentInstanceSynchronizeCollectionTree(const QString &identifier) 0439 { 0440 if (!checkResourceInterface(identifier, QStringLiteral("agentInstanceSynchronizeCollectionTree"))) { 0441 return; 0442 } 0443 0444 mAgentInstances.value(identifier)->resourceInterface()->synchronizeCollectionTree(); 0445 } 0446 0447 void AgentManager::agentInstanceSynchronizeCollection(const QString &identifier, qint64 collection) 0448 { 0449 agentInstanceSynchronizeCollection(identifier, collection, false); 0450 } 0451 0452 void AgentManager::agentInstanceSynchronizeCollection(const QString &identifier, qint64 collection, bool recursive) 0453 { 0454 if (!checkResourceInterface(identifier, QStringLiteral("agentInstanceSynchronizeCollection"))) { 0455 return; 0456 } 0457 0458 mAgentInstances.value(identifier)->resourceInterface()->synchronizeCollection(collection, recursive); 0459 } 0460 0461 void AgentManager::agentInstanceSynchronizeTags(const QString &identifier) 0462 { 0463 if (!checkResourceInterface(identifier, QStringLiteral("agentInstanceSynchronizeTags"))) { 0464 return; 0465 } 0466 0467 mAgentInstances.value(identifier)->resourceInterface()->synchronizeTags(); 0468 } 0469 0470 void AgentManager::agentInstanceSynchronizeRelations(const QString &identifier) 0471 { 0472 if (!checkResourceInterface(identifier, QStringLiteral("agentInstanceSynchronizeRelations"))) { 0473 return; 0474 } 0475 0476 mAgentInstances.value(identifier)->resourceInterface()->synchronizeRelations(); 0477 } 0478 0479 void AgentManager::restartAgentInstance(const QString &identifier) 0480 { 0481 if (!checkInstance(identifier)) { 0482 return; 0483 } 0484 0485 mAgentInstances.value(identifier)->restartWhenIdle(); 0486 } 0487 0488 void AgentManager::updatePluginInfos() 0489 { 0490 const QHash<QString, AgentType> oldInfos = mAgents; 0491 readPluginInfos(); 0492 0493 for (const AgentType &oldInfo : oldInfos) { 0494 if (!mAgents.contains(oldInfo.identifier)) { 0495 Q_EMIT agentTypeRemoved(oldInfo.identifier); 0496 } 0497 } 0498 0499 for (const AgentType &newInfo : std::as_const(mAgents)) { 0500 if (!oldInfos.contains(newInfo.identifier)) { 0501 Q_EMIT agentTypeAdded(newInfo.identifier); 0502 ensureAutoStart(newInfo); 0503 } 0504 } 0505 } 0506 0507 void AgentManager::readPluginInfos() 0508 { 0509 mAgents.clear(); 0510 0511 const QStringList pathList = pluginInfoPathList(); 0512 0513 for (const QString &path : pathList) { 0514 const QDir directory(path, QStringLiteral("*.desktop")); 0515 readPluginInfos(directory); 0516 } 0517 } 0518 0519 void AgentManager::readPluginInfos(const QDir &directory) 0520 { 0521 const QStringList files = directory.entryList(); 0522 qCDebug(AKONADICONTROL_LOG) << "PLUGINS: " << directory.canonicalPath(); 0523 qCDebug(AKONADICONTROL_LOG) << "PLUGINS: " << files; 0524 0525 for (int i = 0; i < files.count(); ++i) { 0526 const QString fileName = directory.absoluteFilePath(files[i]); 0527 0528 AgentType agentInfo; 0529 if (agentInfo.load(fileName, this)) { 0530 if (mAgents.contains(agentInfo.identifier)) { 0531 qCWarning(AKONADICONTROL_LOG) << "Duplicated agent identifier" << agentInfo.identifier << "from file" << fileName; 0532 continue; 0533 } 0534 0535 const QString disableAutostart = akGetEnv("AKONADI_DISABLE_AGENT_AUTOSTART"); 0536 if (!disableAutostart.isEmpty()) { 0537 qCDebug(AKONADICONTROL_LOG) << "Autostarting of agents is disabled."; 0538 agentInfo.capabilities.removeOne(AgentType::CapabilityAutostart); 0539 } 0540 0541 if (!mAgentServerEnabled && agentInfo.launchMethod == AgentType::Server) { 0542 agentInfo.launchMethod = AgentType::Launcher; 0543 } 0544 0545 if (agentInfo.launchMethod == AgentType::Process) { 0546 const QString executable = Akonadi::StandardDirs::findExecutable(agentInfo.exec); 0547 if (executable.isEmpty()) { 0548 qCWarning(AKONADICONTROL_LOG) << "Executable" << agentInfo.exec << "for agent" << agentInfo.identifier << "could not be found!"; 0549 continue; 0550 } 0551 } 0552 0553 qCDebug(AKONADICONTROL_LOG) << "PLUGINS inserting: " << agentInfo.identifier << agentInfo.instanceCounter << agentInfo.capabilities; 0554 mAgents.insert(agentInfo.identifier, agentInfo); 0555 } 0556 } 0557 } 0558 0559 QStringList AgentManager::pluginInfoPathList() 0560 { 0561 return Akonadi::StandardDirs::locateAllResourceDirs(QStringLiteral("akonadi/agents")); 0562 } 0563 0564 void AgentManager::load() 0565 { 0566 org::freedesktop::Akonadi::ResourceManager resmanager(Akonadi::DBus::serviceName(Akonadi::DBus::Server), 0567 QStringLiteral("/ResourceManager"), 0568 QDBusConnection::sessionBus(), 0569 this); 0570 const QStringList knownResources = resmanager.resourceInstances(); 0571 0572 QSettings file(Akonadi::StandardDirs::agentsConfigFile(Akonadi::StandardDirs::ReadOnly), QSettings::IniFormat); 0573 file.beginGroup(QStringLiteral("Instances")); 0574 const QStringList entries = file.childGroups(); 0575 for (int i = 0; i < entries.count(); ++i) { 0576 const QString instanceIdentifier = entries[i]; 0577 0578 if (mAgentInstances.contains(instanceIdentifier)) { 0579 qCWarning(AKONADICONTROL_LOG) << "Duplicated instance identifier" << instanceIdentifier << "found in agentsrc"; 0580 continue; 0581 } 0582 0583 file.beginGroup(entries[i]); 0584 0585 const QString agentType = file.value(QStringLiteral("AgentType")).toString(); 0586 const auto typeIter = mAgents.constFind(agentType); 0587 if (typeIter == mAgents.cend() || typeIter->exec.isEmpty()) { 0588 qCWarning(AKONADICONTROL_LOG) << "Reference to unknown agent type" << agentType << "in agentsrc, creating a fake entry."; 0589 if (typeIter == mAgents.cend()) { 0590 AgentType type; 0591 type.identifier = type.name = agentType; 0592 mAgents.insert(type.identifier, type); 0593 } 0594 0595 auto brokenInstance = AgentInstance::Ptr{new Akonadi::AgentBrokenInstance{agentType, *this}}; 0596 brokenInstance->setIdentifier(instanceIdentifier); 0597 mAgentInstances.insert(instanceIdentifier, brokenInstance); 0598 file.endGroup(); 0599 continue; 0600 } 0601 0602 const AgentType &type = *typeIter; 0603 0604 // recover if the db has been deleted in the meantime or got otherwise corrupted 0605 if (!knownResources.contains(instanceIdentifier) && type.capabilities.contains(AgentType::CapabilityResource)) { 0606 qCDebug(AKONADICONTROL_LOG) << "Recovering instance" << instanceIdentifier << "after database loss"; 0607 registerAgentAtServer(instanceIdentifier, type); 0608 } 0609 0610 const AgentInstance::Ptr instance = createAgentInstance(type); 0611 instance->setIdentifier(instanceIdentifier); 0612 if (instance->start(type)) { 0613 mAgentInstances.insert(instanceIdentifier, instance); 0614 } 0615 0616 file.endGroup(); 0617 } 0618 0619 file.endGroup(); 0620 } 0621 0622 void AgentManager::save() 0623 { 0624 QSettings file(Akonadi::StandardDirs::agentsConfigFile(Akonadi::StandardDirs::WriteOnly), QSettings::IniFormat); 0625 0626 for (const AgentType &info : std::as_const(mAgents)) { 0627 info.save(&file); 0628 } 0629 0630 file.beginGroup(QStringLiteral("Instances")); 0631 file.remove(QString()); 0632 for (const AgentInstance::Ptr &instance : std::as_const(mAgentInstances)) { 0633 file.beginGroup(instance->identifier()); 0634 file.setValue(QStringLiteral("AgentType"), instance->agentType()); 0635 file.endGroup(); 0636 } 0637 0638 file.endGroup(); 0639 } 0640 0641 void AgentManager::serviceOwnerChanged(const QString &name, const QString &oldOwner, const QString &newOwner) 0642 { 0643 Q_UNUSED(oldOwner) 0644 // This is called by the D-Bus server when a service comes up, goes down or changes ownership for some reason 0645 // and this is where we "hook up" our different Agent interfaces. 0646 0647 // Ignore DBus address name (e.g. :1.310) 0648 if (name.startsWith(QLatin1Char(':'))) { 0649 return; 0650 } 0651 0652 // Ignore services belonging to another Akonadi instance 0653 const auto parsedInstance = Akonadi::DBus::parseInstanceIdentifier(name); 0654 const auto currentInstance = Akonadi::Instance::hasIdentifier() ? std::optional<QString>(Akonadi::Instance::identifier()) : std::nullopt; 0655 if (parsedInstance != currentInstance) { 0656 return; 0657 } 0658 0659 qCDebug(AKONADICONTROL_LOG) << "Service" << name << "owner changed from" << oldOwner << "to" << newOwner; 0660 0661 if ((name == Akonadi::DBus::serviceName(Akonadi::DBus::Server) || name == Akonadi::DBus::serviceName(Akonadi::DBus::AgentServer)) && !newOwner.isEmpty()) { 0662 if (QDBusConnection::sessionBus().interface()->isServiceRegistered(Akonadi::DBus::serviceName(Akonadi::DBus::Server)) 0663 && (!mAgentServer || QDBusConnection::sessionBus().interface()->isServiceRegistered(Akonadi::DBus::serviceName(Akonadi::DBus::AgentServer)))) { 0664 // server is operational, start agents 0665 continueStartup(); 0666 } 0667 } 0668 0669 const auto service = Akonadi::DBus::parseAgentServiceName(name); 0670 if (!service.has_value()) { 0671 return; 0672 } 0673 switch (service->agentType) { 0674 case Akonadi::DBus::Agent: { 0675 // An agent service went up or down 0676 if (newOwner.isEmpty()) { 0677 return; // It went down: we don't care here. 0678 } 0679 0680 if (!mAgentInstances.contains(service->identifier)) { 0681 return; 0682 } 0683 0684 const AgentInstance::Ptr instance = mAgentInstances.value(service->identifier); 0685 const bool restarting = instance->hasAgentInterface(); 0686 if (!instance->obtainAgentInterface()) { 0687 return; 0688 } 0689 0690 Q_ASSERT(mAgents.contains(instance->agentType())); 0691 const bool isResource = mAgents.value(instance->agentType()).capabilities.contains(AgentType::CapabilityResource); 0692 0693 if (!restarting && (!isResource || instance->hasResourceInterface())) { 0694 Q_EMIT agentInstanceAdded(service->identifier); 0695 } 0696 0697 break; 0698 } 0699 case Akonadi::DBus::Resource: { 0700 // A resource service went up or down 0701 if (newOwner.isEmpty()) { 0702 return; // It went down: we don't care here. 0703 } 0704 0705 if (!mAgentInstances.contains(service->identifier)) { 0706 return; 0707 } 0708 0709 const AgentInstance::Ptr instance = mAgentInstances.value(service->identifier); 0710 const bool restarting = instance->hasResourceInterface(); 0711 if (!instance->obtainResourceInterface()) { 0712 return; 0713 } 0714 0715 if (!restarting && instance->hasAgentInterface()) { 0716 Q_EMIT agentInstanceAdded(service->identifier); 0717 } 0718 0719 break; 0720 } 0721 case Akonadi::DBus::Preprocessor: { 0722 // A preprocessor service went up or down 0723 0724 // If the preprocessor is going up then the org.freedesktop.Akonadi.Agent.* interface 0725 // should be already up (as it's registered before the preprocessor one). 0726 // So if we don't know about the preprocessor as agent instance 0727 // then it's not our preprocessor. 0728 0729 // If the preprocessor is going down then either the agent interface already 0730 // went down (and it has been already unregistered on the manager side) 0731 // or it's still registered as agent and WE have to unregister it. 0732 // The order of interface deletions depends on Qt but we handle both cases. 0733 0734 // Check if we "know" about it. 0735 qCDebug(AKONADICONTROL_LOG) << "Preprocessor " << service->identifier << " is going up or down..."; 0736 0737 if (!mAgentInstances.contains(service->identifier)) { 0738 qCDebug(AKONADICONTROL_LOG) << "But it isn't registered as agent... not mine (anymore?)"; 0739 return; // not our agent (?) 0740 } 0741 0742 org::freedesktop::Akonadi::PreprocessorManager preProcessorManager(Akonadi::DBus::serviceName(Akonadi::DBus::Server), 0743 QStringLiteral("/PreprocessorManager"), 0744 QDBusConnection::sessionBus(), 0745 this); 0746 0747 if (!preProcessorManager.isValid()) { 0748 qCWarning(AKONADICONTROL_LOG) << "Could not connect to PreprocessorManager via D-Bus:" << preProcessorManager.lastError().message(); 0749 } else { 0750 if (newOwner.isEmpty()) { 0751 // The preprocessor went down. Unregister it on server side. 0752 0753 preProcessorManager.unregisterInstance(service->identifier); 0754 0755 } else { 0756 // The preprocessor went up. Register it on server side. 0757 0758 if (!mAgentInstances.value(service->identifier)->obtainPreprocessorInterface()) { 0759 // Hm.. couldn't hook up its preprocessor interface.. 0760 // Make sure we don't have it in the preprocessor chain 0761 qCWarning(AKONADICONTROL_LOG) << "Couldn't obtain preprocessor interface for instance" << service->identifier; 0762 0763 preProcessorManager.unregisterInstance(service->identifier); 0764 return; 0765 } 0766 0767 qCDebug(AKONADICONTROL_LOG) << "Registering preprocessor instance" << service->identifier; 0768 0769 // Add to the preprocessor chain 0770 preProcessorManager.registerInstance(service->identifier); 0771 } 0772 } 0773 0774 break; 0775 } 0776 default: 0777 break; 0778 } 0779 } 0780 0781 bool AgentManager::checkInstance(const QString &identifier) const 0782 { 0783 if (!mAgentInstances.contains(identifier)) { 0784 qCWarning(AKONADICONTROL_LOG) << "Agent instance with identifier " << identifier << " does not exist"; 0785 return false; 0786 } 0787 0788 return true; 0789 } 0790 0791 bool AgentManager::checkResourceInterface(const QString &identifier, const QString &method) const 0792 { 0793 if (!checkInstance(identifier)) { 0794 return false; 0795 } 0796 0797 if (!mAgents[mAgentInstances[identifier]->agentType()].capabilities.contains(QLatin1StringView("Resource"))) { 0798 return false; 0799 } 0800 0801 if (!mAgentInstances[identifier]->hasResourceInterface()) { 0802 qCWarning(AKONADICONTROL_LOG) << QLatin1StringView("AgentManager::") + method << " Agent instance " << identifier << " has no resource interface!"; 0803 return false; 0804 } 0805 0806 return true; 0807 } 0808 0809 bool AgentManager::checkAgentExists(const QString &identifier) const 0810 { 0811 if (!mAgents.contains(identifier)) { 0812 qCWarning(AKONADICONTROL_LOG) << "Agent instance " << identifier << " does not exist."; 0813 return false; 0814 } 0815 0816 return true; 0817 } 0818 0819 bool AgentManager::checkAgentInterfaces(const QString &identifier, const QString &method) const 0820 { 0821 if (!checkInstance(identifier)) { 0822 return false; 0823 } 0824 0825 if (!mAgentInstances.value(identifier)->hasAgentInterface()) { 0826 qCWarning(AKONADICONTROL_LOG) << "Agent instance (" << method << ") " << identifier << " has no agent interface."; 0827 return false; 0828 } 0829 0830 return true; 0831 } 0832 0833 void AgentManager::ensureAutoStart(const AgentType &info) 0834 { 0835 if (!info.capabilities.contains(AgentType::CapabilityAutostart)) { 0836 return; // no an autostart agent 0837 } 0838 0839 org::freedesktop::Akonadi::AgentServer agentServer(Akonadi::DBus::serviceName(Akonadi::DBus::AgentServer), 0840 QStringLiteral("/AgentServer"), 0841 QDBusConnection::sessionBus(), 0842 this); 0843 0844 if (mAgentInstances.contains(info.identifier) || (agentServer.isValid() && agentServer.started(info.identifier))) { 0845 return; // already running 0846 } 0847 0848 const AgentInstance::Ptr instance = createAgentInstance(info); 0849 instance->setIdentifier(info.identifier); 0850 if (instance->start(info)) { 0851 mAgentInstances.insert(instance->identifier(), instance); 0852 registerAgentAtServer(instance->identifier(), info); 0853 save(); 0854 } 0855 } 0856 0857 void AgentManager::agentExeChanged(const QString &fileName) 0858 { 0859 if (!QFile::exists(fileName)) { 0860 return; 0861 } 0862 0863 for (const AgentType &type : std::as_const(mAgents)) { 0864 if (fileName.endsWith(type.exec)) { 0865 for (const AgentInstance::Ptr &instance : std::as_const(mAgentInstances)) { 0866 if (instance->agentType() == type.identifier) { 0867 instance->restartWhenIdle(); 0868 } 0869 } 0870 } 0871 } 0872 } 0873 0874 void AgentManager::registerAgentAtServer(const QString &agentIdentifier, const AgentType &type) 0875 { 0876 if (type.capabilities.contains(AgentType::CapabilityResource)) { 0877 QScopedPointer<org::freedesktop::Akonadi::ResourceManager> resmanager( 0878 new org::freedesktop::Akonadi::ResourceManager(Akonadi::DBus::serviceName(Akonadi::DBus::Server), 0879 QStringLiteral("/ResourceManager"), 0880 QDBusConnection::sessionBus(), 0881 this)); 0882 resmanager->addResourceInstance(agentIdentifier, type.capabilities); 0883 } 0884 } 0885 0886 void AgentManager::addSearch(const QString &query, const QString &queryLanguage, qint64 resultCollectionId) 0887 { 0888 qCDebug(AKONADICONTROL_LOG) << "AgentManager::addSearch" << query << queryLanguage << resultCollectionId; 0889 for (const AgentInstance::Ptr &instance : std::as_const(mAgentInstances)) { 0890 const AgentType type = mAgents.value(instance->agentType()); 0891 if (type.capabilities.contains(AgentType::CapabilitySearch) && instance->searchInterface()) { 0892 instance->searchInterface()->addSearch(query, queryLanguage, resultCollectionId); 0893 } 0894 } 0895 } 0896 0897 void AgentManager::removeSearch(quint64 resultCollectionId) 0898 { 0899 qCDebug(AKONADICONTROL_LOG) << "AgentManager::removeSearch" << resultCollectionId; 0900 for (const AgentInstance::Ptr &instance : std::as_const(mAgentInstances)) { 0901 const AgentType type = mAgents.value(instance->agentType()); 0902 if (type.capabilities.contains(AgentType::CapabilitySearch) && instance->searchInterface()) { 0903 instance->searchInterface()->removeSearch(resultCollectionId); 0904 } 0905 } 0906 } 0907 0908 #include "agentmanager.moc" 0909 0910 #include "moc_agentmanager.cpp"