Akonadi

akonadicontrol/agentmanager.cpp
1 /***************************************************************************
2  * Copyright (C) 2006 by Tobias Koenig <[email protected]> *
3  * Copyright (c) 2007 Volker Krause <[email protected]> *
4  * *
5  * This program is free software; you can redistribute it and/or modify *
6  * it under the terms of the GNU Library General Public License as *
7  * published by the Free Software Foundation; either version 2 of the *
8  * License, or (at your option) any later version. *
9  * *
10  * This program is distributed in the hope that it will be useful, *
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13  * GNU General Public License for more details. *
14  * *
15  * You should have received a copy of the GNU Library General Public *
16  * License along with this program; if not, write to the *
17  * Free Software Foundation, Inc., *
18  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
19  ***************************************************************************/
20 
21 #include "agentmanager.h"
22 
23 #include "agentmanageradaptor.h"
24 #include "agentmanagerinternaladaptor.h"
25 #include "agentprocessinstance.h"
26 #include "agentserverinterface.h"
27 #include "agentbrokeninstance.h"
28 #include "agentthreadinstance.h"
29 #include "preprocessor_manager.h"
30 #include "processcontrol.h"
31 #include "resource_manager.h"
32 #include "serverinterface.h"
33 #include "akonadicontrol_debug.h"
34 
35 #include <private/protocol_p.h>
36 #include <private/instance_p.h>
37 #include <private/standarddirs_p.h>
38 #include <private/dbus_p.h>
39 
40 #include <shared/akapplication.h>
41 
42 #include <QCoreApplication>
43 #include <QDir>
44 #include <QScopedPointer>
45 #include <QSettings>
46 #include <QDBusConnection>
47 
49 using namespace std::chrono_literals;
50 
51 static const bool enableAgentServerDefault = false;
52 
53 class StorageProcessControl : public Akonadi::ProcessControl
54 {
55  Q_OBJECT
56 public:
57  StorageProcessControl(const QStringList &args)
59  {
60  setShutdownTimeout(15s);
61  connect(this, &Akonadi::ProcessControl::unableToStart, this,
62  []() {
64  });
65  start(QStringLiteral("akonadiserver"), args, RestartOnCrash);
66  }
67 
68  ~StorageProcessControl() override
69  {
70  setCrashPolicy(ProcessControl::StopOnCrash);
71  org::freedesktop::Akonadi::Server serverIface(Akonadi::DBus::serviceName(Akonadi::DBus::Server), QStringLiteral("/Server"),
73  serverIface.quit();
74  }
75 };
76 
77 class AgentServerProcessControl : public Akonadi::ProcessControl
78 {
79  Q_OBJECT
80 public:
81  AgentServerProcessControl(const QStringList &args)
83  {
84  connect(this, &Akonadi::ProcessControl::unableToStart, this,
85  []() {
86  qCCritical(AKONADICONTROL_LOG) << "Failed to start AgentServer!";
87  });
88  start(QStringLiteral("akonadi_agent_server"), args, RestartOnCrash);
89  }
90 
91  ~AgentServerProcessControl() override
92  {
93  setCrashPolicy(ProcessControl::StopOnCrash);
94  org::freedesktop::Akonadi::AgentServer agentServerIface(Akonadi::DBus::serviceName(Akonadi::DBus::AgentServer),
95  QStringLiteral("/AgentServer"), QDBusConnection::sessionBus(), this);
96  agentServerIface.quit();
97  }
98 };
99 
100 
101 AgentManager::AgentManager(bool verbose, QObject *parent)
102  : QObject(parent)
103  , mAgentServer(nullptr)
104  , mVerbose(verbose)
105 {
106  new AgentManagerAdaptor(this);
107  new AgentManagerInternalAdaptor(this);
108  QDBusConnection::sessionBus().registerObject(QStringLiteral("/AgentManager"), this);
109 
111  this, &AgentManager::serviceOwnerChanged);
112 
113  if (QDBusConnection::sessionBus().interface()->isServiceRegistered(Akonadi::DBus::serviceName(Akonadi::DBus::Server))) {
114  qFatal("akonadiserver already running!");
115  }
116 
117  const QSettings settings(Akonadi::StandardDirs::agentsConfigFile(Akonadi::StandardDirs::ReadOnly), QSettings::IniFormat);
118  mAgentServerEnabled = settings.value(QStringLiteral("AgentServer/Enabled"), enableAgentServerDefault).toBool();
119 
120  QStringList serviceArgs;
121  if (Akonadi::Instance::hasIdentifier()) {
122  serviceArgs << QStringLiteral("--instance") << Akonadi::Instance::identifier();
123  }
124  if (verbose) {
125  serviceArgs << QStringLiteral("--verbose");
126  }
127 
128  mStorageController = std::unique_ptr<Akonadi::ProcessControl>(new StorageProcessControl(serviceArgs));
129 
130  if (mAgentServerEnabled) {
131  mAgentServer = std::unique_ptr<Akonadi::ProcessControl>(new AgentServerProcessControl(serviceArgs));
132  }
133 }
134 
135 void AgentManager::continueStartup()
136 {
137  // prevent multiple calls in case the server has to be restarted
138  static bool first = true;
139  if (!first) {
140  return;
141  }
142 
143  first = false;
144 
145  readPluginInfos();
146  for (const AgentType &info : qAsConst(mAgents)) {
147  Q_EMIT agentTypeAdded(info.identifier);
148  }
149 
150  load();
151  for (const AgentType &info : qAsConst(mAgents)) {
152  ensureAutoStart(info);
153  }
154 
155  // register the real service name once everything is up an running
156  if (!QDBusConnection::sessionBus().registerService(Akonadi::DBus::serviceName(Akonadi::DBus::Control))) {
157  // besides a race with an older Akonadi server I have no idea how we could possibly get here...
158  qFatal("Unable to register service as %s despite having the lock. Error was: %s",
159  qPrintable(Akonadi::DBus::serviceName(Akonadi::DBus::Control)),
160  qPrintable(QDBusConnection::sessionBus().lastError().message()));
161  }
162  qCInfo(AKONADICONTROL_LOG) << "Akonadi server is now operational.";
163 }
164 
166 {
167  cleanup();
168 }
169 
171 {
172  for (const AgentInstance::Ptr &instance : qAsConst(mAgentInstances)) {
173  instance->quit();
174  }
175  mAgentInstances.clear();
176 
177  mStorageController.reset();
178  mStorageController.reset();
179 }
180 
182 {
183  return mAgents.keys();
184 }
185 
186 QString AgentManager::agentName(const QString &identifier) const
187 {
188  if (!checkAgentExists(identifier)) {
189  return QString();
190  }
191 
192  return mAgents.value(identifier).name;
193 }
194 
196 {
197  if (!checkAgentExists(identifier)) {
198  return QString();
199  }
200 
201  return mAgents.value(identifier).comment;
202 }
203 
204 QString AgentManager::agentIcon(const QString &identifier) const
205 {
206  if (!checkAgentExists(identifier)) {
207  return QString();
208  }
209 
210  const AgentType info = mAgents.value(identifier);
211  if (!info.icon.isEmpty()) {
212  return info.icon;
213  }
214 
215  return QStringLiteral("application-x-executable");
216 }
217 
219 {
220  if (!checkAgentExists(identifier)) {
221  return QStringList();
222  }
223 
224  return mAgents.value(identifier).mimeTypes;
225 }
226 
228 {
229  if (!checkAgentExists(identifier)) {
230  return QStringList();
231  }
232  return mAgents.value(identifier).capabilities;
233 }
234 
236 {
237  if (!checkAgentExists(identifier)) {
238  return QVariantMap();
239  }
240 
241  return mAgents.value(identifier).custom;
242 }
243 
245 {
246  switch (info.launchMethod) {
247  case AgentType::Server:
249  case AgentType::Launcher: // Fall through
250  case AgentType::Process:
252  default:
253  Q_ASSERT_X(false, "AgentManger::createAgentInstance", "Unhandled AgentType::LaunchMethod case");
254  }
255 
256  return AgentInstance::Ptr();
257 }
258 
260 {
261  if (!checkAgentExists(identifier)) {
262  return QString();
263  }
264 
265  const AgentType agentInfo = mAgents[identifier];
266  mAgents[identifier].instanceCounter++;
267 
268  const auto instance = createAgentInstance(agentInfo);
269  if (agentInfo.capabilities.contains(AgentType::CapabilityUnique)) {
270  instance->setIdentifier(identifier);
271  } else {
272  instance->setIdentifier(QStringLiteral("%1_%2").arg(identifier, QString::number(agentInfo.instanceCounter)));
273  }
274 
275  const QString instanceIdentifier = instance->identifier();
276  if (mAgentInstances.contains(instanceIdentifier)) {
277  qCWarning(AKONADICONTROL_LOG) << "Cannot create another instance of agent" << identifier;
278  return QString();
279  }
280 
281  // Return from this dbus call before we do the next. Otherwise dbus brakes for
282  // this process.
283  if (calledFromDBus()) {
284  connection().send(message().createReply(instanceIdentifier));
285  }
286 
287  if (!instance->start(agentInfo)) {
288  return QString();
289  }
290 
291  mAgentInstances.insert(instanceIdentifier, instance);
292  registerAgentAtServer(instanceIdentifier, agentInfo);
293  save();
294 
295  return instanceIdentifier;
296 }
297 
299 {
300  const auto instance = mAgentInstances.value(identifier);
301  if (!instance) {
302  qCWarning(AKONADICONTROL_LOG) << Q_FUNC_INFO << "Agent instance with identifier" << identifier << "does not exist";
303  return;
304  }
305 
306  if (instance->hasAgentInterface()) {
307  instance->cleanup();
308  } else {
309  qCWarning(AKONADICONTROL_LOG) << "Agent instance" << identifier << "has no interface!";
310  }
311 
312  mAgentInstances.remove(identifier);
313 
314  save();
315 
316  org::freedesktop::Akonadi::ResourceManager resmanager(Akonadi::DBus::serviceName(Akonadi::DBus::Server), QStringLiteral("/ResourceManager"), QDBusConnection::sessionBus(), this);
317  resmanager.removeResourceInstance(instance->identifier());
318 
319  // Kill the preprocessor instance, if any.
320  org::freedesktop::Akonadi::PreprocessorManager preProcessorManager(
321  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 
354 int 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 
372 uint 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 
388 void 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 
406 void 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 //
416 void 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 
461 void AgentManager::agentInstanceSynchronizeCollection(const QString &identifier, qint64 collection)
462 {
463  agentInstanceSynchronizeCollection(identifier, collection, false);
464 }
465 
466 void 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 (!checkResourceInterface(identifier, QStringLiteral("agentInstanceSynchronizeRelations"))) {
487  return;
488  }
489 
490  mAgentInstances.value(identifier)->resourceInterface()->synchronizeRelations();
491 }
492 
494 {
495  if (!checkInstance(identifier)) {
496  return;
497  }
498 
499  mAgentInstances.value(identifier)->restartWhenIdle();
500 }
501 
502 void AgentManager::updatePluginInfos()
503 {
504  const QHash<QString, AgentType> oldInfos = mAgents;
505  readPluginInfos();
506 
507  for (const AgentType &oldInfo : oldInfos) {
508  if (!mAgents.contains(oldInfo.identifier)) {
509  Q_EMIT agentTypeRemoved(oldInfo.identifier);
510  }
511  }
512 
513  for (const AgentType &newInfo : qAsConst(mAgents)) {
514  if (!oldInfos.contains(newInfo.identifier)) {
515  Q_EMIT agentTypeAdded(newInfo.identifier);
516  ensureAutoStart(newInfo);
517  }
518  }
519 }
520 
521 void AgentManager::readPluginInfos()
522 {
523  mAgents.clear();
524 
525  const QStringList pathList = pluginInfoPathList();
526 
527  for (const QString &path : pathList) {
528  const QDir directory(path, QStringLiteral("*.desktop"));
529  readPluginInfos(directory);
530  }
531 }
532 
533 void AgentManager::readPluginInfos(const QDir &directory)
534 {
535  const QStringList files = directory.entryList();
536  qCDebug(AKONADICONTROL_LOG) << "PLUGINS: " << directory.canonicalPath();
537  qCDebug(AKONADICONTROL_LOG) << "PLUGINS: " << files;
538 
539  for (int i = 0; i < files.count(); ++i) {
540  const QString fileName = directory.absoluteFilePath(files[i]);
541 
542  AgentType agentInfo;
543  if (agentInfo.load(fileName, this)) {
544  if (mAgents.contains(agentInfo.identifier)) {
545  qCWarning(AKONADICONTROL_LOG) << "Duplicated agent identifier" << agentInfo.identifier << "from file" << fileName;
546  continue;
547  }
548 
549  const QString disableAutostart = akGetEnv("AKONADI_DISABLE_AGENT_AUTOSTART");
550  if (!disableAutostart.isEmpty()) {
551  qCDebug(AKONADICONTROL_LOG) << "Autostarting of agents is disabled.";
552  agentInfo.capabilities.removeOne(AgentType::CapabilityAutostart);
553  }
554 
555  if (!mAgentServerEnabled && agentInfo.launchMethod == AgentType::Server) {
556  agentInfo.launchMethod = AgentType::Launcher;
557  }
558 
559  if (agentInfo.launchMethod == AgentType::Process) {
560  const QString executable = Akonadi::StandardDirs::findExecutable(agentInfo.exec);
561  if (executable.isEmpty()) {
562  qCWarning(AKONADICONTROL_LOG) << "Executable" << agentInfo.exec << "for agent" << agentInfo.identifier << "could not be found!";
563  continue;
564  }
565  }
566 
567  qCDebug(AKONADICONTROL_LOG) << "PLUGINS inserting: " << agentInfo.identifier << agentInfo.instanceCounter << agentInfo.capabilities;
568  mAgents.insert(agentInfo.identifier, agentInfo);
569  }
570  }
571 }
572 
573 QStringList AgentManager::pluginInfoPathList()
574 {
575  return Akonadi::StandardDirs::locateAllResourceDirs(QStringLiteral("akonadi/agents"));
576 }
577 
578 void AgentManager::load()
579 {
580  org::freedesktop::Akonadi::ResourceManager resmanager(Akonadi::DBus::serviceName(Akonadi::DBus::Server), QStringLiteral("/ResourceManager"), QDBusConnection::sessionBus(), this);
581  const QStringList knownResources = resmanager.resourceInstances();
582 
583  QSettings file(Akonadi::StandardDirs::agentsConfigFile(Akonadi::StandardDirs::ReadOnly), QSettings::IniFormat);
584  file.beginGroup(QStringLiteral("Instances"));
585  const QStringList entries = file.childGroups();
586  for (int i = 0; i < entries.count(); ++i) {
587  const QString instanceIdentifier = entries[i];
588 
589  if (mAgentInstances.contains(instanceIdentifier)) {
590  qCWarning(AKONADICONTROL_LOG) << "Duplicated instance identifier" << instanceIdentifier << "found in agentsrc";
591  continue;
592  }
593 
594  file.beginGroup(entries[i]);
595 
596  const QString agentType = file.value(QStringLiteral("AgentType")).toString();
597  const auto typeIter = mAgents.constFind(agentType);
598  if (typeIter == mAgents.cend() || typeIter->exec.isEmpty()) {
599  qCWarning(AKONADICONTROL_LOG) << "Reference to unknown agent type" << agentType << "in agentsrc, creating a fake entry.";
600  if (typeIter == mAgents.cend()) {
601  AgentType type;
602  type.identifier = type.name = agentType;
603  mAgents.insert(type.identifier, type);
604  }
605 
606  auto brokenInstance = AgentInstance::Ptr{new Akonadi::AgentBrokenInstance{agentType, *this}};
607  brokenInstance->setIdentifier(instanceIdentifier);
608  mAgentInstances.insert(instanceIdentifier, brokenInstance);
609  file.endGroup();
610  continue;
611  }
612 
613  const AgentType &type = *typeIter;
614 
615  // recover if the db has been deleted in the meantime or got otherwise corrupted
616  if (!knownResources.contains(instanceIdentifier) && type.capabilities.contains(AgentType::CapabilityResource)) {
617  qCDebug(AKONADICONTROL_LOG) << "Recovering instance" << instanceIdentifier << "after database loss";
618  registerAgentAtServer(instanceIdentifier, type);
619  }
620 
621  const AgentInstance::Ptr instance = createAgentInstance(type);
622  instance->setIdentifier(instanceIdentifier);
623  if (instance->start(type)) {
624  mAgentInstances.insert(instanceIdentifier, instance);
625  }
626 
627  file.endGroup();
628  }
629 
630  file.endGroup();
631 }
632 
633 void AgentManager::save()
634 {
635  QSettings file(Akonadi::StandardDirs::agentsConfigFile(Akonadi::StandardDirs::WriteOnly), QSettings::IniFormat);
636 
637  for (const AgentType &info : qAsConst(mAgents)) {
638  info.save(&file);
639  }
640 
641  file.beginGroup(QStringLiteral("Instances"));
642  file.remove(QString());
643  for (const AgentInstance::Ptr &instance : qAsConst(mAgentInstances)) {
644  file.beginGroup(instance->identifier());
645  file.setValue(QStringLiteral("AgentType"), instance->agentType());
646  file.endGroup();
647  }
648 
649  file.endGroup();
650 }
651 
652 void AgentManager::serviceOwnerChanged(const QString &name, const QString &oldOwner, const QString &newOwner)
653 {
654  Q_UNUSED(oldOwner);
655  // This is called by the D-Bus server when a service comes up, goes down or changes ownership for some reason
656  // and this is where we "hook up" our different Agent interfaces.
657 
658  // Ignore DBus address name (e.g. :1.310)
659  if (name.startsWith(QLatin1Char(':'))) {
660  return;
661  }
662 
663  // Ignore services belonging to another Akonadi instance
664  const auto parsedInstance = Akonadi::DBus::parseInstanceIdentifier(name);
665  const auto currentInstance = Akonadi::Instance::hasIdentifier() ? std::optional<QString>(Akonadi::Instance::identifier()) : std::nullopt;
666  if (parsedInstance != currentInstance) {
667  return;
668  }
669 
670  qCDebug(AKONADICONTROL_LOG) << "Service" << name << "owner changed from" << oldOwner << "to" << newOwner;
671 
672  if ((name == Akonadi::DBus::serviceName(Akonadi::DBus::Server) || name == Akonadi::DBus::serviceName(Akonadi::DBus::AgentServer)) && !newOwner.isEmpty()) {
673  if (QDBusConnection::sessionBus().interface()->isServiceRegistered(Akonadi::DBus::serviceName(Akonadi::DBus::Server))
674  && (!mAgentServer || QDBusConnection::sessionBus().interface()->isServiceRegistered(Akonadi::DBus::serviceName(Akonadi::DBus::AgentServer)))) {
675  // server is operational, start agents
676  continueStartup();
677  }
678  }
679 
680  const auto service = Akonadi::DBus::parseAgentServiceName(name);
681  if (!service.has_value()) {
682  return;
683  }
684  switch (service->agentType) {
685  case Akonadi::DBus::Agent: {
686  // An agent service went up or down
687  if (newOwner.isEmpty()) {
688  return; // It went down: we don't care here.
689  }
690 
691  if (!mAgentInstances.contains(service->identifier)) {
692  return;
693  }
694 
695  const AgentInstance::Ptr instance = mAgentInstances.value(service->identifier);
696  const bool restarting = instance->hasAgentInterface();
697  if (!instance->obtainAgentInterface()) {
698  return;
699  }
700 
701  Q_ASSERT(mAgents.contains(instance->agentType()));
702  const bool isResource = mAgents.value(instance->agentType()).capabilities.contains(AgentType::CapabilityResource);
703 
704  if (!restarting && (!isResource || instance->hasResourceInterface())) {
705  Q_EMIT agentInstanceAdded(service->identifier);
706  }
707 
708  break;
709  }
710  case Akonadi::DBus::Resource: {
711  // A resource service went up or down
712  if (newOwner.isEmpty()) {
713  return; // It went down: we don't care here.
714  }
715 
716  if (!mAgentInstances.contains(service->identifier)) {
717  return;
718  }
719 
720  const AgentInstance::Ptr instance = mAgentInstances.value(service->identifier);
721  const bool restarting = instance->hasResourceInterface();
722  if (!instance->obtainResourceInterface()) {
723  return;
724  }
725 
726  if (!restarting && instance->hasAgentInterface()) {
727  Q_EMIT agentInstanceAdded(service->identifier);
728  }
729 
730  break;
731  }
732  case Akonadi::DBus::Preprocessor: {
733  // A preprocessor service went up or down
734 
735  // If the preprocessor is going up then the org.freedesktop.Akonadi.Agent.* interface
736  // should be already up (as it's registered before the preprocessor one).
737  // So if we don't know about the preprocessor as agent instance
738  // then it's not our preprocessor.
739 
740  // If the preprocessor is going down then either the agent interface already
741  // went down (and it has been already unregistered on the manager side)
742  // or it's still registered as agent and WE have to unregister it.
743  // The order of interface deletions depends on Qt but we handle both cases.
744 
745  // Check if we "know" about it.
746  qCDebug(AKONADICONTROL_LOG) << "Preprocessor " << service->identifier << " is going up or down...";
747 
748  if (!mAgentInstances.contains(service->identifier)) {
749  qCDebug(AKONADICONTROL_LOG) << "But it isn't registered as agent... not mine (anymore?)";
750  return; // not our agent (?)
751  }
752 
753  org::freedesktop::Akonadi::PreprocessorManager preProcessorManager(
754  Akonadi::DBus::serviceName(Akonadi::DBus::Server),
755  QStringLiteral("/PreprocessorManager"),
757  this);
758 
759  if (!preProcessorManager.isValid()) {
760  qCWarning(AKONADICONTROL_LOG) << "Could not connect to PreprocessorManager via D-Bus:" << preProcessorManager.lastError().message();
761  } else {
762  if (newOwner.isEmpty()) {
763  // The preprocessor went down. Unregister it on server side.
764 
765  preProcessorManager.unregisterInstance(service->identifier);
766 
767  } else {
768 
769  // The preprocessor went up. Register it on server side.
770 
771  if (!mAgentInstances.value(service->identifier)->obtainPreprocessorInterface()) {
772  // Hm.. couldn't hook up its preprocessor interface..
773  // Make sure we don't have it in the preprocessor chain
774  qCWarning(AKONADICONTROL_LOG) << "Couldn't obtain preprocessor interface for instance" << service->identifier;
775 
776  preProcessorManager.unregisterInstance(service->identifier);
777  return;
778  }
779 
780  qCDebug(AKONADICONTROL_LOG) << "Registering preprocessor instance" << service->identifier;
781 
782  // Add to the preprocessor chain
783  preProcessorManager.registerInstance(service->identifier);
784  }
785  }
786 
787  break;
788  }
789  default:
790  break;
791  }
792 }
793 
794 bool AgentManager::checkInstance(const QString &identifier) const
795 {
796  if (!mAgentInstances.contains(identifier)) {
797  qCWarning(AKONADICONTROL_LOG) << "Agent instance with identifier " << identifier << " does not exist";
798  return false;
799  }
800 
801  return true;
802 }
803 
804 bool AgentManager::checkResourceInterface(const QString &identifier, const QString &method) const
805 {
806  if (!checkInstance(identifier)) {
807  return false;
808  }
809 
810  if (!mAgents[mAgentInstances[identifier]->agentType()].capabilities.contains(QLatin1String("Resource"))) {
811  return false;
812  }
813 
814  if (!mAgentInstances[identifier]->hasResourceInterface()) {
815  qCWarning(AKONADICONTROL_LOG) << QLatin1String("AgentManager::") + method << " Agent instance "
816  << identifier << " has no resource interface!";
817  return false;
818  }
819 
820  return true;
821 }
822 
823 bool AgentManager::checkAgentExists(const QString &identifier) const
824 {
825  if (!mAgents.contains(identifier)) {
826  qCWarning(AKONADICONTROL_LOG) << "Agent instance " << identifier << " does not exist.";
827  return false;
828  }
829 
830  return true;
831 }
832 
833 bool AgentManager::checkAgentInterfaces(const QString &identifier, const QString &method) const
834 {
835  if (!checkInstance(identifier)) {
836  return false;
837  }
838 
839  if (!mAgentInstances.value(identifier)->hasAgentInterface()) {
840  qCWarning(AKONADICONTROL_LOG) << "Agent instance (" << method << ") " << identifier << " has no agent interface.";
841  return false;
842  }
843 
844  return true;
845 }
846 
847 void AgentManager::ensureAutoStart(const AgentType &info)
848 {
849  if (!info.capabilities.contains(AgentType::CapabilityAutostart)) {
850  return; // no an autostart agent
851  }
852 
853  org::freedesktop::Akonadi::AgentServer agentServer(Akonadi::DBus::serviceName(Akonadi::DBus::AgentServer),
854  QStringLiteral("/AgentServer"), QDBusConnection::sessionBus(), this);
855 
856  if (mAgentInstances.contains(info.identifier) ||
857  (agentServer.isValid() && agentServer.started(info.identifier))) {
858  return; // already running
859  }
860 
861  const AgentInstance::Ptr instance = createAgentInstance(info);
862  instance->setIdentifier(info.identifier);
863  if (instance->start(info)) {
864  mAgentInstances.insert(instance->identifier(), instance);
865  registerAgentAtServer(instance->identifier(), info);
866  save();
867  }
868 }
869 
870 void AgentManager::agentExeChanged(const QString &fileName)
871 {
872  if (!QFile::exists(fileName)) {
873  return;
874  }
875 
876  for (const AgentType &type : qAsConst(mAgents)) {
877  if (fileName.endsWith(type.exec)) {
878  for (const AgentInstance::Ptr &instance : qAsConst(mAgentInstances)) {
879  if (instance->agentType() == type.identifier) {
880  instance->restartWhenIdle();
881  }
882  }
883  }
884  }
885 }
886 
887 void AgentManager::registerAgentAtServer(const QString &agentIdentifier, const AgentType &type)
888 {
889  if (type.capabilities.contains(AgentType::CapabilityResource)) {
891  new org::freedesktop::Akonadi::ResourceManager(Akonadi::DBus::serviceName(Akonadi::DBus::Server),
892  QStringLiteral("/ResourceManager"),
893  QDBusConnection::sessionBus(), this));
894  resmanager->addResourceInstance(agentIdentifier, type.capabilities);
895  }
896 }
897 
898 void AgentManager::addSearch(const QString &query, const QString &queryLanguage, qint64 resultCollectionId)
899 {
900  qCDebug(AKONADICONTROL_LOG) << "AgentManager::addSearch" << query << queryLanguage << resultCollectionId;
901  for (const AgentInstance::Ptr &instance : qAsConst(mAgentInstances)) {
902  const AgentType type = mAgents.value(instance->agentType());
903  if (type.capabilities.contains(AgentType::CapabilitySearch) && instance->searchInterface()) {
904  instance->searchInterface()->addSearch(query, queryLanguage, resultCollectionId);
905  }
906  }
907 }
908 
909 void AgentManager::removeSearch(quint64 resultCollectionId)
910 {
911  qCDebug(AKONADICONTROL_LOG) << "AgentManager::removeSearch" << resultCollectionId;
912  for (const AgentInstance::Ptr &instance : qAsConst(mAgentInstances)) {
913  const AgentType type = mAgents.value(instance->agentType());
914  if (type.capabilities.contains(AgentType::CapabilitySearch) && instance->searchInterface()) {
915  instance->searchInterface()->removeSearch(resultCollectionId);
916  }
917  }
918 }
919 
920 #include "agentmanager.moc"
void agentInstanceRemoved(const QString &agentIdentifier)
This signal is emitted whenever an agent instance was removed.
QString agentInstanceStatusMessage(const QString &identifier) const
Returns the i18n&#39;ed description of the current status of the agent with the given identifier...
QHash::iterator insert(const Key &key, const T &value)
uint agentInstanceProgress(const QString &identifier) const
Returns the current progress of the agent with the given identifier in percentage.
QSharedPointer< T > create(Args &&...args)
int agentInstanceStatus(const QString &identifier) const
Returns the current status code of the agent with the given identifier.
void agentInstanceSynchronizeTags(const QString &identifier)
Trigger a synchronization of tags by the given resource agent.
AgentManager(bool verbose, QObject *parent=nullptr)
Creates a new agent manager.
void endGroup()
~AgentManager()
Destroys the agent manager.
QDBusConnectionInterface * interface() const const
bool calledFromDBus() const const
bool registerObject(const QString &path, QObject *object, QDBusConnection::RegisterOptions options)
bool contains(const QString &str, Qt::CaseSensitivity cs) const const
QVariantMap agentCustomProperties(const QString &identifier) const
Returns a list of Custom added propeties of the agent type for the given identifier.
QHash::const_iterator constFind(const Key &key) const const
QDBusConnection sessionBus()
QString agentComment(const QString &identifier) const
Returns the i18n&#39;ed comment of the agent type for the given identifier.
bool exists() const const
QDBusReply< bool > isServiceRegistered(const QString &serviceName) const const
QHash::const_iterator cend() const const
QString agentIcon(const QString &identifier) const
Returns the icon name of the agent type for the given identifier.
void agentInstanceConfigure(const QString &identifier, qlonglong windowId)
Triggers the agent instance with the given identifier to show its configuration dialog.
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.
QStringList agentTypes() const
Returns the list of identifiers of all available agent types.
void setValue(const QString &key, const QVariant &value)
void exit(int returnCode)
QString createAgentInstance(const QString &identifier)
Creates a new agent of the given agent type identifier.
QString number(int n, int base)
int count(const T &value) const const
void agentInstanceSynchronizeCollectionTree(const QString &identifier)
Trigger a synchronization of the collection tree by the given resource agent.
bool isEmpty() const const
bool send(const QDBusMessage &message) const const
bool startsWith(const QString &s, Qt::CaseSensitivity cs) const const
QStringList childGroups() const const
bool endsWith(const QString &s, Qt::CaseSensitivity cs) const const
QCoreApplication * instance()
QStringList agentInstances() const
Returns the list of identifiers of configured instances.
int remove(const Key &key)
void agentInstanceSynchronize(const QString &identifier)
Triggers the agent instance with the given identifier to start synchronization.
void unableToStart()
Emitted if the process could not be started since it terminated too often.
void agentInstanceSynchronizeCollection(const QString &identifier, qint64 collection)
Trigger a synchronization of the given collection by its owning resource agent.
QStringList agentCapabilities(const QString &identifier) const
Returns a list of supported capabilities of the agent type for the given identifier.
void setAgentInstanceName(const QString &identifier, const QString &name)
Sets the name of the agent instance with the given identifier.
QList< Key > keys() const const
void clear()
const T value(const Key &key) const const
void removeSearch(quint64 resultCollectionId)
Removes a persistent search for the given result collection.
QStringList agentMimeTypes(const QString &identifier) const
Returns a list of supported mimetypes of the agent type for the given identifier. ...
void remove(const QString &key)
QString agentInstanceType(const QString &identifier)
Returns the type of the agent instance with the given identifier.
void agentInstanceSynchronizeRelations(const QString &identifier)
Trigger a synchronization of relations by the given resource agent.
QDBusConnection connection() const const
This class starts and observes a process.
QString agentName(const QString &identifier) const
Returns the i18n&#39;ed name of the agent type for the given identifier.
QVariant value(const QString &key, const QVariant &defaultValue) const const
void cleanup()
Called by the crash handler and dtor to terminate the child processes.
void setAgentInstanceOnline(const QString &identifier, bool state)
Sets agent instance identifier to online or offline mode.
QStringList entryList(QDir::Filters filters, QDir::SortFlags sort) const const
Helper integration between Akonadi and Qt.
QString absoluteFilePath(const QString &fileName) const const
void restartAgentInstance(const QString &identifier)
Restarts the agent instance identifier.
bool toBool() const const
void serviceOwnerChanged(const QString &name, const QString &oldOwner, const QString &newOwner)
QString canonicalPath() const const
bool contains(const Key &key) const const
void removeAgentInstance(const QString &identifier)
Removes the agent with the given identifier.
const QDBusMessage & message() const const
void agentTypeRemoved(const QString &agentType)
This signal is emitted whenever an agent type was removed from the system.
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
QString agentInstanceProgressMessage(const QString &identifier) const
Returns the i18n&#39;ed description of the current progress of the agent with the given identifier...
QString agentInstanceName(const QString &identifier) const
Returns the name of the agent instance with the given identifier.
QString toString() const const
void beginGroup(const QString &prefix)
Q_EMITQ_EMIT
bool agentInstanceOnline(const QString &identifier)
Returns if the agent instance identifier is in online mode.
void agentTypeAdded(const QString &agentType)
This signal is emitted whenever a new agent type was installed on the system.
This file is part of the KDE documentation.
Documentation copyright © 1996-2020 The KDE developers.
Generated on Tue May 26 2020 22:46:17 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.