2 * Copyright (C) 2011 Intel Corporation
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) version 3.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
20 #ifndef SYNCEVO_DBUS_SERVER_H
21 #define SYNCEVO_DBUS_SERVER_H
25 #include <boost/shared_ptr.hpp>
26 #include <boost/weak_ptr.hpp>
27 #include <boost/signals2.hpp>
29 #include <syncevo/SyncConfig.h>
31 #include "exceptions.h"
32 #include "auto-term.h"
34 #include "dbus-callbacks.h"
35 #include "read-operations.h"
37 #include <syncevo/declarations.h>
48 class AutoSyncManager;
51 class NetworkManagerClient;
53 // TODO: avoid polluting namespace
57 * Implements the main org.syncevolution.Server interface.
59 * The Server class is responsible for listening to clients and
60 * spinning of sync sessions as requested by clients.
62 class Server : public GDBusCXX::DBusObjectHelper,
66 bool &m_shutdownRequested;
67 Timespec m_lastFileMod;
68 boost::shared_ptr<SyncEvo::Restart> &m_restart;
70 uint32_t m_lastSession;
71 typedef std::list< std::pair< boost::shared_ptr<GDBusCXX::Watch>, boost::shared_ptr<Client> > > Clients_t;
75 * Functor will never be called, important are the shared pointers
76 * bound to it. m_delayDeletion will be cleared in idle and when
77 * server terminates, thus unrefing anything encapsulated inside
80 std::list< boost::function<void ()> > m_delayDeletion;
83 * Watch all files mapped into our address space. When
84 * modifications are seen (as during a package upgrade), sets
85 * m_shutdownRequested. This prevents adding new sessions and
86 * prevents running already queued ones, because future sessions
87 * might not be able to execute correctly without a restart. For example, a
88 * sync with libsynthesis from 1.1 does not work with
89 * SyncEvolution XML files from 1.2. The daemon then waits
90 * for the changes to settle (see SHUTDOWN_QUIESENCE_SECONDS) and either shuts
91 * down or restarts. The latter is necessary if the daemon has
92 * automatic syncing enabled in a config.
94 list< boost::shared_ptr<GLibNotify> > m_files;
99 * timer which counts seconds until server is meant to shut down
101 Timeout m_shutdownTimer;
105 * The session which currently holds the main lock on the server.
106 * To avoid issues with concurrent modification of data or configs,
107 * only one session may make such modifications at a time. A
108 * plain pointer which is reset by the session's deconstructor.
110 * The server doesn't hold a shared pointer to the session so
111 * that it can be deleted when the last client detaches from it.
113 * A weak pointer alone did not work because it does not provide access
114 * to the underlying pointer after the last corresponding shared
115 * pointer is gone (which triggers the deconstructing of the session).
117 Session *m_activeSession;
120 * The weak pointer that corresponds to m_activeSession.
122 boost::weak_ptr<Session> m_activeSessionRef;
125 * The running sync session. Having a separate reference to it
126 * ensures that the object won't go away prematurely, even if all
127 * clients disconnect.
129 * The session itself needs to request this special treatment with
130 * addSyncSession() and remove itself with removeSyncSession() when
133 boost::shared_ptr<Session> m_syncSession;
135 typedef std::list< boost::weak_ptr<Session> > WorkQueue_t;
137 * A queue of pending, idle Sessions. Sorted by priority, most
138 * important one first. Currently this is used to give client
139 * requests a boost over remote connections and (in the future)
142 * Active sessions are removed from this list and then continue
143 * to exist as long as a client in m_clients references it or
144 * it is the currently running sync session (m_syncSession).
146 WorkQueue_t m_workQueue;
149 * a hash of pending InfoRequest
151 typedef std::map<string, boost::weak_ptr<InfoReq> > InfoReqMap;
153 // hash map of pending info requests
154 InfoReqMap m_infoReqMap;
156 // the index of last info request
157 uint32_t m_lastInfoReq;
159 // a hash to represent matched templates for devices, the key is
161 typedef std::map<string, boost::shared_ptr<SyncConfig::TemplateDescription> > MatchedTemplates;
163 MatchedTemplates m_matchedTempls;
165 boost::shared_ptr<BluezManager> m_bluezManager;
167 /** devices which have sync services */
168 SyncConfig::DeviceList m_syncDevices;
171 * Watch callback for a specific client or connection.
173 void clientGone(Client *c);
177 // D-Bus API, also usable directly
179 /** Server.GetCapabilities() */
180 vector<string> getCapabilities();
182 /** Server.GetVersions() */
183 StringMap getVersions();
185 /** Server.Attach() */
186 void attachClient(const GDBusCXX::Caller_t &caller,
187 const boost::shared_ptr<GDBusCXX::Watch> &watch);
189 /** Server.Detach() */
190 void detachClient(const GDBusCXX::Caller_t &caller);
192 /** Server.DisableNotifications() */
193 void disableNotifications(const GDBusCXX::Caller_t &caller,
194 const string ¬ifications) {
195 setNotifications(false, caller, notifications);
198 /** Server.EnableNotifications() */
199 void enableNotifications(const GDBusCXX::Caller_t &caller,
200 const string ¬ifications) {
201 setNotifications(true, caller, notifications);
204 /** Server.NotificationAction() */
205 void notificationAction(const GDBusCXX::Caller_t &caller) {
207 if((pid = fork()) == 0) {
208 // search sync-ui from $PATH
209 execlp("sync-ui", "sync-ui", (const char*)0);
211 // Failing that, try meego-ux-settings/Sync
212 execlp("meego-qml-launcher",
213 "meego-qml-launcher",
214 "--opengl", "--fullscreen", "--app", "meego-ux-settings",
215 "--cmd", "showPage", "--cdata", "Sync", (const char*)0);
217 // Failing that, simply exit
222 /** actual implementation of enable and disable */
223 void setNotifications(bool enable,
224 const GDBusCXX::Caller_t &caller,
225 const string ¬ifications);
227 /** Server.Connect() */
228 void connect(const GDBusCXX::Caller_t &caller,
229 const boost::shared_ptr<GDBusCXX::Watch> &watch,
230 const StringMap &peer,
231 bool must_authenticate,
232 const std::string &session,
233 GDBusCXX::DBusObject_t &object);
235 /** Server.StartSession() */
236 void startSession(const GDBusCXX::Caller_t &caller,
237 const boost::shared_ptr<GDBusCXX::Watch> &watch,
238 const std::string &server,
239 GDBusCXX::DBusObject_t &object) {
240 startSessionWithFlags(caller, watch, server, std::vector<std::string>(), object);
243 /** Server.StartSessionWithFlags() */
244 void startSessionWithFlags(const GDBusCXX::Caller_t &caller,
245 const boost::shared_ptr<GDBusCXX::Watch> &watch,
246 const std::string &server,
247 const std::vector<std::string> &flags,
248 GDBusCXX::DBusObject_t &object);
250 /** internal representation of D-Bus API Server.StartSessionWithFlags() */
252 SESSION_FLAG_NONE = 0,
253 SESSION_FLAG_NO_SYNC = 1<<0,
254 SESSION_FLAG_ALL_CONFIGS = 1<<1
258 * Creates a session, queues it, then invokes the callback
259 * once the session is active. The caller is responsible
260 * for holding a reference to the session. If it drops
261 * that reference, the session gets deleted and the callback
262 * will not be called.
264 boost::shared_ptr<Session> startInternalSession(const std::string &server,
266 const boost::function<void (const boost::weak_ptr<Session> &session)> &callback);
268 /** Server.GetConfig() */
269 void getConfig(const std::string &config_name,
271 ReadOperations::Config_t &config)
273 ReadOperations ops(config_name, *this);
274 ops.getConfig(getTemplate , config);
277 /** Server.GetReports() */
278 void getReports(const std::string &config_name,
279 uint32_t start, uint32_t count,
280 ReadOperations::Reports_t &reports)
282 ReadOperations ops(config_name, *this);
283 ops.getReports(start, count, reports);
286 /** Server.CheckSource() */
287 void checkSource(const std::string &configName,
288 const std::string &sourceName)
290 ReadOperations ops(configName, *this);
291 ops.checkSource(sourceName);
294 /** Server.GetDatabases() */
295 void getDatabases(const std::string &configName,
296 const string &sourceName,
297 ReadOperations::SourceDatabases_t &databases)
299 ReadOperations ops(configName, *this);
300 ops.getDatabases(sourceName, databases);
303 /** Server.GetConfigs() */
304 void getConfigs(bool getTemplates,
305 std::vector<std::string> &configNames)
307 ReadOperations ops("", *this);
308 ops.getConfigs(getTemplates, configNames);
311 /** Server.CheckPresence() */
312 void checkPresence(const std::string &server,
314 std::vector<std::string> &transports);
316 /** Server.GetSessions() */
317 void getSessions(std::vector<GDBusCXX::DBusObject_t> &sessions);
319 /** Server.InfoResponse() */
320 void infoResponse(const GDBusCXX::Caller_t &caller,
321 const std::string &id,
322 const std::string &state,
323 const std::map<string, string> &response);
325 /** Server.SessionChanged */
326 GDBusCXX::EmitSignal2<const GDBusCXX::DBusObject_t &,
327 bool> sessionChanged;
329 /** Server.PresenceChanged */
330 GDBusCXX::EmitSignal3<const std::string &,
332 const std::string &> presence;
335 * Server.TemplatesChanged, triggered each time m_syncDevices, the
336 * input for the templates, is changed
338 GDBusCXX::EmitSignal0 templatesChanged;
341 * Server.ConfigChanged, triggered each time a session ends
342 * which modified its configuration
344 GDBusCXX::EmitSignal0 configChanged;
346 /** Server.InfoRequest */
347 GDBusCXX::EmitSignal6<const std::string &,
348 const GDBusCXX::DBusObject_t &,
352 const std::map<string, string> &> infoRequest;
354 /** wrapper around Server.LogOutput, filters by DBusLogLevel */
355 void logOutput(const GDBusCXX::DBusObject_t &path,
357 const std::string &explanation,
358 const std::string &procname);
360 void setDBusLogLevel(Logger::Level level) { m_dbusLogLevel = level; }
361 Logger::Level getDBusLogLevel() const { return m_dbusLogLevel; }
364 /** Server.LogOutput */
365 GDBusCXX::EmitSignal4<const GDBusCXX::DBusObject_t &,
368 const std::string &> m_logOutputSignal;
370 friend class InfoReq;
372 /** emit InfoRequest */
373 void emitInfoReq(const InfoReq &);
375 /** get the next id of InfoRequest */
376 std::string getNextInfoReq();
378 /** remove InfoReq from hash map */
379 void removeInfoReq(const std::string &infoReqId);
381 boost::scoped_ptr<PresenceStatus> m_presence;
382 boost::scoped_ptr<ConnmanClient> m_connman;
383 boost::scoped_ptr<NetworkManagerClient> m_networkManager;
385 /** Manager to automatic sync */
386 boost::shared_ptr<AutoSyncManager> m_autoSync;
388 //automatic termination
391 // The level of detail for D-Bus logging signals.
392 Logger::Level m_dbusLogLevel;
394 //records the parent logger, dbus server acts as logger to
395 //send signals to clients and put logs in the parent logger.
396 LoggerBase &m_parentLogger;
399 * All active timeouts created by addTimeout().
400 * Each timeout which requests to be not called
401 * again will be removed from this list.
403 list< boost::shared_ptr<Timeout> > m_timeouts;
406 * called each time a timeout triggers,
407 * removes those which are done
409 bool callTimeout(const boost::shared_ptr<Timeout> &timeout, const boost::function<void ()> &callback);
411 /** called 1 minute after last client detached from a session */
412 static void sessionExpired(const boost::shared_ptr<Session> &session);
415 Server(GMainLoop *loop,
416 bool &shutdownRequested,
417 boost::shared_ptr<Restart> &restart,
418 const GDBusCXX::DBusConnectionPtr &conn,
423 /** access to the GMainLoop reference used by this Server instance */
424 GMainLoop *getLoop() { return m_loop; }
426 /** process D-Bus calls until the server is ready to quit */
429 /** currently running operation */
430 boost::shared_ptr<Session> getSyncSession() const { return m_syncSession; }
432 /** true iff no work is pending */
433 bool isIdle() const { return !m_activeSession && m_workQueue.empty(); }
435 /** isIdle() might have changed its value, current value included */
436 typedef boost::signals2::signal<void (bool isIdle)> IdleSignal_t;
437 IdleSignal_t m_idleSignal;
440 * More specific "config changed signal", called with normalized
441 * config name as parameter. Config name is empty if all configs
444 typedef boost::signals2::signal<void (const std::string &configName)> ConfigChangedSignal_t;
445 ConfigChangedSignal_t m_configChangedSignal;
448 * Called when a session starts its real work (= calls addSyncSession()).
450 typedef boost::signals2::signal<void (const boost::shared_ptr<Session> &)> NewSyncSessionSignal_t;
451 NewSyncSessionSignal_t m_newSyncSessionSignal;
454 * look up client by its ID
456 boost::shared_ptr<Client> findClient(const GDBusCXX::Caller_t &ID);
459 * find client by its ID or create one anew
461 boost::shared_ptr<Client> addClient(const GDBusCXX::Caller_t &ID,
462 const boost::shared_ptr<GDBusCXX::Watch> &watch);
464 /** detach this resource from all clients which own it */
465 void detach(Resource *resource);
468 * Enqueue a session. Might also make it ready immediately,
469 * if nothing else is first in the queue. To be called
470 * by the creator of the session, *after* the session is
473 void enqueue(const boost::shared_ptr<Session> &session);
476 * Remove all sessions with this device ID from the
477 * queue. If the active session also has this ID,
478 * the session will be aborted and/or deactivated.
480 * Has to be asynchronous because it might involve ensuring that
481 * there is no running helper for this device ID, which requires
482 * communicating with the helper.
484 void killSessionsAsync(const std::string &peerDeviceID,
485 const SimpleResult &result);
488 * Remove a session from the work queue. If it is running a sync,
489 * it will keep running and nothing will change. Otherwise, if it
490 * is "ready" (= holds a lock on its configuration), then release
493 void dequeue(Session *session);
496 * Remember that the session is running a sync (or some other
497 * important operation) and keeps a pointer to it, to prevent
498 * deleting it. Currently can only called by the active sync
499 * session. Will fail if all clients have detached already.
501 * If successful, it triggers m_newSyncSessionSignal.
503 void addSyncSession(Session *session);
506 * Session is done, ready to be deleted again.
508 void removeSyncSession(Session *session);
511 * Checks whether the server is ready to run another session
512 * and if so, activates the first one in the queue.
517 * Special behavior for sessions: keep them around for another
518 * minute after the are no longer needed. Must be called by the
519 * creator of the session right before it would normally cause the
520 * destruction of the session.
522 * This allows another client to attach and/or get information
525 * This is implemented as a timeout which holds a reference to the
526 * session. Once the timeout fires, it is called and then removed,
527 * which removes the reference.
529 void delaySessionDestruction(const boost::shared_ptr<Session> &session);
532 * Works for any kind of object: keep shared pointer until the
533 * event loop is idle, then unref it inside. Useful for instances
534 * which need to delete themselves.
536 template <class T> void delayDeletion(const boost::shared_ptr<T> &t) {
537 // The functor will never be called, important here is only
538 // that it contains a copy of the shared pointer.
539 m_delayDeletion.push_back(boost::bind(delayDeletionDummy<T>, t));
540 g_idle_add(&Server::delayDeletionCb, this);
543 template <class T> static void delayDeletionDummy(const boost::shared_ptr<T> &t) throw () {}
544 static gboolean delayDeletionCb(gpointer userData) throw () {
545 Server *me = static_cast<Server *>(userData);
548 me->m_delayDeletion.clear();
550 // Something unexpected went wrong, can only shut down.
551 Exception::handle(HANDLE_EXCEPTION_FATAL);
557 * Handle the password request from a specific session. Ask our
558 * clients, relay answer to session if it is still around at the
559 * time when we get the response.
561 * Server does not keep a strong reference to info request,
562 * caller must do that or the request will automatically be
565 boost::shared_ptr<InfoReq> passwordRequest(const std::string &descr,
566 const ConfigPasswordKey &key,
567 const boost::weak_ptr<Session> &session);
569 /** got response for earlier request, need to extract password and tell session */
570 void passwordResponse(const StringMap &response,
571 const boost::weak_ptr<Session> &session);
574 * Invokes the given callback once in the given amount of seconds.
575 * Keeps a copy of the callback. If the Server is destructed
576 * before that time, then the callback will be deleted without
579 void addTimeout(const boost::function<void ()> &callback,
583 * InfoReq will be added to map automatically and removed again
584 * when it completes or times out. Caller is responsible for
585 * calling removeInfoReq() when the request becomes obsolete
588 boost::shared_ptr<InfoReq> createInfoReq(const string &type,
589 const std::map<string, string> ¶meters,
590 const Session &session);
591 void autoTermRef(int counts = 1) { m_autoTerm.ref(counts); }
593 void autoTermUnref(int counts = 1) { m_autoTerm.unref(counts); }
595 /** callback to reset for auto termination checking */
596 void autoTermCallback() { m_autoTerm.reset(); }
598 /** poll_nm callback for connman, used for presence detection*/
599 void connmanCallback(const std::map <std::string, boost::variant <std::vector <std::string> > >& props, const string &error);
601 PresenceStatus& getPresenceStatus();
603 void clearPeerTempls() { m_matchedTempls.clear(); }
604 void addPeerTempl(const string &templName, const boost::shared_ptr<SyncConfig::TemplateDescription> peerTempl);
606 boost::shared_ptr<SyncConfig::TemplateDescription> getPeerTempl(const string &peer);
609 * methods to operate device list. See DeviceList definition.
610 * The device id here is the identifier of device, the same as definition in DeviceList.
611 * In bluetooth devices, it refers to actually the mac address of the bluetooth.
612 * The finger print and match mode is used to match templates.
614 /** get sync devices */
615 void getDeviceList(SyncConfig::DeviceList &devices);
616 /** get a device according to device id. If not found, return false. */
617 bool getDevice(const string &deviceId, SyncConfig::DeviceDescription &device);
619 void addDevice(const SyncConfig::DeviceDescription &device);
620 /** remove a device by device id. If not found, do nothing */
621 void removeDevice(const string &deviceId);
622 /** update a device with the given device information. If not found, do nothing */
623 void updateDevice(const string &deviceId, const SyncConfig::DeviceDescription &device);
625 /** emit a presence signal */
626 void emitPresence(const string &server, const string &status, const string &transport)
628 presence(server, status, transport);
632 * Returns new unique session ID. Implemented with a running
633 * counter. Checks for overflow, but not currently for active
636 std::string getNextSession();
639 * Number of seconds to wait after file modifications are observed
640 * before shutting down or restarting. Shutting down could be done
641 * immediately, but restarting might not work right away. 10
642 * seconds was chosen because every single package is expected to
643 * be upgraded on disk in that interval. If a long-running system
644 * upgrade replaces additional packages later, then the server
645 * might restart multiple times during a system upgrade. Because it
646 * never runs operations directly after starting, that shouldn't
649 static const int SHUTDOWN_QUIESENCE_SECONDS = 10;
652 * false if any client requested suppression of notifications
654 bool notificationsEnabled();
657 * implement virtual method from LogStdout.
658 * Not only print the message in the console
659 * but also send them as signals to clients
661 virtual void messagev(Level level,
665 const char *function,
668 messagev(level, prefix, file, line,
669 function, format, args,
673 void messagev(Level level,
677 const char *function,
680 const std::string &dbusPath,
681 const std::string &procname);
683 virtual bool isProcessSafe() const { return false; }
686 // extensions to the D-Bus server, created dynamically by main()
687 #ifdef ENABLE_DBUS_PIM
688 boost::shared_ptr<GDBusCXX::DBusObjectHelper> CreateContactManager(const boost::shared_ptr<Server> &server, bool start);
693 #endif // SYNCEVO_DBUS_SERVER_H