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

KDE's Doxygen guidelines are available online.