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
234{
235 if (!checkAgentExists(identifier)) {
236 return QVariantMap();
237 }
238
239 return mAgents.value(identifier).custom;
240}
241
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 (!checkInstance(identifier)) {
427 return {};
428 }
429
430 return mAgentInstances.value(identifier)->resourceInterface()->activities();
431}
432
433void AgentManager::setAgentInstanceActivitiesEnabled(const QString &identifier, bool enabled)
434{
435 if (!checkAgentInterfaces(identifier, QStringLiteral("setAgentInstanceActivitiesEnabled"))) {
436 return;
437 }
438
439 mAgentInstances.value(identifier)->resourceInterface()->setActivitiesEnabled(enabled);
440}
441
442bool AgentManager::agentInstanceActivitiesEnabled(const QString &identifier)
443{
444 if (!checkInstance(identifier)) {
445 return {};
446 }
447
448 return mAgentInstances.value(identifier)->resourceInterface()->activitiesEnabled();
449}
450
451// resource specific methods //
452void AgentManager::setAgentInstanceName(const QString &identifier, const QString &name)
453{
454 if (!checkResourceInterface(identifier, QStringLiteral("setAgentInstanceName"))) {
455 return;
456 }
457
458 mAgentInstances.value(identifier)->resourceInterface()->setName(name);
459}
460
462{
463 if (!checkInstance(identifier)) {
464 return QString();
465 }
466
467 const AgentInstance::Ptr instance = mAgentInstances.value(identifier);
468 if (!instance->resourceName().isEmpty()) {
469 return instance->resourceName();
470 }
471
472 if (!checkAgentExists(instance->agentType())) {
473 return QString();
474 }
475
476 return mAgents.value(instance->agentType()).name;
477}
478
480{
481 if (!checkResourceInterface(identifier, QStringLiteral("agentInstanceSynchronize"))) {
482 return;
483 }
484
485 mAgentInstances.value(identifier)->resourceInterface()->synchronize();
486}
487
489{
490 if (!checkResourceInterface(identifier, QStringLiteral("agentInstanceSynchronizeCollectionTree"))) {
491 return;
492 }
493
494 mAgentInstances.value(identifier)->resourceInterface()->synchronizeCollectionTree();
495}
496
497void AgentManager::agentInstanceSynchronizeCollection(const QString &identifier, qint64 collection)
498{
499 agentInstanceSynchronizeCollection(identifier, collection, false);
500}
501
502void AgentManager::agentInstanceSynchronizeCollection(const QString &identifier, qint64 collection, bool recursive)
503{
504 if (!checkResourceInterface(identifier, QStringLiteral("agentInstanceSynchronizeCollection"))) {
505 return;
506 }
507
508 mAgentInstances.value(identifier)->resourceInterface()->synchronizeCollection(collection, recursive);
509}
510
512{
513 if (!checkResourceInterface(identifier, QStringLiteral("agentInstanceSynchronizeTags"))) {
514 return;
515 }
516
517 mAgentInstances.value(identifier)->resourceInterface()->synchronizeTags();
518}
519
521{
522 if (!checkInstance(identifier)) {
523 return;
524 }
525
526 mAgentInstances.value(identifier)->restartWhenIdle();
527}
528
529void AgentManager::updatePluginInfos()
530{
531 const QHash<QString, AgentType> oldInfos = mAgents;
532 readPluginInfos();
533
534 for (const AgentType &oldInfo : oldInfos) {
535 if (!mAgents.contains(oldInfo.identifier)) {
536 Q_EMIT agentTypeRemoved(oldInfo.identifier);
537 }
538 }
539
540 for (const AgentType &newInfo : std::as_const(mAgents)) {
541 if (!oldInfos.contains(newInfo.identifier)) {
542 Q_EMIT agentTypeAdded(newInfo.identifier);
543 ensureAutoStart(newInfo);
544 }
545 }
546}
547
548void AgentManager::readPluginInfos()
549{
550 mAgents.clear();
551
552 const QStringList pathList = pluginInfoPathList();
553
554 for (const QString &path : pathList) {
555 const QDir directory(path, QStringLiteral("*.desktop"));
556 readPluginInfos(directory);
557 }
558}
559
560void AgentManager::readPluginInfos(const QDir &directory)
561{
562 const QStringList files = directory.entryList();
563 qCDebug(AKONADICONTROL_LOG) << "PLUGINS: " << directory.canonicalPath();
564 qCDebug(AKONADICONTROL_LOG) << "PLUGINS: " << files;
565
566 for (int i = 0; i < files.count(); ++i) {
567 const QString fileName = directory.absoluteFilePath(files[i]);
568
569 AgentType agentInfo;
570 if (agentInfo.load(fileName, this)) {
571 if (mAgents.contains(agentInfo.identifier)) {
572 qCWarning(AKONADICONTROL_LOG) << "Duplicated agent identifier" << agentInfo.identifier << "from file" << fileName;
573 continue;
574 }
575
576 const QString disableAutostart = akGetEnv("AKONADI_DISABLE_AGENT_AUTOSTART");
577 if (!disableAutostart.isEmpty()) {
578 qCDebug(AKONADICONTROL_LOG) << "Autostarting of agents is disabled.";
579 agentInfo.capabilities.removeOne(AgentType::CapabilityAutostart);
580 }
581
582 if (!mAgentServerEnabled && agentInfo.launchMethod == AgentType::Server) {
583 agentInfo.launchMethod = AgentType::Launcher;
584 }
585
586 if (agentInfo.launchMethod == AgentType::Process) {
587 const QString executable = Akonadi::StandardDirs::findExecutable(agentInfo.exec);
588 if (executable.isEmpty()) {
589 qCWarning(AKONADICONTROL_LOG) << "Executable" << agentInfo.exec << "for agent" << agentInfo.identifier << "could not be found!";
590 continue;
591 }
592 }
593
594 qCDebug(AKONADICONTROL_LOG) << "PLUGINS inserting: " << agentInfo.identifier << agentInfo.instanceCounter << agentInfo.capabilities;
595 mAgents.insert(agentInfo.identifier, agentInfo);
596 }
597 }
598}
599
600QStringList AgentManager::pluginInfoPathList()
601{
602 return Akonadi::StandardDirs::locateAllResourceDirs(QStringLiteral("akonadi/agents"));
603}
604
605void AgentManager::load()
606{
607 org::freedesktop::Akonadi::ResourceManager resmanager(Akonadi::DBus::serviceName(Akonadi::DBus::Server),
608 QStringLiteral("/ResourceManager"),
610 this);
611 const QStringList knownResources = resmanager.resourceInstances();
612
613 QSettings file(Akonadi::StandardDirs::agentsConfigFile(Akonadi::StandardDirs::ReadOnly), QSettings::IniFormat);
614 file.beginGroup(QStringLiteral("Instances"));
615 const QStringList entries = file.childGroups();
616 for (int i = 0; i < entries.count(); ++i) {
617 const QString instanceIdentifier = entries[i];
618
619 if (mAgentInstances.contains(instanceIdentifier)) {
620 qCWarning(AKONADICONTROL_LOG) << "Duplicated instance identifier" << instanceIdentifier << "found in agentsrc";
621 continue;
622 }
623
624 file.beginGroup(entries[i]);
625
626 const QString agentType = file.value(QStringLiteral("AgentType")).toString();
627 const auto typeIter = mAgents.constFind(agentType);
628 if (typeIter == mAgents.cend() || typeIter->exec.isEmpty()) {
629 // Check whether this is one of the obsolete agent types and just remove it.
630 if (std::find(std::begin(retiredAgentTypes), std::end(retiredAgentTypes), agentType) != std::end(retiredAgentTypes)) {
631 qCInfo(AKONADICONTROL_LOG) << agentType << "has been retired in a previous version, cleaning up " << entries[i];
632 file.endGroup();
633 file.remove(entries[i]);
634 // This will take care of removing all related data from Akonadi database
635 resmanager.removeResourceInstance(entries[i]);
636 continue;
637 }
638
639 qCWarning(AKONADICONTROL_LOG) << "Reference to unknown agent type" << agentType << "in agentsrc, creating a fake entry.";
640 if (typeIter == mAgents.cend()) {
641 AgentType type;
642 type.identifier = type.name = agentType;
643 mAgents.insert(type.identifier, type);
644 }
645
646 auto brokenInstance = AgentInstance::Ptr{new Akonadi::AgentBrokenInstance{agentType, *this}};
647 brokenInstance->setIdentifier(instanceIdentifier);
648 mAgentInstances.insert(instanceIdentifier, brokenInstance);
649 file.endGroup();
650 continue;
651 }
652
653 const AgentType &type = *typeIter;
654
655 // recover if the db has been deleted in the meantime or got otherwise corrupted
656 if (!knownResources.contains(instanceIdentifier) && type.capabilities.contains(AgentType::CapabilityResource)) {
657 qCDebug(AKONADICONTROL_LOG) << "Recovering instance" << instanceIdentifier << "after database loss";
658 registerAgentAtServer(instanceIdentifier, type);
659 }
660
661 const AgentInstance::Ptr instance = createAgentInstance(type);
662 instance->setIdentifier(instanceIdentifier);
663 if (instance->start(type)) {
664 mAgentInstances.insert(instanceIdentifier, instance);
665 }
666
667 file.endGroup();
668 }
669
670 file.endGroup();
671}
672
673void AgentManager::save()
674{
675 QSettings file(Akonadi::StandardDirs::agentsConfigFile(Akonadi::StandardDirs::WriteOnly), QSettings::IniFormat);
676
677 for (const AgentType &info : std::as_const(mAgents)) {
678 info.save(&file);
679 }
680
681 file.beginGroup(QStringLiteral("Instances"));
682 file.remove(QString());
683 for (const AgentInstance::Ptr &instance : std::as_const(mAgentInstances)) {
684 file.beginGroup(instance->identifier());
685 file.setValue(QStringLiteral("AgentType"), instance->agentType());
686 file.endGroup();
687 }
688
689 file.endGroup();
690}
691
692void AgentManager::serviceOwnerChanged(const QString &name, const QString &oldOwner, const QString &newOwner)
693{
694 Q_UNUSED(oldOwner)
695 // This is called by the D-Bus server when a service comes up, goes down or changes ownership for some reason
696 // and this is where we "hook up" our different Agent interfaces.
697
698 // Ignore DBus address name (e.g. :1.310)
699 if (name.startsWith(QLatin1Char(':'))) {
700 return;
701 }
702
703 // Ignore services belonging to another Akonadi instance
704 const auto parsedInstance = Akonadi::DBus::parseInstanceIdentifier(name);
705 const auto currentInstance = Akonadi::Instance::hasIdentifier() ? std::optional<QString>(Akonadi::Instance::identifier()) : std::nullopt;
706 if (parsedInstance != currentInstance) {
707 return;
708 }
709
710 qCDebug(AKONADICONTROL_LOG) << "Service" << name << "owner changed from" << oldOwner << "to" << newOwner;
711
712 if ((name == Akonadi::DBus::serviceName(Akonadi::DBus::Server) || name == Akonadi::DBus::serviceName(Akonadi::DBus::AgentServer)) && !newOwner.isEmpty()) {
713 if (QDBusConnection::sessionBus().interface()->isServiceRegistered(Akonadi::DBus::serviceName(Akonadi::DBus::Server))
714 && (!mAgentServer || QDBusConnection::sessionBus().interface()->isServiceRegistered(Akonadi::DBus::serviceName(Akonadi::DBus::AgentServer)))) {
715 // server is operational, start agents
716 continueStartup();
717 }
718 }
719
720 const auto service = Akonadi::DBus::parseAgentServiceName(name);
721 if (!service.has_value()) {
722 return;
723 }
724 switch (service->agentType) {
725 case Akonadi::DBus::Agent: {
726 // An agent service went up or down
727 if (newOwner.isEmpty()) {
728 return; // It went down: we don't care here.
729 }
730
731 if (!mAgentInstances.contains(service->identifier)) {
732 return;
733 }
734
735 const AgentInstance::Ptr instance = mAgentInstances.value(service->identifier);
736 const bool restarting = instance->hasAgentInterface();
737 if (!instance->obtainAgentInterface()) {
738 return;
739 }
740
741 Q_ASSERT(mAgents.contains(instance->agentType()));
742 const bool isResource = mAgents.value(instance->agentType()).capabilities.contains(AgentType::CapabilityResource);
743
744 if (!restarting && (!isResource || instance->hasResourceInterface())) {
745 Q_EMIT agentInstanceAdded(service->identifier);
746 }
747
748 break;
749 }
750 case Akonadi::DBus::Resource: {
751 // A resource service went up or down
752 if (newOwner.isEmpty()) {
753 return; // It went down: we don't care here.
754 }
755
756 if (!mAgentInstances.contains(service->identifier)) {
757 return;
758 }
759
760 const AgentInstance::Ptr instance = mAgentInstances.value(service->identifier);
761 const bool restarting = instance->hasResourceInterface();
762 if (!instance->obtainResourceInterface()) {
763 return;
764 }
765
766 if (!restarting && instance->hasAgentInterface()) {
767 Q_EMIT agentInstanceAdded(service->identifier);
768 }
769
770 break;
771 }
772 case Akonadi::DBus::Preprocessor: {
773 // A preprocessor service went up or down
774
775 // If the preprocessor is going up then the org.freedesktop.Akonadi.Agent.* interface
776 // should be already up (as it's registered before the preprocessor one).
777 // So if we don't know about the preprocessor as agent instance
778 // then it's not our preprocessor.
779
780 // If the preprocessor is going down then either the agent interface already
781 // went down (and it has been already unregistered on the manager side)
782 // or it's still registered as agent and WE have to unregister it.
783 // The order of interface deletions depends on Qt but we handle both cases.
784
785 // Check if we "know" about it.
786 qCDebug(AKONADICONTROL_LOG) << "Preprocessor " << service->identifier << " is going up or down...";
787
788 if (!mAgentInstances.contains(service->identifier)) {
789 qCDebug(AKONADICONTROL_LOG) << "But it isn't registered as agent... not mine (anymore?)";
790 return; // not our agent (?)
791 }
792
793 org::freedesktop::Akonadi::PreprocessorManager preProcessorManager(Akonadi::DBus::serviceName(Akonadi::DBus::Server),
794 QStringLiteral("/PreprocessorManager"),
796 this);
797
798 if (!preProcessorManager.isValid()) {
799 qCWarning(AKONADICONTROL_LOG) << "Could not connect to PreprocessorManager via D-Bus:" << preProcessorManager.lastError().message();
800 } else {
801 if (newOwner.isEmpty()) {
802 // The preprocessor went down. Unregister it on server side.
803
804 preProcessorManager.unregisterInstance(service->identifier);
805
806 } else {
807 // The preprocessor went up. Register it on server side.
808
809 if (!mAgentInstances.value(service->identifier)->obtainPreprocessorInterface()) {
810 // Hm.. couldn't hook up its preprocessor interface..
811 // Make sure we don't have it in the preprocessor chain
812 qCWarning(AKONADICONTROL_LOG) << "Couldn't obtain preprocessor interface for instance" << service->identifier;
813
814 preProcessorManager.unregisterInstance(service->identifier);
815 return;
816 }
817
818 qCDebug(AKONADICONTROL_LOG) << "Registering preprocessor instance" << service->identifier;
819
820 // Add to the preprocessor chain
821 preProcessorManager.registerInstance(service->identifier);
822 }
823 }
824
825 break;
826 }
827 default:
828 break;
829 }
830}
831
832bool AgentManager::checkInstance(const QString &identifier) const
833{
834 if (!mAgentInstances.contains(identifier)) {
835 qCWarning(AKONADICONTROL_LOG) << "Agent instance with identifier " << identifier << " does not exist";
836 return false;
837 }
838
839 return true;
840}
841
842bool AgentManager::checkResourceInterface(const QString &identifier, const QString &method) const
843{
844 if (!checkInstance(identifier)) {
845 return false;
846 }
847
848 if (!mAgents[mAgentInstances[identifier]->agentType()].capabilities.contains(QLatin1StringView("Resource"))) {
849 return false;
850 }
851
852 if (!mAgentInstances[identifier]->hasResourceInterface()) {
853 qCWarning(AKONADICONTROL_LOG) << QLatin1StringView("AgentManager::") + method << " Agent instance " << identifier << " has no resource interface!";
854 return false;
855 }
856
857 return true;
858}
859
860bool AgentManager::checkAgentExists(const QString &identifier) const
861{
862 if (!mAgents.contains(identifier)) {
863 qCWarning(AKONADICONTROL_LOG) << "Agent instance " << identifier << " does not exist.";
864 return false;
865 }
866
867 return true;
868}
869
870bool AgentManager::checkAgentInterfaces(const QString &identifier, const QString &method) const
871{
872 if (!checkInstance(identifier)) {
873 return false;
874 }
875
876 if (!mAgentInstances.value(identifier)->hasAgentInterface()) {
877 qCWarning(AKONADICONTROL_LOG) << "Agent instance (" << method << ") " << identifier << " has no agent interface.";
878 return false;
879 }
880
881 return true;
882}
883
884void AgentManager::ensureAutoStart(const AgentType &info)
885{
886 if (!info.capabilities.contains(AgentType::CapabilityAutostart)) {
887 return; // no an autostart agent
888 }
889
890 org::freedesktop::Akonadi::AgentServer agentServer(Akonadi::DBus::serviceName(Akonadi::DBus::AgentServer),
891 QStringLiteral("/AgentServer"),
893 this);
894
895 if (mAgentInstances.contains(info.identifier) || (agentServer.isValid() && agentServer.started(info.identifier))) {
896 return; // already running
897 }
898
899 const AgentInstance::Ptr instance = createAgentInstance(info);
900 instance->setIdentifier(info.identifier);
901 if (instance->start(info)) {
902 mAgentInstances.insert(instance->identifier(), instance);
903 registerAgentAtServer(instance->identifier(), info);
904 save();
905 }
906}
907
908void AgentManager::agentExeChanged(const QString &fileName)
909{
910 if (!QFile::exists(fileName)) {
911 return;
912 }
913
914 for (const AgentType &type : std::as_const(mAgents)) {
915 if (fileName.endsWith(type.exec)) {
916 for (const AgentInstance::Ptr &instance : std::as_const(mAgentInstances)) {
917 if (instance->agentType() == type.identifier) {
918 instance->restartWhenIdle();
919 }
920 }
921 }
922 }
923}
924
925void AgentManager::registerAgentAtServer(const QString &agentIdentifier, const AgentType &type)
926{
927 if (type.capabilities.contains(AgentType::CapabilityResource)) {
929 new org::freedesktop::Akonadi::ResourceManager(Akonadi::DBus::serviceName(Akonadi::DBus::Server),
930 QStringLiteral("/ResourceManager"),
932 this));
933 resmanager->addResourceInstance(agentIdentifier, type.capabilities);
934 }
935}
936
937void AgentManager::addSearch(const QString &query, const QString &queryLanguage, qint64 resultCollectionId)
938{
939 qCDebug(AKONADICONTROL_LOG) << "AgentManager::addSearch" << query << queryLanguage << resultCollectionId;
940 for (const AgentInstance::Ptr &instance : std::as_const(mAgentInstances)) {
941 const AgentType type = mAgents.value(instance->agentType());
942 if (type.capabilities.contains(AgentType::CapabilitySearch) && instance->searchInterface()) {
943 instance->searchInterface()->addSearch(query, queryLanguage, resultCollectionId);
944 }
945 }
946}
947
948void AgentManager::removeSearch(quint64 resultCollectionId)
949{
950 qCDebug(AKONADICONTROL_LOG) << "AgentManager::removeSearch" << resultCollectionId;
951 for (const AgentInstance::Ptr &instance : std::as_const(mAgentInstances)) {
952 const AgentType type = mAgents.value(instance->agentType());
953 if (type.capabilities.contains(AgentType::CapabilitySearch) && instance->searchInterface()) {
954 instance->searchInterface()->removeSearch(resultCollectionId);
955 }
956 }
957}
958
959#include "agentmanager.moc"
960
961#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()
QString name(GameStandardAction id)
VehicleSection::Type type(QStringView coachNumber, QStringView coachClassification)
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
const_iterator cend() const const
void clear()
const_iterator constFind(const Key &key) const const
bool contains(const Key &key) const const
iterator insert(const Key &key, const T &value)
QList< Key > keys() const const
T value(const Key &key) const const
qsizetype count() const const
void remove(qsizetype i, qsizetype n)
bool removeOne(const AT &t)
Q_EMITQ_EMIT
Q_OBJECTQ_OBJECT
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
QVariant value(QAnyStringView key) const const
QSharedPointer< T > create(Args &&... args)
bool endsWith(QChar c, Qt::CaseSensitivity cs) const const
bool isEmpty() const const
QString number(double n, char format, int precision)
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-2024 The KDE developers.
Generated on Fri Nov 22 2024 12:03:33 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.