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