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 20namespace rtc { 21 22// Avoid static object construction/destruction on startup/shutdown. 23static pthread_once_t g_dbus_init_once = PTHREAD_ONCE_INIT; 24static LibDBusGlibSymbolTable *g_dbus_symbol = NULL; 25 26// Releases DBus-Glib symbols. 27static 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. 35static 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 51inline static LibDBusGlibSymbolTable *GetSymbols() { 52 return DBusMonitor::GetDBusGlibSymbolTable(); 53} 54 55// Implementation of class DBusSigMessageData 56DBusSigMessageData::DBusSigMessageData(DBusMessage *message) 57 : TypedMessageData<DBusMessage *>(message) { 58 GetSymbols()->dbus_message_ref()(data()); 59} 60 61DBusSigMessageData::~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. 68std::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. 91DBusHandlerResult 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. 102DBusHandlerResult 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. 111void 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. 126class DBusMonitor::DBusMonitoringThread : public rtc::Thread { 127 public: 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 144 virtual ~DBusMonitoringThread() { 145 Stop(); 146 } 147 148 // Override virtual method of Thread. Context: worker-thread. 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. 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. 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. 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. 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. 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. 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. 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. 307LibDBusGlibSymbolTable *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 315DBusMonitor *DBusMonitor::Create(DBusBusType type) { 316 if (NULL == DBusMonitor::GetDBusGlibSymbolTable()) { 317 return NULL; 318 } 319 return new DBusMonitor(type); 320} 321 322DBusMonitor::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 329DBusMonitor::~DBusMonitor() { 330 StopMonitoring(); 331} 332 333bool 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 344bool 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 380bool DBusMonitor::StopMonitoring() { 381 if (monitoring_thread_) { 382 monitoring_thread_->Stop(); 383 monitoring_thread_ = NULL; 384 } 385 return true; 386} 387 388DBusMonitor::DBusMonitorStatus DBusMonitor::GetStatus() { 389 return status_; 390} 391 392void DBusMonitor::OnMonitoringStatusChanged(DBusMonitorStatus status) { 393 status_ = status; 394} 395 396#undef LATE 397 398} // namespace rtc 399 400#endif // HAVE_DBUS_GLIB 401