3 * Copyright 2004--2011, Google Inc.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
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.
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.
30 #include "talk/base/dbus.h"
34 #include "talk/base/logging.h"
35 #include "talk/base/thread.h"
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;
43 // Releases DBus-Glib symbols.
44 static void ReleaseDBusGlibSymbol() {
45 if (g_dbus_symbol != NULL) {
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();
58 if (NULL == g_dbus_symbol || !g_dbus_symbol->Load()) {
59 LOG(LS_WARNING) << "Failed to load dbus-glib symbol table.";
60 ReleaseDBusGlibSymbol();
62 // Nothing we can do if atexit() failed. Just ignore its returned value.
63 atexit(ReleaseDBusGlibSymbol);
68 inline static LibDBusGlibSymbolTable *GetSymbols() {
69 return DBusMonitor::GetDBusGlibSymbolTable();
72 // Implementation of class DBusSigMessageData
73 DBusSigMessageData::DBusSigMessageData(DBusMessage *message)
74 : TypedMessageData<DBusMessage *>(message) {
75 GetSymbols()->dbus_message_ref()(data());
78 DBusSigMessageData::~DBusSigMessageData() {
79 GetSymbols()->dbus_message_unref()(data());
82 // Implementation of class DBusSigFilter
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 "'");
90 ret += ("," DBUS_PATH "='");
94 if (!interface.empty()) {
95 ret += ("," DBUS_INTERFACE "='");
99 if (!member.empty()) {
100 ret += ("," DBUS_MEMBER "='");
107 // Forwards the message to the given instance.
108 DBusHandlerResult DBusSigFilter::DBusCallback(DBusConnection *dbus_conn,
109 DBusMessage *message,
113 return static_cast<DBusSigFilter *>(instance)->Callback(message);
115 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
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));
123 // Don't "eat" the message here. Let it pop up.
124 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
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);
133 ProcessSignal(msg->data());
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 {
145 explicit DBusMonitoringThread(DBusMonitor *monitor,
146 GMainContext *context,
148 std::vector<DBusSigFilter *> *filter_list)
154 filter_list_(filter_list) {
158 ASSERT(filter_list_);
161 virtual ~DBusMonitoringThread() {
165 // Override virtual method of Thread. Context: worker-thread.
167 ASSERT(NULL == connection_);
169 // Setup DBus connection and start monitoring.
170 monitor_->OnMonitoringStatusChanged(DMS_INITIALIZING);
172 LOG(LS_ERROR) << "DBus monitoring setup failed.";
173 monitor_->OnMonitoringStatusChanged(DMS_FAILED);
177 monitor_->OnMonitoringStatusChanged(DMS_RUNNING);
178 g_main_loop_run(mainloop_);
179 monitor_->OnMonitoringStatusChanged(DMS_STOPPED);
181 // Done normally. Clean up DBus connection.
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();
192 g_source_set_callback(idle_source_, &Idle, this, NULL);
193 g_source_attach(idle_source_, context_);
195 LOG(LS_ERROR) << "g_idle_source_new() failed.";
196 QuitGMainloop(); // Try to quit anyway.
199 Thread::Stop(); // Wait for the thread.
203 // Registers all DBus filters.
204 void RegisterAllFilters() {
205 ASSERT(NULL != GetSymbols()->dbus_g_connection_get_connection()(
208 for (std::vector<DBusSigFilter *>::iterator it = filter_list_->begin();
209 it != filter_list_->end(); ++it) {
210 DBusSigFilter *filter = (*it);
212 LOG(LS_ERROR) << "DBusSigFilter list corrupted.";
216 GetSymbols()->dbus_bus_add_match()(
217 GetSymbols()->dbus_g_connection_get_connection()(connection_),
218 filter->filter().c_str(), NULL);
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();
230 // Unregisters all DBus filters.
231 void UnRegisterAllFilters() {
232 ASSERT(NULL != GetSymbols()->dbus_g_connection_get_connection()(
235 for (std::vector<DBusSigFilter *>::iterator it = filter_list_->begin();
236 it != filter_list_->end(); ++it) {
237 DBusSigFilter *filter = (*it);
239 LOG(LS_ERROR) << "DBusSigFilter list corrupted.";
242 GetSymbols()->dbus_connection_remove_filter()(
243 GetSymbols()->dbus_g_connection_get_connection()(connection_),
244 &DBusSigFilter::DBusCallback, filter);
248 // Sets up the monitoring thread.
250 g_main_context_push_thread_default(context_);
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_,
256 if (NULL == connection_) {
257 LOG(LS_ERROR) << "dbus_g_bus_get_private() unable to get connection.";
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.";
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);
270 // Connect all filters.
271 RegisterAllFilters();
276 // Cleans up the monitoring thread.
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_);
286 if (GetSymbols()->dbus_g_connection_get_connection()(connection_)) {
287 UnRegisterAllFilters();
288 GetSymbols()->dbus_connection_close()(
289 GetSymbols()->dbus_g_connection_get_connection()(connection_));
291 GetSymbols()->dbus_g_connection_unref()(connection_);
294 g_main_loop_unref(mainloop_);
296 g_main_context_unref(context_);
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();
306 // We only hit this when ready to quit.
307 void QuitGMainloop() {
308 g_main_loop_quit(mainloop_);
311 DBusMonitor *monitor_;
313 GMainContext *context_;
314 GMainLoop *mainloop_;
315 DBusGConnection *connection_;
316 GSource *idle_source_;
318 std::vector<DBusSigFilter *> *filter_list_;
321 // Implementation of class DBusMonitor
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);
328 return g_dbus_symbol;
331 // Creates an instance of DBusMonitor
332 DBusMonitor *DBusMonitor::Create(DBusBusType type) {
333 if (NULL == DBusMonitor::GetDBusGlibSymbolTable()) {
336 return new DBusMonitor(type);
339 DBusMonitor::DBusMonitor(DBusBusType type)
341 status_(DMS_NOT_INITIALIZED),
342 monitoring_thread_(NULL) {
343 ASSERT(type_ == DBUS_BUS_SYSTEM || type_ == DBUS_BUS_SESSION);
346 DBusMonitor::~DBusMonitor() {
350 bool DBusMonitor::AddFilter(DBusSigFilter *filter) {
351 if (monitoring_thread_) {
357 filter_list_.push_back(filter);
361 bool DBusMonitor::StartMonitoring() {
362 if (!monitoring_thread_) {
365 GetSymbols()->dbus_g_thread_init()();
367 GMainContext *context = g_main_context_new();
368 if (NULL == context) {
369 LOG(LS_ERROR) << "g_main_context_new() failed.";
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);
380 monitoring_thread_ = new DBusMonitoringThread(this, context, mainloop,
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);
388 monitoring_thread_->Start();
393 bool DBusMonitor::StopMonitoring() {
394 if (monitoring_thread_) {
395 monitoring_thread_->Stop();
396 monitoring_thread_ = NULL;
401 DBusMonitor::DBusMonitorStatus DBusMonitor::GetStatus() {
405 void DBusMonitor::OnMonitoringStatusChanged(DBusMonitorStatus status) {
411 } // namespace talk_base
413 #endif // HAVE_DBUS_GLIB