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