1 /* -*- mode: C; c-file-style: "gnu" -*- */
2 /* signals.c Bus signal connection implementation
4 * Copyright (C) 2003 Red Hat, Inc.
6 * Licensed under the Academic Free License version 1.2
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program 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
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
29 int refcount; /**< reference count */
31 DBusConnection *matches_go_to; /**< Owner of the rule */
33 unsigned int flags; /**< BusMatchFlags */
44 bus_match_rule_new (DBusConnection *matches_go_to)
48 rule = dbus_new0 (BusMatchRule, 1);
53 rule->matches_go_to = matches_go_to;
59 bus_match_rule_ref (BusMatchRule *rule)
61 _dbus_assert (rule->refcount > 0);
67 bus_match_rule_unref (BusMatchRule *rule)
69 _dbus_assert (rule->refcount > 0);
72 if (rule->refcount == 0)
74 dbus_free (rule->interface);
75 dbus_free (rule->member);
76 dbus_free (rule->sender);
77 dbus_free (rule->destination);
78 dbus_free (rule->path);
83 #ifdef DBUS_ENABLE_VERBOSE_MODE
85 match_rule_to_string (BusMatchRule *rule)
90 if (!_dbus_string_init (&str))
93 while ((s = _dbus_strdup ("nomem")) == NULL)
94 ; /* only OK for debug spew... */
98 if (rule->flags & BUS_MATCH_MESSAGE_TYPE)
100 /* FIXME make type readable */
101 if (!_dbus_string_append_printf (&str, "type='%d'", rule->message_type))
105 if (rule->flags & BUS_MATCH_INTERFACE)
107 if (_dbus_string_get_length (&str) > 0)
109 if (!_dbus_string_append (&str, ","))
113 if (!_dbus_string_append_printf (&str, "interface='%s'", rule->interface))
117 if (rule->flags & BUS_MATCH_MEMBER)
119 if (_dbus_string_get_length (&str) > 0)
121 if (!_dbus_string_append (&str, ","))
125 if (!_dbus_string_append_printf (&str, "member='%s'", rule->member))
129 if (rule->flags & BUS_MATCH_PATH)
131 if (_dbus_string_get_length (&str) > 0)
133 if (!_dbus_string_append (&str, ","))
137 if (!_dbus_string_append_printf (&str, "path='%s'", rule->path))
141 if (rule->flags & BUS_MATCH_SENDER)
143 if (_dbus_string_get_length (&str) > 0)
145 if (!_dbus_string_append (&str, ","))
149 if (!_dbus_string_append_printf (&str, "sender='%s'", rule->sender))
153 if (rule->flags & BUS_MATCH_DESTINATION)
155 if (_dbus_string_get_length (&str) > 0)
157 if (!_dbus_string_append (&str, ","))
161 if (!_dbus_string_append_printf (&str, "destination='%s'", rule->destination))
165 if (!_dbus_string_steal_data (&str, &ret))
168 _dbus_string_free (&str);
172 _dbus_string_free (&str);
175 while ((s = _dbus_strdup ("nomem")) == NULL)
176 ; /* only OK for debug spew... */
180 #endif /* DBUS_ENABLE_VERBOSE_MODE */
183 bus_match_rule_set_message_type (BusMatchRule *rule,
186 rule->flags |= BUS_MATCH_MESSAGE_TYPE;
188 rule->message_type = type;
194 bus_match_rule_set_interface (BusMatchRule *rule,
195 const char *interface)
199 _dbus_assert (interface != NULL);
201 new = _dbus_strdup (interface);
205 rule->flags |= BUS_MATCH_INTERFACE;
206 dbus_free (rule->interface);
207 rule->interface = new;
213 bus_match_rule_set_member (BusMatchRule *rule,
218 _dbus_assert (member != NULL);
220 new = _dbus_strdup (member);
224 rule->flags |= BUS_MATCH_MEMBER;
225 dbus_free (rule->member);
232 bus_match_rule_set_sender (BusMatchRule *rule,
237 _dbus_assert (sender != NULL);
239 new = _dbus_strdup (sender);
243 rule->flags |= BUS_MATCH_SENDER;
244 dbus_free (rule->sender);
251 bus_match_rule_set_destination (BusMatchRule *rule,
252 const char *destination)
256 _dbus_assert (destination != NULL);
258 new = _dbus_strdup (destination);
262 rule->flags |= BUS_MATCH_DESTINATION;
263 dbus_free (rule->destination);
264 rule->destination = new;
270 bus_match_rule_set_path (BusMatchRule *rule,
275 _dbus_assert (path != NULL);
277 new = _dbus_strdup (path);
281 rule->flags |= BUS_MATCH_PATH;
282 dbus_free (rule->path);
289 * The format is comma-separated with strings quoted with single quotes
290 * as for the shell (to escape a literal single quote, use '\'').
292 * type='signal',sender='org.freedesktop.DBus',interface='org.freedesktop.DBus',member='Foo',
293 * path='/bar/foo',destination=':452345-34'
297 bus_match_rule_parse (DBusConnection *matches_go_to,
298 const DBusString *rule_text,
303 rule = bus_match_rule_new (matches_go_to);
307 /* FIXME implement for real */
309 if (!bus_match_rule_set_message_type (rule,
310 DBUS_MESSAGE_TYPE_SIGNAL))
317 bus_match_rule_unref (rule);
330 bus_matchmaker_new (void)
332 BusMatchmaker *matchmaker;
334 matchmaker = dbus_new0 (BusMatchmaker, 1);
335 if (matchmaker == NULL)
338 matchmaker->refcount = 1;
344 bus_matchmaker_ref (BusMatchmaker *matchmaker)
346 _dbus_assert (matchmaker->refcount > 0);
348 matchmaker->refcount += 1;
352 bus_matchmaker_unref (BusMatchmaker *matchmaker)
354 _dbus_assert (matchmaker->refcount > 0);
356 matchmaker->refcount -= 1;
357 if (matchmaker->refcount == 0)
359 while (matchmaker->all_rules != NULL)
363 rule = matchmaker->all_rules->data;
364 bus_match_rule_unref (rule);
365 _dbus_list_remove_link (&matchmaker->all_rules,
366 matchmaker->all_rules);
369 dbus_free (matchmaker);
373 /* The rule can't be modified after it's added. */
375 bus_matchmaker_add_rule (BusMatchmaker *matchmaker,
378 _dbus_assert (bus_connection_is_active (rule->matches_go_to));
380 if (!_dbus_list_append (&matchmaker->all_rules, rule))
383 if (!bus_connection_add_match_rule (rule->matches_go_to, rule))
385 _dbus_list_remove_last (&matchmaker->all_rules, rule);
389 bus_match_rule_ref (rule);
391 #ifdef DBUS_ENABLE_VERBOSE_MODE
393 char *s = match_rule_to_string (rule);
395 _dbus_verbose ("Added match rule %s to connection %p\n",
396 s, rule->matches_go_to);
405 match_rule_equal (BusMatchRule *a,
408 if (a->flags != b->flags)
411 if ((a->flags & BUS_MATCH_MESSAGE_TYPE) &&
412 a->message_type != b->message_type)
415 if ((a->flags & BUS_MATCH_MEMBER) &&
416 strcmp (a->member, b->member) != 0)
419 if ((a->flags & BUS_MATCH_PATH) &&
420 strcmp (a->path, b->path) != 0)
423 if ((a->flags & BUS_MATCH_INTERFACE) &&
424 strcmp (a->interface, b->interface) != 0)
427 if ((a->flags & BUS_MATCH_SENDER) &&
428 strcmp (a->sender, b->sender) != 0)
431 if ((a->flags & BUS_MATCH_DESTINATION) &&
432 strcmp (a->destination, b->destination) != 0)
439 bus_matchmaker_remove_rule_link (BusMatchmaker *matchmaker,
442 BusMatchRule *rule = link->data;
444 bus_connection_remove_match_rule (rule->matches_go_to, rule);
445 _dbus_list_remove_link (&matchmaker->all_rules, link);
447 #ifdef DBUS_ENABLE_VERBOSE_MODE
449 char *s = match_rule_to_string (rule);
451 _dbus_verbose ("Removed match rule %s for connection %p\n",
452 s, rule->matches_go_to);
457 bus_match_rule_unref (rule);
461 bus_matchmaker_remove_rule (BusMatchmaker *matchmaker,
464 bus_connection_remove_match_rule (rule->matches_go_to, rule);
465 _dbus_list_remove (&matchmaker->all_rules, rule);
467 #ifdef DBUS_ENABLE_VERBOSE_MODE
469 char *s = match_rule_to_string (rule);
471 _dbus_verbose ("Removed match rule %s for connection %p\n",
472 s, rule->matches_go_to);
477 bus_match_rule_unref (rule);
480 /* Remove a single rule which is equal to the given rule by value */
482 bus_matchmaker_remove_rule_by_value (BusMatchmaker *matchmaker,
486 /* FIXME this is an unoptimized linear scan */
490 /* we traverse backward because bus_connection_remove_match_rule()
491 * removes the most-recently-added rule
493 link = _dbus_list_get_last_link (&matchmaker->all_rules);
500 prev = _dbus_list_get_prev_link (&matchmaker->all_rules, link);
502 if (match_rule_equal (rule, value))
504 bus_matchmaker_remove_rule_link (matchmaker, link);
513 dbus_set_error (error, DBUS_ERROR_MATCH_RULE_NOT_FOUND,
514 "The given match rule wasn't found and can't be removed");
522 bus_matchmaker_disconnected (BusMatchmaker *matchmaker,
523 DBusConnection *disconnected)
529 * This scans all match rules on the bus. We could avoid that
530 * for the rules belonging to the connection, since we keep
531 * a list of those; but for the rules that just refer to
532 * the connection we'd need to do something more elaborate.
536 _dbus_assert (bus_connection_is_active (disconnected));
538 link = _dbus_list_get_first_link (&matchmaker->all_rules);
545 next = _dbus_list_get_next_link (&matchmaker->all_rules, link);
547 if (rule->matches_go_to == disconnected)
549 bus_matchmaker_remove_rule_link (matchmaker, link);
551 else if (((rule->flags & BUS_MATCH_SENDER) && *rule->sender == ':') ||
552 ((rule->flags & BUS_MATCH_DESTINATION) && *rule->destination == ':'))
554 /* The rule matches to/from a base service, see if it's the
555 * one being disconnected, since we know this service name
556 * will never be recycled.
560 name = bus_connection_get_name (disconnected);
561 _dbus_assert (name != NULL); /* because we're an active connection */
563 if (((rule->flags & BUS_MATCH_SENDER) &&
564 strcmp (rule->sender, name) == 0) ||
565 ((rule->flags & BUS_MATCH_DESTINATION) &&
566 strcmp (rule->destination, name) == 0))
568 bus_matchmaker_remove_rule_link (matchmaker, link);
577 connection_is_primary_owner (DBusConnection *connection,
578 const char *service_name)
582 BusRegistry *registry;
584 registry = bus_connection_get_registry (connection);
586 _dbus_string_init_const (&str, service_name);
587 service = bus_registry_lookup (registry, &str);
590 return FALSE; /* Service doesn't exist so connection can't own it. */
592 return bus_service_get_primary_owner (service) == connection;
596 match_rule_matches (BusMatchRule *rule,
597 BusConnections *connections,
598 DBusConnection *sender,
599 DBusConnection *addressed_recipient,
600 DBusMessage *message)
602 /* All features of the match rule are AND'd together,
603 * so FALSE if any of them don't match.
606 if (rule->flags & BUS_MATCH_MESSAGE_TYPE)
608 _dbus_assert (rule->message_type != DBUS_MESSAGE_TYPE_INVALID);
610 if (rule->message_type != dbus_message_get_type (message))
614 if (rule->flags & BUS_MATCH_INTERFACE)
618 _dbus_assert (rule->interface != NULL);
620 iface = dbus_message_get_interface (message);
624 if (strcmp (iface, rule->interface) != 0)
628 if (rule->flags & BUS_MATCH_MEMBER)
632 _dbus_assert (rule->member != NULL);
634 member = dbus_message_get_member (message);
638 if (strcmp (member, rule->member) != 0)
642 if (rule->flags & BUS_MATCH_SENDER)
644 _dbus_assert (rule->sender != NULL);
646 if (!connection_is_primary_owner (sender, rule->sender))
650 if (rule->flags & BUS_MATCH_DESTINATION)
652 const char *destination;
654 _dbus_assert (rule->destination != NULL);
656 if (addressed_recipient == NULL)
659 destination = dbus_message_get_destination (message);
660 if (destination == NULL)
663 if (!connection_is_primary_owner (addressed_recipient, rule->destination))
667 if (rule->flags & BUS_MATCH_PATH)
671 _dbus_assert (rule->path != NULL);
673 path = dbus_message_get_path (message);
677 if (strcmp (path, rule->path) != 0)
685 bus_matchmaker_get_recipients (BusMatchmaker *matchmaker,
686 BusConnections *connections,
687 DBusConnection *sender,
688 DBusConnection *addressed_recipient,
689 DBusMessage *message,
690 DBusList **recipients_p)
692 /* FIXME for now this is a wholly unoptimized linear search */
693 /* Guessing the important optimization is to skip the signal-related
694 * match lists when processing method call and exception messages.
695 * So separate match rule lists for signals?
700 _dbus_assert (*recipients_p == NULL);
702 /* This avoids sending same message to the same connection twice.
703 * Purpose of the stamp instead of a bool is to avoid iterating over
704 * all connections resetting the bool each time.
706 bus_connections_increment_stamp (connections);
708 /* addressed_recipient is already receiving the message, don't add to list.
709 * NULL addressed_recipient means either bus driver, or this is a signal
710 * and thus lacks a specific addressed_recipient.
712 if (addressed_recipient != NULL)
713 bus_connection_mark_stamp (addressed_recipient);
715 link = _dbus_list_get_first_link (&matchmaker->all_rules);
722 #ifdef DBUS_ENABLE_VERBOSE_MODE
724 char *s = match_rule_to_string (rule);
726 _dbus_verbose ("Checking whether message matches rule %s for connection %p\n",
727 s, rule->matches_go_to);
732 if (match_rule_matches (rule, connections,
733 sender, addressed_recipient, message))
735 _dbus_verbose ("Rule matched\n");
737 /* Append to the list if we haven't already */
738 if (bus_connection_mark_stamp (rule->matches_go_to))
740 if (!_dbus_list_append (recipients_p, rule->matches_go_to))
743 #ifdef DBUS_ENABLE_VERBOSE_MODE
746 _dbus_verbose ("Connection already receiving this message, so not adding again\n");
748 #endif /* DBUS_ENABLE_VERBOSE_MODE */
751 link = _dbus_list_get_next_link (&matchmaker->all_rules, link);
757 _dbus_list_clear (recipients_p);
761 #ifdef DBUS_BUILD_TESTS
765 bus_signals_test (const DBusString *test_data_dir)
767 BusMatchmaker *matchmaker;
769 matchmaker = bus_matchmaker_new ();
770 bus_matchmaker_ref (matchmaker);
771 bus_matchmaker_unref (matchmaker);
772 bus_matchmaker_unref (matchmaker);
777 #endif /* DBUS_BUILD_TESTS */