blob: a203d73fdef0ee73c3648b8d7531b22ca1a128f1 [file] [log] [blame]
Emeric Vigier2f625822012-08-06 11:09:52 -04001/*
2 *
3 * D-Bus++ - C++ bindings for D-Bus
4 *
5 * Copyright (C) 2005-2007 Paolo Durante <shackan@gmail.com>
6 *
7 *
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
12 *
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 *
22 */
23
24#ifdef HAVE_CONFIG_H
25#include <config.h>
26#endif
27
28#include <dbus-c++/debug.h>
29#include <dbus-c++/connection.h>
30
31#include <dbus/dbus.h>
32#include <string>
33
34#include "internalerror.h"
35
36#include "connection_p.h"
37#include "dispatcher_p.h"
38#include "server_p.h"
39#include "message_p.h"
40#include "pendingcall_p.h"
41
42using namespace DBus;
43
44Connection::Private::Private(DBusConnection *c, Server::Private *s)
45 : conn(c) , dispatcher(NULL), server(s)
46{
47 init();
48}
49
50Connection::Private::Private(DBusBusType type)
51 : dispatcher(NULL), server(NULL)
52{
53 InternalError e;
54
55 conn = dbus_bus_get_private(type, e);
56
57 if (e) throw Error(e);
58
59 init();
60}
61
62Connection::Private::~Private()
63{
64 debug_log("terminating connection 0x%08x", conn);
65
66 detach_server();
67
68 if (dbus_connection_get_is_connected(conn))
69 {
70 std::vector<std::string>::iterator i = names.begin();
71
72 while (i != names.end())
73 {
74 debug_log("%s: releasing bus name %s", dbus_bus_get_unique_name(conn), i->c_str());
75 dbus_bus_release_name(conn, i->c_str(), NULL);
76 ++i;
77 }
78 dbus_connection_close(conn);
79 }
80 dbus_connection_unref(conn);
81}
82
83void Connection::Private::init()
84{
85 dbus_connection_ref(conn);
86 dbus_connection_ref(conn); //todo: the library has to own another reference
87
88 disconn_filter = new Callback<Connection::Private, bool, const Message &>(
89 this, &Connection::Private::disconn_filter_function
90 );
91
92 dbus_connection_add_filter(conn, message_filter_stub, &disconn_filter, NULL); // TODO: some assert at least
93
94 dbus_connection_set_dispatch_status_function(conn, dispatch_status_stub, this, 0);
95 dbus_connection_set_exit_on_disconnect(conn, false); //why was this set to true??
96}
97
98void Connection::Private::detach_server()
99{
100 /* Server::Private *tmp = server;
101
102 server = NULL;
103
104 if (tmp)
105 {
106 ConnectionList::iterator i;
107
108 for (i = tmp->connections.begin(); i != tmp->connections.end(); ++i)
109 {
110 if (i->_pvt.get() == this)
111 {
112 tmp->connections.erase(i);
113 break;
114 }
115 }
116 }*/
117}
118
119bool Connection::Private::do_dispatch()
120{
121 debug_log("dispatching on %p", conn);
122
123 if (!dbus_connection_get_is_connected(conn))
124 {
125 debug_log("connection terminated");
126
127 detach_server();
128
129 return true;
130 }
131
132 return dbus_connection_dispatch(conn) != DBUS_DISPATCH_DATA_REMAINS;
133}
134
135void Connection::Private::dispatch_status_stub(DBusConnection *dc, DBusDispatchStatus status, void *data)
136{
137 Private *p = static_cast<Private *>(data);
138
139 switch (status)
140 {
141 case DBUS_DISPATCH_DATA_REMAINS:
142 debug_log("some dispatching to do on %p", dc);
143 p->dispatcher->queue_connection(p);
144 break;
145
146 case DBUS_DISPATCH_COMPLETE:
147 debug_log("all dispatching done on %p", dc);
148 break;
149
150 case DBUS_DISPATCH_NEED_MEMORY: //uh oh...
151 debug_log("connection %p needs memory", dc);
152 break;
153 }
154}
155
156DBusHandlerResult Connection::Private::message_filter_stub(DBusConnection *conn, DBusMessage *dmsg, void *data)
157{
158 MessageSlot *slot = static_cast<MessageSlot *>(data);
159
160 Message msg = Message(new Message::Private(dmsg));
161
162 return slot && !slot->empty() && slot->call(msg)
163 ? DBUS_HANDLER_RESULT_HANDLED
164 : DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
165}
166
167bool Connection::Private::disconn_filter_function(const Message &msg)
168{
169 if (msg.is_signal(DBUS_INTERFACE_LOCAL, "Disconnected"))
170 {
171 debug_log("%p disconnected by local bus", conn);
172 dbus_connection_close(conn);
173
174 return true;
175 }
176 return false;
177}
178
179DBusDispatchStatus Connection::Private::dispatch_status()
180{
181 return dbus_connection_get_dispatch_status(conn);
182}
183
184bool Connection::Private::has_something_to_dispatch()
185{
186 return dispatch_status() == DBUS_DISPATCH_DATA_REMAINS;
187}
188
189
190Connection Connection::SystemBus()
191{
192 return Connection(new Private(DBUS_BUS_SYSTEM));
193}
194
195Connection Connection::SessionBus()
196{
197 return Connection(new Private(DBUS_BUS_SESSION));
198}
199
200Connection Connection::ActivationBus()
201{
202 return Connection(new Private(DBUS_BUS_STARTER));
203}
204
205Connection::Connection(const char *address, bool priv)
206 : _timeout(-1)
207{
208 InternalError e;
209 DBusConnection *conn = priv
210 ? dbus_connection_open_private(address, e)
211 : dbus_connection_open(address, e);
212
213 if (e) throw Error(e);
214
215 _pvt = new Private(conn);
216
217 setup(default_dispatcher);
218
219 debug_log("connected to %s", address);
220}
221
222Connection::Connection(Connection::Private *p)
223 : _pvt(p), _timeout(-1)
224{
225 setup(default_dispatcher);
226}
227
228Connection::Connection(const Connection &c)
229 : _pvt(c._pvt), _timeout(c._timeout)
230{
231 dbus_connection_ref(_pvt->conn);
232}
233
234Connection::~Connection()
235{
236 dbus_connection_unref(_pvt->conn);
237}
238
239Dispatcher *Connection::setup(Dispatcher *dispatcher)
240{
241 debug_log("registering stubs for connection %p", _pvt->conn);
242
243 if (!dispatcher) dispatcher = default_dispatcher;
244
245 if (!dispatcher) throw ErrorFailed("no default dispatcher set for new connection");
246
247 Dispatcher *prev = _pvt->dispatcher;
248
249 _pvt->dispatcher = dispatcher;
250
251 dispatcher->queue_connection(_pvt.get());
252
253 dbus_connection_set_watch_functions(
254 _pvt->conn,
255 Dispatcher::Private::on_add_watch,
256 Dispatcher::Private::on_rem_watch,
257 Dispatcher::Private::on_toggle_watch,
258 dispatcher,
259 0
260 );
261
262 dbus_connection_set_timeout_functions(
263 _pvt->conn,
264 Dispatcher::Private::on_add_timeout,
265 Dispatcher::Private::on_rem_timeout,
266 Dispatcher::Private::on_toggle_timeout,
267 dispatcher,
268 0
269 );
270
271 return prev;
272}
273
274bool Connection::operator == (const Connection &c) const
275{
276 return _pvt->conn == c._pvt->conn;
277}
278
279bool Connection::register_bus()
280{
281 InternalError e;
282
283 bool r = dbus_bus_register(_pvt->conn, e);
284
285 if (e) throw(e);
286
287 return r;
288}
289
290bool Connection::connected() const
291{
292 return dbus_connection_get_is_connected(_pvt->conn);
293}
294
295void Connection::disconnect()
296{
297// dbus_connection_disconnect(_pvt->conn); // disappeared in 0.9x
298 dbus_connection_close(_pvt->conn);
299}
300
301void Connection::exit_on_disconnect(bool exit)
302{
303 dbus_connection_set_exit_on_disconnect(_pvt->conn, exit);
304}
305
306bool Connection::unique_name(const char *n)
307{
308 return dbus_bus_set_unique_name(_pvt->conn, n);
309}
310
311const char *Connection::unique_name() const
312{
313 return dbus_bus_get_unique_name(_pvt->conn);
314}
315
316void Connection::flush()
317{
318 dbus_connection_flush(_pvt->conn);
319}
320
321void Connection::add_match(const char *rule)
322{
323 InternalError e;
324
325 dbus_bus_add_match(_pvt->conn, rule, e);
326
327 debug_log("%s: added match rule %s", unique_name(), rule);
328
329 if (e) throw Error(e);
330}
331
332void Connection::remove_match(const char *rule,
333 bool throw_on_error)
334{
335 InternalError e;
336
337 dbus_bus_remove_match(_pvt->conn, rule, e);
338
339 debug_log("%s: removed match rule %s", unique_name(), rule);
340
341 if (e)
342 {
343 if (throw_on_error)
344 throw Error(e);
345 else
346 debug_log("DBus::Connection::remove_match: %s (%s).",
347 static_cast<DBusError *>(e)->message,
348 static_cast<DBusError *>(e)->name);
349 }
350}
351
352bool Connection::add_filter(MessageSlot &s)
353{
354 debug_log("%s: adding filter", unique_name());
355 return dbus_connection_add_filter(_pvt->conn, Private::message_filter_stub, &s, NULL);
356}
357
358void Connection::remove_filter(MessageSlot &s)
359{
360 debug_log("%s: removing filter", unique_name());
361 dbus_connection_remove_filter(_pvt->conn, Private::message_filter_stub, &s);
362}
363
364bool Connection::send(const Message &msg, unsigned int *serial)
365{
366 return dbus_connection_send(_pvt->conn, msg._pvt->msg, serial);
367}
368
369Message Connection::send_blocking(Message &msg, int timeout)
370{
371 DBusMessage *reply;
372 InternalError e;
373
374 if (this->_timeout != -1)
375 {
376 reply = dbus_connection_send_with_reply_and_block(_pvt->conn, msg._pvt->msg, this->_timeout, e);
377 }
378 else
379 {
380 reply = dbus_connection_send_with_reply_and_block(_pvt->conn, msg._pvt->msg, timeout, e);
381 }
382
383 if (e) throw Error(e);
384
385 return Message(new Message::Private(reply), false);
386}
387
388PendingCall Connection::send_async(Message &msg, int timeout)
389{
390 DBusPendingCall *pending;
391
392 if (!dbus_connection_send_with_reply(_pvt->conn, msg._pvt->msg, &pending, timeout))
393 {
394 throw ErrorNoMemory("Unable to start asynchronous call");
395 }
396 return PendingCall(new PendingCall::Private(pending));
397}
398
399void Connection::request_name(const char *name, int flags)
400{
401 InternalError e;
402
403 debug_log("%s: registering bus name %s", unique_name(), name);
404
405 /*
406 * TODO:
407 * Think about giving back the 'ret' value. Some people on the list
408 * requested about this...
409 */
410 int ret = dbus_bus_request_name(_pvt->conn, name, flags, e);
411
412 if (ret == -1)
413 {
414 if (e) throw Error(e);
415 }
416
417// this->remove_match("destination");
418
419 if (name)
420 {
421 _pvt->names.push_back(name);
422 std::string match = "destination='" + _pvt->names.back() + "'";
423 add_match(match.c_str());
424 }
425}
426
427unsigned long Connection::sender_unix_uid(const char *sender)
428{
429 InternalError e;
430
431 unsigned long ul = dbus_bus_get_unix_user(_pvt->conn, sender, e);
432
433 if (e) throw Error(e);
434
435 return ul;
436}
437
438bool Connection::has_name(const char *name)
439{
440 InternalError e;
441
442 bool b = dbus_bus_name_has_owner(_pvt->conn, name, e);
443
444 if (e) throw Error(e);
445
446 return b;
447}
448
449const std::vector<std::string>& Connection::names()
450{
451 return _pvt->names;
452}
453
454bool Connection::start_service(const char *name, unsigned long flags)
455{
456 InternalError e;
457
458 bool b = dbus_bus_start_service_by_name(_pvt->conn, name, flags, NULL, e);
459
460 if (e) throw Error(e);
461
462 return b;
463}
464
465void Connection::set_timeout(int timeout)
466{
467 _timeout = timeout;
468}
469
470int Connection::get_timeout()
471{
472 return _timeout;
473}
474