Home | History | Annotate | Download | only in base
      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.
     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.
     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 
     51 inline static LibDBusGlibSymbolTable *GetSymbols() {
     52   return DBusMonitor::GetDBusGlibSymbolTable();
     53 }
     54 
     55 // Implementation of class DBusSigMessageData
     56 DBusSigMessageData::DBusSigMessageData(DBusMessage *message)
     57     : TypedMessageData<DBusMessage *>(message) {
     58   GetSymbols()->dbus_message_ref()(data());
     59 }
     60 
     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.
     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.
     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.
    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.
    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:
    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.
    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
    315 DBusMonitor *DBusMonitor::Create(DBusBusType type) {
    316   if (NULL == DBusMonitor::GetDBusGlibSymbolTable()) {
    317     return NULL;
    318   }
    319   return new DBusMonitor(type);
    320 }
    321 
    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 
    329 DBusMonitor::~DBusMonitor() {
    330   StopMonitoring();
    331 }
    332 
    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 
    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 
    380 bool DBusMonitor::StopMonitoring() {
    381   if (monitoring_thread_) {
    382     monitoring_thread_->Stop();
    383     monitoring_thread_ = NULL;
    384   }
    385   return true;
    386 }
    387 
    388 DBusMonitor::DBusMonitorStatus DBusMonitor::GetStatus() {
    389   return status_;
    390 }
    391 
    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