• Skip to content
  • Skip to link menu
KDE API Reference
  • KDE API Reference
  • kdepimlibs API Reference
  • KDE Home
  • Contact Us
 

akonadi

  • sources
  • kde-4.14
  • kdepimlibs
  • akonadi
servermanager.cpp
1 /*
2  Copyright (c) 2008 Volker Krause <vkrause@kde.org>
3 
4  This library is free software; you can redistribute it and/or modify it
5  under the terms of the GNU Library General Public License as published by
6  the Free Software Foundation; either version 2 of the License, or (at your
7  option) any later version.
8 
9  This library is distributed in the hope that it will be useful, but WITHOUT
10  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11  FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public
12  License for more details.
13 
14  You should have received a copy of the GNU Library General Public License
15  along with this library; see the file COPYING.LIB. If not, write to the
16  Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17  02110-1301, USA.
18 */
19 
20 #include "servermanager.h"
21 #include "servermanager_p.h"
22 
23 #include "agenttype.h"
24 #include "agentbase.h"
25 #include "agentmanager.h"
26 #include "dbusconnectionpool.h"
27 #include "selftestdialog_p.h"
28 #include "session_p.h"
29 #include "firstrun_p.h"
30 
31 #include <KDebug>
32 #include <KGlobal>
33 
34 #include <akonadi/private/protocol_p.h>
35 #include <akonadi/private/xdgbasedirs_p.h>
36 
37 #include <QtDBus>
38 #include <QPointer>
39 #include <QTimer>
40 
41 #include <boost/scoped_ptr.hpp>
42 
43 using namespace Akonadi;
44 
45 class Akonadi::ServerManagerPrivate
46 {
47 public:
48  ServerManagerPrivate()
49  : instance(new ServerManager(this))
50  , mState(ServerManager::NotRunning)
51  , mSafetyTimer(new QTimer)
52  , mFirstRunner(0)
53  {
54  mState = instance->state();
55  mSafetyTimer->setSingleShot(true);
56  mSafetyTimer->setInterval(30000);
57  QObject::connect(mSafetyTimer.get(), SIGNAL(timeout()), instance, SLOT(timeout()));
58  KGlobal::locale()->insertCatalog(QString::fromLatin1("libakonadi"));
59  if (mState == ServerManager::Running && Internal::clientType() == Internal::User && !ServerManager::hasInstanceIdentifier()) {
60  mFirstRunner = new Firstrun(instance);
61  }
62  }
63 
64  ~ServerManagerPrivate()
65  {
66  delete instance;
67  }
68 
69  void serviceOwnerChanged( const QString &name, const QString &oldOwner, const QString &newOwner )
70  {
71  if (name == ServerManager::serviceName(ServerManager::ControlLock ) && !oldOwner.isEmpty() && newOwner.isEmpty()) {
72  // Control.Lock has disappeared during startup, which means that akonadi_control
73  // has terminated, most probably because it was not able to start akonadiserver
74  // process. Don't wait 30 seconds for sefetyTimeout, but go into Broken state
75  // immediately.
76  if (mState == ServerManager::Starting) {
77  setState(ServerManager::Broken);
78  return;
79  }
80  }
81 
82  serverProtocolVersion = -1,
83  checkStatusChanged();
84  }
85 
86  void checkStatusChanged()
87  {
88  setState(instance->state());
89  }
90 
91  void setState(ServerManager::State state)
92  {
93 
94  if (mState != state) {
95  mState = state;
96  emit instance->stateChanged(state);
97  if (state == ServerManager::Running) {
98  emit instance->started();
99  if (!mFirstRunner && Internal::clientType() == Internal::User && !ServerManager::hasInstanceIdentifier()) {
100  mFirstRunner = new Firstrun(instance);
101  }
102  } else if (state == ServerManager::NotRunning || state == ServerManager::Broken) {
103  emit instance->stopped();
104  }
105 
106  if (state == ServerManager::Starting || state == ServerManager::Stopping) {
107  QMetaObject::invokeMethod(mSafetyTimer.get(), "start", Qt::QueuedConnection); // in case we are in a different thread
108  } else {
109  QMetaObject::invokeMethod(mSafetyTimer.get(), "stop", Qt::QueuedConnection); // in case we are in a different thread
110  }
111  }
112  }
113 
114  void timeout()
115  {
116  if (mState == ServerManager::Starting || mState == ServerManager::Stopping) {
117  setState(ServerManager::Broken);
118  }
119  }
120 
121  ServerManager *instance;
122  static int serverProtocolVersion;
123  ServerManager::State mState;
124  boost::scoped_ptr<QTimer> mSafetyTimer;
125  Firstrun *mFirstRunner;
126  static Internal::ClientType clientType;
127 };
128 
129 int ServerManagerPrivate::serverProtocolVersion = -1;
130 Internal::ClientType ServerManagerPrivate::clientType = Internal::User;
131 
132 K_GLOBAL_STATIC(ServerManagerPrivate, sInstance)
133 
134 ServerManager::ServerManager(ServerManagerPrivate *dd)
135  : d(dd)
136 {
137  qRegisterMetaType<Akonadi::ServerManager::State>();
138 
139  QDBusServiceWatcher *watcher = new QDBusServiceWatcher(ServerManager::serviceName(ServerManager::Server),
140  DBusConnectionPool::threadConnection(),
141  QDBusServiceWatcher::WatchForOwnerChange, this);
142  watcher->addWatchedService(ServerManager::serviceName(ServerManager::Control));
143  watcher->addWatchedService(ServerManager::serviceName(ServerManager::ControlLock));
144  watcher->addWatchedService(ServerManager::serviceName(ServerManager::UpgradeIndicator));
145 
146  // this (and also the two connects below) are queued so that they trigger after AgentManager is done loading
147  // the current agent types and instances
148  // this ensures the invariant of AgentManager reporting a consistent state if ServerManager::state() == Running
149  // that's the case with direct connections as well, but only after you enter the event loop once
150  connect(watcher, SIGNAL(serviceOwnerChanged(QString,QString,QString)),
151  this, SLOT(serviceOwnerChanged(QString,QString,QString)), Qt::QueuedConnection);
152 
153  // AgentManager is dangerous to use for agents themselves
154  if (Internal::clientType() != Internal::User) {
155  return;
156  }
157  connect(AgentManager::self(), SIGNAL(typeAdded(Akonadi::AgentType)), SLOT(checkStatusChanged()), Qt::QueuedConnection);
158  connect(AgentManager::self(), SIGNAL(typeRemoved(Akonadi::AgentType)), SLOT(checkStatusChanged()), Qt::QueuedConnection);
159 }
160 
161 ServerManager *Akonadi::ServerManager::self()
162 {
163  return sInstance->instance;
164 }
165 
166 bool ServerManager::start()
167 {
168  const bool controlRegistered = DBusConnectionPool::threadConnection().interface()->isServiceRegistered(ServerManager::serviceName(ServerManager::Control));
169  const bool serverRegistered = DBusConnectionPool::threadConnection().interface()->isServiceRegistered(ServerManager::serviceName(ServerManager::Server));
170  if (controlRegistered && serverRegistered) {
171  return true;
172  }
173 
174  const bool controlLockRegistered = DBusConnectionPool::threadConnection().interface()->isServiceRegistered(ServerManager::serviceName(ServerManager::ControlLock));
175  if (controlLockRegistered || controlRegistered) {
176  kDebug() << "Akonadi server is already starting up";
177  sInstance->setState(Starting);
178  return true;
179  }
180 
181  kDebug() << "executing akonadi_control";
182  QStringList args;
183  if (hasInstanceIdentifier()) {
184  args << QLatin1String("--instance") << instanceIdentifier();
185  }
186  const bool ok = QProcess::startDetached(QLatin1String("akonadi_control"), args);
187  if (!ok) {
188  kWarning() << "Unable to execute akonadi_control, falling back to D-Bus auto-launch";
189  QDBusReply<void> reply = DBusConnectionPool::threadConnection().interface()->startService(ServerManager::serviceName(ServerManager::Control));
190  if (!reply.isValid()) {
191  kDebug() << "Akonadi server could not be started via D-Bus either: "
192  << reply.error().message();
193  return false;
194  }
195  }
196  sInstance->setState(Starting);
197  return true;
198 }
199 
200 bool ServerManager::stop()
201 {
202  QDBusInterface iface(ServerManager::serviceName(ServerManager::Control),
203  QString::fromLatin1("/ControlManager"),
204  QString::fromLatin1("org.freedesktop.Akonadi.ControlManager"));
205  if (!iface.isValid()) {
206  return false;
207  }
208  iface.call(QDBus::NoBlock, QString::fromLatin1("shutdown"));
209  sInstance->setState(Stopping);
210  return true;
211 }
212 
213 void ServerManager::showSelfTestDialog(QWidget *parent)
214 {
215  QPointer<Akonadi::SelfTestDialog> dlg(new Akonadi::SelfTestDialog(parent));
216  dlg->hideIntroduction();
217  dlg->exec();
218  delete dlg;
219 }
220 
221 bool ServerManager::isRunning()
222 {
223  return state() == Running;
224 }
225 
226 ServerManager::State ServerManager::state()
227 {
228  ServerManager::State previousState = NotRunning;
229  if (sInstance.exists()) { // be careful, this is called from the ServerManager::Private ctor, so using sInstance unprotected can cause infinite recursion
230  previousState = sInstance->mState;
231  }
232 
233  const bool serverUpgrading = DBusConnectionPool::threadConnection().interface()->isServiceRegistered(ServerManager::serviceName(ServerManager::UpgradeIndicator));
234  if (serverUpgrading) {
235  return Upgrading;
236  }
237 
238  const bool controlRegistered = DBusConnectionPool::threadConnection().interface()->isServiceRegistered(ServerManager::serviceName(ServerManager::Control));
239  const bool serverRegistered = DBusConnectionPool::threadConnection().interface()->isServiceRegistered(ServerManager::serviceName(ServerManager::Server));
240  if (controlRegistered && serverRegistered) {
241  // check if the server protocol is recent enough
242  if (sInstance.exists()) {
243  if (Internal::serverProtocolVersion() >= 0 &&
244  Internal::serverProtocolVersion() < SessionPrivate::minimumProtocolVersion()) {
245  return Broken;
246  }
247  }
248 
249  // AgentManager is dangerous to use for agents themselves
250  if (Internal::clientType() == Internal::User) {
251  // besides the running server processes we also need at least one resource to be operational
252  AgentType::List agentTypes = AgentManager::self()->types();
253  foreach (const AgentType &type, agentTypes) {
254  if (type.capabilities().contains(QLatin1String("Resource"))) {
255  return Running;
256  }
257  }
258  return Broken;
259  } else {
260  return Running;
261  }
262  }
263 
264  const bool controlLockRegistered = DBusConnectionPool::threadConnection().interface()->isServiceRegistered(ServerManager::serviceName(ServerManager::ControlLock));
265  if (controlLockRegistered || controlRegistered) {
266  kDebug() << "Akonadi server is already starting up";
267  if (previousState == Running) {
268  return NotRunning; // we don't know if it's starting or stopping, probably triggered by someone else
269  }
270  return previousState;
271  }
272 
273  if (serverRegistered) {
274  kWarning() << "Akonadi server running without control process!";
275  return Broken;
276  }
277 
278  if (previousState == Starting) { // valid case where nothing is running (yet)
279  return previousState;
280  }
281  return NotRunning;
282 }
283 
284 QString ServerManager::instanceIdentifier()
285 {
286  return QLatin1String(qgetenv("AKONADI_INSTANCE"));
287 }
288 
289 bool ServerManager::hasInstanceIdentifier()
290 {
291  return !instanceIdentifier().isEmpty();
292 }
293 
294 static QString makeServiceName(const char *base, const QString &name = QString())
295 {
296  if (ServerManager::instanceIdentifier().isEmpty()) {
297  return QLatin1String(base) % name;
298  }
299  return QLatin1String(base) % name % QLatin1Literal(".") % ServerManager::instanceIdentifier();
300 }
301 
302 // remove once we require Akonadi 1.9
303 #ifndef AKONADI_DBUS_SERVER_SERVICE_UPGRADING
304 #define AKONADI_DBUS_SERVER_SERVICE_UPGRADING "org.freedesktop.Akonadi.upgrading"
305 #endif
306 
307 QString ServerManager::serviceName(ServerManager::ServiceType serviceType)
308 {
309  switch (serviceType) {
310  case Server:
311  return makeServiceName(AKONADI_DBUS_SERVER_SERVICE);
312  case Control:
313  return makeServiceName(AKONADI_DBUS_CONTROL_SERVICE);
314  case ControlLock:
315  return makeServiceName(AKONADI_DBUS_CONTROL_SERVICE_LOCK);
316  case UpgradeIndicator:
317  return makeServiceName(AKONADI_DBUS_SERVER_SERVICE_UPGRADING);
318  }
319  Q_ASSERT(!"WTF?");
320  return QString();
321 }
322 
323 QString ServerManager::agentServiceName(ServiceAgentType agentType, const QString &identifier)
324 {
325  switch (agentType) {
326  case Agent:
327  return makeServiceName(AKONADI_DBUS_SERVER_SERVICE, QString::fromLatin1(".Agent.%1").arg(identifier));
328  case Resource:
329  return makeServiceName(AKONADI_DBUS_SERVER_SERVICE, QString::fromLatin1(".Resource.%1").arg(identifier));
330  case Preprocessor:
331  return makeServiceName(AKONADI_DBUS_SERVER_SERVICE, QString::fromLatin1(".Preprocessor.%1").arg(identifier));
332  }
333  Q_ASSERT(!"WTF?");
334  return QString();
335 }
336 
337 QString ServerManager::addNamespace(const QString &string)
338 {
339  if (ServerManager::hasInstanceIdentifier()) {
340  return string % QLatin1Char('_') % ServerManager::instanceIdentifier();
341  }
342  return string;
343 }
344 
345 int Internal::serverProtocolVersion()
346 {
347  return ServerManagerPrivate::serverProtocolVersion;
348 }
349 
350 void Internal::setServerProtocolVersion(int version)
351 {
352  ServerManagerPrivate::serverProtocolVersion = version;
353  if (sInstance.exists()) {
354  sInstance->checkStatusChanged();
355  }
356 }
357 
358 Internal::ClientType Internal::clientType()
359 {
360  return ServerManagerPrivate::clientType;
361 }
362 
363 void Internal::setClientType(ClientType type)
364 {
365  ServerManagerPrivate::clientType = type;
366 }
367 
368 QString Internal::xdgSaveDir(const char *resource, const QString &relPath)
369 {
370  QString fullRelPath = QLatin1String("akonadi");
371  if (!ServerManager::instanceIdentifier().isEmpty()) {
372  fullRelPath += QLatin1String("/instance/") + ServerManager::instanceIdentifier();
373  }
374  if (!relPath.isEmpty()) {
375  fullRelPath += QLatin1Char('/') + relPath;
376  }
377  return XdgBaseDirs::saveDir(resource, fullRelPath);
378 }
379 
380 #include "moc_servermanager.cpp"
QWidget
QDBusAbstractInterface::isValid
bool isValid() const
Akonadi::ServerManager::instanceIdentifier
static QString instanceIdentifier()
Returns the identifier of the Akonadi instance we are connected to.
Definition: servermanager.cpp:284
Akonadi::ServerManager::ServiceAgentType
ServiceAgentType
Known agent types.
Definition: servermanager.h:135
QDBusReply
QProcess::startDetached
bool startDetached(const QString &program, const QStringList &arguments, const QString &workingDirectory, qint64 *pid)
QPointer
QStringList::contains
bool contains(const QString &str, Qt::CaseSensitivity cs) const
Akonadi::ServerManager::self
static ServerManager * self()
Returns the singleton instance of this class, for connecting to its signals.
Definition: servermanager.cpp:161
Akonadi::Control
Provides methods to control the Akonadi server process.
Definition: control.h:62
QDBusError::message
QString message() const
QDBusReply::isValid
bool isValid() const
Akonadi::ServerManager::serviceName
static QString serviceName(ServiceType serviceType)
Returns the namespaced D-Bus service name for serviceType.
Definition: servermanager.cpp:307
Akonadi::ServerManager
Provides methods to control the Akonadi server process.
Definition: servermanager.h:42
QDBusAbstractInterface::call
QDBusMessage call(const QString &method, const QVariant &arg1, const QVariant &arg2, const QVariant &arg3, const QVariant &arg4, const QVariant &arg5, const QVariant &arg6, const QVariant &arg7, const QVariant &arg8)
Akonadi::AgentType
A representation of an agent type.
Definition: agenttype.h:58
QTimer
Akonadi::ServerManager::Stopping
Server is shutting down.
Definition: servermanager.h:54
Akonadi::ServerManager::showSelfTestDialog
static void showSelfTestDialog(QWidget *parent)
Shows the Akonadi self test dialog, which tests Akonadi for various problems and reports these to the...
Definition: servermanager.cpp:213
Akonadi::ServerManager::state
static State state()
Returns the state of the server.
Definition: servermanager.cpp:226
QString::isEmpty
bool isEmpty() const
Akonadi::AgentType::capabilities
QStringList capabilities() const
Returns the list of supported capabilities of the agent type.
Definition: agenttype.cpp:76
Akonadi::ServerManager::Upgrading
Server is performing a database upgrade as part of a new startup.
Definition: servermanager.h:56
QString
QList
Akonadi::ServerManager::NotRunning
Server is not running, could be no one started it yet or it failed to start.
Definition: servermanager.h:51
Akonadi::ServerManager::agentServiceName
static QString agentServiceName(ServiceAgentType agentType, const QString &identifier)
Returns the namespaced D-Bus service name for an agent of type agentType with agent identifier identi...
Definition: servermanager.cpp:323
Akonadi::ServerManager::start
static bool start()
Starts the server.
Definition: servermanager.cpp:166
Akonadi::ServerManager::Starting
Server was started but is not yet running.
Definition: servermanager.h:52
QStringList
QDBusInterface
QLatin1Char
QMetaObject::invokeMethod
bool invokeMethod(QObject *obj, const char *member, Qt::ConnectionType type, QGenericReturnArgument ret, QGenericArgument val0, QGenericArgument val1, QGenericArgument val2, QGenericArgument val3, QGenericArgument val4, QGenericArgument val5, QGenericArgument val6, QGenericArgument val7, QGenericArgument val8, QGenericArgument val9)
Akonadi::ServerManager::Running
Server is running and operational.
Definition: servermanager.h:53
QLatin1String
Akonadi::AgentManager::types
AgentType::List types() const
Returns the list of all available agent types.
Definition: agentmanager.cpp:386
Akonadi::ServerManager::ServiceType
ServiceType
Types of known D-Bus services.
Definition: servermanager.h:115
Akonadi::AgentManager::self
static AgentManager * self()
Returns the global instance of the agent manager.
Definition: agentmanager.cpp:377
QString::fromLatin1
QString fromLatin1(const char *str, int size)
Akonadi::ServerManager::State
State
Enum for the various states the server can be in.
Definition: servermanager.h:50
Akonadi::SelfTestDialog
A dialog that checks the current status of the Akonadi system.
Definition: selftestdialog_p.h:44
Akonadi::Firstrun
Takes care of setting up default resource agents when running Akonadi for the first time...
Definition: firstrun_p.h:67
Akonadi::ServerManager::stop
static bool stop()
Stops the server.
Definition: servermanager.cpp:200
QObject::connect
bool connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
Akonadi::ServerManager::addNamespace
static QString addNamespace(const QString &string)
Adds the multi-instance namespace to string if required (with '_' as separator).
Definition: servermanager.cpp:337
Akonadi::ServerManager::isRunning
static bool isRunning()
Checks if the server is available currently.
Definition: servermanager.cpp:221
QDBusReply::error
const QDBusError & error()
Akonadi::ServerManager::hasInstanceIdentifier
static bool hasInstanceIdentifier()
Returns true if we are connected to a non-default Akonadi server instance.
Definition: servermanager.cpp:289
Akonadi::ServerManager::Broken
Server is not operational and an error has been detected.
Definition: servermanager.h:55
QDBusServiceWatcher
This file is part of the KDE documentation.
Documentation copyright © 1996-2020 The KDE developers.
Generated on Mon Jun 22 2020 13:38:03 by doxygen 1.8.7 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.

akonadi

Skip menu "akonadi"
  • Main Page
  • Namespace List
  • Namespace Members
  • Alphabetical List
  • Class List
  • Class Hierarchy
  • Class Members
  • File List
  • Modules
  • Related Pages

kdepimlibs API Reference

Skip menu "kdepimlibs API Reference"
  • akonadi
  •   contact
  •   kmime
  •   socialutils
  • kabc
  • kalarmcal
  • kblog
  • kcal
  • kcalcore
  • kcalutils
  • kholidays
  • kimap
  • kioslave
  •   imap4
  •   mbox
  •   nntp
  • kldap
  • kmbox
  • kmime
  • kontactinterface
  • kpimidentities
  • kpimtextedit
  • kpimutils
  • kresources
  • ktnef
  • kxmlrpcclient
  • mailtransport
  • microblog
  • qgpgme
  • syndication
  •   atom
  •   rdf
  •   rss2

Search



Report problems with this website to our bug tracking system.
Contact the specific authors with questions and comments about the page contents.

KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal