Akonadi

akonadicontrol/agentmanager.cpp
1/***************************************************************************
2 * SPDX-FileCopyrightText: 2006 Tobias Koenig <tokoe@kde.org> *
3 * SPDX-FileCopyrightText: 2007 Volker Krause <vkrause@kde.org> *
4 * *
5 * SPDX-License-Identifier: LGPL-2.0-or-later *
6 ***************************************************************************/
7
8#include "agentmanager.h"
9
10#include "agentbrokeninstance.h"
11#include "agentmanageradaptor.h"
12#include "agentmanagerinternaladaptor.h"
13#include "agentprocessinstance.h"
14#include "agentserverinterface.h"
15#include "agentthreadinstance.h"
16#include "akonadicontrol_debug.h"
17#include "preprocessor_manager.h"
18#include "processcontrol.h"
19#include "resource_manager.h"
20#include "serverinterface.h"
21
22#include "private/dbus_p.h"
23#include "private/instance_p.h"
24#include "private/protocol_p.h"
25#include "private/standarddirs_p.h"
26
27#include "shared/akapplication.h"
28
29#include <QCoreApplication>
30#include <QDBusConnection>
31#include <QDir>
32#include <QScopedPointer>
33#include <QSettings>
34
36using namespace std::chrono_literals;
37
38static const bool enableAgentServerDefault = false;
39
40// Here lie the once mighty resources, now deprecated and voided. They battled
41// valiantly against crashes and segfaults, and now, they can finally enjoy
42// their eternal null pointers. We'll miss you (not), but let's face it,
43// your stacks have overflown a long time ago. Farewell, old code comrades and
44// may your functions forever return void!
45//
46// Those are agent types that akonadicontrol can safely remove if it finds any leftover
47// instances of them configured in the agentsrc file or on Akonadi Server.
48static const QStringView retiredAgentTypes[] = {
49 u"akonadi_kolab_resource",
50 u"akonadi_akonotes_resources",
51 u"akonadi_notes_resource",
52};
53
54class StorageProcessControl : public Akonadi::ProcessControl
55{
57public:
58 explicit StorageProcessControl(const QStringList &args)
59 {
63 });
64 start(QStringLiteral("akonadiserver"), args, RestartOnCrash);
65 }
66
67 ~StorageProcessControl() override
68 {
69 setCrashPolicy(ProcessControl::StopOnCrash);
70 org::freedesktop::Akonadi::Server serverIface(Akonadi::DBus::serviceName(Akonadi::DBus::Server),
71 QStringLiteral("/Server"),
73 serverIface.quit();
74 }
75};
76
77class AgentServerProcessControl : public Akonadi::ProcessControl
78{
80public:
81 explicit AgentServerProcessControl(const QStringList &args)
82 {
84 qCCritical(AKONADICONTROL_LOG) << "Failed to start AgentServer!";
85 });
86 start(QStringLiteral("akonadi_agent_server"), args, RestartOnCrash);
87 }
88
89 ~AgentServerProcessControl() override
90 {
91 setCrashPolicy(ProcessControl::StopOnCrash);
92 org::freedesktop::Akonadi::AgentServer agentServerIface(Akonadi::DBus::serviceName(Akonadi::DBus::AgentServer),
93 QStringLiteral("/AgentServer"),
95 this);
96 agentServerIface.quit();
97 }
98};
99
101 : QObject(parent)
102 , mAgentServer(nullptr)
103 , mVerbose(verbose)
104{
105 new AgentManagerAdaptor(this);
106 new AgentManagerInternalAdaptor(this);
107 QDBusConnection::sessionBus().registerObject(QStringLiteral("/AgentManager"), this);
108
109 connect(QDBusConnection::sessionBus().interface(), &QDBusConnectionInterface::serviceOwnerChanged, this, &AgentManager::serviceOwnerChanged);
110
111 if (QDBusConnection::sessionBus().interface()->isServiceRegistered(Akonadi::DBus::serviceName(Akonadi::DBus::Server))) {
112 qFatal("akonadiserver already running!");
113 }
114
115 const QSettings settings(Akonadi::StandardDirs::agentsConfigFile(Akonadi::StandardDirs::ReadOnly), QSettings::IniFormat);
116 mAgentServerEnabled = settings.value(QStringLiteral("AgentServer/Enabled"), enableAgentServerDefault).toBool();
117
118 QStringList serviceArgs;
119 if (Akonadi::Instance::hasIdentifier()) {
120 serviceArgs << QStringLiteral("--instance") << Akonadi::Instance::identifier();
121 }
122 if (verbose) {
123 serviceArgs << QStringLiteral("--verbose");
124 }
125
126 mStorageController = std::unique_ptr<Akonadi::ProcessControl>(new StorageProcessControl(serviceArgs));
127
128 if (mAgentServerEnabled) {
129 mAgentServer = std::unique_ptr<Akonadi::ProcessControl>(new AgentServerProcessControl(serviceArgs));
130 }
131}
132
133void AgentManager::continueStartup()
134{
135 // prevent multiple calls in case the server has to be restarted
136 static bool first = true;
137 if (!first) {
138 return;
139 }
140
141 first = false;
142
143 readPluginInfos();
144 for (const AgentType &info : std::as_const(mAgents)) {
145 Q_EMIT agentTypeAdded(info.identifier);
146 }
147
148 load();
149 for (const AgentType &info : std::as_const(mAgents)) {
150 ensureAutoStart(info);
151 }
152
153 // register the real service name once everything is up an running
154 if (!QDBusConnection::sessionBus().registerService(Akonadi::DBus::serviceName(Akonadi::DBus::Control))) {
155 // besides a race with an older Akonadi server I have no idea how we could possibly get here...
156 qFatal("Unable to register service as %s despite having the lock. Error was: %s",
157 qPrintable(Akonadi::DBus::serviceName(Akonadi::DBus::Control)),
158 qPrintable(QDBusConnection::sessionBus().lastError().message()));
159 }
160 qCInfo(AKONADICONTROL_LOG) << "Akonadi server is now operational.";
161}
162
167
169{
170 for (const AgentInstance::Ptr &instance : std::as_const(mAgentInstances)) {
171 instance->quit();
172 }
173 mAgentInstances.clear();
174
175 mStorageController.reset();
176 mStorageController.reset();
177}
178
180{
181 return mAgents.keys();
182}
183
185{
186 if (!checkAgentExists(identifier)) {
187 return QString();
188 }
189
190 return mAgents.value(identifier).name;
191}
192
194{
195 if (!checkAgentExists(identifier)) {
196 return QString();
197 }
198
199 return mAgents.value(identifier).comment;
200}
201
203{
204 if (!checkAgentExists(identifier)) {
205 return QString();
206 }
207
208 const AgentType info = mAgents.value(identifier);
209 if (!info.icon.isEmpty()) {
210 return info.icon;
211 }
212
213 return QStringLiteral("application-x-executable");
214}
215
217{
218 if (!checkAgentExists(identifier)) {
219 return QStringList();
220 }
221
222 return mAgents.value(identifier).mimeTypes;
223}
224
226{
227 if (!checkAgentExists(identifier)) {
228 return QStringList();
229 }
230 return mAgents.value(identifier).capabilities;
231}
232
233QVariantMap AgentManager::agentCustomProperties(const QString &identifier) const
234{
235 if (!checkAgentExists(identifier)) {
236 return QVariantMap();
237 }
238
239 return mAgents.value(identifier).custom;
240}
241
242AgentInstance::Ptr AgentManager::createAgentInstance(const AgentType &info)
243{
244 switch (info.launchMethod) {
245 case AgentType::Server:
247 case AgentType::Launcher: // Fall through
248 case AgentType::Process:
250 default:
251 Q_ASSERT_X(false, "AgentManger::createAgentInstance", "Unhandled AgentType::LaunchMethod case");
252 }
253
254 return AgentInstance::Ptr();
255}
256
258{
259 if (!checkAgentExists(identifier)) {
260 return QString();
261 }
262
263 const AgentType agentInfo = mAgents[identifier];
264 mAgents[identifier].instanceCounter++;
265
266 const auto instance = createAgentInstance(agentInfo);
267 if (agentInfo.capabilities.contains(AgentType::CapabilityUnique)) {
268 instance->setIdentifier(identifier);
269 } else {
270 instance->setIdentifier(QStringLiteral("%1_%2").arg(identifier, QString::number(agentInfo.instanceCounter)));
271 }
272
273 const QString instanceIdentifier = instance->identifier();
274 if (mAgentInstances.contains(instanceIdentifier)) {
275 qCWarning(AKONADICONTROL_LOG) << "Cannot create another instance of agent" << identifier;
276 return QString();
277 }
278
279 // Return from this dbus call before we do the next. Otherwise dbus brakes for
280 // this process.
281 if (calledFromDBus()) {
282 connection().send(message().createReply(instanceIdentifier));
283 }
284
285 if (!instance->start(agentInfo)) {
286 return QString();
287 }
288
289 mAgentInstances.insert(instanceIdentifier, instance);
290 registerAgentAtServer(instanceIdentifier, agentInfo);
291 save();
292
293 return instanceIdentifier;
294}
295
297{
298 const auto instance = mAgentInstances.value(identifier);
299 if (!instance) {
300 qCWarning(AKONADICONTROL_LOG) << Q_FUNC_INFO << "Agent instance with identifier" << identifier << "does not exist";
301 return;
302 }
303
304 if (instance->hasAgentInterface()) {
305 instance->cleanup();
306 } else {
307 qCWarning(AKONADICONTROL_LOG) << "Agent instance" << identifier << "has no interface!";
308 }
309
310 mAgentInstances.remove(identifier);
311
312 save();
313
314 org::freedesktop::Akonadi::ResourceManager resmanager(Akonadi::DBus::serviceName(Akonadi::DBus::Server),
315 QStringLiteral("/ResourceManager"),
317 this);
318 resmanager.removeResourceInstance(instance->identifier());
319
320 // Kill the preprocessor instance, if any.
321 org::freedesktop::Akonadi::PreprocessorManager preProcessorManager(Akonadi::DBus::serviceName(Akonadi::DBus::Server),
322 QStringLiteral("/PreprocessorManager"),
324 this);
325
326 preProcessorManager.unregisterInstance(instance->identifier());
327
328 if (instance->hasAgentInterface()) {
329 qCDebug(AKONADICONTROL_LOG) << "AgentManager::removeAgentInstance: calling instance->quit()";
330 instance->quit();
331 } else {
332 qCWarning(AKONADICONTROL_LOG) << "Agent instance" << identifier << "has no interface!";
333 }
334
335 Q_EMIT agentInstanceRemoved(identifier);
336}
337
339{
340 const AgentInstance::Ptr agent = mAgentInstances.value(identifier);
341 if (!agent) {
342 qCWarning(AKONADICONTROL_LOG) << "Agent instance with identifier" << identifier << "does not exist";
343 return QString();
344 }
345
346 return agent->agentType();
347}
348
350{
351 return mAgentInstances.keys();
352}
353
354int AgentManager::agentInstanceStatus(const QString &identifier) const
355{
356 if (!checkInstance(identifier)) {
357 return 2;
358 }
359
360 return mAgentInstances.value(identifier)->status();
361}
362
364{
365 if (!checkInstance(identifier)) {
366 return QString();
367 }
368
369 return mAgentInstances.value(identifier)->statusMessage();
370}
371
372uint AgentManager::agentInstanceProgress(const QString &identifier) const
373{
374 if (!checkInstance(identifier)) {
375 return 0;
376 }
377
378 return mAgentInstances.value(identifier)->progress();
379}
380
382{
383 Q_UNUSED(identifier)
384
385 return QString();
386}
387
388void AgentManager::agentInstanceConfigure(const QString &identifier, qlonglong windowId)
389{
390 if (!checkAgentInterfaces(identifier, QStringLiteral("agentInstanceConfigure"))) {
391 return;
392 }
393
394 mAgentInstances.value(identifier)->configure(windowId);
395}
396
398{
399 if (!checkInstance(identifier)) {
400 return false;
401 }
402
403 return mAgentInstances.value(identifier)->isOnline();
404}
405
406void AgentManager::setAgentInstanceOnline(const QString &identifier, bool state)
407{
408 if (!checkAgentInterfaces(identifier, QStringLiteral("setAgentInstanceOnline"))) {
409 return;
410 }
411
412 mAgentInstances.value(identifier)->statusInterface()->setOnline(state);
413}
414
415void AgentManager::setAgentInstanceActivities(const QString &identifier, const QStringList &activities)
416{
417 if (!checkAgentInterfaces(identifier, QStringLiteral("setAgentInstanceActivities"))) {
418 return;
419 }
420
421 mAgentInstances.value(identifier)->resourceInterface()->setActivities(activities);
422}
423
424QStringList AgentManager::agentInstanceActivities(const QString &identifier)
425{
426 if (!checkAgentInterfaces(identifier, QStringLiteral("agentInstanceActivities"))) {
427 return {};
428 }
429 if (!mAgentInstances.value(identifier)->resourceInterface()) {
430 return {};
431 }
432
433 return mAgentInstances.value(identifier)->resourceInterface()->activities();
434}
435
436void AgentManager::setAgentInstanceActivitiesEnabled(const QString &identifier, bool enabled)
437{
438 if (!checkAgentInterfaces(identifier, QStringLiteral("setAgentInstanceActivitiesEnabled"))) {
439 return;
440 }
441
442 mAgentInstances.value(identifier)->resourceInterface()->setActivitiesEnabled(enabled);
443}
444
445bool AgentManager::agentInstanceActivitiesEnabled(const QString &identifier)
446{
447 if (!checkAgentInterfaces(identifier, QStringLiteral("agentInstanceActivitiesEnabled"))) {
448 return false;
449 }
450 if (!mAgentInstances.value(identifier)->resourceInterface()) {
451 return false;
452 }
453
454 return mAgentInstances.value(identifier)->resourceInterface()->activitiesEnabled();
455}
456
457// resource specific methods //
458void AgentManager::setAgentInstanceName(const QString &identifier, const QString &name)
459{
460 if (!checkResourceInterface(identifier, QStringLiteral("setAgentInstanceName"))) {
461 return;
462 }
463
464 mAgentInstances.value(identifier)->resourceInterface()->setName(name);
465}
466
468{
469 if (!checkInstance(identifier)) {
470 return QString();
471 }
472
473 const AgentInstance::Ptr instance = mAgentInstances.value(identifier);
474 if (!instance->resourceName().isEmpty()) {
475 return instance->resourceName();
476 }
477
478 if (!checkAgentExists(instance->agentType())) {
479 return QString();
480 }
481
482 return mAgents.value(instance->agentType()).name;
483}
484
486{
487 if (!checkResourceInterface(identifier, QStringLiteral("agentInstanceSynchronize"))) {
488 return;
489 }
490
491 mAgentInstances.value(identifier)->resourceInterface()->synchronize();
492}
493
495{
496 if (!checkResourceInterface(identifier, QStringLiteral("agentInstanceSynchronizeCollectionTree"))) {
497 return;
498 }
499
500 mAgentInstances.value(identifier)->resourceInterface()->synchronizeCollectionTree();
501}
502
503void AgentManager::agentInstanceSynchronizeCollection(const QString &identifier, qint64 collection)
504{
505 agentInstanceSynchronizeCollection(identifier, collection, false);
506}
507
508void AgentManager::agentInstanceSynchronizeCollection(const QString &identifier, qint64 collection, bool recursive)
509{
510 if (!checkResourceInterface(identifier, QStringLiteral("agentInstanceSynchronizeCollection"))) {
511 return;
512 }
513
514 mAgentInstances.value(identifier)->resourceInterface()->synchronizeCollection(collection, recursive);
515}
516
518{
519 if (!checkResourceInterface(identifier, QStringLiteral("agentInstanceSynchronizeTags"))) {
520 return;
521 }
522
523 mAgentInstances.value(identifier)->resourceInterface()->synchronizeTags();
524}
525
527{
528 if (!checkInstance(identifier)) {
529 return;
530 }
531
532 mAgentInstances.value(identifier)->restartWhenIdle();
533}
534
535void AgentManager::updatePluginInfos()
536{
537 const QHash<QString, AgentType> oldInfos = mAgents;
538 readPluginInfos();
539
540 for (const AgentType &oldInfo : oldInfos) {
541 if (!mAgents.contains(oldInfo.identifier)) {
542 Q_EMIT agentTypeRemoved(oldInfo.identifier);
543 }
544 }
545
546 for (const AgentType &newInfo : std::as_const(mAgents)) {
547 if (!oldInfos.contains(newInfo.identifier)) {
548 Q_EMIT agentTypeAdded(newInfo.identifier);
549 ensureAutoStart(newInfo);
550 }
551 }
552}
553
554void AgentManager::readPluginInfos()
555{
556 mAgents.clear();
557
558 const QStringList pathList = pluginInfoPathList();
559
560 for (const QString &path : pathList) {
561 const QDir directory(path, QStringLiteral("*.desktop"));
562 readPluginInfos(directory);
563 }
564}
565
566void AgentManager::readPluginInfos(const QDir &directory)
567{
568 const QStringList files = directory.entryList();
569 qCDebug(AKONADICONTROL_LOG) << "PLUGINS: " << directory.canonicalPath();
570 qCDebug(AKONADICONTROL_LOG) << "PLUGINS: " << files;
571
572 for (int i = 0; i < files.count(); ++i) {
573 const QString fileName = directory.absoluteFilePath(files[i]);
574
575 AgentType agentInfo;
576 if (agentInfo.load(fileName, this)) {
577 if (mAgents.contains(agentInfo.identifier)) {
578 qCWarning(AKONADICONTROL_LOG) << "Duplicated agent identifier" << agentInfo.identifier << "from file" << fileName;
579 continue;
580 }
581
582 const QString disableAutostart = akGetEnv("AKONADI_DISABLE_AGENT_AUTOSTART");
583 if (!disableAutostart.isEmpty()) {
584 qCDebug(AKONADICONTROL_LOG) << "Autostarting of agents is disabled.";
585 agentInfo.capabilities.removeOne(AgentType::CapabilityAutostart);
586 }
587
588 if (!mAgentServerEnabled && agentInfo.launchMethod == AgentType::Server) {
589 agentInfo.launchMethod = AgentType::Launcher;
590 }
591
592 if (agentInfo.launchMethod == AgentType::Process) {
593 const QString executable = Akonadi::StandardDirs::findExecutable(agentInfo.exec);
594 if (executable.isEmpty()) {
595 qCWarning(AKONADICONTROL_LOG) << "Executable" << agentInfo.exec << "for agent" << agentInfo.identifier << "could not be found!";
596 continue;
597 }
598 }
599
600 qCDebug(AKONADICONTROL_LOG) << "PLUGINS inserting: " << agentInfo.identifier << agentInfo.instanceCounter << agentInfo.capabilities;
601 mAgents.insert(agentInfo.identifier, agentInfo);
602 }
603 }
604}
605
606QStringList AgentManager::pluginInfoPathList()
607{
608 return Akonadi::StandardDirs::locateAllResourceDirs(QStringLiteral("akonadi/agents"));
609}
610
611void AgentManager::load()
612{
613 org::freedesktop::Akonadi::ResourceManager resmanager(Akonadi::DBus::serviceName(Akonadi::DBus::Server),
614 QStringLiteral("/ResourceManager"),
616 this);
617 const QStringList knownResources = resmanager.resourceInstances();
618
619 QSettings file(Akonadi::StandardDirs::agentsConfigFile(Akonadi::StandardDirs::ReadOnly), QSettings::IniFormat);
620 file.beginGroup(QStringLiteral("Instances"));
621 const QStringList entries = file.childGroups();
622 for (int i = 0; i < entries.count(); ++i) {
623 const QString instanceIdentifier = entries[i];
624
625 if (mAgentInstances.contains(instanceIdentifier)) {
626 qCWarning(AKONADICONTROL_LOG) << "Duplicated instance identifier" << instanceIdentifier << "found in agentsrc";
627 continue;
628 }
629
630 file.beginGroup(entries[i]);
631
632 const QString agentType = file.value(QStringLiteral("AgentType")).toString();
633 const auto typeIter = mAgents.constFind(agentType);
634 if (typeIter == mAgents.cend() || typeIter->exec.isEmpty()) {
635 // Check whether this is one of the obsolete agent types and just remove it.
636 if (std::find(std::begin(retiredAgentTypes), std::end(retiredAgentTypes), agentType) != std::end(retiredAgentTypes)) {
637 qCInfo(AKONADICONTROL_LOG) << agentType << "has been retired in a previous version, cleaning up " << entries[i];
638 file.endGroup();
639 file.remove(entries[i]);
640 // This will take care of removing all related data from Akonadi database
641 resmanager.removeResourceInstance(entries[i]);
642 continue;
643 }
644
645 qCWarning(AKONADICONTROL_LOG) << "Reference to unknown agent type" << agentType << "in agentsrc, creating a fake entry.";
646 if (typeIter == mAgents.cend()) {
647 AgentType type;
648 type.identifier = type.name = agentType;
649 mAgents.insert(type.identifier, type);
650 }
651
652 auto brokenInstance = AgentInstance::Ptr{new Akonadi::AgentBrokenInstance{agentType, *this}};
653 brokenInstance->setIdentifier(instanceIdentifier);
654 mAgentInstances.insert(instanceIdentifier, brokenInstance);
655 file.endGroup();
656 continue;
657 }
658
659 const AgentType &type = *typeIter;
660
661 // recover if the db has been deleted in the meantime or got otherwise corrupted
662 if (!knownResources.contains(instanceIdentifier) && type.capabilities.contains(AgentType::CapabilityResource)) {
663 qCDebug(AKONADICONTROL_LOG) << "Recovering instance" << instanceIdentifier << "after database loss";
664 registerAgentAtServer(instanceIdentifier, type);
665 }
666
667 const AgentInstance::Ptr instance = createAgentInstance(type);
668 instance->setIdentifier(instanceIdentifier);
669 if (instance->start(type)) {
670 mAgentInstances.insert(instanceIdentifier, instance);
671 }
672
673 file.endGroup();
674 }
675
676 file.endGroup();
677}
678
679void AgentManager::save()
680{
681 QSettings file(Akonadi::StandardDirs::agentsConfigFile(Akonadi::StandardDirs::WriteOnly), QSettings::IniFormat);
682
683 for (const AgentType &info : std::as_const(mAgents)) {
684 info.save(&file);
685 }
686
687 file.beginGroup(QStringLiteral("Instances"));
688 file.remove(QString());
689 for (const AgentInstance::Ptr &instance : std::as_const(mAgentInstances)) {
690 file.beginGroup(instance->identifier());
691 file.setValue(QStringLiteral("AgentType"), instance->agentType());
692 file.endGroup();
693 }
694
695 file.endGroup();
696}
697
698void AgentManager::serviceOwnerChanged(const QString &name, const QString &oldOwner, const QString &newOwner)
699{
700 Q_UNUSED(oldOwner)
701 // This is called by the D-Bus server when a service comes up, goes down or changes ownership for some reason
702 // and this is where we "hook up" our different Agent interfaces.
703
704 // Ignore DBus address name (e.g. :1.310)
705 if (name.startsWith(QLatin1Char(':'))) {
706 return;
707 }
708
709 // Ignore services belonging to another Akonadi instance
710 const auto parsedInstance = Akonadi::DBus::parseInstanceIdentifier(name);
711 const auto currentInstance = Akonadi::Instance::hasIdentifier() ? std::optional<QString>(Akonadi::Instance::identifier()) : std::nullopt;
712 if (parsedInstance != currentInstance) {
713 return;
714 }
715
716 qCDebug(AKONADICONTROL_LOG) << "Service" << name << "owner changed from" << oldOwner << "to" << newOwner;
717
718 if ((name == Akonadi::DBus::serviceName(Akonadi::DBus::Server) || name == Akonadi::DBus::serviceName(Akonadi::DBus::AgentServer)) && !newOwner.isEmpty()) {
719 if (QDBusConnection::sessionBus().interface()->isServiceRegistered(Akonadi::DBus::serviceName(Akonadi::DBus::Server))
720 && (!mAgentServer || QDBusConnection::sessionBus().interface()->isServiceRegistered(Akonadi::DBus::serviceName(Akonadi::DBus::AgentServer)))) {
721 // server is operational, start agents
722 continueStartup();
723 }
724 }
725
726 const auto service = Akonadi::DBus::parseAgentServiceName(name);
727 if (!service.has_value()) {
728 return;
729 }
730 switch (service->agentType) {
731 case Akonadi::DBus::Agent: {
732 // An agent service went up or down
733 if (newOwner.isEmpty()) {
734 return; // It went down: we don't care here.
735 }
736
737 if (!mAgentInstances.contains(service->identifier)) {
738 return;
739 }
740
741 const AgentInstance::Ptr instance = mAgentInstances.value(service->identifier);
742 const bool restarting = instance->hasAgentInterface();
743 if (!instance->obtainAgentInterface()) {
744 return;
745 }
746
747 Q_ASSERT(mAgents.contains(instance->agentType()));
748 const bool isResource = mAgents.value(instance->agentType()).capabilities.contains(AgentType::CapabilityResource);
749
750 if (!restarting && (!isResource || instance->hasResourceInterface())) {
751 Q_EMIT agentInstanceAdded(service->identifier);
752 }
753
754 break;
755 }
756 case Akonadi::DBus::Resource: {
757 // A resource service went up or down
758 if (newOwner.isEmpty()) {
759 return; // It went down: we don't care here.
760 }
761
762 if (!mAgentInstances.contains(service->identifier)) {
763 return;
764 }
765
766 const AgentInstance::Ptr instance = mAgentInstances.value(service->identifier);
767 const bool restarting = instance->hasResourceInterface();
768 if (!instance->obtainResourceInterface()) {
769 return;
770 }
771
772 if (!restarting && instance->hasAgentInterface()) {
773 Q_EMIT agentInstanceAdded(service->identifier);
774 }
775
776 break;
777 }
778 case Akonadi::DBus::Preprocessor: {
779 // A preprocessor service went up or down
780
781 // If the preprocessor is going up then the org.freedesktop.Akonadi.Agent.* interface
782 // should be already up (as it's registered before the preprocessor one).
783 // So if we don't know about the preprocessor as agent instance
784 // then it's not our preprocessor.
785
786 // If the preprocessor is going down then either the agent interface already
787 // went down (and it has been already unregistered on the manager side)
788 // or it's still registered as agent and WE have to unregister it.
789 // The order of interface deletions depends on Qt but we handle both cases.
790
791 // Check if we "know" about it.
792 qCDebug(AKONADICONTROL_LOG) << "Preprocessor " << service->identifier << " is going up or down...";
793
794 if (!mAgentInstances.contains(service->identifier)) {
795 qCDebug(AKONADICONTROL_LOG) << "But it isn't registered as agent... not mine (anymore?)";
796 return; // not our agent (?)
797 }
798
799 org::freedesktop::Akonadi::PreprocessorManager preProcessorManager(Akonadi::DBus::serviceName(Akonadi::DBus::Server),
800 QStringLiteral("/PreprocessorManager"),
802 this);
803
804 if (!preProcessorManager.isValid()) {
805 qCWarning(AKONADICONTROL_LOG) << "Could not connect to PreprocessorManager via D-Bus:" << preProcessorManager.lastError().message();
806 } else {
807 if (newOwner.isEmpty()) {
808 // The preprocessor went down. Unregister it on server side.
809
810 preProcessorManager.unregisterInstance(service->identifier);
811
812 } else {
813 // The preprocessor went up. Register it on server side.
814
815 if (!mAgentInstances.value(service->identifier)->obtainPreprocessorInterface()) {
816 // Hm.. couldn't hook up its preprocessor interface..
817 // Make sure we don't have it in the preprocessor chain
818 qCWarning(AKONADICONTROL_LOG) << "Couldn't obtain preprocessor interface for instance" << service->identifier;
819
820 preProcessorManager.unregisterInstance(service->identifier);
821 return;
822 }
823
824 qCDebug(AKONADICONTROL_LOG) << "Registering preprocessor instance" << service->identifier;
825
826 // Add to the preprocessor chain
827 preProcessorManager.registerInstance(service->identifier);
828 }
829 }
830
831 break;
832 }
833 default:
834 break;
835 }
836}
837
838bool AgentManager::checkInstance(const QString &identifier) const
839{
840 if (!mAgentInstances.contains(identifier)) {
841 qCWarning(AKONADICONTROL_LOG) << "Agent instance with identifier " << identifier << " does not exist";
842 return false;
843 }
844
845 return true;
846}
847
848bool AgentManager::checkResourceInterface(const QString &identifier, const QString &method) const
849{
850 if (!checkInstance(identifier)) {
851 return false;
852 }
853
854 if (!mAgents[mAgentInstances[identifier]->agentType()].capabilities.contains(QLatin1StringView("Resource"))) {
855 return false;
856 }
857
858 if (!mAgentInstances[identifier]->hasResourceInterface()) {
859 qCWarning(AKONADICONTROL_LOG) << QLatin1StringView("AgentManager::") + method << " Agent instance " << identifier << " has no resource interface!";
860 return false;
861 }
862
863 return true;
864}
865
866bool AgentManager::checkAgentExists(const QString &identifier) const
867{
868 if (!mAgents.contains(identifier)) {
869 qCWarning(AKONADICONTROL_LOG) << "Agent instance " << identifier << " does not exist.";
870 return false;
871 }
872
873 return true;
874}
875
876bool AgentManager::checkAgentInterfaces(const QString &identifier, const QString &method) const
877{
878 if (!checkInstance(identifier)) {
879 return false;
880 }
881
882 if (!mAgentInstances.value(identifier)->hasAgentInterface()) {
883 qCWarning(AKONADICONTROL_LOG) << "Agent instance (" << method << ") " << identifier << " has no agent interface.";
884 return false;
885 }
886
887 return true;
888}
889
890void AgentManager::ensureAutoStart(const AgentType &info)
891{
892 if (!info.capabilities.contains(AgentType::CapabilityAutostart)) {
893 return; // no an autostart agent
894 }
895
896 org::freedesktop::Akonadi::AgentServer agentServer(Akonadi::DBus::serviceName(Akonadi::DBus::AgentServer),
897 QStringLiteral("/AgentServer"),
899 this);
900
901 if (mAgentInstances.contains(info.identifier) || (agentServer.isValid() && agentServer.started(info.identifier))) {
902 return; // already running
903 }
904
905 const AgentInstance::Ptr instance = createAgentInstance(info);
906 instance->setIdentifier(info.identifier);
907 if (instance->start(info)) {
908 mAgentInstances.insert(instance->identifier(), instance);
909 registerAgentAtServer(instance->identifier(), info);
910 save();
911 }
912}
913
914void AgentManager::agentExeChanged(const QString &fileName)
915{
916 if (!QFile::exists(fileName)) {
917 return;
918 }
919
920 for (const AgentType &type : std::as_const(mAgents)) {
921 if (fileName.endsWith(type.exec)) {
922 for (const AgentInstance::Ptr &instance : std::as_const(mAgentInstances)) {
923 if (instance->agentType() == type.identifier) {
924 instance->restartWhenIdle();
925 }
926 }
927 }
928 }
929}
930
931void AgentManager::registerAgentAtServer(const QString &agentIdentifier, const AgentType &type)
932{
933 if (type.capabilities.contains(AgentType::CapabilityResource)) {
934 QScopedPointer<org::freedesktop::Akonadi::ResourceManager> resmanager(
935 new org::freedesktop::Akonadi::ResourceManager(Akonadi::DBus::serviceName(Akonadi::DBus::Server),
936 QStringLiteral("/ResourceManager"),
938 this));
939 resmanager->addResourceInstance(agentIdentifier, type.capabilities);
940 }
941}
942
943void AgentManager::addSearch(const QString &query, const QString &queryLanguage, qint64 resultCollectionId)
944{
945 qCDebug(AKONADICONTROL_LOG) << "AgentManager::addSearch" << query << queryLanguage << resultCollectionId;
946 for (const AgentInstance::Ptr &instance : std::as_const(mAgentInstances)) {
947 const AgentType type = mAgents.value(instance->agentType());
948 if (type.capabilities.contains(AgentType::CapabilitySearch) && instance->searchInterface()) {
949 instance->searchInterface()->addSearch(query, queryLanguage, resultCollectionId);
950 }
951 }
952}
953
954void AgentManager::removeSearch(quint64 resultCollectionId)
955{
956 qCDebug(AKONADICONTROL_LOG) << "AgentManager::removeSearch" << resultCollectionId;
957 for (const AgentInstance::Ptr &instance : std::as_const(mAgentInstances)) {
958 const AgentType type = mAgents.value(instance->agentType());
959 if (type.capabilities.contains(AgentType::CapabilitySearch) && instance->searchInterface()) {
960 instance->searchInterface()->removeSearch(resultCollectionId);
961 }
962 }
963}
964
965#include "agentmanager.moc"
966
967#include "moc_agentmanager.cpp"
bool agentInstanceOnline(const QString &identifier)
Returns if the agent instance identifier is in online mode.
void setAgentInstanceName(const QString &identifier, const QString &name)
Sets the name of the agent instance with the given identifier.
QString agentInstanceStatusMessage(const QString &identifier) const
Returns the i18n'ed description of the current status of the agent with the given identifier.
void agentInstanceAdded(const QString &agentIdentifier)
This signal is emitted whenever a new agent instance was created.
void agentTypeAdded(const QString &agentType)
This signal is emitted whenever a new agent type was installed on the system.
QStringList agentInstances() const
Returns the list of identifiers of configured instances.
QString agentInstanceName(const QString &identifier) const
Returns the name of the agent instance with the given identifier.
QStringList agentTypes() const
Returns the list of identifiers of all available agent types.
void removeAgentInstance(const QString &identifier)
Removes the agent with the given identifier.
void agentInstanceSynchronizeCollectionTree(const QString &identifier)
Trigger a synchronization of the collection tree by the given resource agent.
AgentManager(bool verbose, QObject *parent=nullptr)
Creates a new agent manager.
QString agentInstanceProgressMessage(const QString &identifier) const
Returns the i18n'ed description of the current progress of the agent with the given identifier.
uint agentInstanceProgress(const QString &identifier) const
Returns the current progress of the agent with the given identifier in percentage.
void agentInstanceSynchronizeCollection(const QString &identifier, qint64 collection)
Trigger a synchronization of the given collection by its owning resource agent.
void removeSearch(quint64 resultCollectionId)
Removes a persistent search for the given result collection.
QVariantMap agentCustomProperties(const QString &identifier) const
Returns a list of Custom added properties of the agent type for the given identifier.
QStringList agentMimeTypes(const QString &identifier) const
Returns a list of supported mimetypes of the agent type for the given identifier.
QString agentInstanceType(const QString &identifier)
Returns the type of the agent instance with the given identifier.
void agentInstanceSynchronizeTags(const QString &identifier)
Trigger a synchronization of tags by the given resource agent.
void agentInstanceRemoved(const QString &agentIdentifier)
This signal is emitted whenever an agent instance was removed.
QString agentComment(const QString &identifier) const
Returns the i18n'ed comment of the agent type for the given identifier.
QStringList agentCapabilities(const QString &identifier) const
Returns a list of supported capabilities of the agent type for the given identifier.
QString agentName(const QString &identifier) const
Returns the i18n'ed name of the agent type for the given identifier.
void addSearch(const QString &query, const QString &queryLanguage, qint64 resultCollectionId)
Add a persistent search to remote search agents.
void setAgentInstanceOnline(const QString &identifier, bool state)
Sets agent instance identifier to online or offline mode.
void agentInstanceConfigure(const QString &identifier, qlonglong windowId)
Triggers the agent instance with the given identifier to show its configuration dialog.
void restartAgentInstance(const QString &identifier)
Restarts the agent instance identifier.
void cleanup()
Called by the crash handler and dtor to terminate the child processes.
~AgentManager() override
Destroys the agent manager.
void agentTypeRemoved(const QString &agentType)
This signal is emitted whenever an agent type was removed from the system.
QString agentIcon(const QString &identifier) const
Returns the icon name of the agent type for the given identifier.
int agentInstanceStatus(const QString &identifier) const
Returns the current status code of the agent with the given identifier.
QString createAgentInstance(const QString &identifier)
Creates a new agent of the given agent type identifier.
void agentInstanceSynchronize(const QString &identifier)
Triggers the agent instance with the given identifier to start synchronization.
This class starts and observes a process.
void setCrashPolicy(CrashPolicy policy)
Sets the crash policy.
void start()
Starts the process with the previously set application and arguments.
void unableToStart()
Emitted if the process could not be started since it terminated too often.
void setShutdownTimeout(std::chrono::milliseconds timeout)
Sets the time (in msecs) we wait for the process to shut down before we send terminate/kill signals.
Capabilities capabilities()
VehicleSection::Type type(QStringView coachNumber, QStringView coachClassification)
QString name(StandardAction id)
void exit(int returnCode)
QCoreApplication * instance()
bool registerObject(const QString &path, QObject *object, RegisterOptions options)
bool send(const QDBusMessage &message) const const
QDBusConnection sessionBus()
void serviceOwnerChanged(const QString &name, const QString &oldOwner, const QString &newOwner)
bool calledFromDBus() const const
QDBusConnection connection() const const
const QDBusMessage & message() const const
QString absoluteFilePath(const QString &fileName) const const
QString canonicalPath() const const
QStringList entryList(Filters filters, SortFlags sort) const const
bool exists() const const
bool contains(const Key &key) const const
qsizetype count() const const
iterator insert(const_iterator before, parameter_type value)
void remove(qsizetype i, qsizetype n)
bool removeOne(const AT &t)
T value(qsizetype i) const const
T value(const Key &key, const T &defaultValue) const const
QObject(QObject *parent)
Q_EMITQ_EMIT
Q_OBJECTQ_OBJECT
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
QObject * parent() const const
QVariant value(QAnyStringView key) const const
QSharedPointer< T > create(Args &&... args)
bool endsWith(QChar c, Qt::CaseSensitivity cs) const const
QString & insert(qsizetype position, QChar ch)
bool isEmpty() const const
QString number(double n, char format, int precision)
QString & remove(QChar ch, Qt::CaseSensitivity cs)
bool startsWith(QChar c, Qt::CaseSensitivity cs) const const
bool contains(QLatin1StringView str, Qt::CaseSensitivity cs) const const
bool toBool() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2025 The KDE developers.
Generated on Fri Feb 28 2025 11:52:29 by doxygen 1.13.2 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.