Add missing header text
[ladish.git] / cdbus / helpers.c
blob02b859a1b58285e2ab2415907915036d0b19ba9f
1 /* -*- Mode: C ; c-basic-offset: 2 -*- */
2 /*
3 * LADI Session Handler (ladish)
5 * Copyright (C) 2008,2009,2010,2011,2012 Nedko Arnaudov <nedko@arnaudov.name>
6 * Copyright (C) 2008 Juuso Alasuutari <juuso.alasuutari@gmail.com>
8 **************************************************************************
9 * This file contains code of the D-Bus helpers
10 **************************************************************************
12 * Licensed under the Academic Free License version 2.1
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.
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.
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.
30 #include <stdbool.h>
31 #include <dbus/dbus.h>
32 #include <string.h>
33 #include <stdlib.h>
35 #include "helpers.h"
36 #include "method.h"
37 #include "../common.h"
38 #include "../common/klist.h"
40 /* D-Bus versions earlier than 1.4.12 dont define DBUS_TIMEOUT_INFINITE */
41 #if !defined(DBUS_TIMEOUT_INFINITE)
42 #define DBUS_TIMEOUT_INFINITE ((int)0x7fffffff)
43 #endif
45 #define DBUS_CALL_DEFAULT_TIMEOUT 3000 // in milliseconds
47 DBusConnection * cdbus_g_dbus_connection;
48 DBusError cdbus_g_dbus_error;
49 static char * g_dbus_call_last_error_name;
50 static char * g_dbus_call_last_error_message;
52 struct cdbus_signal_hook_descriptor
54 struct list_head siblings;
55 char * object;
56 char * interface;
57 void * hook_context;
58 const struct cdbus_signal_hook * signal_hooks;
61 struct cdbus_service_descriptor
63 struct list_head siblings;
64 char * service_name;
65 void (* lifetime_hook_function)(bool appeared);
66 struct list_head hooks;
69 static LIST_HEAD(g_dbus_services);
72 void cdbus_call_last_error_cleanup(void)
74 free(g_dbus_call_last_error_name);
75 g_dbus_call_last_error_name = NULL;
77 free(g_dbus_call_last_error_message);
78 g_dbus_call_last_error_message = NULL;
81 bool cdbus_call_last_error_is_name(const char * name)
83 return g_dbus_call_last_error_name != NULL && strcmp(name, g_dbus_call_last_error_name) == 0;
86 const char * cdbus_call_last_error_get_message(void)
88 return g_dbus_call_last_error_message != NULL ? g_dbus_call_last_error_message : "";
91 static void cdbus_call_last_error_set(void)
93 cdbus_call_last_error_cleanup();
95 if (cdbus_g_dbus_error.name != NULL)
97 g_dbus_call_last_error_name = strdup(cdbus_g_dbus_error.name);
100 if (cdbus_g_dbus_error.message != NULL)
102 g_dbus_call_last_error_message = strdup(cdbus_g_dbus_error.message);
106 bool cdbus_iter_get_dict_entry(DBusMessageIter * iter_ptr, const char * key, void * value, int * type, int * size)
108 DBusMessageIter dict_iter;
109 DBusMessageIter entry_iter;
110 DBusMessageIter variant_iter;
111 const char * current_key;
112 DBusMessageIter array_iter;
113 int n;
114 int detype;
116 dbus_message_iter_recurse(iter_ptr, &dict_iter);
118 loop:
119 detype = dbus_message_iter_get_arg_type(&dict_iter);
121 if (detype == DBUS_TYPE_INVALID)
123 return false;
126 if (detype != DBUS_TYPE_DICT_ENTRY)
128 log_error("Iterator does not point to a dict entry container");
129 return false;
132 dbus_message_iter_recurse(&dict_iter, &entry_iter);
134 if (dbus_message_iter_get_arg_type(&entry_iter) != DBUS_TYPE_STRING)
136 log_error("Cannot find key in dict entry container");
137 return false;
140 dbus_message_iter_get_basic(&entry_iter, &current_key);
141 if (strcmp(current_key, key) != 0)
143 dbus_message_iter_next(&dict_iter);
144 goto loop;
147 if (!dbus_message_iter_next(&entry_iter) || dbus_message_iter_get_arg_type(&entry_iter) != DBUS_TYPE_VARIANT)
149 log_error("Cannot find variant container in dict entry");
150 return false;
153 dbus_message_iter_recurse(&entry_iter, &variant_iter);
155 *type = dbus_message_iter_get_arg_type(&variant_iter);
156 if (*type == DBUS_TYPE_INVALID)
158 log_error("Cannot find value in variant container");
159 return false;
162 if (*type == DBUS_TYPE_ARRAY)
164 if (dbus_message_iter_get_element_type(&variant_iter) != DBUS_TYPE_BYTE)
166 log_error("Dict entry value is a non-byte array");
167 return false;
169 *type = '-';
171 dbus_message_iter_recurse(&variant_iter, &array_iter);
172 dbus_message_iter_get_fixed_array(&array_iter, value, &n);
174 if (size != NULL)
176 *size = n;
179 else
181 dbus_message_iter_get_basic(&variant_iter, value);
184 return true;
187 bool cdbus_iter_get_dict_entry_string(DBusMessageIter * iter_ptr, const char * key, const char ** value)
189 int type;
191 if (!cdbus_iter_get_dict_entry(iter_ptr, key, value, &type, NULL))
193 return false;
196 if (type != DBUS_TYPE_STRING)
198 log_error("value of the dict entry '%s' is not a string", key);
199 return false;
202 return true;
206 * Append a variant type to a D-Bus message.
207 * Return false if something fails, true otherwise.
209 bool cdbus_iter_append_variant(DBusMessageIter * iter, int type, const void * arg)
211 DBusMessageIter sub_iter;
212 char s[2];
214 s[0] = (char)type;
215 s[1] = '\0';
217 /* Open a variant container. */
218 if (!dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT, (const char *)s, &sub_iter))
219 return false;
221 /* Append the supplied value. */
222 if (!dbus_message_iter_append_basic(&sub_iter, type, arg))
224 dbus_message_iter_close_container(iter, &sub_iter);
225 return false;
228 /* Close the container. */
229 if (!dbus_message_iter_close_container(iter, &sub_iter))
230 return false;
232 return true;
235 static __inline__ bool cdbus_iter_append_variant_raw(DBusMessageIter * iter, const void * buf, int len)
237 DBusMessageIter variant_iter, array_iter;
239 /* Open a variant container. */
240 if (!dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT,
241 "ay", &variant_iter))
242 return false;
244 /* Open an array container. */
245 if (!dbus_message_iter_open_container(&variant_iter, DBUS_TYPE_ARRAY,
246 "y", &array_iter))
247 goto fail;
249 /* Append the supplied data. */
250 if (!dbus_message_iter_append_fixed_array(&array_iter, DBUS_TYPE_BYTE, buf, len)) {
251 dbus_message_iter_close_container(&variant_iter, &array_iter);
252 goto fail;
255 /* Close the containers. */
256 if (!dbus_message_iter_close_container(&variant_iter, &array_iter))
257 goto fail;
258 else if (!dbus_message_iter_close_container(iter, &variant_iter))
259 return false;
261 return true;
263 fail:
264 dbus_message_iter_close_container(iter, &variant_iter);
265 return false;
268 bool cdbus_iter_append_dict_entry(DBusMessageIter * iter, int type, const char * key, const void * value, int length)
270 DBusMessageIter dict_iter;
272 if (!dbus_message_iter_open_container(iter, DBUS_TYPE_DICT_ENTRY, NULL, &dict_iter))
273 return false;
275 if (!dbus_message_iter_append_basic(&dict_iter, DBUS_TYPE_STRING, &key))
276 goto fail;
278 if (type == '-')
280 if (!cdbus_iter_append_variant_raw(&dict_iter, value, length))
281 goto fail;
283 else if (!cdbus_iter_append_variant(&dict_iter, type, value))
285 goto fail;
288 if (!dbus_message_iter_close_container(iter, &dict_iter))
289 return false;
291 return true;
293 fail:
294 dbus_message_iter_close_container(iter, &dict_iter);
295 return false;
298 bool cdbus_maybe_add_dict_entry_string(DBusMessageIter *dict_iter_ptr, const char * key, const char * value)
300 DBusMessageIter dict_entry_iter;
302 if (value == NULL)
304 return true;
307 if (!dbus_message_iter_open_container(dict_iter_ptr, DBUS_TYPE_DICT_ENTRY, NULL, &dict_entry_iter))
309 return false;
312 if (!dbus_message_iter_append_basic(&dict_entry_iter, DBUS_TYPE_STRING, (const void *) &key))
314 dbus_message_iter_close_container(dict_iter_ptr, &dict_entry_iter);
315 return false;
318 cdbus_iter_append_variant(&dict_entry_iter, DBUS_TYPE_STRING, &value);
320 if (!dbus_message_iter_close_container(dict_iter_ptr, &dict_entry_iter))
322 return false;
325 return true;
328 bool cdbus_add_dict_entry_uint32(DBusMessageIter * dict_iter_ptr, const char * key, dbus_uint32_t value)
330 DBusMessageIter dict_entry_iter;
332 if (!dbus_message_iter_open_container(dict_iter_ptr, DBUS_TYPE_DICT_ENTRY, NULL, &dict_entry_iter))
334 return false;
337 if (!dbus_message_iter_append_basic(&dict_entry_iter, DBUS_TYPE_STRING, (const void *) &key))
339 dbus_message_iter_close_container(dict_iter_ptr, &dict_entry_iter);
340 return false;
343 cdbus_iter_append_variant(&dict_entry_iter, DBUS_TYPE_UINT32, &value);
345 if (!dbus_message_iter_close_container(dict_iter_ptr, &dict_entry_iter))
347 return false;
350 return true;
353 bool cdbus_add_dict_entry_bool(DBusMessageIter * dict_iter_ptr, const char * key, dbus_bool_t value)
355 DBusMessageIter dict_entry_iter;
357 if (!dbus_message_iter_open_container(dict_iter_ptr, DBUS_TYPE_DICT_ENTRY, NULL, &dict_entry_iter))
359 return false;
362 if (!dbus_message_iter_append_basic(&dict_entry_iter, DBUS_TYPE_STRING, (const void *) &key))
364 dbus_message_iter_close_container(dict_iter_ptr, &dict_entry_iter);
365 return false;
368 cdbus_iter_append_variant(&dict_entry_iter, DBUS_TYPE_BOOLEAN, &value);
370 if (!dbus_message_iter_close_container(dict_iter_ptr, &dict_entry_iter))
372 return false;
375 return true;
378 DBusMessage *
379 cdbus_call_raw(
380 unsigned int timeout,
381 DBusMessage * request_ptr)
383 DBusMessage * reply_ptr;
385 if (timeout == 0)
387 timeout = DBUS_CALL_DEFAULT_TIMEOUT;
390 reply_ptr = dbus_connection_send_with_reply_and_block(
391 cdbus_g_dbus_connection,
392 request_ptr,
393 timeout,
394 &cdbus_g_dbus_error);
395 if (reply_ptr == NULL)
397 cdbus_call_last_error_set();
398 dbus_error_free(&cdbus_g_dbus_error);
401 return reply_ptr;
404 static
405 DBusMessage *
406 cdbus_new_method_call_message_valist(
407 const char * service,
408 const char * object,
409 const char * iface,
410 const char * method,
411 const char * input_signature,
412 va_list * vargs_ptr)
414 DBusMessage * msg_ptr;
415 DBusSignatureIter sig_iter;
416 int type;
417 void * parameter_ptr;
418 DBusMessageIter iter;
420 if (!dbus_signature_validate(input_signature, NULL))
422 log_error("input signature '%s' is invalid", input_signature);
423 goto fail;
426 dbus_signature_iter_init(&sig_iter, input_signature);
428 msg_ptr = dbus_message_new_method_call(service, object, iface, method);
429 if (msg_ptr == NULL)
431 log_error("dbus_message_new_method_call() failed.");
432 goto fail;
435 dbus_message_iter_init_append(msg_ptr, &iter);
437 while (*input_signature != '\0')
439 type = dbus_signature_iter_get_current_type(&sig_iter);
440 if (!dbus_type_is_basic(type))
442 log_error("non-basic input parameter '%c' (%d)", *input_signature, type);
443 goto unref;
446 parameter_ptr = va_arg(*vargs_ptr, void *);
448 if (!dbus_message_iter_append_basic(&iter, type, parameter_ptr))
450 log_error("dbus_message_iter_append_basic() failed.");
451 goto unref;
454 dbus_signature_iter_next(&sig_iter);
455 input_signature++;
458 return msg_ptr;
459 unref:
460 dbus_message_unref(msg_ptr);
461 fail:
462 return NULL;
465 DBusMessage *
466 cdbus_new_method_call_message(
467 const char * service,
468 const char * object,
469 const char * iface,
470 const char * method,
471 const char * input_signature,
472 ...)
474 va_list vargs;
475 DBusMessage * msg_ptr;
477 va_start(vargs, input_signature);
478 msg_ptr = cdbus_new_method_call_message_valist(service, object, iface, method, input_signature, &vargs);
479 va_end(vargs);
481 return msg_ptr;
484 bool
485 cdbus_call(
486 unsigned int timeout,
487 const char * service,
488 const char * object,
489 const char * iface,
490 const char * method,
491 const char * input_signature,
492 ...)
494 DBusMessageIter iter;
495 DBusMessage * request_ptr;
496 DBusMessage * reply_ptr;
497 const char * output_signature;
498 const char * reply_signature;
499 va_list ap;
500 bool ret;
501 void * parameter_ptr;
503 //log_info("dbus_call('%s', '%s', '%s', '%s')", service, object, iface, method);
505 ret = false;
506 va_start(ap, input_signature);
508 if (input_signature != NULL)
510 request_ptr = cdbus_new_method_call_message_valist(service, object, iface, method, input_signature, &ap);
511 if (request_ptr == NULL)
513 goto fail;
516 else
518 request_ptr = va_arg(ap, DBusMessage *);
521 output_signature = va_arg(ap, const char *);
523 reply_ptr = cdbus_call_raw(timeout, request_ptr);
525 if (input_signature != NULL)
527 dbus_message_unref(request_ptr);
530 if (reply_ptr == NULL)
532 goto fail;
535 if (output_signature != NULL)
537 reply_signature = dbus_message_get_signature(reply_ptr);
539 if (strcmp(reply_signature, output_signature) != 0)
541 log_error("reply signature is '%s' but expected signature is '%s'", reply_signature, output_signature);
544 dbus_message_iter_init(reply_ptr, &iter);
546 while (*output_signature++ != '\0')
548 ASSERT(dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_INVALID); /* we've checked the signature, this should not happen */
549 parameter_ptr = va_arg(ap, void *);
550 dbus_message_iter_get_basic(&iter, parameter_ptr);
551 dbus_message_iter_next(&iter);
554 ASSERT(dbus_message_iter_get_arg_type(&iter) == DBUS_TYPE_INVALID); /* we've checked the signature, this should not happen */
555 dbus_message_unref(reply_ptr);
557 else
559 parameter_ptr = va_arg(ap, DBusMessage **);
560 *(DBusMessage **)parameter_ptr = reply_ptr;
563 ret = true;
565 fail:
566 va_end(ap);
567 return ret;
570 struct cdbus_async_call_context
572 void * context;
573 void (* callback)(void * context, void * cookie, DBusMessage * reply_ptr);
574 dbus_uint64_t cookie[0];
577 #define ctx_ptr ((struct cdbus_async_call_context *)user_data)
579 static void cdbus_async_call_reply_handler(DBusPendingCall * pending_call_ptr, void * user_data)
581 DBusMessage * reply_ptr;
583 reply_ptr = dbus_pending_call_steal_reply(pending_call_ptr);
584 if (reply_ptr == NULL)
586 log_error("pending call notify called but reply is NULL");
588 else
590 ctx_ptr->callback(ctx_ptr->context, ctx_ptr->cookie, reply_ptr);
591 ctx_ptr->callback = NULL; /* mark that callback is already called */
593 dbus_message_unref(reply_ptr);
597 static void cdbus_async_call_reply_context_free(void * user_data)
599 if (ctx_ptr->callback != NULL)
601 ctx_ptr->callback(ctx_ptr->context, ctx_ptr->cookie, NULL);
604 free(ctx_ptr);
607 #undef ctx_ptr
609 bool
610 cdbus_call_async(
611 DBusMessage * request_ptr,
612 void * context,
613 void * cookie,
614 size_t cookie_size,
615 void (* callback)(void * context, void * cookie, DBusMessage * reply_ptr))
617 bool ret;
618 DBusPendingCall * pending_call_ptr;
619 struct cdbus_async_call_context * ctx_ptr;
621 ret = false;
623 if (!dbus_connection_send_with_reply(cdbus_g_dbus_connection, request_ptr, &pending_call_ptr, DBUS_TIMEOUT_INFINITE))
625 log_error("dbus_connection_send_with_reply() failed.");
626 goto exit;
629 if (pending_call_ptr == NULL)
631 log_error("dbus_connection_send_with_reply() returned NULL pending call object pointer.");
632 goto exit;
635 ctx_ptr = malloc(sizeof(struct cdbus_async_call_context) + cookie_size);
636 if (ctx_ptr == NULL)
638 log_error("malloc() failed to allocate cdbus_async_call_context struct with cookie size of %zu", cookie_size);
639 goto unref;
642 ctx_ptr->context = context;
643 ctx_ptr->callback = callback;
644 memcpy(ctx_ptr->cookie, cookie, cookie_size);
646 ret = dbus_pending_call_set_notify(pending_call_ptr, cdbus_async_call_reply_handler, ctx_ptr, cdbus_async_call_reply_context_free);
647 if (!ret)
649 log_error("dbus_pending_call_set_notify() failed.");
650 goto free;
653 goto unref;
655 free:
656 free(ctx_ptr);
657 unref:
658 dbus_pending_call_unref(pending_call_ptr);
659 exit:
660 return ret;
663 static
664 const char *
665 cdbus_compose_signal_match(
666 const char * service,
667 const char * object,
668 const char * iface,
669 const char * signal)
671 static char rule[1024];
672 snprintf(rule, sizeof(rule), "type='signal',sender='%s',path='%s',interface='%s',member='%s'", service, object, iface, signal);
673 return rule;
676 static const char * cdbus_compose_name_owner_match(const char * service)
678 static char rule[1024];
679 snprintf(rule, sizeof(rule), "type='signal',interface='"DBUS_INTERFACE_DBUS"',member=NameOwnerChanged,arg0='%s'", service);
680 return rule;
683 bool
684 cdbus_register_object_signal_handler(
685 DBusConnection * connection,
686 const char * service,
687 const char * object,
688 const char * iface,
689 const char * const * signals,
690 DBusHandleMessageFunction handler,
691 void * handler_data)
693 const char * const * signal;
695 for (signal = signals; *signal != NULL; signal++)
697 dbus_bus_add_match(connection, cdbus_compose_signal_match(service, object, iface, *signal), &cdbus_g_dbus_error);
698 if (dbus_error_is_set(&cdbus_g_dbus_error))
700 log_error("Failed to add D-Bus match rule: %s", cdbus_g_dbus_error.message);
701 dbus_error_free(&cdbus_g_dbus_error);
702 return false;
706 dbus_connection_add_filter(cdbus_g_dbus_connection, handler, handler_data, NULL);
708 return true;
711 bool
712 cdbus_unregister_object_signal_handler(
713 DBusConnection * connection,
714 const char * service,
715 const char * object,
716 const char * iface,
717 const char * const * signals,
718 DBusHandleMessageFunction handler,
719 void * handler_data)
721 const char * const * signal;
723 for (signal = signals; *signal != NULL; signal++)
725 dbus_bus_remove_match(connection, cdbus_compose_signal_match(service, object, iface, *signal), &cdbus_g_dbus_error);
726 if (dbus_error_is_set(&cdbus_g_dbus_error))
728 log_error("Failed to remove D-Bus match rule: %s", cdbus_g_dbus_error.message);
729 dbus_error_free(&cdbus_g_dbus_error);
730 return false;
734 dbus_connection_remove_filter(cdbus_g_dbus_connection, handler, handler_data);
736 return true;
739 static
740 struct cdbus_signal_hook_descriptor *
741 find_signal_hook_descriptor(
742 struct cdbus_service_descriptor * service_ptr,
743 const char * object,
744 const char * interface)
746 struct list_head * node_ptr;
747 struct cdbus_signal_hook_descriptor * hook_ptr;
749 list_for_each(node_ptr, &service_ptr->hooks)
751 hook_ptr = list_entry(node_ptr, struct cdbus_signal_hook_descriptor, siblings);
752 if (strcmp(hook_ptr->object, object) == 0 &&
753 strcmp(hook_ptr->interface, interface) == 0)
755 return hook_ptr;
759 return NULL;
762 #define service_ptr ((struct cdbus_service_descriptor *)data)
764 static
765 DBusHandlerResult
766 cdbus_signal_handler(
767 DBusConnection * UNUSED(connection_ptr),
768 DBusMessage * message_ptr,
769 void * data)
771 const char * object_path;
772 const char * interface;
773 const char * signal_name;
774 const char * object_name;
775 const char * old_owner;
776 const char * new_owner;
777 struct cdbus_signal_hook_descriptor * hook_ptr;
778 const struct cdbus_signal_hook * signal_ptr;
780 /* Non-signal messages are ignored */
781 if (dbus_message_get_type(message_ptr) != DBUS_MESSAGE_TYPE_SIGNAL)
783 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
786 interface = dbus_message_get_interface(message_ptr);
787 if (interface == NULL)
789 /* Signals with no interface are ignored */
790 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
793 object_path = dbus_message_get_path(message_ptr);
795 signal_name = dbus_message_get_member(message_ptr);
796 if (signal_name == NULL)
798 log_error("Received signal with NULL member");
799 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
802 log_debug("'%s' sent signal '%s'::'%s'", object_path, interface, signal_name);
804 /* Handle session bus signals to track service alive state */
805 if (strcmp(interface, DBUS_INTERFACE_DBUS) == 0)
807 if (strcmp(signal_name, "NameOwnerChanged") != 0)
809 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
812 if (service_ptr->lifetime_hook_function == NULL)
814 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
817 //log_info("NameOwnerChanged signal received");
819 dbus_error_init(&cdbus_g_dbus_error);
820 if (!dbus_message_get_args(
821 message_ptr,
822 &cdbus_g_dbus_error,
823 DBUS_TYPE_STRING, &object_name,
824 DBUS_TYPE_STRING, &old_owner,
825 DBUS_TYPE_STRING, &new_owner,
826 DBUS_TYPE_INVALID))
828 log_error("Cannot get message arguments: %s", cdbus_g_dbus_error.message);
829 dbus_error_free(&cdbus_g_dbus_error);
830 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
833 if (strcmp(object_name, service_ptr->service_name) != 0)
835 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
838 if (old_owner[0] == '\0')
840 service_ptr->lifetime_hook_function(true);
842 else if (new_owner[0] == '\0')
844 service_ptr->lifetime_hook_function(false);
847 return DBUS_HANDLER_RESULT_HANDLED;
850 /* Handle object interface signals */
851 if (object_path != NULL)
853 hook_ptr = find_signal_hook_descriptor(service_ptr, object_path, interface);
854 if (hook_ptr != NULL)
856 for (signal_ptr = hook_ptr->signal_hooks; signal_ptr->signal_name != NULL; signal_ptr++)
858 if (strcmp(signal_name, signal_ptr->signal_name) == 0)
860 signal_ptr->hook_function(hook_ptr->hook_context, message_ptr);
861 return DBUS_HANDLER_RESULT_HANDLED;
867 /* Let everything else pass through */
868 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
871 #undef service_ptr
873 static struct cdbus_service_descriptor * find_service_descriptor(const char * service_name)
875 struct list_head * node_ptr;
876 struct cdbus_service_descriptor * descr_ptr;
878 list_for_each(node_ptr, &g_dbus_services)
880 descr_ptr = list_entry(node_ptr, struct cdbus_service_descriptor, siblings);
881 if (strcmp(descr_ptr->service_name, service_name) == 0)
883 return descr_ptr;
887 return NULL;
890 static struct cdbus_service_descriptor * find_or_create_service_descriptor(const char * service_name)
892 struct cdbus_service_descriptor * descr_ptr;
894 descr_ptr = find_service_descriptor(service_name);
895 if (descr_ptr != NULL)
897 return descr_ptr;
900 descr_ptr = malloc(sizeof(struct cdbus_service_descriptor));
901 if (descr_ptr == NULL)
903 log_error("malloc() failed to allocate struct cdbus_service_descriptor");
904 return NULL;
907 descr_ptr->service_name = strdup(service_name);
908 if (descr_ptr->service_name == NULL)
910 log_error("strdup() failed for service name '%s'", service_name);
911 free(descr_ptr);
912 return NULL;
915 descr_ptr->lifetime_hook_function = NULL;
916 INIT_LIST_HEAD(&descr_ptr->hooks);
918 list_add_tail(&descr_ptr->siblings, &g_dbus_services);
920 dbus_connection_add_filter(cdbus_g_dbus_connection, cdbus_signal_handler, descr_ptr, NULL);
922 return descr_ptr;
925 static void free_service_descriptor_if_empty(struct cdbus_service_descriptor * service_ptr)
927 if (service_ptr->lifetime_hook_function != NULL)
929 return;
932 if (!list_empty(&service_ptr->hooks))
934 return;
937 dbus_connection_remove_filter(cdbus_g_dbus_connection, cdbus_signal_handler, service_ptr);
939 list_del(&service_ptr->siblings);
940 free(service_ptr->service_name);
941 free(service_ptr);
944 bool
945 cdbus_register_object_signal_hooks(
946 DBusConnection * connection,
947 const char * service_name,
948 const char * object,
949 const char * iface,
950 void * hook_context,
951 const struct cdbus_signal_hook * signal_hooks)
953 struct cdbus_service_descriptor * service_ptr;
954 struct cdbus_signal_hook_descriptor * hook_ptr;
955 const struct cdbus_signal_hook * signal_ptr;
957 if (connection != cdbus_g_dbus_connection)
959 log_error("multiple connections are not implemented yet");
960 ASSERT_NO_PASS;
961 goto fail;
964 service_ptr = find_or_create_service_descriptor(service_name);
965 if (service_ptr == NULL)
967 log_error("find_or_create_service_descriptor() failed.");
968 goto fail;
971 hook_ptr = find_signal_hook_descriptor(service_ptr, object, iface);
972 if (hook_ptr != NULL)
974 log_error("refusing to register two signal monitors for '%s':'%s':'%s'", service_name, object, iface);
975 ASSERT_NO_PASS;
976 goto maybe_free_service;
979 hook_ptr = malloc(sizeof(struct cdbus_signal_hook_descriptor));
980 if (hook_ptr == NULL)
982 log_error("malloc() failed to allocate struct cdbus_signal_hook_descriptor");
983 goto maybe_free_service;
986 hook_ptr->object = strdup(object);
987 if (hook_ptr->object == NULL)
989 log_error("strdup() failed for object name");
990 goto free_hook;
993 hook_ptr->interface = strdup(iface);
994 if (hook_ptr->interface == NULL)
996 log_error("strdup() failed for interface name");
997 goto free_object_name;
1000 hook_ptr->hook_context = hook_context;
1001 hook_ptr->signal_hooks = signal_hooks;
1003 list_add_tail(&hook_ptr->siblings, &service_ptr->hooks);
1005 for (signal_ptr = signal_hooks; signal_ptr->signal_name != NULL; signal_ptr++)
1007 dbus_bus_add_match(connection, cdbus_compose_signal_match(service_name, object, iface, signal_ptr->signal_name), &cdbus_g_dbus_error);
1008 if (dbus_error_is_set(&cdbus_g_dbus_error))
1010 log_error("Failed to add D-Bus match rule: %s", cdbus_g_dbus_error.message);
1011 dbus_error_free(&cdbus_g_dbus_error);
1013 while (signal_ptr != signal_hooks)
1015 ASSERT(signal_ptr > signal_hooks);
1016 signal_ptr--;
1018 dbus_bus_remove_match(connection, cdbus_compose_signal_match(service_name, object, iface, signal_ptr->signal_name), &cdbus_g_dbus_error);
1019 if (dbus_error_is_set(&cdbus_g_dbus_error))
1021 log_error("Failed to remove D-Bus match rule: %s", cdbus_g_dbus_error.message);
1022 dbus_error_free(&cdbus_g_dbus_error);
1026 goto remove_hook;
1030 return true;
1032 remove_hook:
1033 list_del(&hook_ptr->siblings);
1034 free(hook_ptr->interface);
1035 free_object_name:
1036 free(hook_ptr->object);
1037 free_hook:
1038 free(hook_ptr);
1039 maybe_free_service:
1040 free_service_descriptor_if_empty(service_ptr);
1041 fail:
1042 return false;
1045 void
1046 cdbus_unregister_object_signal_hooks(
1047 DBusConnection * connection,
1048 const char * service_name,
1049 const char * object,
1050 const char * iface)
1052 struct cdbus_service_descriptor * service_ptr;
1053 struct cdbus_signal_hook_descriptor * hook_ptr;
1054 const struct cdbus_signal_hook * signal_ptr;
1056 if (connection != cdbus_g_dbus_connection)
1058 log_error("multiple connections are not implemented yet");
1059 ASSERT_NO_PASS;
1060 return;
1063 service_ptr = find_service_descriptor(service_name);
1064 if (service_ptr == NULL)
1066 log_error("find_service_descriptor() failed.");
1067 ASSERT_NO_PASS;
1068 return;
1071 hook_ptr = find_signal_hook_descriptor(service_ptr, object, iface);
1072 if (hook_ptr == NULL)
1074 log_error("cannot unregister non-existing signal monitor for '%s':'%s':'%s'", service_name, object, iface);
1075 ASSERT_NO_PASS;
1076 return;
1079 for (signal_ptr = hook_ptr->signal_hooks; signal_ptr->signal_name != NULL; signal_ptr++)
1081 dbus_bus_remove_match(connection, cdbus_compose_signal_match(service_name, object, iface, signal_ptr->signal_name), &cdbus_g_dbus_error);
1082 if (dbus_error_is_set(&cdbus_g_dbus_error))
1084 if (dbus_error_is_set(&cdbus_g_dbus_error))
1086 log_error("Failed to remove D-Bus match rule: %s", cdbus_g_dbus_error.message);
1087 dbus_error_free(&cdbus_g_dbus_error);
1092 list_del(&hook_ptr->siblings);
1094 free(hook_ptr->interface);
1095 free(hook_ptr->object);
1096 free(hook_ptr);
1098 free_service_descriptor_if_empty(service_ptr);
1101 bool
1102 cdbus_register_service_lifetime_hook(
1103 DBusConnection * connection,
1104 const char * service_name,
1105 void (* hook_function)(bool appeared))
1107 struct cdbus_service_descriptor * service_ptr;
1109 if (connection != cdbus_g_dbus_connection)
1111 log_error("multiple connections are not implemented yet");
1112 ASSERT_NO_PASS;
1113 goto fail;
1116 service_ptr = find_or_create_service_descriptor(service_name);
1117 if (service_ptr == NULL)
1119 log_error("find_or_create_service_descriptor() failed.");
1120 goto fail;
1123 if (service_ptr->lifetime_hook_function != NULL)
1125 log_error("cannot register two lifetime hooks for '%s'", service_name);
1126 ASSERT_NO_PASS;
1127 goto maybe_free_service;
1130 service_ptr->lifetime_hook_function = hook_function;
1132 dbus_bus_add_match(connection, cdbus_compose_name_owner_match(service_name), &cdbus_g_dbus_error);
1133 if (dbus_error_is_set(&cdbus_g_dbus_error))
1135 log_error("Failed to add D-Bus match rule: %s", cdbus_g_dbus_error.message);
1136 dbus_error_free(&cdbus_g_dbus_error);
1137 goto clear_hook;
1140 return true;
1142 clear_hook:
1143 service_ptr->lifetime_hook_function = NULL;
1144 maybe_free_service:
1145 free_service_descriptor_if_empty(service_ptr);
1146 fail:
1147 return false;
1150 void
1151 cdbus_unregister_service_lifetime_hook(
1152 DBusConnection * connection,
1153 const char * service_name)
1155 struct cdbus_service_descriptor * service_ptr;
1157 if (connection != cdbus_g_dbus_connection)
1159 log_error("multiple connections are not implemented yet");
1160 ASSERT_NO_PASS;
1161 return;
1164 service_ptr = find_service_descriptor(service_name);
1165 if (service_ptr == NULL)
1167 log_error("find_service_descriptor() failed.");
1168 return;
1171 if (service_ptr->lifetime_hook_function == NULL)
1173 log_error("cannot unregister non-existent lifetime hook for '%s'", service_name);
1174 ASSERT_NO_PASS;
1175 return;
1178 service_ptr->lifetime_hook_function = NULL;
1180 dbus_bus_remove_match(connection, cdbus_compose_name_owner_match(service_name), &cdbus_g_dbus_error);
1181 if (dbus_error_is_set(&cdbus_g_dbus_error))
1183 log_error("Failed to remove D-Bus match rule: %s", cdbus_g_dbus_error.message);
1184 dbus_error_free(&cdbus_g_dbus_error);
1187 free_service_descriptor_if_empty(service_ptr);