1 /* 2 * 3 * D-Bus helper library 4 * 5 * Copyright (C) 2004-2011 Marcel Holtmann <marcel (at) holtmann.org> 6 * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 21 * 22 */ 23 24 #ifdef HAVE_CONFIG_H 25 #include <config.h> 26 #endif 27 28 #include <stdio.h> 29 #include <string.h> 30 31 #include <glib.h> 32 #include <dbus/dbus.h> 33 34 #include "gdbus.h" 35 36 #define info(fmt...) 37 #define error(fmt...) 38 #define debug(fmt...) 39 40 static DBusHandlerResult message_filter(DBusConnection *connection, 41 DBusMessage *message, void *user_data); 42 43 static guint listener_id = 0; 44 static GSList *listeners = NULL; 45 46 struct service_data { 47 DBusConnection *conn; 48 DBusPendingCall *call; 49 char *name; 50 const char *owner; 51 guint id; 52 struct filter_callback *callback; 53 }; 54 55 struct filter_callback { 56 GDBusWatchFunction conn_func; 57 GDBusWatchFunction disc_func; 58 GDBusSignalFunction signal_func; 59 GDBusDestroyFunction destroy_func; 60 struct service_data *data; 61 void *user_data; 62 guint id; 63 }; 64 65 struct filter_data { 66 DBusConnection *connection; 67 DBusHandleMessageFunction handle_func; 68 char *name; 69 char *owner; 70 char *path; 71 char *interface; 72 char *member; 73 char *argument; 74 GSList *callbacks; 75 GSList *processed; 76 guint name_watch; 77 gboolean lock; 78 gboolean registered; 79 }; 80 81 static struct filter_data *filter_data_find(DBusConnection *connection, 82 const char *name, 83 const char *owner, 84 const char *path, 85 const char *interface, 86 const char *member, 87 const char *argument) 88 { 89 GSList *current; 90 91 for (current = listeners; 92 current != NULL; current = current->next) { 93 struct filter_data *data = current->data; 94 95 if (connection != data->connection) 96 continue; 97 98 if (name && data->name && 99 g_str_equal(name, data->name) == FALSE) 100 continue; 101 102 if (owner && data->owner && 103 g_str_equal(owner, data->owner) == FALSE) 104 continue; 105 106 if (path && data->path && 107 g_str_equal(path, data->path) == FALSE) 108 continue; 109 110 if (interface && data->interface && 111 g_str_equal(interface, data->interface) == FALSE) 112 continue; 113 114 if (member && data->member && 115 g_str_equal(member, data->member) == FALSE) 116 continue; 117 118 if (argument && data->argument && 119 g_str_equal(argument, data->argument) == FALSE) 120 continue; 121 122 return data; 123 } 124 125 return NULL; 126 } 127 128 static void format_rule(struct filter_data *data, char *rule, size_t size) 129 { 130 const char *sender; 131 int offset; 132 133 offset = snprintf(rule, size, "type='signal'"); 134 sender = data->name ? : data->owner; 135 136 if (sender) 137 offset += snprintf(rule + offset, size - offset, 138 ",sender='%s'", sender); 139 if (data->path) 140 offset += snprintf(rule + offset, size - offset, 141 ",path='%s'", data->path); 142 if (data->interface) 143 offset += snprintf(rule + offset, size - offset, 144 ",interface='%s'", data->interface); 145 if (data->member) 146 offset += snprintf(rule + offset, size - offset, 147 ",member='%s'", data->member); 148 if (data->argument) 149 snprintf(rule + offset, size - offset, 150 ",arg0='%s'", data->argument); 151 } 152 153 static gboolean add_match(struct filter_data *data, 154 DBusHandleMessageFunction filter) 155 { 156 DBusError err; 157 char rule[DBUS_MAXIMUM_MATCH_RULE_LENGTH]; 158 159 format_rule(data, rule, sizeof(rule)); 160 dbus_error_init(&err); 161 162 dbus_bus_add_match(data->connection, rule, &err); 163 if (dbus_error_is_set(&err)) { 164 error("Adding match rule \"%s\" failed: %s", rule, 165 err.message); 166 dbus_error_free(&err); 167 return FALSE; 168 } 169 170 data->handle_func = filter; 171 data->registered = TRUE; 172 173 return TRUE; 174 } 175 176 static gboolean remove_match(struct filter_data *data) 177 { 178 DBusError err; 179 char rule[DBUS_MAXIMUM_MATCH_RULE_LENGTH]; 180 181 format_rule(data, rule, sizeof(rule)); 182 183 dbus_error_init(&err); 184 185 dbus_bus_remove_match(data->connection, rule, &err); 186 if (dbus_error_is_set(&err)) { 187 error("Removing owner match rule for %s failed: %s", 188 rule, err.message); 189 dbus_error_free(&err); 190 return FALSE; 191 } 192 193 return TRUE; 194 } 195 196 static struct filter_data *filter_data_get(DBusConnection *connection, 197 DBusHandleMessageFunction filter, 198 const char *sender, 199 const char *path, 200 const char *interface, 201 const char *member, 202 const char *argument) 203 { 204 struct filter_data *data; 205 const char *name = NULL, *owner = NULL; 206 207 if (filter_data_find(connection, NULL, NULL, NULL, NULL, NULL, NULL) == NULL) { 208 if (!dbus_connection_add_filter(connection, 209 message_filter, NULL, NULL)) { 210 error("dbus_connection_add_filter() failed"); 211 return NULL; 212 } 213 } 214 215 if (sender == NULL) 216 goto proceed; 217 218 if (sender[0] == ':') 219 owner = sender; 220 else 221 name = sender; 222 223 proceed: 224 data = filter_data_find(connection, name, owner, path, interface, 225 member, argument); 226 if (data) 227 return data; 228 229 data = g_new0(struct filter_data, 1); 230 231 data->connection = dbus_connection_ref(connection); 232 data->name = name ? g_strdup(name) : NULL; 233 data->owner = owner ? g_strdup(owner) : NULL; 234 data->path = g_strdup(path); 235 data->interface = g_strdup(interface); 236 data->member = g_strdup(member); 237 data->argument = g_strdup(argument); 238 239 if (!add_match(data, filter)) { 240 g_free(data); 241 return NULL; 242 } 243 244 listeners = g_slist_append(listeners, data); 245 246 return data; 247 } 248 249 static struct filter_callback *filter_data_find_callback( 250 struct filter_data *data, 251 guint id) 252 { 253 GSList *l; 254 255 for (l = data->callbacks; l; l = l->next) { 256 struct filter_callback *cb = l->data; 257 if (cb->id == id) 258 return cb; 259 } 260 for (l = data->processed; l; l = l->next) { 261 struct filter_callback *cb = l->data; 262 if (cb->id == id) 263 return cb; 264 } 265 266 return NULL; 267 } 268 269 static void filter_data_free(struct filter_data *data) 270 { 271 GSList *l; 272 273 for (l = data->callbacks; l != NULL; l = l->next) 274 g_free(l->data); 275 276 g_slist_free(data->callbacks); 277 g_dbus_remove_watch(data->connection, data->name_watch); 278 g_free(data->name); 279 g_free(data->owner); 280 g_free(data->path); 281 g_free(data->interface); 282 g_free(data->member); 283 g_free(data->argument); 284 dbus_connection_unref(data->connection); 285 g_free(data); 286 } 287 288 static void filter_data_call_and_free(struct filter_data *data) 289 { 290 GSList *l; 291 292 for (l = data->callbacks; l != NULL; l = l->next) { 293 struct filter_callback *cb = l->data; 294 if (cb->disc_func) 295 cb->disc_func(data->connection, cb->user_data); 296 if (cb->destroy_func) 297 cb->destroy_func(cb->user_data); 298 g_free(cb); 299 } 300 301 filter_data_free(data); 302 } 303 304 static struct filter_callback *filter_data_add_callback( 305 struct filter_data *data, 306 GDBusWatchFunction connect, 307 GDBusWatchFunction disconnect, 308 GDBusSignalFunction signal, 309 GDBusDestroyFunction destroy, 310 void *user_data) 311 { 312 struct filter_callback *cb = NULL; 313 314 cb = g_new0(struct filter_callback, 1); 315 316 cb->conn_func = connect; 317 cb->disc_func = disconnect; 318 cb->signal_func = signal; 319 cb->destroy_func = destroy; 320 cb->user_data = user_data; 321 cb->id = ++listener_id; 322 323 if (data->lock) 324 data->processed = g_slist_append(data->processed, cb); 325 else 326 data->callbacks = g_slist_append(data->callbacks, cb); 327 328 return cb; 329 } 330 331 static void service_data_free(struct service_data *data) 332 { 333 struct filter_callback *callback = data->callback; 334 335 dbus_connection_unref(data->conn); 336 337 if (data->call) 338 dbus_pending_call_unref(data->call); 339 340 if (data->id) 341 g_source_remove(data->id); 342 343 g_free(data->name); 344 g_free(data); 345 346 callback->data = NULL; 347 } 348 349 static gboolean filter_data_remove_callback(struct filter_data *data, 350 struct filter_callback *cb) 351 { 352 DBusConnection *connection; 353 354 data->callbacks = g_slist_remove(data->callbacks, cb); 355 data->processed = g_slist_remove(data->processed, cb); 356 357 /* Cancel pending operations */ 358 if (cb->data) { 359 if (cb->data->call) 360 dbus_pending_call_cancel(cb->data->call); 361 service_data_free(cb->data); 362 } 363 364 if (cb->destroy_func) 365 cb->destroy_func(cb->user_data); 366 367 g_free(cb); 368 369 /* Don't remove the filter if other callbacks exist or data is lock 370 * processing callbacks */ 371 if (data->callbacks || data->lock) 372 return TRUE; 373 374 if (data->registered && !remove_match(data)) 375 return FALSE; 376 377 connection = dbus_connection_ref(data->connection); 378 listeners = g_slist_remove(listeners, data); 379 filter_data_free(data); 380 381 /* Remove filter if there are no listeners left for the connection */ 382 data = filter_data_find(connection, NULL, NULL, NULL, NULL, NULL, 383 NULL); 384 if (data == NULL) 385 dbus_connection_remove_filter(connection, message_filter, 386 NULL); 387 388 dbus_connection_unref(connection); 389 390 return TRUE; 391 } 392 393 static DBusHandlerResult signal_filter(DBusConnection *connection, 394 DBusMessage *message, void *user_data) 395 { 396 struct filter_data *data = user_data; 397 struct filter_callback *cb; 398 399 while (data->callbacks) { 400 cb = data->callbacks->data; 401 402 if (cb->signal_func && !cb->signal_func(connection, message, 403 cb->user_data)) { 404 filter_data_remove_callback(data, cb); 405 continue; 406 } 407 408 /* Check if the watch was removed/freed by the callback 409 * function */ 410 if (!g_slist_find(data->callbacks, cb)) 411 continue; 412 413 data->callbacks = g_slist_remove(data->callbacks, cb); 414 data->processed = g_slist_append(data->processed, cb); 415 } 416 417 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; 418 } 419 420 static void update_name_cache(const char *name, const char *owner) 421 { 422 GSList *l; 423 424 for (l = listeners; l != NULL; l = l->next) { 425 struct filter_data *data = l->data; 426 427 if (g_strcmp0(data->name, name) != 0) 428 continue; 429 430 g_free(data->owner); 431 data->owner = g_strdup(owner); 432 } 433 } 434 435 static const char *check_name_cache(const char *name) 436 { 437 GSList *l; 438 439 for (l = listeners; l != NULL; l = l->next) { 440 struct filter_data *data = l->data; 441 442 if (g_strcmp0(data->name, name) != 0) 443 continue; 444 445 return data->owner; 446 } 447 448 return NULL; 449 } 450 451 static DBusHandlerResult service_filter(DBusConnection *connection, 452 DBusMessage *message, void *user_data) 453 { 454 struct filter_data *data = user_data; 455 struct filter_callback *cb; 456 char *name, *old, *new; 457 458 if (!dbus_message_get_args(message, NULL, 459 DBUS_TYPE_STRING, &name, 460 DBUS_TYPE_STRING, &old, 461 DBUS_TYPE_STRING, &new, 462 DBUS_TYPE_INVALID)) { 463 error("Invalid arguments for NameOwnerChanged signal"); 464 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; 465 } 466 467 update_name_cache(name, new); 468 469 while (data->callbacks) { 470 cb = data->callbacks->data; 471 472 if (*new == '\0') { 473 if (cb->disc_func) 474 cb->disc_func(connection, cb->user_data); 475 } else { 476 if (cb->conn_func) 477 cb->conn_func(connection, cb->user_data); 478 } 479 480 /* Check if the watch was removed/freed by the callback 481 * function */ 482 if (!g_slist_find(data->callbacks, cb)) 483 continue; 484 485 /* Only auto remove if it is a bus name watch */ 486 if (data->argument[0] == ':' && 487 (cb->conn_func == NULL || cb->disc_func == NULL)) { 488 filter_data_remove_callback(data, cb); 489 continue; 490 } 491 492 data->callbacks = g_slist_remove(data->callbacks, cb); 493 data->processed = g_slist_append(data->processed, cb); 494 } 495 496 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; 497 } 498 499 500 static DBusHandlerResult message_filter(DBusConnection *connection, 501 DBusMessage *message, void *user_data) 502 { 503 struct filter_data *data; 504 const char *sender, *path, *iface, *member, *arg = NULL; 505 506 /* Only filter signals */ 507 if (dbus_message_get_type(message) != DBUS_MESSAGE_TYPE_SIGNAL) 508 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; 509 510 sender = dbus_message_get_sender(message); 511 path = dbus_message_get_path(message); 512 iface = dbus_message_get_interface(message); 513 member = dbus_message_get_member(message); 514 dbus_message_get_args(message, NULL, DBUS_TYPE_STRING, &arg, DBUS_TYPE_INVALID); 515 516 /* Sender is always bus name */ 517 data = filter_data_find(connection, NULL, sender, path, iface, member, 518 arg); 519 if (data == NULL) { 520 error("Got %s.%s signal which has no listeners", iface, member); 521 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; 522 } 523 524 if (data->handle_func) { 525 data->lock = TRUE; 526 527 data->handle_func(connection, message, data); 528 529 data->callbacks = data->processed; 530 data->processed = NULL; 531 data->lock = FALSE; 532 } 533 534 if (data->callbacks) 535 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; 536 537 remove_match(data); 538 539 listeners = g_slist_remove(listeners, data); 540 filter_data_free(data); 541 542 /* Remove filter if there no listener left for the connection */ 543 data = filter_data_find(connection, NULL, NULL, NULL, NULL, NULL, 544 NULL); 545 if (data == NULL) 546 dbus_connection_remove_filter(connection, message_filter, 547 NULL); 548 549 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; 550 } 551 552 static gboolean update_service(void *user_data) 553 { 554 struct service_data *data = user_data; 555 struct filter_callback *cb = data->callback; 556 557 update_name_cache(data->name, data->owner); 558 if (cb->conn_func) 559 cb->conn_func(data->conn, cb->user_data); 560 561 service_data_free(data); 562 563 return FALSE; 564 } 565 566 static void service_reply(DBusPendingCall *call, void *user_data) 567 { 568 struct service_data *data = user_data; 569 DBusMessage *reply; 570 DBusError err; 571 572 reply = dbus_pending_call_steal_reply(call); 573 if (reply == NULL) 574 return; 575 576 dbus_error_init(&err); 577 578 if (dbus_set_error_from_message(&err, reply)) 579 goto fail; 580 581 if (dbus_message_get_args(reply, &err, 582 DBUS_TYPE_STRING, &data->owner, 583 DBUS_TYPE_INVALID) == FALSE) 584 goto fail; 585 586 update_service(data); 587 588 goto done; 589 590 fail: 591 error("%s", err.message); 592 dbus_error_free(&err); 593 service_data_free(data); 594 done: 595 dbus_message_unref(reply); 596 } 597 598 static void check_service(DBusConnection *connection, 599 const char *name, 600 struct filter_callback *callback) 601 { 602 DBusMessage *message; 603 struct service_data *data; 604 605 data = g_try_malloc0(sizeof(*data)); 606 if (data == NULL) { 607 error("Can't allocate data structure"); 608 return; 609 } 610 611 data->conn = dbus_connection_ref(connection); 612 data->name = g_strdup(name); 613 data->callback = callback; 614 callback->data = data; 615 616 data->owner = check_name_cache(name); 617 if (data->owner != NULL) { 618 data->id = g_idle_add(update_service, data); 619 return; 620 } 621 622 message = dbus_message_new_method_call(DBUS_SERVICE_DBUS, 623 DBUS_PATH_DBUS, DBUS_INTERFACE_DBUS, "GetNameOwner"); 624 if (message == NULL) { 625 error("Can't allocate new message"); 626 g_free(data); 627 return; 628 } 629 630 dbus_message_append_args(message, DBUS_TYPE_STRING, &name, 631 DBUS_TYPE_INVALID); 632 633 if (dbus_connection_send_with_reply(connection, message, 634 &data->call, -1) == FALSE) { 635 error("Failed to execute method call"); 636 g_free(data); 637 goto done; 638 } 639 640 if (data->call == NULL) { 641 error("D-Bus connection not available"); 642 g_free(data); 643 goto done; 644 } 645 646 dbus_pending_call_set_notify(data->call, service_reply, data, NULL); 647 648 done: 649 dbus_message_unref(message); 650 } 651 652 guint g_dbus_add_service_watch(DBusConnection *connection, const char *name, 653 GDBusWatchFunction connect, 654 GDBusWatchFunction disconnect, 655 void *user_data, GDBusDestroyFunction destroy) 656 { 657 struct filter_data *data; 658 struct filter_callback *cb; 659 660 if (name == NULL) 661 return 0; 662 663 data = filter_data_get(connection, service_filter, NULL, NULL, 664 DBUS_INTERFACE_DBUS, "NameOwnerChanged", 665 name); 666 if (data == NULL) 667 return 0; 668 669 cb = filter_data_add_callback(data, connect, disconnect, NULL, NULL, 670 user_data); 671 if (cb == NULL) 672 return 0; 673 674 if (connect) 675 check_service(connection, name, cb); 676 677 return cb->id; 678 } 679 680 guint g_dbus_add_disconnect_watch(DBusConnection *connection, const char *name, 681 GDBusWatchFunction func, 682 void *user_data, GDBusDestroyFunction destroy) 683 { 684 return g_dbus_add_service_watch(connection, name, NULL, func, 685 user_data, destroy); 686 } 687 688 guint g_dbus_add_signal_watch(DBusConnection *connection, 689 const char *sender, const char *path, 690 const char *interface, const char *member, 691 GDBusSignalFunction function, void *user_data, 692 GDBusDestroyFunction destroy) 693 { 694 struct filter_data *data; 695 struct filter_callback *cb; 696 697 data = filter_data_get(connection, signal_filter, sender, path, 698 interface, member, NULL); 699 if (data == NULL) 700 return 0; 701 702 cb = filter_data_add_callback(data, NULL, NULL, function, destroy, 703 user_data); 704 if (cb == NULL) 705 return 0; 706 707 if (data->name != NULL && data->name_watch == 0) 708 data->name_watch = g_dbus_add_service_watch(connection, 709 data->name, NULL, 710 NULL, NULL, NULL); 711 712 return cb->id; 713 } 714 715 gboolean g_dbus_remove_watch(DBusConnection *connection, guint id) 716 { 717 struct filter_data *data; 718 struct filter_callback *cb; 719 GSList *ldata; 720 721 if (id == 0) 722 return FALSE; 723 724 for (ldata = listeners; ldata; ldata = ldata->next) { 725 data = ldata->data; 726 727 cb = filter_data_find_callback(data, id); 728 if (cb) { 729 filter_data_remove_callback(data, cb); 730 return TRUE; 731 } 732 } 733 734 return FALSE; 735 } 736 737 void g_dbus_remove_all_watches(DBusConnection *connection) 738 { 739 struct filter_data *data; 740 741 while ((data = filter_data_find(connection, NULL, NULL, NULL, NULL, 742 NULL, NULL))) { 743 listeners = g_slist_remove(listeners, data); 744 filter_data_call_and_free(data); 745 } 746 747 dbus_connection_remove_filter(connection, message_filter, NULL); 748 } 749