Akonadi

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

KDE's Doxygen guidelines are available online.