signals.c revision 6a65f4802e95ba442c520f8e225da837e0a9f73b
1/* -*- mode: C; c-file-style: "gnu" -*- */ 2/* signals.c Bus signal connection implementation 3 * 4 * Copyright (C) 2003 Red Hat, Inc. 5 * 6 * Licensed under the Academic Free License version 1.2 7 * 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. 12 * 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. 17 * 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 21 * 22 */ 23#include "signals.h" 24#include "services.h" 25#include "utils.h" 26 27struct BusMatchRule 28{ 29 int refcount; /**< reference count */ 30 31 DBusConnection *matches_go_to; /**< Owner of the rule */ 32 33 unsigned int flags; /**< BusMatchFlags */ 34 35 int message_type; 36 char *interface; 37 char *member; 38 char *sender; 39 char *destination; 40 char *path; 41}; 42 43BusMatchRule* 44bus_match_rule_new (DBusConnection *matches_go_to) 45{ 46 BusMatchRule *rule; 47 48 rule = dbus_new0 (BusMatchRule, 1); 49 if (rule == NULL) 50 return NULL; 51 52 rule->refcount = 1; 53 rule->matches_go_to = matches_go_to; 54 55#ifndef DBUS_BUILD_TESTS 56 _dbus_assert (rule->matches_go_to != NULL); 57#endif 58 59 return rule; 60} 61 62void 63bus_match_rule_ref (BusMatchRule *rule) 64{ 65 _dbus_assert (rule->refcount > 0); 66 67 rule->refcount += 1; 68} 69 70void 71bus_match_rule_unref (BusMatchRule *rule) 72{ 73 _dbus_assert (rule->refcount > 0); 74 75 rule->refcount -= 1; 76 if (rule->refcount == 0) 77 { 78 dbus_free (rule->interface); 79 dbus_free (rule->member); 80 dbus_free (rule->sender); 81 dbus_free (rule->destination); 82 dbus_free (rule->path); 83 dbus_free (rule); 84 } 85} 86 87#ifdef DBUS_ENABLE_VERBOSE_MODE 88static char* 89match_rule_to_string (BusMatchRule *rule) 90{ 91 DBusString str; 92 char *ret; 93 94 if (!_dbus_string_init (&str)) 95 { 96 char *s; 97 while ((s = _dbus_strdup ("nomem")) == NULL) 98 ; /* only OK for debug spew... */ 99 return s; 100 } 101 102 if (rule->flags & BUS_MATCH_MESSAGE_TYPE) 103 { 104 /* FIXME make type readable */ 105 if (!_dbus_string_append_printf (&str, "type='%d'", rule->message_type)) 106 goto nomem; 107 } 108 109 if (rule->flags & BUS_MATCH_INTERFACE) 110 { 111 if (_dbus_string_get_length (&str) > 0) 112 { 113 if (!_dbus_string_append (&str, ",")) 114 goto nomem; 115 } 116 117 if (!_dbus_string_append_printf (&str, "interface='%s'", rule->interface)) 118 goto nomem; 119 } 120 121 if (rule->flags & BUS_MATCH_MEMBER) 122 { 123 if (_dbus_string_get_length (&str) > 0) 124 { 125 if (!_dbus_string_append (&str, ",")) 126 goto nomem; 127 } 128 129 if (!_dbus_string_append_printf (&str, "member='%s'", rule->member)) 130 goto nomem; 131 } 132 133 if (rule->flags & BUS_MATCH_PATH) 134 { 135 if (_dbus_string_get_length (&str) > 0) 136 { 137 if (!_dbus_string_append (&str, ",")) 138 goto nomem; 139 } 140 141 if (!_dbus_string_append_printf (&str, "path='%s'", rule->path)) 142 goto nomem; 143 } 144 145 if (rule->flags & BUS_MATCH_SENDER) 146 { 147 if (_dbus_string_get_length (&str) > 0) 148 { 149 if (!_dbus_string_append (&str, ",")) 150 goto nomem; 151 } 152 153 if (!_dbus_string_append_printf (&str, "sender='%s'", rule->sender)) 154 goto nomem; 155 } 156 157 if (rule->flags & BUS_MATCH_DESTINATION) 158 { 159 if (_dbus_string_get_length (&str) > 0) 160 { 161 if (!_dbus_string_append (&str, ",")) 162 goto nomem; 163 } 164 165 if (!_dbus_string_append_printf (&str, "destination='%s'", rule->destination)) 166 goto nomem; 167 } 168 169 if (!_dbus_string_steal_data (&str, &ret)) 170 goto nomem; 171 172 _dbus_string_free (&str); 173 return ret; 174 175 nomem: 176 _dbus_string_free (&str); 177 { 178 char *s; 179 while ((s = _dbus_strdup ("nomem")) == NULL) 180 ; /* only OK for debug spew... */ 181 return s; 182 } 183} 184#endif /* DBUS_ENABLE_VERBOSE_MODE */ 185 186dbus_bool_t 187bus_match_rule_set_message_type (BusMatchRule *rule, 188 int type) 189{ 190 rule->flags |= BUS_MATCH_MESSAGE_TYPE; 191 192 rule->message_type = type; 193 194 return TRUE; 195} 196 197dbus_bool_t 198bus_match_rule_set_interface (BusMatchRule *rule, 199 const char *interface) 200{ 201 char *new; 202 203 _dbus_assert (interface != NULL); 204 205 new = _dbus_strdup (interface); 206 if (new == NULL) 207 return FALSE; 208 209 rule->flags |= BUS_MATCH_INTERFACE; 210 dbus_free (rule->interface); 211 rule->interface = new; 212 213 return TRUE; 214} 215 216dbus_bool_t 217bus_match_rule_set_member (BusMatchRule *rule, 218 const char *member) 219{ 220 char *new; 221 222 _dbus_assert (member != NULL); 223 224 new = _dbus_strdup (member); 225 if (new == NULL) 226 return FALSE; 227 228 rule->flags |= BUS_MATCH_MEMBER; 229 dbus_free (rule->member); 230 rule->member = new; 231 232 return TRUE; 233} 234 235dbus_bool_t 236bus_match_rule_set_sender (BusMatchRule *rule, 237 const char *sender) 238{ 239 char *new; 240 241 _dbus_assert (sender != NULL); 242 243 new = _dbus_strdup (sender); 244 if (new == NULL) 245 return FALSE; 246 247 rule->flags |= BUS_MATCH_SENDER; 248 dbus_free (rule->sender); 249 rule->sender = new; 250 251 return TRUE; 252} 253 254dbus_bool_t 255bus_match_rule_set_destination (BusMatchRule *rule, 256 const char *destination) 257{ 258 char *new; 259 260 _dbus_assert (destination != NULL); 261 262 new = _dbus_strdup (destination); 263 if (new == NULL) 264 return FALSE; 265 266 rule->flags |= BUS_MATCH_DESTINATION; 267 dbus_free (rule->destination); 268 rule->destination = new; 269 270 return TRUE; 271} 272 273dbus_bool_t 274bus_match_rule_set_path (BusMatchRule *rule, 275 const char *path) 276{ 277 char *new; 278 279 _dbus_assert (path != NULL); 280 281 new = _dbus_strdup (path); 282 if (new == NULL) 283 return FALSE; 284 285 rule->flags |= BUS_MATCH_PATH; 286 dbus_free (rule->path); 287 rule->path = new; 288 289 return TRUE; 290} 291 292#define ISWHITE(c) (((c) == ' ') || ((c) == '\t') || ((c) == '\n') || ((c) == '\r')) 293 294static dbus_bool_t 295find_key (const DBusString *str, 296 int start, 297 DBusString *key, 298 int *value_pos, 299 DBusError *error) 300{ 301 const char *p; 302 const char *s; 303 const char *key_start; 304 const char *key_end; 305 306 _DBUS_ASSERT_ERROR_IS_CLEAR (error); 307 308 s = _dbus_string_get_const_data (str); 309 310 p = s + start; 311 312 while (*p && ISWHITE (*p)) 313 ++p; 314 315 key_start = p; 316 317 while (*p && *p != '=' && !ISWHITE (*p)) 318 ++p; 319 320 key_end = p; 321 322 while (*p && ISWHITE (*p)) 323 ++p; 324 325 if (key_start == key_end) 326 { 327 /* Empty match rules or trailing whitespace are OK */ 328 *value_pos = p - s; 329 return TRUE; 330 } 331 332 if (*p != '=') 333 { 334 dbus_set_error (error, DBUS_ERROR_MATCH_RULE_INVALID, 335 "Match rule has a key with no subsequent '=' character"); 336 return FALSE; 337 } 338 ++p; 339 340 if (!_dbus_string_append_len (key, key_start, key_end - key_start)) 341 { 342 BUS_SET_OOM (error); 343 return FALSE; 344 } 345 346 *value_pos = p - s; 347 348 return TRUE; 349} 350 351static dbus_bool_t 352find_value (const DBusString *str, 353 int start, 354 const char *key, 355 DBusString *value, 356 int *value_end, 357 DBusError *error) 358{ 359 const char *p; 360 const char *s; 361 char quote_char; 362 int orig_len; 363 364 _DBUS_ASSERT_ERROR_IS_CLEAR (error); 365 366 orig_len = _dbus_string_get_length (value); 367 368 s = _dbus_string_get_const_data (str); 369 370 p = s + start; 371 372 quote_char = '\0'; 373 374 while (*p) 375 { 376 if (quote_char == '\0') 377 { 378 switch (*p) 379 { 380 case '\0': 381 goto done; 382 383 case '\'': 384 quote_char = '\''; 385 goto next; 386 387 case ',': 388 ++p; 389 goto done; 390 391 case '\\': 392 quote_char = '\\'; 393 goto next; 394 395 default: 396 if (!_dbus_string_append_byte (value, *p)) 397 { 398 BUS_SET_OOM (error); 399 goto failed; 400 } 401 } 402 } 403 else if (quote_char == '\\') 404 { 405 /* \ only counts as an escape if escaping a quote mark */ 406 if (*p != '\'') 407 { 408 if (!_dbus_string_append_byte (value, '\\')) 409 { 410 BUS_SET_OOM (error); 411 goto failed; 412 } 413 } 414 415 if (!_dbus_string_append_byte (value, *p)) 416 { 417 BUS_SET_OOM (error); 418 goto failed; 419 } 420 421 quote_char = '\0'; 422 } 423 else 424 { 425 _dbus_assert (quote_char == '\''); 426 427 if (*p == '\'') 428 { 429 quote_char = '\0'; 430 } 431 else 432 { 433 if (!_dbus_string_append_byte (value, *p)) 434 { 435 BUS_SET_OOM (error); 436 goto failed; 437 } 438 } 439 } 440 441 next: 442 ++p; 443 } 444 445 done: 446 447 if (quote_char == '\\') 448 { 449 if (!_dbus_string_append_byte (value, '\\')) 450 { 451 BUS_SET_OOM (error); 452 goto failed; 453 } 454 } 455 else if (quote_char == '\'') 456 { 457 dbus_set_error (error, DBUS_ERROR_MATCH_RULE_INVALID, 458 "Unbalanced quotation marks in match rule"); 459 goto failed; 460 } 461 else 462 _dbus_assert (quote_char == '\0'); 463 464 /* Zero-length values are allowed */ 465 466 *value_end = p - s; 467 468 return TRUE; 469 470 failed: 471 _DBUS_ASSERT_ERROR_IS_SET (error); 472 _dbus_string_set_length (value, orig_len); 473 return FALSE; 474} 475 476/* duplicates aren't allowed so the real legitimate max is only 6 or 477 * so. Leaving extra so we don't have to bother to update it. 478 */ 479#define MAX_RULE_TOKENS 16 480 481/* this is slightly too high level to be termed a "token" 482 * but let's not be pedantic. 483 */ 484typedef struct 485{ 486 char *key; 487 char *value; 488} RuleToken; 489 490static dbus_bool_t 491tokenize_rule (const DBusString *rule_text, 492 RuleToken tokens[MAX_RULE_TOKENS], 493 DBusError *error) 494{ 495 int i; 496 int pos; 497 DBusString key; 498 DBusString value; 499 dbus_bool_t retval; 500 501 retval = FALSE; 502 503 if (!_dbus_string_init (&key)) 504 { 505 BUS_SET_OOM (error); 506 return FALSE; 507 } 508 509 if (!_dbus_string_init (&value)) 510 { 511 _dbus_string_free (&key); 512 BUS_SET_OOM (error); 513 return FALSE; 514 } 515 516 i = 0; 517 pos = 0; 518 while (i < MAX_RULE_TOKENS && 519 pos < _dbus_string_get_length (rule_text)) 520 { 521 _dbus_assert (tokens[i].key == NULL); 522 _dbus_assert (tokens[i].value == NULL); 523 524 if (!find_key (rule_text, pos, &key, &pos, error)) 525 goto out; 526 527 if (_dbus_string_get_length (&key) == 0) 528 goto next; 529 530 if (!_dbus_string_steal_data (&key, &tokens[i].key)) 531 { 532 BUS_SET_OOM (error); 533 goto out; 534 } 535 536 if (!find_value (rule_text, pos, tokens[i].key, &value, &pos, error)) 537 goto out; 538 539 if (!_dbus_string_steal_data (&value, &tokens[i].value)) 540 { 541 BUS_SET_OOM (error); 542 goto out; 543 } 544 545 next: 546 ++i; 547 } 548 549 retval = TRUE; 550 551 out: 552 if (!retval) 553 { 554 i = 0; 555 while (tokens[i].key || tokens[i].value) 556 { 557 dbus_free (tokens[i].key); 558 dbus_free (tokens[i].value); 559 tokens[i].key = NULL; 560 tokens[i].value = NULL; 561 ++i; 562 } 563 } 564 565 _dbus_string_free (&key); 566 _dbus_string_free (&value); 567 568 return retval; 569} 570 571/* 572 * The format is comma-separated with strings quoted with single quotes 573 * as for the shell (to escape a literal single quote, use '\''). 574 * 575 * type='signal',sender='org.freedesktop.DBus',interface='org.freedesktop.DBus',member='Foo', 576 * path='/bar/foo',destination=':452345-34' 577 * 578 */ 579BusMatchRule* 580bus_match_rule_parse (DBusConnection *matches_go_to, 581 const DBusString *rule_text, 582 DBusError *error) 583{ 584 BusMatchRule *rule; 585 RuleToken tokens[MAX_RULE_TOKENS+1]; /* NULL termination + 1 */ 586 int i; 587 588 _DBUS_ASSERT_ERROR_IS_CLEAR (error); 589 590 memset (tokens, '\0', sizeof (tokens)); 591 592 rule = bus_match_rule_new (matches_go_to); 593 if (rule == NULL) 594 { 595 BUS_SET_OOM (error); 596 goto failed; 597 } 598 599 if (!tokenize_rule (rule_text, tokens, error)) 600 goto failed; 601 602 i = 0; 603 while (tokens[i].key != NULL) 604 { 605 const char *key = tokens[i].key; 606 const char *value = tokens[i].value; 607 608 if (strcmp (key, "type") == 0) 609 { 610 int t; 611 612 if (rule->flags & BUS_MATCH_MESSAGE_TYPE) 613 { 614 dbus_set_error (error, DBUS_ERROR_MATCH_RULE_INVALID, 615 "Key %s specified twice in match rule\n", key); 616 goto failed; 617 } 618 619 t = dbus_message_type_from_string (value); 620 621 if (!bus_match_rule_set_message_type (rule, t)) 622 { 623 BUS_SET_OOM (error); 624 goto failed; 625 } 626 } 627 else if (strcmp (key, "sender") == 0) 628 { 629 if (rule->flags & BUS_MATCH_SENDER) 630 { 631 dbus_set_error (error, DBUS_ERROR_MATCH_RULE_INVALID, 632 "Key %s specified twice in match rule\n", key); 633 goto failed; 634 } 635 636 if (!bus_match_rule_set_sender (rule, value)) 637 { 638 BUS_SET_OOM (error); 639 goto failed; 640 } 641 } 642 else if (strcmp (key, "interface") == 0) 643 { 644 if (rule->flags & BUS_MATCH_INTERFACE) 645 { 646 dbus_set_error (error, DBUS_ERROR_MATCH_RULE_INVALID, 647 "Key %s specified twice in match rule\n", key); 648 goto failed; 649 } 650 651 if (!bus_match_rule_set_interface (rule, value)) 652 { 653 BUS_SET_OOM (error); 654 goto failed; 655 } 656 } 657 else if (strcmp (key, "member") == 0) 658 { 659 if (rule->flags & BUS_MATCH_MEMBER) 660 { 661 dbus_set_error (error, DBUS_ERROR_MATCH_RULE_INVALID, 662 "Key %s specified twice in match rule\n", key); 663 goto failed; 664 } 665 666 if (!bus_match_rule_set_member (rule, value)) 667 { 668 BUS_SET_OOM (error); 669 goto failed; 670 } 671 } 672 else if (strcmp (key, "path") == 0) 673 { 674 if (rule->flags & BUS_MATCH_PATH) 675 { 676 dbus_set_error (error, DBUS_ERROR_MATCH_RULE_INVALID, 677 "Key %s specified twice in match rule\n", key); 678 goto failed; 679 } 680 681 if (!bus_match_rule_set_path (rule, value)) 682 { 683 BUS_SET_OOM (error); 684 goto failed; 685 } 686 } 687 else if (strcmp (key, "destination") == 0) 688 { 689 if (rule->flags & BUS_MATCH_DESTINATION) 690 { 691 dbus_set_error (error, DBUS_ERROR_MATCH_RULE_INVALID, 692 "Key %s specified twice in match rule\n", key); 693 goto failed; 694 } 695 696 if (!bus_match_rule_set_destination (rule, value)) 697 { 698 BUS_SET_OOM (error); 699 goto failed; 700 } 701 } 702 else 703 { 704 dbus_set_error (error, DBUS_ERROR_MATCH_RULE_INVALID, 705 "Unknown key \"%s\" in match rule", 706 key); 707 goto failed; 708 } 709 710 ++i; 711 } 712 713 714 goto out; 715 716 failed: 717 _DBUS_ASSERT_ERROR_IS_SET (error); 718 if (rule) 719 { 720 bus_match_rule_unref (rule); 721 rule = NULL; 722 } 723 724 out: 725 726 i = 0; 727 while (tokens[i].key || tokens[i].value) 728 { 729 _dbus_assert (i < MAX_RULE_TOKENS); 730 dbus_free (tokens[i].key); 731 dbus_free (tokens[i].value); 732 ++i; 733 } 734 735 return rule; 736} 737 738struct BusMatchmaker 739{ 740 int refcount; 741 742 DBusList *all_rules; 743}; 744 745BusMatchmaker* 746bus_matchmaker_new (void) 747{ 748 BusMatchmaker *matchmaker; 749 750 matchmaker = dbus_new0 (BusMatchmaker, 1); 751 if (matchmaker == NULL) 752 return NULL; 753 754 matchmaker->refcount = 1; 755 756 return matchmaker; 757} 758 759void 760bus_matchmaker_ref (BusMatchmaker *matchmaker) 761{ 762 _dbus_assert (matchmaker->refcount > 0); 763 764 matchmaker->refcount += 1; 765} 766 767void 768bus_matchmaker_unref (BusMatchmaker *matchmaker) 769{ 770 _dbus_assert (matchmaker->refcount > 0); 771 772 matchmaker->refcount -= 1; 773 if (matchmaker->refcount == 0) 774 { 775 while (matchmaker->all_rules != NULL) 776 { 777 BusMatchRule *rule; 778 779 rule = matchmaker->all_rules->data; 780 bus_match_rule_unref (rule); 781 _dbus_list_remove_link (&matchmaker->all_rules, 782 matchmaker->all_rules); 783 } 784 785 dbus_free (matchmaker); 786 } 787} 788 789/* The rule can't be modified after it's added. */ 790dbus_bool_t 791bus_matchmaker_add_rule (BusMatchmaker *matchmaker, 792 BusMatchRule *rule) 793{ 794 _dbus_assert (bus_connection_is_active (rule->matches_go_to)); 795 796 if (!_dbus_list_append (&matchmaker->all_rules, rule)) 797 return FALSE; 798 799 if (!bus_connection_add_match_rule (rule->matches_go_to, rule)) 800 { 801 _dbus_list_remove_last (&matchmaker->all_rules, rule); 802 return FALSE; 803 } 804 805 bus_match_rule_ref (rule); 806 807#ifdef DBUS_ENABLE_VERBOSE_MODE 808 { 809 char *s = match_rule_to_string (rule); 810 811 _dbus_verbose ("Added match rule %s to connection %p\n", 812 s, rule->matches_go_to); 813 dbus_free (s); 814 } 815#endif 816 817 return TRUE; 818} 819 820static dbus_bool_t 821match_rule_equal (BusMatchRule *a, 822 BusMatchRule *b) 823{ 824 if (a->flags != b->flags) 825 return FALSE; 826 827 if ((a->flags & BUS_MATCH_MESSAGE_TYPE) && 828 a->message_type != b->message_type) 829 return FALSE; 830 831 if ((a->flags & BUS_MATCH_MEMBER) && 832 strcmp (a->member, b->member) != 0) 833 return FALSE; 834 835 if ((a->flags & BUS_MATCH_PATH) && 836 strcmp (a->path, b->path) != 0) 837 return FALSE; 838 839 if ((a->flags & BUS_MATCH_INTERFACE) && 840 strcmp (a->interface, b->interface) != 0) 841 return FALSE; 842 843 if ((a->flags & BUS_MATCH_SENDER) && 844 strcmp (a->sender, b->sender) != 0) 845 return FALSE; 846 847 if ((a->flags & BUS_MATCH_DESTINATION) && 848 strcmp (a->destination, b->destination) != 0) 849 return FALSE; 850 851 return TRUE; 852} 853 854static void 855bus_matchmaker_remove_rule_link (BusMatchmaker *matchmaker, 856 DBusList *link) 857{ 858 BusMatchRule *rule = link->data; 859 860 bus_connection_remove_match_rule (rule->matches_go_to, rule); 861 _dbus_list_remove_link (&matchmaker->all_rules, link); 862 863#ifdef DBUS_ENABLE_VERBOSE_MODE 864 { 865 char *s = match_rule_to_string (rule); 866 867 _dbus_verbose ("Removed match rule %s for connection %p\n", 868 s, rule->matches_go_to); 869 dbus_free (s); 870 } 871#endif 872 873 bus_match_rule_unref (rule); 874} 875 876void 877bus_matchmaker_remove_rule (BusMatchmaker *matchmaker, 878 BusMatchRule *rule) 879{ 880 bus_connection_remove_match_rule (rule->matches_go_to, rule); 881 _dbus_list_remove (&matchmaker->all_rules, rule); 882 883#ifdef DBUS_ENABLE_VERBOSE_MODE 884 { 885 char *s = match_rule_to_string (rule); 886 887 _dbus_verbose ("Removed match rule %s for connection %p\n", 888 s, rule->matches_go_to); 889 dbus_free (s); 890 } 891#endif 892 893 bus_match_rule_unref (rule); 894} 895 896/* Remove a single rule which is equal to the given rule by value */ 897dbus_bool_t 898bus_matchmaker_remove_rule_by_value (BusMatchmaker *matchmaker, 899 BusMatchRule *value, 900 DBusError *error) 901{ 902 /* FIXME this is an unoptimized linear scan */ 903 904 DBusList *link; 905 906 /* we traverse backward because bus_connection_remove_match_rule() 907 * removes the most-recently-added rule 908 */ 909 link = _dbus_list_get_last_link (&matchmaker->all_rules); 910 while (link != NULL) 911 { 912 BusMatchRule *rule; 913 DBusList *prev; 914 915 rule = link->data; 916 prev = _dbus_list_get_prev_link (&matchmaker->all_rules, link); 917 918 if (match_rule_equal (rule, value)) 919 { 920 bus_matchmaker_remove_rule_link (matchmaker, link); 921 break; 922 } 923 924 link = prev; 925 } 926 927 if (link == NULL) 928 { 929 dbus_set_error (error, DBUS_ERROR_MATCH_RULE_NOT_FOUND, 930 "The given match rule wasn't found and can't be removed"); 931 return FALSE; 932 } 933 934 return TRUE; 935} 936 937void 938bus_matchmaker_disconnected (BusMatchmaker *matchmaker, 939 DBusConnection *disconnected) 940{ 941 DBusList *link; 942 943 /* FIXME 944 * 945 * This scans all match rules on the bus. We could avoid that 946 * for the rules belonging to the connection, since we keep 947 * a list of those; but for the rules that just refer to 948 * the connection we'd need to do something more elaborate. 949 * 950 */ 951 952 _dbus_assert (bus_connection_is_active (disconnected)); 953 954 link = _dbus_list_get_first_link (&matchmaker->all_rules); 955 while (link != NULL) 956 { 957 BusMatchRule *rule; 958 DBusList *next; 959 960 rule = link->data; 961 next = _dbus_list_get_next_link (&matchmaker->all_rules, link); 962 963 if (rule->matches_go_to == disconnected) 964 { 965 bus_matchmaker_remove_rule_link (matchmaker, link); 966 } 967 else if (((rule->flags & BUS_MATCH_SENDER) && *rule->sender == ':') || 968 ((rule->flags & BUS_MATCH_DESTINATION) && *rule->destination == ':')) 969 { 970 /* The rule matches to/from a base service, see if it's the 971 * one being disconnected, since we know this service name 972 * will never be recycled. 973 */ 974 const char *name; 975 976 name = bus_connection_get_name (disconnected); 977 _dbus_assert (name != NULL); /* because we're an active connection */ 978 979 if (((rule->flags & BUS_MATCH_SENDER) && 980 strcmp (rule->sender, name) == 0) || 981 ((rule->flags & BUS_MATCH_DESTINATION) && 982 strcmp (rule->destination, name) == 0)) 983 { 984 bus_matchmaker_remove_rule_link (matchmaker, link); 985 } 986 } 987 988 link = next; 989 } 990} 991 992static dbus_bool_t 993connection_is_primary_owner (DBusConnection *connection, 994 const char *service_name) 995{ 996 BusService *service; 997 DBusString str; 998 BusRegistry *registry; 999 1000 registry = bus_connection_get_registry (connection); 1001 1002 _dbus_string_init_const (&str, service_name); 1003 service = bus_registry_lookup (registry, &str); 1004 1005 if (service == NULL) 1006 return FALSE; /* Service doesn't exist so connection can't own it. */ 1007 1008 return bus_service_get_primary_owner (service) == connection; 1009} 1010 1011static dbus_bool_t 1012match_rule_matches (BusMatchRule *rule, 1013 BusConnections *connections, 1014 DBusConnection *sender, 1015 DBusConnection *addressed_recipient, 1016 DBusMessage *message) 1017{ 1018 /* All features of the match rule are AND'd together, 1019 * so FALSE if any of them don't match. 1020 */ 1021 1022 if (rule->flags & BUS_MATCH_MESSAGE_TYPE) 1023 { 1024 _dbus_assert (rule->message_type != DBUS_MESSAGE_TYPE_INVALID); 1025 1026 if (rule->message_type != dbus_message_get_type (message)) 1027 return FALSE; 1028 } 1029 1030 if (rule->flags & BUS_MATCH_INTERFACE) 1031 { 1032 const char *iface; 1033 1034 _dbus_assert (rule->interface != NULL); 1035 1036 iface = dbus_message_get_interface (message); 1037 if (iface == NULL) 1038 return FALSE; 1039 1040 if (strcmp (iface, rule->interface) != 0) 1041 return FALSE; 1042 } 1043 1044 if (rule->flags & BUS_MATCH_MEMBER) 1045 { 1046 const char *member; 1047 1048 _dbus_assert (rule->member != NULL); 1049 1050 member = dbus_message_get_member (message); 1051 if (member == NULL) 1052 return FALSE; 1053 1054 if (strcmp (member, rule->member) != 0) 1055 return FALSE; 1056 } 1057 1058 if (rule->flags & BUS_MATCH_SENDER) 1059 { 1060 _dbus_assert (rule->sender != NULL); 1061 1062 if (!connection_is_primary_owner (sender, rule->sender)) 1063 return FALSE; 1064 } 1065 1066 if (rule->flags & BUS_MATCH_DESTINATION) 1067 { 1068 const char *destination; 1069 1070 _dbus_assert (rule->destination != NULL); 1071 1072 if (addressed_recipient == NULL) 1073 return FALSE; 1074 1075 destination = dbus_message_get_destination (message); 1076 if (destination == NULL) 1077 return FALSE; 1078 1079 if (!connection_is_primary_owner (addressed_recipient, rule->destination)) 1080 return FALSE; 1081 } 1082 1083 if (rule->flags & BUS_MATCH_PATH) 1084 { 1085 const char *path; 1086 1087 _dbus_assert (rule->path != NULL); 1088 1089 path = dbus_message_get_path (message); 1090 if (path == NULL) 1091 return FALSE; 1092 1093 if (strcmp (path, rule->path) != 0) 1094 return FALSE; 1095 } 1096 1097 return TRUE; 1098} 1099 1100dbus_bool_t 1101bus_matchmaker_get_recipients (BusMatchmaker *matchmaker, 1102 BusConnections *connections, 1103 DBusConnection *sender, 1104 DBusConnection *addressed_recipient, 1105 DBusMessage *message, 1106 DBusList **recipients_p) 1107{ 1108 /* FIXME for now this is a wholly unoptimized linear search */ 1109 /* Guessing the important optimization is to skip the signal-related 1110 * match lists when processing method call and exception messages. 1111 * So separate match rule lists for signals? 1112 */ 1113 1114 DBusList *link; 1115 1116 _dbus_assert (*recipients_p == NULL); 1117 1118 /* This avoids sending same message to the same connection twice. 1119 * Purpose of the stamp instead of a bool is to avoid iterating over 1120 * all connections resetting the bool each time. 1121 */ 1122 bus_connections_increment_stamp (connections); 1123 1124 /* addressed_recipient is already receiving the message, don't add to list. 1125 * NULL addressed_recipient means either bus driver, or this is a signal 1126 * and thus lacks a specific addressed_recipient. 1127 */ 1128 if (addressed_recipient != NULL) 1129 bus_connection_mark_stamp (addressed_recipient); 1130 1131 link = _dbus_list_get_first_link (&matchmaker->all_rules); 1132 while (link != NULL) 1133 { 1134 BusMatchRule *rule; 1135 1136 rule = link->data; 1137 1138#ifdef DBUS_ENABLE_VERBOSE_MODE 1139 { 1140 char *s = match_rule_to_string (rule); 1141 1142 _dbus_verbose ("Checking whether message matches rule %s for connection %p\n", 1143 s, rule->matches_go_to); 1144 dbus_free (s); 1145 } 1146#endif 1147 1148 if (match_rule_matches (rule, connections, 1149 sender, addressed_recipient, message)) 1150 { 1151 _dbus_verbose ("Rule matched\n"); 1152 1153 /* Append to the list if we haven't already */ 1154 if (bus_connection_mark_stamp (rule->matches_go_to)) 1155 { 1156 if (!_dbus_list_append (recipients_p, rule->matches_go_to)) 1157 goto nomem; 1158 } 1159#ifdef DBUS_ENABLE_VERBOSE_MODE 1160 else 1161 { 1162 _dbus_verbose ("Connection already receiving this message, so not adding again\n"); 1163 } 1164#endif /* DBUS_ENABLE_VERBOSE_MODE */ 1165 } 1166 1167 link = _dbus_list_get_next_link (&matchmaker->all_rules, link); 1168 } 1169 1170 return TRUE; 1171 1172 nomem: 1173 _dbus_list_clear (recipients_p); 1174 return FALSE; 1175} 1176 1177#ifdef DBUS_BUILD_TESTS 1178#include "test.h" 1179#include <stdlib.h> 1180 1181static BusMatchRule* 1182check_parse (dbus_bool_t should_succeed, 1183 const char *text) 1184{ 1185 BusMatchRule *rule; 1186 DBusString str; 1187 DBusError error; 1188 1189 dbus_error_init (&error); 1190 1191 _dbus_string_init_const (&str, text); 1192 1193 rule = bus_match_rule_parse (NULL, &str, &error); 1194 if (dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY)) 1195 { 1196 dbus_error_free (&error); 1197 return NULL; 1198 } 1199 1200 if (should_succeed && rule == NULL) 1201 { 1202 _dbus_warn ("Failed to parse: %s: %s: \"%s\"\n", 1203 error.name, error.message, 1204 _dbus_string_get_const_data (&str)); 1205 exit (1); 1206 } 1207 1208 if (!should_succeed && rule != NULL) 1209 { 1210 _dbus_warn ("Failed to fail to parse: \"%s\"\n", 1211 _dbus_string_get_const_data (&str)); 1212 exit (1); 1213 } 1214 1215 dbus_error_free (&error); 1216 1217 return rule; 1218} 1219 1220static void 1221assert_large_rule (BusMatchRule *rule) 1222{ 1223 _dbus_assert (rule->flags & BUS_MATCH_MESSAGE_TYPE); 1224 _dbus_assert (rule->flags & BUS_MATCH_SENDER); 1225 _dbus_assert (rule->flags & BUS_MATCH_INTERFACE); 1226 _dbus_assert (rule->flags & BUS_MATCH_MEMBER); 1227 _dbus_assert (rule->flags & BUS_MATCH_DESTINATION); 1228 _dbus_assert (rule->flags & BUS_MATCH_PATH); 1229 1230 _dbus_assert (rule->message_type == DBUS_MESSAGE_TYPE_SIGNAL); 1231 _dbus_assert (rule->interface != NULL); 1232 _dbus_assert (rule->member != NULL); 1233 _dbus_assert (rule->sender != NULL); 1234 _dbus_assert (rule->destination != NULL); 1235 _dbus_assert (rule->path != NULL); 1236 1237 _dbus_assert (strcmp (rule->interface, "org.freedesktop.DBusInterface") == 0); 1238 _dbus_assert (strcmp (rule->sender, "org.freedesktop.DBusSender") == 0); 1239 _dbus_assert (strcmp (rule->member, "Foo") == 0); 1240 _dbus_assert (strcmp (rule->path, "/bar/foo") == 0); 1241 _dbus_assert (strcmp (rule->destination, ":452345-34") == 0); 1242} 1243 1244static dbus_bool_t 1245test_parsing (void *data) 1246{ 1247 BusMatchRule *rule; 1248 1249 rule = check_parse (TRUE, "type='signal',sender='org.freedesktop.DBusSender',interface='org.freedesktop.DBusInterface',member='Foo',path='/bar/foo',destination=':452345-34'"); 1250 if (rule != NULL) 1251 { 1252 assert_large_rule (rule); 1253 bus_match_rule_unref (rule); 1254 } 1255 1256 /* With extra whitespace and useless quotes */ 1257 rule = check_parse (TRUE, " type='signal', \tsender='org.freedes''ktop.DBusSender', interface='org.freedesktop.DBusInterface''''', \tmember='Foo',path='/bar/foo',destination=':452345-34'''''"); 1258 if (rule != NULL) 1259 { 1260 assert_large_rule (rule); 1261 bus_match_rule_unref (rule); 1262 } 1263 1264 1265 /* A simple signal connection */ 1266 rule = check_parse (TRUE, "type='signal',path='/foo',interface='org.Bar'"); 1267 if (rule != NULL) 1268 { 1269 _dbus_assert (rule->flags & BUS_MATCH_MESSAGE_TYPE); 1270 _dbus_assert (rule->flags & BUS_MATCH_INTERFACE); 1271 _dbus_assert (rule->flags & BUS_MATCH_PATH); 1272 1273 _dbus_assert (rule->message_type == DBUS_MESSAGE_TYPE_SIGNAL); 1274 _dbus_assert (rule->interface != NULL); 1275 _dbus_assert (rule->path != NULL); 1276 1277 _dbus_assert (strcmp (rule->interface, "org.Bar") == 0); 1278 _dbus_assert (strcmp (rule->path, "/foo") == 0); 1279 1280 bus_match_rule_unref (rule); 1281 } 1282 1283 /* Reject duplicates */ 1284 rule = check_parse (FALSE, "type='signal',type='method_call'"); 1285 _dbus_assert (rule == NULL); 1286 1287 /* Reject broken keys */ 1288 rule = check_parse (FALSE, "blah='signal'"); 1289 _dbus_assert (rule == NULL); 1290 1291 /* Allow empty rule */ 1292 rule = check_parse (TRUE, ""); 1293 if (rule != NULL) 1294 { 1295 _dbus_assert (rule->flags == 0); 1296 1297 bus_match_rule_unref (rule); 1298 } 1299 1300 /* All-whitespace rule is the same as empty */ 1301 rule = check_parse (TRUE, " \t"); 1302 if (rule != NULL) 1303 { 1304 _dbus_assert (rule->flags == 0); 1305 1306 bus_match_rule_unref (rule); 1307 } 1308 1309 /* But with non-whitespace chars and no =value, it's not OK */ 1310 rule = check_parse (FALSE, "type"); 1311 _dbus_assert (rule == NULL); 1312 1313 /* Empty string values are allowed at the moment */ 1314 rule = check_parse (TRUE, "interface="); 1315 if (rule != NULL) 1316 { 1317 _dbus_assert (rule->flags == BUS_MATCH_INTERFACE); 1318 _dbus_assert (rule->interface); 1319 _dbus_assert (strlen (rule->interface) == 0); 1320 1321 bus_match_rule_unref (rule); 1322 } 1323 1324 /* Empty string expressed with quotes */ 1325 rule = check_parse (TRUE, "interface=''"); 1326 if (rule != NULL) 1327 { 1328 _dbus_assert (rule->flags == BUS_MATCH_INTERFACE); 1329 _dbus_assert (rule->interface); 1330 _dbus_assert (strlen (rule->interface) == 0); 1331 1332 bus_match_rule_unref (rule); 1333 } 1334 1335 /* Check whitespace in a value */ 1336 rule = check_parse (TRUE, "interface= "); 1337 if (rule != NULL) 1338 { 1339 _dbus_assert (rule->flags == BUS_MATCH_INTERFACE); 1340 _dbus_assert (rule->interface); 1341 _dbus_assert (strcmp (rule->interface, " ") == 0); 1342 1343 bus_match_rule_unref (rule); 1344 } 1345 1346 /* Check whitespace mixed with non-whitespace in a value */ 1347 rule = check_parse (TRUE, "interface= foo "); 1348 if (rule != NULL) 1349 { 1350 _dbus_assert (rule->flags == BUS_MATCH_INTERFACE); 1351 _dbus_assert (rule->interface); 1352 _dbus_assert (strcmp (rule->interface, " foo ") == 0); 1353 1354 bus_match_rule_unref (rule); 1355 } 1356 1357 return TRUE; 1358} 1359 1360dbus_bool_t 1361bus_signals_test (const DBusString *test_data_dir) 1362{ 1363 BusMatchmaker *matchmaker; 1364 1365 matchmaker = bus_matchmaker_new (); 1366 bus_matchmaker_ref (matchmaker); 1367 bus_matchmaker_unref (matchmaker); 1368 bus_matchmaker_unref (matchmaker); 1369 1370 if (!_dbus_test_oom_handling ("parsing match rules", test_parsing, NULL)) 1371 _dbus_assert_not_reached ("Parsing match rules test failed"); 1372 1373 return TRUE; 1374} 1375 1376#endif /* DBUS_BUILD_TESTS */ 1377 1378