root/dbus/helpers.c @ 78f5665ac5956da690a2a2f02bbcc30028929bd2

Revision 78f5665ac5956da690a2a2f02bbcc30028929bd2, 24.1 KB (checked in by Nedko Arnaudov <nedko@…>, 9 months ago)

helpers for handling dbus signals

  • Property mode set to 100644
Line 
1/* -*- Mode: C ; c-basic-offset: 2 -*- */
2/*
3 * LADI Session Handler (ladish)
4 *
5 * Copyright (C) 2008, 2009 Nedko Arnaudov <nedko@arnaudov.name>
6 * Copyright (C) 2008 Juuso Alasuutari <juuso.alasuutari@gmail.com>
7 *
8 **************************************************************************
9 * This file contains code of the D-Bus helpers
10 **************************************************************************
11 *
12 * Licensed under the Academic Free License version 2.1
13 *
14 * LADI Session Handler is free software; you can redistribute it and/or modify
15 * it under the terms of the GNU General Public License as published by
16 * the Free Software Foundation; either version 2 of the License, or
17 * (at your option) any later version.
18 *
19 * LADI Session Handler is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22 * GNU General Public License for more details.
23 *
24 * You should have received a copy of the GNU General Public License
25 * along with LADI Session Handler. If not, see <http://www.gnu.org/licenses/>
26 * or write to the Free Software Foundation, Inc.,
27 * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
28 */
29
30#include <stdbool.h>
31#include <dbus/dbus.h>
32#include <string.h>
33#include <stdlib.h>
34
35#include "helpers.h"
36#include "method.h"
37#include "../log.h"
38#include "../assert.h"
39#include "../common/klist.h"
40
41DBusConnection * g_dbus_connection;
42DBusError g_dbus_error;
43
44struct dbus_signal_hook_descriptor
45{
46  struct list_head siblings;
47  char * object;
48  char * interface;
49  void * hook_context;
50  const struct dbus_signal_hook * signal_hooks;
51};
52
53struct dbus_service_descriptor
54{
55  struct list_head siblings;
56  char * service_name;
57  void (* lifetime_hook_function)(bool appeared);
58  struct list_head hooks;
59};
60
61LIST_HEAD(g_dbus_services);
62
63bool dbus_iter_get_dict_entry(DBusMessageIter * iter, const char ** key_ptr, void * value_ptr, int * type_ptr, int * size_ptr)
64{
65  if (!iter || !key_ptr || !value_ptr || !type_ptr) {
66    log_error("Invalid arguments");
67    return false;
68  }
69
70  DBusMessageIter dict_iter, variant_iter;
71
72  if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_DICT_ENTRY) {
73    log_error("Iterator does not point to a dict entry container");
74    return false;
75  }
76
77  dbus_message_iter_recurse(iter, &dict_iter);
78
79  if (dbus_message_iter_get_arg_type(&dict_iter) != DBUS_TYPE_STRING) {
80    log_error("Cannot find key in dict entry container");
81    return false;
82  }
83
84  dbus_message_iter_get_basic(&dict_iter, key_ptr);
85
86  if (!dbus_message_iter_next(&dict_iter)
87      || dbus_message_iter_get_arg_type(&dict_iter) != DBUS_TYPE_VARIANT) {
88    log_error("Cannot find variant container in dict entry");
89    return false;
90  }
91
92  dbus_message_iter_recurse(&dict_iter, &variant_iter);
93
94  *type_ptr = dbus_message_iter_get_arg_type(&variant_iter);
95  if (*type_ptr == DBUS_TYPE_INVALID) {
96    log_error("Cannot find value in variant container");
97    return false;
98  }
99
100  if (*type_ptr == DBUS_TYPE_ARRAY) {
101    DBusMessageIter array_iter;
102    int n;
103
104    if (dbus_message_iter_get_element_type(&variant_iter)
105        != DBUS_TYPE_BYTE) {
106      log_error("Dict entry value is a non-byte array");
107      return false;
108    }
109    *type_ptr = '-';
110
111    dbus_message_iter_recurse(&variant_iter, &array_iter);
112    dbus_message_iter_get_fixed_array(&array_iter, value_ptr, &n);
113
114    if (size_ptr)
115      *size_ptr = n;
116  } else
117    dbus_message_iter_get_basic(&variant_iter, value_ptr);
118
119  return true;
120}
121
122/*
123 * Append a variant type to a D-Bus message.
124 * Return false if something fails, true otherwise.
125 */
126bool dbus_iter_append_variant(DBusMessageIter * iter, int type, const void * arg)
127{
128  DBusMessageIter sub_iter;
129  char s[2];
130
131  s[0] = (char)type;
132  s[1] = '\0';
133
134  /* Open a variant container. */
135  if (!dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT, (const char *)s, &sub_iter))
136    return false;
137
138  /* Append the supplied value. */
139  if (!dbus_message_iter_append_basic(&sub_iter, type, arg))
140  {
141    dbus_message_iter_close_container(iter, &sub_iter);
142    return false;
143  }
144
145  /* Close the container. */
146  if (!dbus_message_iter_close_container(iter, &sub_iter))
147    return false;
148
149  return true;
150}
151
152static __inline__ bool dbus_iter_append_variant_raw(DBusMessageIter * iter, const void * buf, int len)
153{
154  DBusMessageIter variant_iter, array_iter;
155
156  /* Open a variant container. */
157  if (!dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT,
158                                        "ay", &variant_iter))
159    return false;
160
161  /* Open an array container. */
162  if (!dbus_message_iter_open_container(&variant_iter, DBUS_TYPE_ARRAY,
163                                        "y", &array_iter))
164    goto fail;
165
166  /* Append the supplied data. */
167  if (!dbus_message_iter_append_fixed_array(&array_iter, DBUS_TYPE_BYTE, buf, len)) {
168    dbus_message_iter_close_container(&variant_iter, &array_iter);
169    goto fail;
170  }
171
172  /* Close the containers. */
173  if (!dbus_message_iter_close_container(&variant_iter, &array_iter))
174    goto fail;
175  else if (!dbus_message_iter_close_container(iter, &variant_iter))
176    return false;
177
178  return true;
179
180fail:
181  dbus_message_iter_close_container(iter, &variant_iter);
182  return false;
183}
184
185bool dbus_iter_append_dict_entry(DBusMessageIter * iter, int type, const char * key, const void * value, int length)
186{
187  DBusMessageIter dict_iter;
188
189  if (!dbus_message_iter_open_container(iter, DBUS_TYPE_DICT_ENTRY, NULL, &dict_iter))
190    return false;
191
192  if (!dbus_message_iter_append_basic(&dict_iter, DBUS_TYPE_STRING, &key))
193    goto fail;
194
195  if (type == '-')
196  {
197    if (!dbus_iter_append_variant_raw(&dict_iter, value, length))
198      goto fail;
199  }
200  else if (!dbus_iter_append_variant(&dict_iter, type, value))
201  {
202    goto fail;
203  }
204
205  if (!dbus_message_iter_close_container(iter, &dict_iter))
206    return false;
207
208  return true;
209
210fail:
211  dbus_message_iter_close_container(iter, &dict_iter);
212  return false;
213}
214
215bool dbus_maybe_add_dict_entry_string(DBusMessageIter *dict_iter_ptr, const char * key, const char * value)
216{
217  DBusMessageIter dict_entry_iter;
218
219  if (value == NULL)
220  {
221    return true;
222  }
223
224  if (!dbus_message_iter_open_container(dict_iter_ptr, DBUS_TYPE_DICT_ENTRY, NULL, &dict_entry_iter))
225  {
226    return false;
227  }
228
229  if (!dbus_message_iter_append_basic(&dict_entry_iter, DBUS_TYPE_STRING, (const void *) &key))
230  {
231    dbus_message_iter_close_container(dict_iter_ptr, &dict_entry_iter);
232    return false;
233  }
234
235  dbus_iter_append_variant(&dict_entry_iter, DBUS_TYPE_STRING, &value);
236
237  if (!dbus_message_iter_close_container(dict_iter_ptr, &dict_entry_iter))
238  {
239    return false;
240  }
241
242  return true;
243}
244
245bool dbus_add_dict_entry_uint32(DBusMessageIter * dict_iter_ptr, const char * key, dbus_uint32_t value)
246{
247  DBusMessageIter dict_entry_iter;
248
249  if (!dbus_message_iter_open_container(dict_iter_ptr, DBUS_TYPE_DICT_ENTRY, NULL, &dict_entry_iter))
250  {
251    return false;
252  }
253
254  if (!dbus_message_iter_append_basic(&dict_entry_iter, DBUS_TYPE_STRING, (const void *) &key))
255  {
256    dbus_message_iter_close_container(dict_iter_ptr, &dict_entry_iter);
257    return false;
258  }
259
260  dbus_iter_append_variant(&dict_entry_iter, DBUS_TYPE_UINT32, &value);
261
262  if (!dbus_message_iter_close_container(dict_iter_ptr, &dict_entry_iter))
263  {
264    return false;
265  }
266
267  return true;
268}
269
270bool dbus_add_dict_entry_bool(DBusMessageIter * dict_iter_ptr, const char * key, dbus_bool_t value)
271{
272  DBusMessageIter dict_entry_iter;
273
274  if (!dbus_message_iter_open_container(dict_iter_ptr, DBUS_TYPE_DICT_ENTRY, NULL, &dict_entry_iter))
275  {
276    return false;
277  }
278
279  if (!dbus_message_iter_append_basic(&dict_entry_iter, DBUS_TYPE_STRING, (const void *) &key))
280  {
281    dbus_message_iter_close_container(dict_iter_ptr, &dict_entry_iter);
282    return false;
283  }
284
285  dbus_iter_append_variant(&dict_entry_iter, DBUS_TYPE_BOOLEAN, &value);
286
287  if (!dbus_message_iter_close_container(dict_iter_ptr, &dict_entry_iter))
288  {
289    return false;
290  }
291
292  return true;
293}
294
295bool
296dbus_call(
297  const char * service,
298  const char * object,
299  const char * iface,
300  const char * method,
301  const char * input_signature,
302  ...)
303{
304  DBusMessageIter iter;
305  DBusMessage * request_ptr;
306  DBusMessage * reply_ptr;
307  const char * output_signature;
308  const char * reply_signature;
309  va_list ap;
310  bool ret;
311  void * parameter_ptr;
312  int type;
313  DBusSignatureIter sig_iter;
314
315  //log_info("dbus_call('%s', '%s', '%s', '%s')", service, object, iface, method);
316
317  ret = false;
318  va_start(ap, input_signature);
319
320  if (input_signature != NULL)
321  {
322    if (!dbus_signature_validate(input_signature, NULL))
323    {
324      log_error("input signature '%s' is invalid", input_signature);
325      goto fail;
326    }
327
328    dbus_signature_iter_init(&sig_iter, input_signature);
329
330    request_ptr = dbus_message_new_method_call(service, object, iface, method);
331    if (request_ptr == NULL)
332    {
333      log_error("dbus_message_new_method_call() failed.");
334      goto fail;
335    }
336
337    dbus_message_iter_init_append(request_ptr, &iter);
338
339    while (*input_signature != '\0')
340    {
341      type = dbus_signature_iter_get_current_type(&sig_iter);
342      if (!dbus_type_is_basic(type))
343      {
344        log_error("non-basic input parameter '%c' (%d)", *input_signature, type);
345        goto fail;
346      }
347
348      parameter_ptr = va_arg(ap, void *);
349
350      if (!dbus_message_iter_append_basic(&iter, type, parameter_ptr))
351      {
352        log_error("dbus_message_iter_append_basic() failed.");
353        goto fail;
354      }
355
356      dbus_signature_iter_next(&sig_iter);
357      input_signature++;
358    }
359  }
360  else
361  {
362    request_ptr = va_arg(ap, DBusMessage *);
363  }
364
365  output_signature = va_arg(ap, const char *);
366
367  reply_ptr = dbus_connection_send_with_reply_and_block(
368    g_dbus_connection,
369    request_ptr,
370    DBUS_CALL_DEFAULT_TIMEOUT,
371    &g_dbus_error);
372
373  if (input_signature != NULL)
374  {
375    dbus_message_unref(request_ptr);
376  }
377
378  if (reply_ptr == NULL)
379  {
380    log_error("calling method '%s' failed, error is '%s'", method, g_dbus_error.message);
381    dbus_error_free(&g_dbus_error);
382    goto fail;
383  }
384
385  if (output_signature != NULL)
386  {
387    reply_signature = dbus_message_get_signature(reply_ptr);
388
389    if (strcmp(reply_signature, output_signature) != 0)
390    {
391      log_error("reply signature is '%s' but expected signature is '%s'", reply_signature, output_signature);
392    }
393
394    dbus_message_iter_init(reply_ptr, &iter);
395
396    while (*output_signature++ != '\0')
397    {
398      ASSERT(dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_INVALID); /* we've checked the signature, this should not happen */
399      parameter_ptr = va_arg(ap, void *);
400      dbus_message_iter_get_basic(&iter, parameter_ptr);
401      dbus_message_iter_next(&iter);
402    }
403
404    ASSERT(dbus_message_iter_get_arg_type(&iter) == DBUS_TYPE_INVALID); /* we've checked the signature, this should not happen */
405    dbus_message_unref(reply_ptr);
406  }
407  else
408  {
409    parameter_ptr = va_arg(ap, DBusMessage **);
410    *(DBusMessage **)parameter_ptr = reply_ptr;
411  }
412
413  ret = true;
414
415fail:
416  va_end(ap);
417  return ret;
418}
419
420static
421const char *
422compose_signal_match(
423  const char * service,
424  const char * object,
425  const char * iface,
426  const char * signal)
427{
428  static char rule[1024];
429  snprintf(rule, sizeof(rule), "type='signal',sender='%s',path='%s',interface='%s',member='%s'", service, object, iface, signal);
430  return rule;
431}
432
433static const char * compose_name_owner_match(const char * service)
434{
435  static char rule[1024];
436  snprintf(rule, sizeof(rule), "type='signal',interface='"DBUS_INTERFACE_DBUS"',member=NameOwnerChanged,arg0='%s'", service);
437  return rule;
438}
439
440bool
441dbus_register_object_signal_handler(
442  DBusConnection * connection,
443  const char * service,
444  const char * object,
445  const char * iface,
446  const char * const * signals,
447  DBusHandleMessageFunction handler,
448  void * handler_data)
449{
450  const char * const * signal;
451
452  for (signal = signals; *signal != NULL; signal++)
453  {
454    dbus_bus_add_match(connection, compose_signal_match(service, object, iface, *signal), &g_dbus_error);
455    if (dbus_error_is_set(&g_dbus_error))
456    {
457      log_error("Failed to add D-Bus match rule: %s", g_dbus_error.message);
458      dbus_error_free(&g_dbus_error);
459      return false;
460    }
461  }
462
463  dbus_connection_add_filter(g_dbus_connection, handler, handler_data, NULL);
464
465  return true;
466}
467
468bool
469dbus_unregister_object_signal_handler(
470  DBusConnection * connection,
471  const char * service,
472  const char * object,
473  const char * iface,
474  const char * const * signals,
475  DBusHandleMessageFunction handler,
476  void * handler_data)
477{
478  const char * const * signal;
479
480  for (signal = signals; *signal != NULL; signal++)
481  {
482    dbus_bus_remove_match(connection, compose_signal_match(service, object, iface, *signal), &g_dbus_error);
483    if (dbus_error_is_set(&g_dbus_error))
484    {
485      log_error("Failed to remove D-Bus match rule: %s", g_dbus_error.message);
486      dbus_error_free(&g_dbus_error);
487      return false;
488    }
489  }
490
491  dbus_connection_remove_filter(g_dbus_connection, handler, handler_data);
492
493  return true;
494}
495
496static
497struct dbus_signal_hook_descriptor *
498find_signal_hook_descriptor(
499  struct dbus_service_descriptor * service_ptr,
500  const char * object,
501  const char * interface)
502{
503  struct list_head * node_ptr;
504  struct dbus_signal_hook_descriptor * hook_ptr;
505
506  list_for_each(node_ptr, &service_ptr->hooks)
507  {
508    hook_ptr = list_entry(node_ptr, struct dbus_signal_hook_descriptor, siblings);
509    if (strcmp(hook_ptr->object, object) == 0 &&
510        strcmp(hook_ptr->interface, interface) == 0)
511    {
512      return hook_ptr;
513    }
514  }
515
516  return NULL;
517}
518
519#define service_ptr ((struct dbus_service_descriptor *)data)
520
521static
522DBusHandlerResult
523dbus_signal_handler(
524  DBusConnection * connection_ptr,
525  DBusMessage * message_ptr,
526  void * data)
527{
528  const char * object_path;
529  const char * interface;
530  const char * signal_name;
531  const char * object_name;
532  const char * old_owner;
533  const char * new_owner;
534  struct dbus_signal_hook_descriptor * hook_ptr;
535  const struct dbus_signal_hook * signal_ptr;
536
537  /* Non-signal messages are ignored */
538  if (dbus_message_get_type(message_ptr) != DBUS_MESSAGE_TYPE_SIGNAL)
539  {
540    return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
541  }
542
543  interface = dbus_message_get_interface(message_ptr);
544  if (interface == NULL)
545  {
546    /* Signals with no interface are ignored */
547    return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
548  }
549
550  object_path = dbus_message_get_path(message_ptr);
551
552  signal_name = dbus_message_get_member(message_ptr);
553  if (signal_name == NULL)
554  {
555    log_error("Received signal with NULL member");
556    return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
557  }
558
559  log_debug("'%s' sent signal '%s'::'%s'", object_path, interface, signal_name);
560
561  /* Handle session bus signals to track service alive state */
562  if (strcmp(interface, DBUS_INTERFACE_DBUS) == 0)
563  {
564    if (strcmp(signal_name, "NameOwnerChanged") != 0)
565    {
566      return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
567    }
568
569    if (service_ptr->lifetime_hook_function == NULL)
570    {
571      return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
572    }
573
574    //log_info("NameOwnerChanged signal received");
575
576    dbus_error_init(&g_dbus_error);
577    if (!dbus_message_get_args(
578          message_ptr,
579          &g_dbus_error,
580          DBUS_TYPE_STRING, &object_name,
581          DBUS_TYPE_STRING, &old_owner,
582          DBUS_TYPE_STRING, &new_owner,
583          DBUS_TYPE_INVALID))
584    {
585      log_error("Cannot get message arguments: %s", g_dbus_error.message);
586      dbus_error_free(&g_dbus_error);
587      return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
588    }
589
590    if (strcmp(object_name, service_ptr->service_name) != 0)
591    {
592      return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
593    }
594
595    if (old_owner[0] == '\0')
596    {
597      service_ptr->lifetime_hook_function(true);
598    }
599    else if (new_owner[0] == '\0')
600    {
601      service_ptr->lifetime_hook_function(false);
602    }
603
604    return DBUS_HANDLER_RESULT_HANDLED;
605  }
606
607  /* Handle object interface signals */
608  if (object_path != NULL)
609  {
610    hook_ptr = find_signal_hook_descriptor(service_ptr, object_path, interface);
611    if (hook_ptr != NULL)
612    {
613      for (signal_ptr = hook_ptr->signal_hooks; signal_ptr->signal_name != NULL; signal_ptr++)
614      {
615        if (strcmp(signal_name, signal_ptr->signal_name) == 0)
616        {
617          signal_ptr->hook_function(hook_ptr->hook_context, message_ptr);
618          return DBUS_HANDLER_RESULT_HANDLED;
619        }
620      }
621    }
622  }
623
624  /* Let everything else pass through */
625  return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
626}
627
628#undef service_ptr
629
630static struct dbus_service_descriptor * find_service_descriptor(const char * service_name)
631{
632  struct list_head * node_ptr;
633  struct dbus_service_descriptor * descr_ptr;
634
635  list_for_each(node_ptr, &g_dbus_services)
636  {
637    descr_ptr = list_entry(node_ptr, struct dbus_service_descriptor, siblings);
638    if (strcmp(descr_ptr->service_name, service_name) == 0)
639    {
640      return descr_ptr;
641    }
642  }
643
644  return NULL;
645}
646
647static struct dbus_service_descriptor * find_or_create_service_descriptor(const char * service_name)
648{
649  struct dbus_service_descriptor * descr_ptr;
650
651  descr_ptr = find_service_descriptor(service_name);
652  if (descr_ptr != NULL)
653  {
654    return descr_ptr;
655  }
656
657  descr_ptr = malloc(sizeof(struct dbus_service_descriptor));
658  if (descr_ptr == NULL)
659  {
660    log_error("malloc() failed to allocate struct dbus_service_descriptor");
661    return NULL;
662  }
663
664  descr_ptr->service_name = strdup(service_name);
665  if (descr_ptr->service_name == NULL)
666  {
667    log_error("strdup() failed for service name '%s'", service_name);
668    free(descr_ptr);
669    return NULL;
670  }
671
672  descr_ptr->lifetime_hook_function = NULL;
673  INIT_LIST_HEAD(&descr_ptr->hooks);
674
675  list_add_tail(&descr_ptr->siblings, &g_dbus_services);
676
677  dbus_connection_add_filter(g_dbus_connection, dbus_signal_handler, descr_ptr, NULL);
678
679  return descr_ptr;
680}
681
682static void free_service_descriptor_if_empty(struct dbus_service_descriptor * service_ptr)
683{
684  if (service_ptr->lifetime_hook_function != NULL)
685  {
686    return;
687  }
688
689  if (!list_empty(&service_ptr->hooks))
690  {
691    return;
692  }
693
694  dbus_connection_remove_filter(g_dbus_connection, dbus_signal_handler, service_ptr);
695
696  list_del(&service_ptr->siblings);
697  free(service_ptr->service_name);
698  free(service_ptr);
699}
700
701bool
702dbus_register_object_signal_hooks(
703  DBusConnection * connection,
704  const char * service_name,
705  const char * object,
706  const char * iface,
707  void * hook_context,
708  const struct dbus_signal_hook * signal_hooks)
709{
710  struct dbus_service_descriptor * service_ptr;
711  struct dbus_signal_hook_descriptor * hook_ptr;
712  const struct dbus_signal_hook * signal_ptr;
713
714  if (connection != g_dbus_connection)
715  {
716    log_error("multiple connections are not implemented yet");
717    ASSERT_NO_PASS;
718    goto fail;
719  }
720
721  service_ptr = find_or_create_service_descriptor(service_name);
722  if (service_ptr == NULL)
723  {
724    log_error("find_or_create_service_descriptor() failed.");
725    goto fail;
726  }
727
728  hook_ptr = find_signal_hook_descriptor(service_ptr, object, iface);
729  if (hook_ptr != NULL)
730  {
731    log_error("refusing to register two signal monitors for '%s':'%s':'%s'", service_name, object, iface);
732    ASSERT_NO_PASS;
733    goto maybe_free_service;
734  }
735
736  hook_ptr = malloc(sizeof(struct dbus_signal_hook_descriptor));
737  if (hook_ptr == NULL)
738  {
739    log_error("malloc() failed to allocate struct dbus_signal_hook_descriptor");
740    goto maybe_free_service;
741  }
742
743  hook_ptr->object = strdup(object);
744  if (hook_ptr->object == NULL)
745  {
746    log_error("strdup() failed for object name");
747    goto free_hook;
748  }
749
750  hook_ptr->interface = strdup(iface);
751  if (hook_ptr->interface == NULL)
752  {
753    log_error("strdup() failed for interface name");
754    goto free_object_name;
755  }
756
757  hook_ptr->hook_context = hook_context;
758  hook_ptr->signal_hooks = signal_hooks;
759
760  list_add_tail(&hook_ptr->siblings, &service_ptr->hooks);
761
762  for (signal_ptr = signal_hooks; signal_ptr->signal_name != NULL; signal_ptr++)
763  {
764    dbus_bus_add_match(connection, compose_signal_match(service_name, object, iface, signal_ptr->signal_name), &g_dbus_error);
765    if (dbus_error_is_set(&g_dbus_error))
766    {
767      log_error("Failed to add D-Bus match rule: %s", g_dbus_error.message);
768      dbus_error_free(&g_dbus_error);
769
770      while (signal_ptr != signal_hooks)
771      {
772        ASSERT(signal_ptr > signal_hooks);
773        signal_ptr--;
774
775        dbus_bus_remove_match(connection, compose_signal_match(service_name, object, iface, signal_ptr->signal_name), &g_dbus_error);
776        if (dbus_error_is_set(&g_dbus_error))
777        {
778          log_error("Failed to remove D-Bus match rule: %s", g_dbus_error.message);
779          dbus_error_free(&g_dbus_error);
780        }
781      }
782
783      goto remove_hook;
784    }
785  }
786
787  return true;
788
789remove_hook:
790  list_del(&hook_ptr->siblings);
791  free(hook_ptr->interface);
792free_object_name:
793  free(hook_ptr->object);
794free_hook:
795  free(hook_ptr);
796maybe_free_service:
797  free_service_descriptor_if_empty(service_ptr);
798fail:
799  return false;
800}
801
802void
803dbus_unregister_object_signal_hooks(
804  DBusConnection * connection,
805  const char * service_name,
806  const char * object,
807  const char * iface)
808{
809  struct dbus_service_descriptor * service_ptr;
810  struct dbus_signal_hook_descriptor * hook_ptr;
811  const struct dbus_signal_hook * signal_ptr;
812
813  if (connection != g_dbus_connection)
814  {
815    log_error("multiple connections are not implemented yet");
816    ASSERT_NO_PASS;
817    return;
818  }
819
820  service_ptr = find_service_descriptor(service_name);
821  if (service_ptr == NULL)
822  {
823    log_error("find_service_descriptor() failed.");
824    ASSERT_NO_PASS;
825    return;
826  }
827
828  hook_ptr = find_signal_hook_descriptor(service_ptr, object, iface);
829  if (hook_ptr == NULL)
830  {
831    log_error("cannot unregister non-existing signal monitor for '%s':'%s':'%s'", service_name, object, iface);
832    ASSERT_NO_PASS;
833    return;
834  }
835
836  for (signal_ptr = hook_ptr->signal_hooks; signal_ptr->signal_name != NULL; signal_ptr++)
837  {
838    dbus_bus_remove_match(connection, compose_signal_match(service_name, object, iface, signal_ptr->signal_name), &g_dbus_error);
839    if (dbus_error_is_set(&g_dbus_error))
840    {
841      if (dbus_error_is_set(&g_dbus_error))
842      {
843        log_error("Failed to remove D-Bus match rule: %s", g_dbus_error.message);
844        dbus_error_free(&g_dbus_error);
845      }
846    }
847  }
848
849  list_del(&hook_ptr->siblings);
850
851  free(hook_ptr->interface);
852  free(hook_ptr->object);
853  free(hook_ptr);
854
855  free_service_descriptor_if_empty(service_ptr);
856}
857
858bool
859dbus_register_service_lifetime_hook(
860  DBusConnection * connection,
861  const char * service_name,
862  void (* hook_function)(bool appeared))
863{
864  struct dbus_service_descriptor * service_ptr;
865
866  if (connection != g_dbus_connection)
867  {
868    log_error("multiple connections are not implemented yet");
869    ASSERT_NO_PASS;
870    goto fail;
871  }
872
873  service_ptr = find_or_create_service_descriptor(service_name);
874  if (service_ptr == NULL)
875  {
876    log_error("find_or_create_service_descriptor() failed.");
877    goto fail;
878  }
879
880  if (service_ptr->lifetime_hook_function != NULL)
881  {
882    log_error("cannot register two lifetime hooks for '%s'", service_name);
883    ASSERT_NO_PASS;
884    goto maybe_free_service;
885  }
886
887  service_ptr->lifetime_hook_function = hook_function;
888
889  dbus_bus_add_match(connection, compose_name_owner_match(service_name), &g_dbus_error);
890  if (dbus_error_is_set(&g_dbus_error))
891  {
892    log_error("Failed to add D-Bus match rule: %s", g_dbus_error.message);
893    dbus_error_free(&g_dbus_error);
894    goto clear_hook;
895  }
896
897  return true;
898
899clear_hook:
900  service_ptr->lifetime_hook_function = NULL;
901maybe_free_service:
902  free_service_descriptor_if_empty(service_ptr);
903fail:
904  return false;
905}
906
907void
908dbus_unregister_service_lifetime_hook(
909  DBusConnection * connection,
910  const char * service_name)
911{
912  struct dbus_service_descriptor * service_ptr;
913
914  if (connection != g_dbus_connection)
915  {
916    log_error("multiple connections are not implemented yet");
917    ASSERT_NO_PASS;
918    return;
919  }
920
921  service_ptr = find_service_descriptor(service_name);
922  if (service_ptr == NULL)
923  {
924    log_error("find_service_descriptor() failed.");
925    return;
926  }
927
928  if (service_ptr->lifetime_hook_function == NULL)
929  {
930    log_error("cannot unregister non-existent lifetime hook for '%s'", service_name);
931    ASSERT_NO_PASS;
932    return;
933  }
934
935  service_ptr->lifetime_hook_function = NULL;
936
937  dbus_bus_remove_match(connection, compose_name_owner_match(service_name), &g_dbus_error);
938  if (dbus_error_is_set(&g_dbus_error))
939  {
940    log_error("Failed to remove D-Bus match rule: %s", g_dbus_error.message);
941    dbus_error_free(&g_dbus_error);
942  }
943
944  free_service_descriptor_if_empty(service_ptr);
945}
Note: See TracBrowser for help on using the browser.