/* * * D-Bus++ - C++ bindings for D-Bus * * Copyright (C) 2005-2007 Paolo Durante * * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include "internalerror.h" #include "connection_p.h" #include "dispatcher_p.h" #include "server_p.h" #include "message_p.h" #include "pendingcall_p.h" using namespace DBus; Connection::Private::Private(DBusConnection *c, Server::Private *s) : conn(c) , dispatcher(NULL), server(s) { init(); } Connection::Private::Private(DBusBusType type) : dispatcher(NULL), server(NULL) { InternalError e; conn = dbus_bus_get_private(type, e); if (e) throw Error(e); init(); } Connection::Private::~Private() { debug_log("terminating connection 0x%08x", conn); detach_server(); if (dbus_connection_get_is_connected(conn)) { std::vector::iterator i = names.begin(); while (i != names.end()) { debug_log("%s: releasing bus name %s", dbus_bus_get_unique_name(conn), i->c_str()); dbus_bus_release_name(conn, i->c_str(), NULL); ++i; } dbus_connection_close(conn); } dbus_connection_unref(conn); } void Connection::Private::init() { dbus_connection_ref(conn); dbus_connection_ref(conn); //todo: the library has to own another reference disconn_filter = new Callback( this, &Connection::Private::disconn_filter_function ); dbus_connection_add_filter(conn, message_filter_stub, &disconn_filter, NULL); // TODO: some assert at least dbus_connection_set_dispatch_status_function(conn, dispatch_status_stub, this, 0); dbus_connection_set_exit_on_disconnect(conn, false); //why was this set to true?? } void Connection::Private::detach_server() { /* Server::Private *tmp = server; server = NULL; if (tmp) { ConnectionList::iterator i; for (i = tmp->connections.begin(); i != tmp->connections.end(); ++i) { if (i->_pvt.get() == this) { tmp->connections.erase(i); break; } } }*/ } bool Connection::Private::do_dispatch() { debug_log("dispatching on %p", conn); if (!dbus_connection_get_is_connected(conn)) { debug_log("connection terminated"); detach_server(); return true; } return dbus_connection_dispatch(conn) != DBUS_DISPATCH_DATA_REMAINS; } void Connection::Private::dispatch_status_stub(DBusConnection *dc, DBusDispatchStatus status, void *data) { Private *p = static_cast(data); switch (status) { case DBUS_DISPATCH_DATA_REMAINS: debug_log("some dispatching to do on %p", dc); p->dispatcher->queue_connection(p); break; case DBUS_DISPATCH_COMPLETE: debug_log("all dispatching done on %p", dc); break; case DBUS_DISPATCH_NEED_MEMORY: //uh oh... debug_log("connection %p needs memory", dc); break; } } DBusHandlerResult Connection::Private::message_filter_stub(DBusConnection *conn, DBusMessage *dmsg, void *data) { MessageSlot *slot = static_cast(data); Message msg = Message(new Message::Private(dmsg)); return slot && !slot->empty() && slot->call(msg) ? DBUS_HANDLER_RESULT_HANDLED : DBUS_HANDLER_RESULT_NOT_YET_HANDLED; } bool Connection::Private::disconn_filter_function(const Message &msg) { if (msg.is_signal(DBUS_INTERFACE_LOCAL, "Disconnected")) { debug_log("%p disconnected by local bus", conn); dbus_connection_close(conn); return true; } return false; } DBusDispatchStatus Connection::Private::dispatch_status() { return dbus_connection_get_dispatch_status(conn); } bool Connection::Private::has_something_to_dispatch() { return dispatch_status() == DBUS_DISPATCH_DATA_REMAINS; } Connection Connection::SystemBus() { return Connection(new Private(DBUS_BUS_SYSTEM)); } Connection Connection::SessionBus() { return Connection(new Private(DBUS_BUS_SESSION)); } Connection Connection::ActivationBus() { return Connection(new Private(DBUS_BUS_STARTER)); } Connection::Connection(const char *address, bool priv) : _timeout(-1) { InternalError e; DBusConnection *conn = priv ? dbus_connection_open_private(address, e) : dbus_connection_open(address, e); if (e) throw Error(e); _pvt = new Private(conn); setup(default_dispatcher); debug_log("connected to %s", address); } Connection::Connection(Connection::Private *p) : _pvt(p), _timeout(-1) { setup(default_dispatcher); } Connection::Connection(const Connection &c) : _pvt(c._pvt), _timeout(c._timeout) { dbus_connection_ref(_pvt->conn); } Connection::~Connection() { dbus_connection_unref(_pvt->conn); } Dispatcher *Connection::setup(Dispatcher *dispatcher) { debug_log("registering stubs for connection %p", _pvt->conn); if (!dispatcher) dispatcher = default_dispatcher; if (!dispatcher) throw ErrorFailed("no default dispatcher set for new connection"); Dispatcher *prev = _pvt->dispatcher; _pvt->dispatcher = dispatcher; dispatcher->queue_connection(_pvt.get()); dbus_connection_set_watch_functions( _pvt->conn, Dispatcher::Private::on_add_watch, Dispatcher::Private::on_rem_watch, Dispatcher::Private::on_toggle_watch, dispatcher, 0 ); dbus_connection_set_timeout_functions( _pvt->conn, Dispatcher::Private::on_add_timeout, Dispatcher::Private::on_rem_timeout, Dispatcher::Private::on_toggle_timeout, dispatcher, 0 ); return prev; } bool Connection::operator == (const Connection &c) const { return _pvt->conn == c._pvt->conn; } bool Connection::register_bus() { InternalError e; bool r = dbus_bus_register(_pvt->conn, e); if (e) throw(e); return r; } bool Connection::connected() const { return dbus_connection_get_is_connected(_pvt->conn); } void Connection::disconnect() { // dbus_connection_disconnect(_pvt->conn); // disappeared in 0.9x dbus_connection_close(_pvt->conn); } void Connection::exit_on_disconnect(bool exit) { dbus_connection_set_exit_on_disconnect(_pvt->conn, exit); } bool Connection::unique_name(const char *n) { return dbus_bus_set_unique_name(_pvt->conn, n); } const char *Connection::unique_name() const { return dbus_bus_get_unique_name(_pvt->conn); } void Connection::flush() { dbus_connection_flush(_pvt->conn); } void Connection::add_match(const char *rule) { InternalError e; dbus_bus_add_match(_pvt->conn, rule, e); debug_log("%s: added match rule %s", unique_name(), rule); if (e) throw Error(e); } void Connection::remove_match(const char *rule, bool throw_on_error) { InternalError e; dbus_bus_remove_match(_pvt->conn, rule, e); debug_log("%s: removed match rule %s", unique_name(), rule); if (e) { if (throw_on_error) throw Error(e); else debug_log("DBus::Connection::remove_match: %s (%s).", static_cast(e)->message, static_cast(e)->name); } } bool Connection::add_filter(MessageSlot &s) { debug_log("%s: adding filter", unique_name()); return dbus_connection_add_filter(_pvt->conn, Private::message_filter_stub, &s, NULL); } void Connection::remove_filter(MessageSlot &s) { debug_log("%s: removing filter", unique_name()); dbus_connection_remove_filter(_pvt->conn, Private::message_filter_stub, &s); } bool Connection::send(const Message &msg, unsigned int *serial) { return dbus_connection_send(_pvt->conn, msg._pvt->msg, serial); } Message Connection::send_blocking(Message &msg, int timeout) { DBusMessage *reply; InternalError e; if (this->_timeout != -1) { reply = dbus_connection_send_with_reply_and_block(_pvt->conn, msg._pvt->msg, this->_timeout, e); } else { reply = dbus_connection_send_with_reply_and_block(_pvt->conn, msg._pvt->msg, timeout, e); } if (e) throw Error(e); return Message(new Message::Private(reply), false); } PendingCall Connection::send_async(Message &msg, int timeout) { DBusPendingCall *pending; if (!dbus_connection_send_with_reply(_pvt->conn, msg._pvt->msg, &pending, timeout)) { throw ErrorNoMemory("Unable to start asynchronous call"); } return PendingCall(new PendingCall::Private(pending)); } void Connection::request_name(const char *name, int flags) { InternalError e; debug_log("%s: registering bus name %s", unique_name(), name); /* * TODO: * Think about giving back the 'ret' value. Some people on the list * requested about this... */ int ret = dbus_bus_request_name(_pvt->conn, name, flags, e); if (ret == -1) { if (e) throw Error(e); } // this->remove_match("destination"); if (name) { _pvt->names.push_back(name); std::string match = "destination='" + _pvt->names.back() + "'"; add_match(match.c_str()); } } unsigned long Connection::sender_unix_uid(const char *sender) { InternalError e; unsigned long ul = dbus_bus_get_unix_user(_pvt->conn, sender, e); if (e) throw Error(e); return ul; } bool Connection::has_name(const char *name) { InternalError e; bool b = dbus_bus_name_has_owner(_pvt->conn, name, e); if (e) throw Error(e); return b; } const std::vector& Connection::names() { return _pvt->names; } bool Connection::start_service(const char *name, unsigned long flags) { InternalError e; bool b = dbus_bus_start_service_by_name(_pvt->conn, name, flags, NULL, e); if (e) throw Error(e); return b; } void Connection::set_timeout(int timeout) { _timeout = timeout; } int Connection::get_timeout() { return _timeout; }