1 /*
2  * libjingle
3  * Copyright 2004--2011, Google Inc.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are met:
7  *
8  *  1. Redistributions of source code must retain the above copyright notice,
9  *     this list of conditions and the following disclaimer.
10  *  2. Redistributions in binary form must reproduce the above copyright notice,
11  *     this list of conditions and the following disclaimer in the documentation
12  *     and/or other materials provided with the distribution.
13  *  3. The name of the author may not be used to endorse or promote products
14  *     derived from this software without specific prior written permission.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
17  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
19  * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
27 
28 #ifdef HAVE_DBUS_GLIB
29 
30 #include "talk/base/dbus.h"
31 
32 #include <dbus/dbus-glib-lowlevel.h>
33 
34 #include "talk/base/thread.h"
35 
36 namespace talk_base {
37 
38 // Avoid static object construction/destruction on startup/shutdown.
39 static pthread_once_t g_dbus_init_once = PTHREAD_ONCE_INIT;
40 static LibDBusGlibSymbolTable *g_dbus_symbol = NULL;
41 
42 // Releases DBus-Glib symbols.
ReleaseDBusGlibSymbol()43 static void ReleaseDBusGlibSymbol() {
44   if (g_dbus_symbol != NULL) {
45     delete g_dbus_symbol;
46     g_dbus_symbol = NULL;
47   }
48 }
49 
50 // Loads DBus-Glib symbols.
InitializeDBusGlibSymbol()51 static void InitializeDBusGlibSymbol() {
52   // This is thread safe.
53   if (NULL == g_dbus_symbol) {
54     g_dbus_symbol = new LibDBusGlibSymbolTable();
55 
56     // Loads dbus-glib
57     if (NULL == g_dbus_symbol || !g_dbus_symbol->Load()) {
58       LOG(LS_WARNING) << "Failed to load dbus-glib symbol table.";
59       ReleaseDBusGlibSymbol();
60     } else {
61       // Nothing we can do if atexit() failed. Just ignore its returned value.
62       atexit(ReleaseDBusGlibSymbol);
63     }
64   }
65 }
66 
67 // Returns a reference to the given late-binded symbol, with the correct type.
68 #define LATE(sym) LATESYM_GET(LibDBusGlibSymbolTable, \
69     DBusMonitor::GetDBusGlibSymbolTable(), sym)
70 
71 // Implementation of class DBusSigMessageData
DBusSigMessageData(DBusMessage * message)72 DBusSigMessageData::DBusSigMessageData(DBusMessage *message)
73     : TypedMessageData<DBusMessage *>(message) {
74   LATE(dbus_message_ref)(data());
75 }
76 
~DBusSigMessageData()77 DBusSigMessageData::~DBusSigMessageData() {
78   LATE(dbus_message_unref)(data());
79 }
80 
81 // Implementation of class DBusSigFilter
82 
83 // Builds a DBus filter string from given DBus path, interface and member.
BuildFilterString(const std::string & path,const std::string & interface,const std::string & member)84 std::string DBusSigFilter::BuildFilterString(const std::string &path,
85                                              const std::string &interface,
86                                              const std::string &member) {
87   std::string ret(DBUS_TYPE "='" DBUS_SIGNAL "'");
88   if (!path.empty()) {
89     ret += ("," DBUS_PATH "='");
90     ret += path;
91     ret += "'";
92   }
93   if (!interface.empty()) {
94     ret += ("," DBUS_INTERFACE "='");
95     ret += interface;
96     ret += "'";
97   }
98   if (!member.empty()) {
99     ret += ("," DBUS_MEMBER "='");
100     ret += member;
101     ret += "'";
102   }
103   return ret;
104 }
105 
106 // Forwards the message to the given instance.
DBusCallback(DBusConnection * dbus_conn,DBusMessage * message,void * instance)107 DBusHandlerResult DBusSigFilter::DBusCallback(DBusConnection *dbus_conn,
108                                               DBusMessage *message,
109                                               void *instance) {
110   ASSERT(instance);
111   if (instance) {
112     return static_cast<DBusSigFilter *>(instance)->Callback(message);
113   }
114   return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
115 }
116 
117 // Posts a message to caller thread.
Callback(DBusMessage * message)118 DBusHandlerResult DBusSigFilter::Callback(DBusMessage *message) {
119   if (caller_thread_) {
120     caller_thread_->Post(this, DSM_SIGNAL, new DBusSigMessageData(message));
121   }
122   // Don't "eat" the message here. Let it pop up.
123   return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
124 }
125 
126 // From MessageHandler.
OnMessage(Message * message)127 void DBusSigFilter::OnMessage(Message *message) {
128   if (message != NULL && DSM_SIGNAL == message->message_id) {
129     DBusSigMessageData *msg =
130         static_cast<DBusSigMessageData *>(message->pdata);
131     if (msg) {
132       ProcessSignal(msg->data());
133       delete msg;
134     }
135   }
136 }
137 
138 // Definition of private class DBusMonitoringThread.
139 // It creates a worker-thread to listen signals on DBus. The worker-thread will
140 // be running in a priate GMainLoop forever until either Stop() has been invoked
141 // or it hits an error.
142 class DBusMonitor::DBusMonitoringThread : public talk_base::Thread {
143  public:
DBusMonitoringThread(DBusMonitor * monitor,GMainContext * context,GMainLoop * mainloop,std::vector<DBusSigFilter * > * filter_list)144   explicit DBusMonitoringThread(DBusMonitor *monitor,
145                                 GMainContext *context,
146                                 GMainLoop *mainloop,
147                                 std::vector<DBusSigFilter *> *filter_list)
148       : monitor_(monitor),
149         context_(context),
150         mainloop_(mainloop),
151         connection_(NULL),
152         idle_source_(NULL),
153         filter_list_(filter_list) {
154     ASSERT(monitor_);
155     ASSERT(context_);
156     ASSERT(mainloop_);
157     ASSERT(filter_list_);
158   }
159 
160   // Override virtual method of Thread. Context: worker-thread.
Run()161   virtual void Run() {
162     ASSERT(NULL == connection_);
163 
164     // Setup DBus connection and start monitoring.
165     monitor_->OnMonitoringStatusChanged(DMS_INITIALIZING);
166     if (!Setup()) {
167       LOG(LS_ERROR) << "DBus monitoring setup failed.";
168       monitor_->OnMonitoringStatusChanged(DMS_FAILED);
169       CleanUp();
170       return;
171     }
172     monitor_->OnMonitoringStatusChanged(DMS_RUNNING);
173     LATE(g_main_loop_run)(mainloop_);
174     monitor_->OnMonitoringStatusChanged(DMS_STOPPED);
175 
176     // Done normally. Clean up DBus connection.
177     CleanUp();
178     return;
179   }
180 
181   // Override virtual method of Thread. Context: caller-thread.
Stop()182   virtual void Stop() {
183     ASSERT(NULL == idle_source_);
184     // Add an idle source and let the gmainloop quit on idle.
185     idle_source_ = LATE(g_idle_source_new)();
186     if (idle_source_) {
187       LATE(g_source_set_callback)(idle_source_, &Idle, this, NULL);
188       LATE(g_source_attach)(idle_source_, context_);
189     } else {
190       LOG(LS_ERROR) << "g_idle_source_new() failed.";
191       QuitGMainloop();  // Try to quit anyway.
192     }
193 
194     Thread::Stop();  // Wait for the thread.
195   }
196 
197  private:
198   // Registers all DBus filters.
RegisterAllFilters()199   void RegisterAllFilters() {
200     ASSERT(NULL != LATE(dbus_g_connection_get_connection)(connection_));
201 
202     for (std::vector<DBusSigFilter *>::iterator it = filter_list_->begin();
203          it != filter_list_->end(); ++it) {
204       DBusSigFilter *filter = (*it);
205       if (!filter) {
206         LOG(LS_ERROR) << "DBusSigFilter list corrupted.";
207         continue;
208       }
209 
210       LATE(dbus_bus_add_match)(
211           LATE(dbus_g_connection_get_connection)(connection_),
212           filter->filter().c_str(), NULL);
213 
214       if (!LATE(dbus_connection_add_filter)(
215               LATE(dbus_g_connection_get_connection)(connection_),
216               &DBusSigFilter::DBusCallback, filter, NULL)) {
217         LOG(LS_ERROR) << "dbus_connection_add_filter() failed."
218                       << "Filter: " << filter->filter();
219         continue;
220       }
221     }
222   }
223 
224   // Unregisters all DBus filters.
UnRegisterAllFilters()225   void UnRegisterAllFilters() {
226     ASSERT(NULL != LATE(dbus_g_connection_get_connection)(connection_));
227 
228     for (std::vector<DBusSigFilter *>::iterator it = filter_list_->begin();
229          it != filter_list_->end(); ++it) {
230       DBusSigFilter *filter = (*it);
231       if (!filter) {
232         LOG(LS_ERROR) << "DBusSigFilter list corrupted.";
233         continue;
234       }
235       LATE(dbus_connection_remove_filter)(
236           LATE(dbus_g_connection_get_connection)(connection_),
237           &DBusSigFilter::DBusCallback, filter);
238     }
239   }
240 
241   // Sets up the monitoring thread.
Setup()242   bool Setup() {
243     LATE(g_main_context_push_thread_default)(context_);
244 
245     // Start connection to dbus.
246     // If dbus daemon is not running, returns false immediately.
247     connection_ = LATE(dbus_g_bus_get_private)(monitor_->type_, context_, NULL);
248     if (NULL == connection_) {
249       LOG(LS_ERROR) << "dbus_g_bus_get_private() unable to get connection.";
250       return false;
251     }
252     if (NULL == LATE(dbus_g_connection_get_connection)(connection_)) {
253       LOG(LS_ERROR) << "dbus_g_connection_get_connection() returns NULL. "
254                     << "DBus daemon is probably not running.";
255       return false;
256     }
257 
258     // Application don't exit if DBus daemon die.
259     LATE(dbus_connection_set_exit_on_disconnect)(
260         LATE(dbus_g_connection_get_connection)(connection_), FALSE);
261 
262     // Connect all filters.
263     RegisterAllFilters();
264 
265     return true;
266   }
267 
268   // Cleans up the monitoring thread.
CleanUp()269   void CleanUp() {
270     if (idle_source_) {
271       // We did an attach() with the GSource, so we need to destroy() it.
272       LATE(g_source_destroy)(idle_source_);
273       // We need to unref() the GSource to end the last reference we got.
274       LATE(g_source_unref)(idle_source_);
275       idle_source_ = NULL;
276     }
277     if (connection_) {
278       if (LATE(dbus_g_connection_get_connection)(connection_)) {
279         UnRegisterAllFilters();
280         LATE(dbus_connection_close)(
281             LATE(dbus_g_connection_get_connection)(connection_));
282       }
283       LATE(dbus_g_connection_unref)(connection_);
284       connection_ = NULL;
285     }
286     LATE(g_main_loop_unref)(mainloop_);
287     mainloop_ = NULL;
288     LATE(g_main_context_unref)(context_);
289     context_ = NULL;
290   }
291 
292   // Handles callback on Idle. We only add this source when ready to stop.
Idle(gpointer data)293   static gboolean Idle(gpointer data) {
294     static_cast<DBusMonitoringThread *>(data)->QuitGMainloop();
295     return TRUE;
296   }
297 
298   // We only hit this when ready to quit.
QuitGMainloop()299   void QuitGMainloop() {
300     LATE(g_main_loop_quit)(mainloop_);
301   }
302 
303   DBusMonitor *monitor_;
304 
305   GMainContext *context_;
306   GMainLoop *mainloop_;
307   DBusGConnection *connection_;
308   GSource *idle_source_;
309 
310   std::vector<DBusSigFilter *> *filter_list_;
311 };
312 
313 // Implementation of class DBusMonitor
314 
315 // Returns DBus-Glib symbol handle. Initialize it first if hasn't.
GetDBusGlibSymbolTable()316 LibDBusGlibSymbolTable *DBusMonitor::GetDBusGlibSymbolTable() {
317   // This is multi-thread safe.
318   pthread_once(&g_dbus_init_once, InitializeDBusGlibSymbol);
319 
320   return g_dbus_symbol;
321 };
322 
323 // Creates an instance of DBusMonitor
Create(DBusBusType type)324 DBusMonitor *DBusMonitor::Create(DBusBusType type) {
325   if (NULL == DBusMonitor::GetDBusGlibSymbolTable()) {
326     return NULL;
327   }
328   return new DBusMonitor(type);
329 }
330 
DBusMonitor(DBusBusType type)331 DBusMonitor::DBusMonitor(DBusBusType type)
332     : type_(type),
333       status_(DMS_NOT_INITIALIZED),
334       monitoring_thread_(NULL) {
335   ASSERT(type_ == DBUS_BUS_SYSTEM || type_ == DBUS_BUS_SESSION);
336 }
337 
~DBusMonitor()338 DBusMonitor::~DBusMonitor() {
339   StopMonitoring();
340 }
341 
AddFilter(DBusSigFilter * filter)342 bool DBusMonitor::AddFilter(DBusSigFilter *filter) {
343   if (monitoring_thread_) {
344     return false;
345   }
346   if (!filter) {
347     return false;
348   }
349   filter_list_.push_back(filter);
350   return true;
351 }
352 
StartMonitoring()353 bool DBusMonitor::StartMonitoring() {
354   if (!monitoring_thread_) {
355     LATE(g_type_init)();
356     LATE(g_thread_init)(NULL);
357     LATE(dbus_g_thread_init)();
358 
359     GMainContext *context = LATE(g_main_context_new)();
360     if (NULL == context) {
361       LOG(LS_ERROR) << "g_main_context_new() failed.";
362       return false;
363     }
364 
365     GMainLoop *mainloop = LATE(g_main_loop_new)(context, FALSE);
366     if (NULL == mainloop) {
367       LOG(LS_ERROR) << "g_main_loop_new() failed.";
368       LATE(g_main_context_unref)(context);
369       return false;
370     }
371 
372     monitoring_thread_ = new DBusMonitoringThread(this, context, mainloop,
373                                                   &filter_list_);
374     if (monitoring_thread_ == NULL) {
375       LOG(LS_ERROR) << "Failed to create DBus monitoring thread.";
376       LATE(g_main_context_unref)(context);
377       LATE(g_main_loop_unref)(mainloop);
378       return false;
379     }
380     monitoring_thread_->Start();
381   }
382   return true;
383 }
384 
StopMonitoring()385 bool DBusMonitor::StopMonitoring() {
386   if (monitoring_thread_) {
387     monitoring_thread_->Stop();
388     monitoring_thread_ = NULL;
389   }
390   return true;
391 }
392 
GetStatus()393 DBusMonitor::DBusMonitorStatus DBusMonitor::GetStatus() {
394   return status_;
395 }
396 
OnMonitoringStatusChanged(DBusMonitorStatus status)397 void DBusMonitor::OnMonitoringStatusChanged(DBusMonitorStatus status) {
398   status_ = status;
399 }
400 
401 #undef LATE
402 
403 }  // namespace talk_base
404 
405 #endif  // HAVE_DBUS_GLIB
406