connection.cpp
Go to the documentation of this file.
00001 /*
00002  *
00003  *  D-Bus++ - C++ bindings for D-Bus
00004  *
00005  *  Copyright (C) 2005-2007  Paolo Durante <shackan@gmail.com>
00006  *
00007  *
00008  *  This library is free software; you can redistribute it and/or
00009  *  modify it under the terms of the GNU Lesser General Public
00010  *  License as published by the Free Software Foundation; either
00011  *  version 2.1 of the License, or (at your option) any later version.
00012  *
00013  *  This library is distributed in the hope that it will be useful,
00014  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00015  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00016  *  Lesser General Public License for more details.
00017  *
00018  *  You should have received a copy of the GNU Lesser General Public
00019  *  License along with this library; if not, write to the Free Software
00020  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00021  *
00022  */
00023 
00024 #ifdef HAVE_CONFIG_H
00025 #include <config.h>
00026 #endif
00027 
00028 #include <dbus-c++/debug.h>
00029 #include <dbus-c++/connection.h>
00030 
00031 #include <dbus/dbus.h>
00032 #include <string>
00033 
00034 #include "internalerror.h"
00035 
00036 #include "connection_p.h"
00037 #include "dispatcher_p.h"
00038 #include "server_p.h"
00039 #include "message_p.h"
00040 #include "pendingcall_p.h"
00041 
00042 using namespace DBus;
00043 
00044 Connection::Private::Private(DBusConnection *c, Server::Private *s)
00045   : conn(c) , dispatcher(NULL), server(s)
00046 {
00047   init();
00048 }
00049 
00050 Connection::Private::Private(DBusBusType type)
00051   : dispatcher(NULL), server(NULL)
00052 {
00053   InternalError e;
00054 
00055   conn = dbus_bus_get_private(type, e);
00056 
00057   if (e) throw Error(e);
00058 
00059   init();
00060 }
00061 
00062 Connection::Private::~Private()
00063 {
00064   debug_log("terminating connection 0x%08x", conn);
00065 
00066   detach_server();
00067 
00068   if (dbus_connection_get_is_connected(conn))
00069   {
00070     std::vector<std::string>::iterator i = names.begin();
00071 
00072     while (i != names.end())
00073     {
00074       debug_log("%s: releasing bus name %s", dbus_bus_get_unique_name(conn), i->c_str());
00075       dbus_bus_release_name(conn, i->c_str(), NULL);
00076       ++i;
00077     }
00078     dbus_connection_close(conn);
00079   }
00080   dbus_connection_unref(conn);
00081 }
00082 
00083 void Connection::Private::init()
00084 {
00085   dbus_connection_ref(conn);
00086   dbus_connection_ref(conn);  //todo: the library has to own another reference
00087 
00088   disconn_filter = new Callback<Connection::Private, bool, const Message &>(
00089     this, &Connection::Private::disconn_filter_function
00090   );
00091 
00092   dbus_connection_add_filter(conn, message_filter_stub, &disconn_filter, NULL); // TODO: some assert at least
00093 
00094   dbus_connection_set_dispatch_status_function(conn, dispatch_status_stub, this, 0);
00095   dbus_connection_set_exit_on_disconnect(conn, false); //why was this set to true??
00096   dbus_connection_set_unix_user_function (conn, 0, 0, 0);
00097 }
00098 
00099 void Connection::Private::detach_server()
00100 {
00101   /*  Server::Private *tmp = server;
00102 
00103     server = NULL;
00104 
00105     if (tmp)
00106     {
00107       ConnectionList::iterator i;
00108 
00109       for (i = tmp->connections.begin(); i != tmp->connections.end(); ++i)
00110       {
00111         if (i->_pvt.get() == this)
00112         {
00113           tmp->connections.erase(i);
00114           break;
00115         }
00116       }
00117     }*/
00118 }
00119 
00120 bool Connection::Private::do_dispatch()
00121 {
00122   debug_log("dispatching on %p", conn);
00123 
00124   if (!dbus_connection_get_is_connected(conn))
00125   {
00126     debug_log("connection terminated");
00127 
00128     detach_server();
00129 
00130     return true;
00131   }
00132 
00133   return dbus_connection_dispatch(conn) != DBUS_DISPATCH_DATA_REMAINS;
00134 }
00135 
00136 void Connection::Private::dispatch_status_stub(DBusConnection *dc, DBusDispatchStatus status, void *data)
00137 {
00138   Private *p = static_cast<Private *>(data);
00139 
00140   switch (status)
00141   {
00142   case DBUS_DISPATCH_DATA_REMAINS:
00143     debug_log("some dispatching to do on %p", dc);
00144     p->dispatcher->queue_connection(p);
00145     break;
00146 
00147   case DBUS_DISPATCH_COMPLETE:
00148     debug_log("all dispatching done on %p", dc);
00149     break;
00150 
00151   case DBUS_DISPATCH_NEED_MEMORY: //uh oh...
00152     debug_log("connection %p needs memory", dc);
00153     break;
00154   }
00155 }
00156 
00157 DBusHandlerResult Connection::Private::message_filter_stub(DBusConnection *conn, DBusMessage *dmsg, void *data)
00158 {
00159   MessageSlot *slot = static_cast<MessageSlot *>(data);
00160 
00161   Message msg = Message(new Message::Private(dmsg));
00162 
00163   return slot && !slot->empty() && slot->call(msg)
00164          ? DBUS_HANDLER_RESULT_HANDLED
00165          : DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
00166 }
00167 
00168 bool Connection::Private::disconn_filter_function(const Message &msg)
00169 {
00170   if (msg.is_signal(DBUS_INTERFACE_LOCAL, "Disconnected"))
00171   {
00172     debug_log("%p disconnected by local bus", conn);
00173     dbus_connection_close(conn);
00174 
00175     return true;
00176   }
00177   return false;
00178 }
00179 
00180 DBusDispatchStatus Connection::Private::dispatch_status()
00181 {
00182   return dbus_connection_get_dispatch_status(conn);
00183 }
00184 
00185 bool Connection::Private::has_something_to_dispatch()
00186 {
00187   return dispatch_status() == DBUS_DISPATCH_DATA_REMAINS;
00188 }
00189 
00190 
00191 Connection Connection::SystemBus()
00192 {
00193   return Connection(new Private(DBUS_BUS_SYSTEM));
00194 }
00195 
00196 Connection Connection::SessionBus()
00197 {
00198   return Connection(new Private(DBUS_BUS_SESSION));
00199 }
00200 
00201 Connection Connection::ActivationBus()
00202 {
00203   return Connection(new Private(DBUS_BUS_STARTER));
00204 }
00205 
00206 Connection::Connection(const char *address, bool priv)
00207   : _timeout(-1)
00208 {
00209   InternalError e;
00210   DBusConnection *conn = priv
00211                          ? dbus_connection_open_private(address, e)
00212                          : dbus_connection_open(address, e);
00213 
00214   if (e) throw Error(e);
00215 
00216   _pvt = new Private(conn);
00217 
00218   setup(default_dispatcher);
00219 
00220   debug_log("connected to %s", address);
00221 }
00222 
00223 Connection::Connection(Connection::Private *p)
00224   : _pvt(p), _timeout(-1)
00225 {
00226   setup(default_dispatcher);
00227 }
00228 
00229 Connection::Connection(const Connection &c)
00230   : _pvt(c._pvt), _timeout(c._timeout)
00231 {
00232   dbus_connection_ref(_pvt->conn);
00233 }
00234 
00235 Connection::~Connection()
00236 {
00237   dbus_connection_unref(_pvt->conn);
00238 }
00239 
00240 Dispatcher *Connection::setup(Dispatcher *dispatcher)
00241 {
00242   debug_log("registering stubs for connection %p", _pvt->conn);
00243 
00244   if (!dispatcher) dispatcher = default_dispatcher;
00245 
00246   if (!dispatcher) throw ErrorFailed("no default dispatcher set for new connection");
00247 
00248   Dispatcher *prev = _pvt->dispatcher;
00249 
00250   _pvt->dispatcher = dispatcher;
00251 
00252   dispatcher->queue_connection(_pvt.get());
00253 
00254   dbus_connection_set_watch_functions(
00255     _pvt->conn,
00256     Dispatcher::Private::on_add_watch,
00257     Dispatcher::Private::on_rem_watch,
00258     Dispatcher::Private::on_toggle_watch,
00259     dispatcher,
00260     0
00261   );
00262 
00263   dbus_connection_set_timeout_functions(
00264     _pvt->conn,
00265     Dispatcher::Private::on_add_timeout,
00266     Dispatcher::Private::on_rem_timeout,
00267     Dispatcher::Private::on_toggle_timeout,
00268     dispatcher,
00269     0
00270   );
00271 
00272   return prev;
00273 }
00274 
00275 bool Connection::operator == (const Connection &c) const
00276 {
00277   return _pvt->conn == c._pvt->conn;
00278 }
00279 
00280 bool Connection::register_bus()
00281 {
00282   InternalError e;
00283 
00284   bool r = dbus_bus_register(_pvt->conn, e);
00285 
00286   if (e) throw(e);
00287 
00288   return r;
00289 }
00290 
00291 bool Connection::connected() const
00292 {
00293   return dbus_connection_get_is_connected(_pvt->conn);
00294 }
00295 
00296 void Connection::disconnect()
00297 {
00298 //  dbus_connection_disconnect(_pvt->conn); // disappeared in 0.9x
00299   dbus_connection_close(_pvt->conn);
00300 }
00301 
00302 void Connection::exit_on_disconnect(bool exit)
00303 {
00304   dbus_connection_set_exit_on_disconnect(_pvt->conn, exit);
00305 }
00306 
00307 bool Connection::unique_name(const char *n)
00308 {
00309   return dbus_bus_set_unique_name(_pvt->conn, n);
00310 }
00311 
00312 const char *Connection::unique_name() const
00313 {
00314   return dbus_bus_get_unique_name(_pvt->conn);
00315 }
00316 
00317 void Connection::flush()
00318 {
00319   dbus_connection_flush(_pvt->conn);
00320 }
00321 
00322 void Connection::add_match(const char *rule)
00323 {
00324   InternalError e;
00325 
00326   dbus_bus_add_match(_pvt->conn, rule, e);
00327 
00328   debug_log("%s: added match rule %s", unique_name(), rule);
00329 
00330   if (e) throw Error(e);
00331 }
00332 
00333 void Connection::remove_match(const char  *rule,
00334                               bool    throw_on_error)
00335 {
00336   InternalError e;
00337 
00338   dbus_bus_remove_match(_pvt->conn, rule, e);
00339 
00340   debug_log("%s: removed match rule %s", unique_name(), rule);
00341 
00342   if (e)
00343   {
00344     if (throw_on_error)
00345       throw Error(e);
00346     else
00347       debug_log("DBus::Connection::remove_match: %s (%s).",
00348                 static_cast<DBusError *>(e)->message,
00349                 static_cast<DBusError *>(e)->name);
00350   }
00351 }
00352 
00353 bool Connection::add_filter(MessageSlot &s)
00354 {
00355   debug_log("%s: adding filter", unique_name());
00356   return dbus_connection_add_filter(_pvt->conn, Private::message_filter_stub, &s, NULL);
00357 }
00358 
00359 void Connection::remove_filter(MessageSlot &s)
00360 {
00361   debug_log("%s: removing filter", unique_name());
00362   dbus_connection_remove_filter(_pvt->conn, Private::message_filter_stub, &s);
00363 }
00364 
00365 bool Connection::send(const Message &msg, unsigned int *serial)
00366 {
00367   return dbus_connection_send(_pvt->conn, msg._pvt->msg, serial);
00368 }
00369 
00370 Message Connection::send_blocking(Message &msg, int timeout)
00371 {
00372   DBusMessage *reply;
00373   InternalError e;
00374 
00375   if (this->_timeout != -1)
00376   {
00377     reply = dbus_connection_send_with_reply_and_block(_pvt->conn, msg._pvt->msg, this->_timeout, e);
00378   }
00379   else
00380   {
00381     reply = dbus_connection_send_with_reply_and_block(_pvt->conn, msg._pvt->msg, timeout, e);
00382   }
00383 
00384   if (e) throw Error(e);
00385 
00386   return Message(new Message::Private(reply), false);
00387 }
00388 
00389 PendingCall Connection::send_async(Message &msg, int timeout)
00390 {
00391   DBusPendingCall *pending;
00392 
00393   if (!dbus_connection_send_with_reply(_pvt->conn, msg._pvt->msg, &pending, timeout))
00394   {
00395     throw ErrorNoMemory("Unable to start asynchronous call");
00396   }
00397   return PendingCall(new PendingCall::Private(pending));
00398 }
00399 
00400 void Connection::request_name(const char *name, int flags)
00401 {
00402   InternalError e;
00403 
00404   debug_log("%s: registering bus name %s", unique_name(), name);
00405 
00406   /*
00407    * TODO:
00408    * Think about giving back the 'ret' value. Some people on the list
00409    * requested about this...
00410    */
00411   int ret = dbus_bus_request_name(_pvt->conn, name, flags, e);
00412 
00413   if (ret == -1)
00414   {
00415     if (e) throw Error(e);
00416   }
00417 
00418 //  this->remove_match("destination");
00419 
00420   if (name)
00421   {
00422     _pvt->names.push_back(name);
00423     std::string match = "destination='" + _pvt->names.back() + "'";
00424     add_match(match.c_str());
00425   }
00426 }
00427 
00428 unsigned long Connection::sender_unix_uid(const char *sender)
00429 {
00430   InternalError e;
00431 
00432   unsigned long ul = dbus_bus_get_unix_user(_pvt->conn, sender, e);
00433 
00434   if (e) throw Error(e);
00435 
00436   return ul;
00437 }
00438 
00439 bool Connection::has_name(const char *name)
00440 {
00441   InternalError e;
00442 
00443   bool b = dbus_bus_name_has_owner(_pvt->conn, name, e);
00444 
00445   if (e) throw Error(e);
00446 
00447   return b;
00448 }
00449 
00450 const std::vector<std::string>& Connection::names()
00451 {
00452   return _pvt->names;
00453 }
00454 
00455 bool Connection::start_service(const char *name, unsigned long flags)
00456 {
00457   InternalError e;
00458 
00459   bool b = dbus_bus_start_service_by_name(_pvt->conn, name, flags, NULL, e);
00460 
00461   if (e) throw Error(e);
00462 
00463   return b;
00464 }
00465 
00466 void Connection::set_timeout(int timeout)
00467 {
00468   _timeout = timeout;
00469 }
00470 
00471 int Connection::get_timeout()
00472 {
00473   return _timeout;
00474 }
00475