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