3
* dbus_proxy.c - D-Bus remote object proxy implementation
5
* Copyright © 2009 Scott James Remnant <scott@netsplit.com>.
6
* Copyright © 2009 Canonical Ltd.
8
* This program is free software; you can redistribute it and/or modify
9
* it under the terms of the GNU General Public License version 2, as
10
* published by the Free Software Foundation.
12
* This program is distributed in the hope that it will be useful,
13
* but WITHOUT ANY WARRANTY; without even the implied warranty of
14
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
* GNU General Public License for more details.
17
* You should have received a copy of the GNU General Public License along
18
* with this program; if not, write to the Free Software Foundation, Inc.,
19
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
24
#endif /* HAVE_CONFIG_H */
27
#include <dbus/dbus.h>
31
#include <nih/macros.h>
32
#include <nih/alloc.h>
33
#include <nih/string.h>
34
#include <nih/logging.h>
35
#include <nih/error.h>
37
#include <nih-dbus/dbus_error.h>
38
#include <nih-dbus/dbus_object.h>
40
#include "dbus_proxy.h"
43
/* Prototypes for static functions */
44
static int nih_dbus_proxy_destroy (NihDBusProxy *proxy);
45
static int nih_dbus_proxy_name_track (NihDBusProxy *proxy)
46
__attribute__ ((warn_unused_result));
47
static char *nih_dbus_proxy_name_rule (const void *parent,
49
__attribute__ ((warn_unused_result, malloc));
50
static int nih_dbus_proxy_signal_destroy (NihDBusProxySignal *proxied);
51
static char *nih_dbus_proxy_signal_rule (const void *parent,
52
NihDBusProxySignal *proxied)
53
__attribute__ ((warn_unused_result, malloc));
55
/* Prototypes for handler functions */
56
static DBusHandlerResult nih_dbus_proxy_name_owner_changed (DBusConnection *connection,
63
* @parent: parent object for new proxy,
64
* @connection: D-Bus connection to associate with,
65
* @name: well-known name of object owner,
66
* @path: path of object,
67
* @lost_handler: optional handler for remote object loss.
68
* @data: data pointer for handlers.
70
* Creates a new D-Bus proxy for a remote object on @connection with the
71
* well-known or unique bus name @name at @path. The returned structure
72
* is allocated with nih_alloc() and holds a reference to @connection.
74
* @name may be NULL for peer-to-peer D-Bus connections.
76
* Proxies are not generally bound to the life-time of the connection or
77
* the remote object, thus there may be periods when functions will fail
78
* or signal filter functions left dormant due to unavailability of the
79
* remote object or even cease permanently when the bus connection is
82
* @name will be tracked on the bus, with the current owner's unique name
83
* being available in the returned structure's owner member. Should the
84
* name be lost from the bus, the optional @lost_handler function will be
85
* called to allow clean-up of the proxy.
87
* If @parent is not NULL, it should be a pointer to another object which
88
* will be used as a parent for the returned proxy. When all parents
89
* of the returned proxy are freed, the returned proxy will also be
92
* Returns: new NihDBusProxy structure on success, or NULL on raised
96
nih_dbus_proxy_new (const void * parent,
97
DBusConnection * connection,
100
NihDBusLostHandler lost_handler,
105
nih_assert (connection != NULL);
106
nih_assert (path != NULL);
107
nih_assert ((lost_handler == NULL) || (name != NULL));
109
proxy = nih_new (parent, NihDBusProxy);
111
nih_return_no_memory_error (NULL);
113
proxy->connection = connection;
117
proxy->name = nih_strdup (proxy, name);
120
nih_return_no_memory_error (NULL);
126
proxy->path = nih_strdup (proxy, path);
129
nih_return_no_memory_error (NULL);
132
proxy->auto_start = TRUE;
134
proxy->lost_handler = lost_handler;
138
if (nih_dbus_proxy_name_track (proxy) < 0) {
144
dbus_connection_ref (proxy->connection);
145
nih_alloc_set_destructor (proxy, nih_dbus_proxy_destroy);
151
* nih_dbus_proxy_destroy:
152
* @proxy: proxy object being destroyed.
154
* Destructor function for an NihDBusProxy structure; drops the bus rule
155
* matching the NameOwnerChanged signal, the associated filter function,
156
* and the reference to the D-Bus connection it holds.
158
* Returns: always zero.
161
nih_dbus_proxy_destroy (NihDBusProxy *proxy)
163
nih_local char *rule = NULL;
164
DBusError dbus_error;
166
nih_assert (proxy != NULL);
169
rule = NIH_MUST (nih_dbus_proxy_name_rule (NULL, proxy));
171
dbus_error_init (&dbus_error);
172
dbus_bus_remove_match (proxy->connection, rule, &dbus_error);
173
dbus_error_free (&dbus_error);
175
dbus_connection_remove_filter (proxy->connection,
176
(DBusHandleMessageFunction)nih_dbus_proxy_name_owner_changed,
180
dbus_connection_unref (proxy->connection);
187
* nih_dbus_proxy_name_track:
188
* @proxy: proxy object.
190
* Set up name tracking for the given @proxy object. We get the current
191
* owner of the name in a synchronous call and set the connection up to
192
* watch for a change in that owner updating the proxy's owner member in
195
* If the proxy has no owner, the connection is instead set up to wait
196
* for it to come onto the bus, and then reset later.
198
* Returns: 0 on success, negative value on raised error.
201
nih_dbus_proxy_name_track (NihDBusProxy *proxy)
203
nih_local char *rule = NULL;
204
DBusError dbus_error;
205
DBusMessage * method_call;
209
nih_assert (proxy != NULL);
210
nih_assert (proxy->name != NULL);
212
/* Add the filter function that handles the NameOwnerChanged
213
* signal. We need to do this first so that we can handle anything
214
* that arrives after we add the signal match.
216
if (! dbus_connection_add_filter (proxy->connection,
217
(DBusHandleMessageFunction)nih_dbus_proxy_name_owner_changed,
219
nih_return_no_memory_error (-1);
221
/* Ask the bus to send us matching signals. We've put the filter
222
* function in place so we'll get callbacks straight away; but we
223
* still need to do this before asking for the current name so
224
* we don't miss something.
226
rule = nih_dbus_proxy_name_rule (NULL, proxy);
228
nih_error_raise_no_memory ();
229
goto error_after_filter;
232
dbus_error_init (&dbus_error);
234
dbus_bus_add_match (proxy->connection, rule, &dbus_error);
235
if (dbus_error_is_set (&dbus_error)) {
236
if (dbus_error_has_name (&dbus_error, DBUS_ERROR_NO_MEMORY)) {
237
nih_error_raise_no_memory ();
239
nih_dbus_error_raise (dbus_error.name,
243
dbus_error_free (&dbus_error);
244
goto error_after_filter;
247
/* Now that the bus will send us signals about changes in the name's
248
* owner, and we'll handle them, we can get the current owner of the
249
* name. We may have some signals in the queue that predate this,
250
* but the end result will be the same.
252
method_call = dbus_message_new_method_call (DBUS_SERVICE_DBUS,
257
nih_error_raise_no_memory ();
259
dbus_error_free (&dbus_error);
260
goto error_after_match;
263
if (! dbus_message_append_args (method_call,
264
DBUS_TYPE_STRING, &proxy->name,
265
DBUS_TYPE_INVALID)) {
266
nih_error_raise_no_memory ();
268
dbus_message_unref (method_call);
269
dbus_error_free (&dbus_error);
270
goto error_after_match;
273
/* Parse the reply; an owner is returned, we fill in the owner
274
* member of the proxy - otherwise we set it to NULL.
276
reply = dbus_connection_send_with_reply_and_block (proxy->connection, method_call,
279
if (dbus_error_has_name (&dbus_error,
280
DBUS_ERROR_NAME_HAS_NO_OWNER)) {
281
nih_debug ("%s is not currently owned", proxy->name);
283
dbus_message_unref (method_call);
284
dbus_error_free (&dbus_error);
289
} else if (dbus_error_has_name (&dbus_error,
290
DBUS_ERROR_NO_MEMORY)) {
291
nih_error_raise_no_memory ();
293
nih_dbus_error_raise (dbus_error.name,
297
dbus_message_unref (method_call);
298
dbus_error_free (&dbus_error);
299
goto error_after_match;
302
dbus_message_unref (method_call);
304
if (! dbus_message_get_args (reply, &dbus_error,
305
DBUS_TYPE_STRING, &owner,
306
DBUS_TYPE_INVALID)) {
307
if (dbus_error_has_name (&dbus_error, DBUS_ERROR_NO_MEMORY)) {
308
nih_error_raise_no_memory ();
310
nih_dbus_error_raise (dbus_error.name,
314
dbus_message_unref (reply);
315
dbus_error_free (&dbus_error);
316
goto error_after_match;
319
dbus_error_free (&dbus_error);
321
proxy->owner = nih_strdup (proxy, owner);
322
if (! proxy->owner) {
323
nih_error_raise_no_memory ();
325
dbus_message_unref (reply);
326
goto error_after_match;
329
dbus_message_unref (reply);
331
nih_debug ("%s is currently owned by %s", proxy->name, proxy->owner);
336
dbus_error_init (&dbus_error);
337
dbus_bus_remove_match (proxy->connection, rule, &dbus_error);
338
dbus_error_free (&dbus_error);
340
dbus_connection_remove_filter (proxy->connection,
341
(DBusHandleMessageFunction)nih_dbus_proxy_name_owner_changed,
348
* nih_dbus_proxy_name_rule:
349
* @parent: parent object for new string,
350
* @proxy: proxy object.
352
* Generates a D-Bus match rule for the NameOwnerChanged signal for the
355
* If @parent is not NULL, it should be a pointer to another object which
356
* will be used as a parent for the returned string. When all parents
357
* of the returned string are freed, the returned string will also be
360
* Returns: newly allocated string or NULL on insufficient memory.
363
nih_dbus_proxy_name_rule (const void * parent,
368
nih_assert (proxy != NULL);
369
nih_assert (proxy->name != NULL);
371
rule = nih_sprintf (parent, ("type='%s',sender='%s',path='%s',"
372
"interface='%s',member='%s',"
385
* nih_dbus_proxy_name_owner_changed:
386
* @connection: D-Bus connection signal received on,
387
* @message: signal message,
388
* @proxy: associated proxy object.
390
* This function is called by D-Bus on receipt of the NameOwnerChanged
391
* signal for the registered name that @proxy represents. The proxy's
392
* lost_handler function is called to decide what to do about it.
394
* Returns: usually DBUS_HANDLER_RESULT_NOT_YET_HANDLED so other signal
395
* handlers also get a look-in, DBUS_HANDLED_RESULT_NEED_MEMORY if
396
* insufficient memory.
398
static DBusHandlerResult
399
nih_dbus_proxy_name_owner_changed (DBusConnection *connection,
400
DBusMessage * message,
401
NihDBusProxy * proxy)
403
DBusError dbus_error;
405
const char *old_owner;
406
const char *new_owner;
408
nih_assert (connection != NULL);
409
nih_assert (message != NULL);
410
nih_assert (proxy->connection == connection);
411
nih_assert (proxy->name != NULL);
413
if (! dbus_message_is_signal (message, DBUS_INTERFACE_DBUS,
415
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
417
if (! dbus_message_has_path (message, DBUS_PATH_DBUS))
418
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
420
if (! dbus_message_has_sender (message, DBUS_SERVICE_DBUS))
421
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
423
dbus_error_init (&dbus_error);
424
if (! dbus_message_get_args (message, &dbus_error,
425
DBUS_TYPE_STRING, &name,
426
DBUS_TYPE_STRING, &old_owner,
427
DBUS_TYPE_STRING, &new_owner,
428
DBUS_TYPE_INVALID)) {
429
dbus_error_free (&dbus_error);
430
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
433
dbus_error_free (&dbus_error);
435
if (strcmp (name, proxy->name))
436
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
438
/* Ok, it's really the right NameOwnerChanged signal. If the name
439
* has a new owner, update the owner property (tracking a well known
440
* name between instances) otherwise call the lost handler.
442
if (strlen (new_owner)) {
443
nih_debug ("%s changed owner from %s to %s",
444
proxy->name, old_owner, new_owner);
447
nih_unref (proxy->owner, proxy);
448
proxy->owner = NIH_MUST (nih_strdup (proxy, new_owner));
450
nih_debug ("%s owner left the bus", proxy->name);
453
nih_unref (proxy->owner, proxy);
456
if (proxy->lost_handler) {
457
nih_error_push_context ();
458
proxy->lost_handler (proxy->data, proxy);
459
nih_error_pop_context ();
463
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
468
* nih_dbus_proxy_connect:
469
* @proxy: proxy for remote object,
470
* @interface: signal interface definition,
471
* @name: name of signal,
472
* @handler: signal handler function,
473
* @data: data to pass to @handler.
475
* Connect the signal @name on @interface to @proxy so that the @handler
476
* function is passed to the filter function defined by @signal when it
477
* is received on the proxied D-Bus connection.
479
* The signal can be disconnected by freeing the returned structure, the
480
* signal is also bound to the lifetime of @proxy so that the signal is
481
* disconnected when the proxy is freed.
483
* Returns: newly allocated NihDBusProxySignal structure or NULL on raised
487
nih_dbus_proxy_connect (NihDBusProxy * proxy,
488
const NihDBusInterface *interface,
490
NihDBusSignalHandler handler,
493
NihDBusProxySignal *proxied;
494
nih_local char * rule = NULL;
495
DBusError dbus_error;
497
nih_assert (proxy != NULL);
498
nih_assert (interface != NULL);
499
nih_assert (name != NULL);
500
nih_assert (handler != NULL);
502
proxied = nih_new (proxy, NihDBusProxySignal);
504
nih_return_no_memory_error (NULL);
506
proxied->proxy = proxy;
507
proxied->interface = interface;
508
proxied->signal = NULL;
509
proxied->handler = handler;
510
proxied->data = data;
512
for (const NihDBusSignal *signal = interface->signals;
513
signal && signal->name; signal++) {
514
if (! strcmp (signal->name, name)) {
515
proxied->signal = signal;
519
nih_assert (proxied->signal != NULL);
521
if (! dbus_connection_add_filter (proxied->proxy->connection,
522
(DBusHandleMessageFunction)proxied->signal->filter,
525
nih_return_no_memory_error (NULL);
528
if (proxied->proxy->name) {
529
rule = nih_dbus_proxy_signal_rule (NULL, proxied);
531
nih_error_raise_no_memory ();
535
dbus_error_init (&dbus_error);
537
dbus_bus_add_match (proxied->proxy->connection, rule, &dbus_error);
538
if (dbus_error_is_set (&dbus_error)) {
539
if (dbus_error_has_name (&dbus_error, DBUS_ERROR_NO_MEMORY)) {
540
nih_error_raise_no_memory ();
542
nih_dbus_error_raise (dbus_error.name,
546
dbus_error_free (&dbus_error);
551
nih_alloc_set_destructor (proxied, nih_dbus_proxy_signal_destroy);
556
dbus_connection_remove_filter (proxied->proxy->connection,
557
(DBusHandleMessageFunction)proxied->signal->filter,
565
* nih_dbus_proxy_signal_destroy:
566
* @proxied: proxied signal being destroyed.
568
* Destructor function for an NihDBusProxySignal structure; drops the bus
569
* rule matching the signal and the associated filter function.
571
* Returns: always zero.
574
nih_dbus_proxy_signal_destroy (NihDBusProxySignal *proxied)
576
nih_local char *rule = NULL;
577
DBusError dbus_error;
579
nih_assert (proxied != NULL);
581
if (proxied->proxy->name) {
582
rule = NIH_MUST (nih_dbus_proxy_signal_rule (NULL, proxied));
584
dbus_error_init (&dbus_error);
585
dbus_bus_remove_match (proxied->proxy->connection,
587
dbus_error_free (&dbus_error);
590
dbus_connection_remove_filter (proxied->proxy->connection,
591
(DBusHandleMessageFunction)proxied->signal->filter,
598
* nih_dbus_proxy_signal_rule:
599
* @parent: parent object for new string,
600
* @proxied: proxied signal.
602
* Generates a D-Bus match rule for the @proxied signal.
604
* If @parent is not NULL, it should be a pointer to another object which
605
* will be used as a parent for the returned string. When all parents
606
* of the returned string are freed, the returned string will also be
609
* Returns: newly allocated string or NULL on insufficient memory.
612
nih_dbus_proxy_signal_rule (const void * parent,
613
NihDBusProxySignal *proxied)
617
nih_assert (proxied != NULL);
618
nih_assert (proxied->proxy->name != NULL);
620
rule = nih_sprintf (parent, ("type='%s',sender='%s',path='%s',"
621
"interface='%s',member='%s'"),
623
proxied->proxy->name,
624
proxied->proxy->path,
625
proxied->interface->name,
626
proxied->signal->name);