1 /* 2 * WPA Supplicant / dbus-based control interface 3 * Copyright (c) 2006, Dan Williams <dcbw@redhat.com> and Red Hat, Inc. 4 * Copyright (c) 2009, Witold Sowa <witold.sowa@gmail.com> 5 * 6 * This program is free software; you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License version 2 as 8 * published by the Free Software Foundation. 9 * 10 * Alternatively, this software may be distributed under the terms of BSD 11 * license. 12 * 13 * See README and COPYING for more details. 14 */ 15 16 #include "utils/includes.h" 17 18 #include "utils/common.h" 19 #include "utils/eloop.h" 20 #include "dbus_common.h" 21 #include "dbus_common_i.h" 22 #include "dbus_new.h" 23 #include "dbus_new_helpers.h" 24 25 26 /** 27 * recursive_iter_copy - Reads arguments from one iterator and 28 * writes to another recursively 29 * @from: iterator to read from 30 * @to: iterator to write to 31 * 32 * Copies one iterator's elements to another. If any element in 33 * iterator is of container type, its content is copied recursively 34 */ 35 static void recursive_iter_copy(DBusMessageIter *from, DBusMessageIter *to) 36 { 37 38 char *subtype = NULL; 39 int type; 40 41 /* iterate over iterator to copy */ 42 while ((type = dbus_message_iter_get_arg_type(from)) != 43 DBUS_TYPE_INVALID) { 44 45 /* simply copy basic type entries */ 46 if (dbus_type_is_basic(type)) { 47 if (dbus_type_is_fixed(type)) { 48 /* 49 * According to DBus documentation all 50 * fixed-length types are guaranteed to fit 51 * 8 bytes 52 */ 53 dbus_uint64_t v; 54 dbus_message_iter_get_basic(from, &v); 55 dbus_message_iter_append_basic(to, type, &v); 56 } else { 57 char *v; 58 dbus_message_iter_get_basic(from, &v); 59 dbus_message_iter_append_basic(to, type, &v); 60 } 61 } else { 62 /* recursively copy container type entries */ 63 DBusMessageIter write_subiter, read_subiter; 64 65 dbus_message_iter_recurse(from, &read_subiter); 66 67 if (type == DBUS_TYPE_VARIANT || 68 type == DBUS_TYPE_ARRAY) { 69 subtype = dbus_message_iter_get_signature( 70 &read_subiter); 71 } 72 73 dbus_message_iter_open_container(to, type, subtype, 74 &write_subiter); 75 76 recursive_iter_copy(&read_subiter, &write_subiter); 77 78 dbus_message_iter_close_container(to, &write_subiter); 79 if (subtype) 80 dbus_free(subtype); 81 } 82 83 dbus_message_iter_next(from); 84 } 85 } 86 87 88 static unsigned int fill_dict_with_properties( 89 DBusMessageIter *dict_iter, const struct wpa_dbus_property_desc *props, 90 const char *interface, const void *user_data) 91 { 92 DBusMessage *reply; 93 DBusMessageIter entry_iter, ret_iter; 94 unsigned int counter = 0; 95 const struct wpa_dbus_property_desc *dsc; 96 97 for (dsc = props; dsc && dsc->dbus_property; dsc++) { 98 if (!os_strncmp(dsc->dbus_interface, interface, 99 WPAS_DBUS_INTERFACE_MAX) && 100 dsc->access != W && dsc->getter) { 101 reply = dsc->getter(NULL, user_data); 102 if (!reply) 103 continue; 104 105 if (dbus_message_get_type(reply) == 106 DBUS_MESSAGE_TYPE_ERROR) { 107 dbus_message_unref(reply); 108 continue; 109 } 110 111 dbus_message_iter_init(reply, &ret_iter); 112 113 dbus_message_iter_open_container(dict_iter, 114 DBUS_TYPE_DICT_ENTRY, 115 NULL, &entry_iter); 116 dbus_message_iter_append_basic( 117 &entry_iter, DBUS_TYPE_STRING, 118 &dsc->dbus_property); 119 120 recursive_iter_copy(&ret_iter, &entry_iter); 121 122 dbus_message_iter_close_container(dict_iter, 123 &entry_iter); 124 dbus_message_unref(reply); 125 counter++; 126 } 127 } 128 129 return counter; 130 } 131 132 133 /** 134 * get_all_properties - Responds for GetAll properties calls on object 135 * @message: Message with GetAll call 136 * @interface: interface name which properties will be returned 137 * @property_dsc: list of object's properties 138 * Returns: Message with dict of variants as argument with properties values 139 * 140 * Iterates over all properties registered with object and execute getters 141 * of those, which are readable and which interface matches interface 142 * specified as argument. Returned message contains one dict argument 143 * with properties names as keys and theirs values as values. 144 */ 145 static DBusMessage * get_all_properties( 146 DBusMessage *message, char *interface, 147 struct wpa_dbus_object_desc *obj_dsc) 148 { 149 /* Create and initialize the return message */ 150 DBusMessage *reply = dbus_message_new_method_return(message); 151 DBusMessageIter iter, dict_iter; 152 int props_num; 153 154 dbus_message_iter_init_append(reply, &iter); 155 156 dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, 157 DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING 158 DBUS_TYPE_STRING_AS_STRING 159 DBUS_TYPE_VARIANT_AS_STRING 160 DBUS_DICT_ENTRY_END_CHAR_AS_STRING, 161 &dict_iter); 162 163 props_num = fill_dict_with_properties(&dict_iter, obj_dsc->properties, 164 interface, obj_dsc->user_data); 165 166 dbus_message_iter_close_container(&iter, &dict_iter); 167 168 if (props_num == 0) { 169 dbus_message_unref(reply); 170 reply = dbus_message_new_error(message, 171 DBUS_ERROR_INVALID_ARGS, 172 "No readable properties in " 173 "this interface"); 174 } 175 176 return reply; 177 } 178 179 180 static int is_signature_correct(DBusMessage *message, 181 const struct wpa_dbus_method_desc *method_dsc) 182 { 183 /* According to DBus documentation max length of signature is 255 */ 184 #define MAX_SIG_LEN 256 185 char registered_sig[MAX_SIG_LEN], *pos; 186 const char *sig = dbus_message_get_signature(message); 187 int ret; 188 const struct wpa_dbus_argument *arg; 189 190 pos = registered_sig; 191 *pos = '\0'; 192 193 for (arg = method_dsc->args; arg && arg->name; arg++) { 194 if (arg->dir == ARG_IN) { 195 size_t blen = registered_sig + MAX_SIG_LEN - pos; 196 ret = os_snprintf(pos, blen, "%s", arg->type); 197 if (ret < 0 || (size_t) ret >= blen) 198 return 0; 199 pos += ret; 200 } 201 } 202 203 return !os_strncmp(registered_sig, sig, MAX_SIG_LEN); 204 } 205 206 207 static DBusMessage * properties_get_all(DBusMessage *message, char *interface, 208 struct wpa_dbus_object_desc *obj_dsc) 209 { 210 if (os_strcmp(dbus_message_get_signature(message), "s") != 0) 211 return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS, 212 NULL); 213 214 return get_all_properties(message, interface, obj_dsc); 215 } 216 217 218 static DBusMessage * properties_get(DBusMessage *message, 219 const struct wpa_dbus_property_desc *dsc, 220 void *user_data) 221 { 222 if (os_strcmp(dbus_message_get_signature(message), "ss")) 223 return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS, 224 NULL); 225 226 if (dsc->access != W && dsc->getter) 227 return dsc->getter(message, user_data); 228 229 return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS, 230 "Property is write-only"); 231 } 232 233 234 static DBusMessage * properties_set(DBusMessage *message, 235 const struct wpa_dbus_property_desc *dsc, 236 void *user_data) 237 { 238 if (os_strcmp(dbus_message_get_signature(message), "ssv")) 239 return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS, 240 NULL); 241 242 if (dsc->access != R && dsc->setter) 243 return dsc->setter(message, user_data); 244 245 return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS, 246 "Property is read-only"); 247 } 248 249 250 static DBusMessage * 251 properties_get_or_set(DBusMessage *message, DBusMessageIter *iter, 252 char *interface, 253 struct wpa_dbus_object_desc *obj_dsc) 254 { 255 const struct wpa_dbus_property_desc *property_dsc; 256 char *property; 257 const char *method; 258 259 method = dbus_message_get_member(message); 260 property_dsc = obj_dsc->properties; 261 262 /* Second argument: property name (DBUS_TYPE_STRING) */ 263 if (!dbus_message_iter_next(iter) || 264 dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_STRING) { 265 return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS, 266 NULL); 267 } 268 dbus_message_iter_get_basic(iter, &property); 269 270 while (property_dsc && property_dsc->dbus_property) { 271 /* compare property names and 272 * interfaces */ 273 if (!os_strncmp(property_dsc->dbus_property, property, 274 WPAS_DBUS_METHOD_SIGNAL_PROP_MAX) && 275 !os_strncmp(property_dsc->dbus_interface, interface, 276 WPAS_DBUS_INTERFACE_MAX)) 277 break; 278 279 property_dsc++; 280 } 281 if (property_dsc == NULL || property_dsc->dbus_property == NULL) { 282 wpa_printf(MSG_DEBUG, "no property handler for %s.%s on %s", 283 interface, property, 284 dbus_message_get_path(message)); 285 return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS, 286 "No such property"); 287 } 288 289 if (os_strncmp(WPA_DBUS_PROPERTIES_GET, method, 290 WPAS_DBUS_METHOD_SIGNAL_PROP_MAX) == 0) 291 return properties_get(message, property_dsc, 292 obj_dsc->user_data); 293 294 return properties_set(message, property_dsc, obj_dsc->user_data); 295 } 296 297 298 static DBusMessage * properties_handler(DBusMessage *message, 299 struct wpa_dbus_object_desc *obj_dsc) 300 { 301 DBusMessageIter iter; 302 char *interface; 303 const char *method; 304 305 method = dbus_message_get_member(message); 306 dbus_message_iter_init(message, &iter); 307 308 if (!os_strncmp(WPA_DBUS_PROPERTIES_GET, method, 309 WPAS_DBUS_METHOD_SIGNAL_PROP_MAX) || 310 !os_strncmp(WPA_DBUS_PROPERTIES_SET, method, 311 WPAS_DBUS_METHOD_SIGNAL_PROP_MAX) || 312 !os_strncmp(WPA_DBUS_PROPERTIES_GETALL, method, 313 WPAS_DBUS_METHOD_SIGNAL_PROP_MAX)) { 314 /* First argument: interface name (DBUS_TYPE_STRING) */ 315 if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING) 316 { 317 return dbus_message_new_error(message, 318 DBUS_ERROR_INVALID_ARGS, 319 NULL); 320 } 321 322 dbus_message_iter_get_basic(&iter, &interface); 323 324 if (!os_strncmp(WPA_DBUS_PROPERTIES_GETALL, method, 325 WPAS_DBUS_METHOD_SIGNAL_PROP_MAX)) { 326 /* GetAll */ 327 return properties_get_all(message, interface, obj_dsc); 328 } 329 /* Get or Set */ 330 return properties_get_or_set(message, &iter, interface, 331 obj_dsc); 332 } 333 return dbus_message_new_error(message, DBUS_ERROR_UNKNOWN_METHOD, 334 NULL); 335 } 336 337 338 static DBusMessage * msg_method_handler(DBusMessage *message, 339 struct wpa_dbus_object_desc *obj_dsc) 340 { 341 const struct wpa_dbus_method_desc *method_dsc = obj_dsc->methods; 342 const char *method; 343 const char *msg_interface; 344 345 method = dbus_message_get_member(message); 346 msg_interface = dbus_message_get_interface(message); 347 348 /* try match call to any registered method */ 349 while (method_dsc && method_dsc->dbus_method) { 350 /* compare method names and interfaces */ 351 if (!os_strncmp(method_dsc->dbus_method, method, 352 WPAS_DBUS_METHOD_SIGNAL_PROP_MAX) && 353 !os_strncmp(method_dsc->dbus_interface, msg_interface, 354 WPAS_DBUS_INTERFACE_MAX)) 355 break; 356 357 method_dsc++; 358 } 359 if (method_dsc == NULL || method_dsc->dbus_method == NULL) { 360 wpa_printf(MSG_DEBUG, "no method handler for %s.%s on %s", 361 msg_interface, method, 362 dbus_message_get_path(message)); 363 return dbus_message_new_error(message, 364 DBUS_ERROR_UNKNOWN_METHOD, NULL); 365 } 366 367 if (!is_signature_correct(message, method_dsc)) { 368 return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS, 369 NULL); 370 } 371 372 return method_dsc->method_handler(message, 373 obj_dsc->user_data); 374 } 375 376 377 /** 378 * message_handler - Handles incoming DBus messages 379 * @connection: DBus connection on which message was received 380 * @message: Received message 381 * @user_data: pointer to description of object to which message was sent 382 * Returns: Returns information whether message was handled or not 383 * 384 * Reads message interface and method name, then checks if they matches one 385 * of the special cases i.e. introspection call or properties get/getall/set 386 * methods and handles it. Else it iterates over registered methods list 387 * and tries to match method's name and interface to those read from message 388 * If appropriate method was found its handler function is called and 389 * response is sent. Otherwise, the DBUS_ERROR_UNKNOWN_METHOD error message 390 * will be sent. 391 */ 392 static DBusHandlerResult message_handler(DBusConnection *connection, 393 DBusMessage *message, void *user_data) 394 { 395 struct wpa_dbus_object_desc *obj_dsc = user_data; 396 const char *method; 397 const char *path; 398 const char *msg_interface; 399 DBusMessage *reply; 400 401 /* get method, interface and path the message is addressed to */ 402 method = dbus_message_get_member(message); 403 path = dbus_message_get_path(message); 404 msg_interface = dbus_message_get_interface(message); 405 if (!method || !path || !msg_interface) 406 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; 407 408 wpa_printf(MSG_MSGDUMP, "dbus: %s.%s (%s)", 409 msg_interface, method, path); 410 411 /* if message is introspection method call */ 412 if (!os_strncmp(WPA_DBUS_INTROSPECTION_METHOD, method, 413 WPAS_DBUS_METHOD_SIGNAL_PROP_MAX) && 414 !os_strncmp(WPA_DBUS_INTROSPECTION_INTERFACE, msg_interface, 415 WPAS_DBUS_INTERFACE_MAX)) { 416 #ifdef CONFIG_CTRL_IFACE_DBUS_INTRO 417 reply = wpa_dbus_introspect(message, obj_dsc); 418 #else /* CONFIG_CTRL_IFACE_DBUS_INTRO */ 419 reply = dbus_message_new_error( 420 message, DBUS_ERROR_UNKNOWN_METHOD, 421 "wpa_supplicant was compiled without " 422 "introspection support."); 423 #endif /* CONFIG_CTRL_IFACE_DBUS_INTRO */ 424 } else if (!os_strncmp(WPA_DBUS_PROPERTIES_INTERFACE, msg_interface, 425 WPAS_DBUS_INTERFACE_MAX)) { 426 /* if message is properties method call */ 427 reply = properties_handler(message, obj_dsc); 428 } else { 429 reply = msg_method_handler(message, obj_dsc); 430 } 431 432 /* If handler succeed returning NULL, reply empty message */ 433 if (!reply) 434 reply = dbus_message_new_method_return(message); 435 if (reply) { 436 if (!dbus_message_get_no_reply(message)) 437 dbus_connection_send(connection, reply, NULL); 438 dbus_message_unref(reply); 439 } 440 441 wpa_dbus_flush_all_changed_properties(connection); 442 443 return DBUS_HANDLER_RESULT_HANDLED; 444 } 445 446 447 /** 448 * free_dbus_object_desc - Frees object description data structure 449 * @connection: DBus connection 450 * @obj_dsc: Object description to free 451 * 452 * Frees each of properties, methods and signals description lists and 453 * the object description structure itself. 454 */ 455 void free_dbus_object_desc(struct wpa_dbus_object_desc *obj_dsc) 456 { 457 if (!obj_dsc) 458 return; 459 460 /* free handler's argument */ 461 if (obj_dsc->user_data_free_func) 462 obj_dsc->user_data_free_func(obj_dsc->user_data); 463 464 os_free(obj_dsc->path); 465 os_free(obj_dsc->prop_changed_flags); 466 os_free(obj_dsc); 467 } 468 469 470 static void free_dbus_object_desc_cb(DBusConnection *connection, void *obj_dsc) 471 { 472 free_dbus_object_desc(obj_dsc); 473 } 474 475 /** 476 * wpa_dbus_ctrl_iface_init - Initialize dbus control interface 477 * @application_data: Pointer to application specific data structure 478 * @dbus_path: DBus path to interface object 479 * @dbus_service: DBus service name to register with 480 * @messageHandler: a pointer to function which will handle dbus messages 481 * coming on interface 482 * Returns: 0 on success, -1 on failure 483 * 484 * Initialize the dbus control interface and start receiving commands from 485 * external programs over the bus. 486 */ 487 int wpa_dbus_ctrl_iface_init(struct wpas_dbus_priv *iface, 488 char *dbus_path, char *dbus_service, 489 struct wpa_dbus_object_desc *obj_desc) 490 { 491 DBusError error; 492 int ret = -1; 493 DBusObjectPathVTable wpa_vtable = { 494 &free_dbus_object_desc_cb, &message_handler, 495 NULL, NULL, NULL, NULL 496 }; 497 498 obj_desc->connection = iface->con; 499 obj_desc->path = os_strdup(dbus_path); 500 501 /* Register the message handler for the global dbus interface */ 502 if (!dbus_connection_register_object_path(iface->con, 503 dbus_path, &wpa_vtable, 504 obj_desc)) { 505 wpa_printf(MSG_ERROR, "dbus: Could not set up message " 506 "handler"); 507 return -1; 508 } 509 510 /* Register our service with the message bus */ 511 dbus_error_init(&error); 512 switch (dbus_bus_request_name(iface->con, dbus_service, 513 0, &error)) { 514 case DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER: 515 ret = 0; 516 break; 517 case DBUS_REQUEST_NAME_REPLY_EXISTS: 518 case DBUS_REQUEST_NAME_REPLY_IN_QUEUE: 519 case DBUS_REQUEST_NAME_REPLY_ALREADY_OWNER: 520 wpa_printf(MSG_ERROR, "dbus: Could not request service name: " 521 "already registered"); 522 break; 523 default: 524 wpa_printf(MSG_ERROR, "dbus: Could not request service name: " 525 "%s %s", error.name, error.message); 526 break; 527 } 528 dbus_error_free(&error); 529 530 if (ret != 0) 531 return -1; 532 533 wpa_printf(MSG_DEBUG, "Providing DBus service '%s'.", dbus_service); 534 535 return 0; 536 } 537 538 539 /** 540 * wpa_dbus_register_object_per_iface - Register a new object with dbus 541 * @ctrl_iface: pointer to dbus private data 542 * @path: DBus path to object 543 * @ifname: interface name 544 * @obj_desc: description of object's methods, signals and properties 545 * Returns: 0 on success, -1 on error 546 * 547 * Registers a new interface with dbus and assigns it a dbus object path. 548 */ 549 int wpa_dbus_register_object_per_iface( 550 struct wpas_dbus_priv *ctrl_iface, 551 const char *path, const char *ifname, 552 struct wpa_dbus_object_desc *obj_desc) 553 { 554 DBusConnection *con; 555 556 DBusObjectPathVTable vtable = { 557 &free_dbus_object_desc_cb, &message_handler, 558 NULL, NULL, NULL, NULL 559 }; 560 561 /* Do nothing if the control interface is not turned on */ 562 if (ctrl_iface == NULL) 563 return 0; 564 565 con = ctrl_iface->con; 566 obj_desc->connection = con; 567 obj_desc->path = os_strdup(path); 568 569 /* Register the message handler for the interface functions */ 570 if (!dbus_connection_register_object_path(con, path, &vtable, 571 obj_desc)) { 572 wpa_printf(MSG_ERROR, "dbus: Could not set up message " 573 "handler for interface %s object %s", ifname, path); 574 return -1; 575 } 576 577 return 0; 578 } 579 580 581 static void flush_object_timeout_handler(void *eloop_ctx, void *timeout_ctx); 582 583 584 /** 585 * wpa_dbus_unregister_object_per_iface - Unregisters DBus object 586 * @ctrl_iface: Pointer to dbus private data 587 * @path: DBus path to object which will be unregistered 588 * Returns: Zero on success and -1 on failure 589 * 590 * Unregisters DBus object given by its path 591 */ 592 int wpa_dbus_unregister_object_per_iface( 593 struct wpas_dbus_priv *ctrl_iface, const char *path) 594 { 595 DBusConnection *con = ctrl_iface->con; 596 struct wpa_dbus_object_desc *obj_desc = NULL; 597 598 dbus_connection_get_object_path_data(con, path, (void **) &obj_desc); 599 if (!obj_desc) { 600 wpa_printf(MSG_ERROR, "dbus: %s: Could not obtain object's " 601 "private data: %s", __func__, path); 602 } else { 603 eloop_cancel_timeout(flush_object_timeout_handler, con, 604 obj_desc); 605 } 606 607 if (!dbus_connection_unregister_object_path(con, path)) 608 return -1; 609 610 return 0; 611 } 612 613 614 static void put_changed_properties(const struct wpa_dbus_object_desc *obj_dsc, 615 const char *interface, 616 DBusMessageIter *dict_iter) 617 { 618 DBusMessage *getter_reply; 619 DBusMessageIter prop_iter, entry_iter; 620 const struct wpa_dbus_property_desc *dsc; 621 int i; 622 623 for (dsc = obj_dsc->properties, i = 0; dsc && dsc->dbus_property; 624 dsc++, i++) { 625 if (obj_dsc->prop_changed_flags == NULL || 626 !obj_dsc->prop_changed_flags[i]) 627 continue; 628 if (os_strcmp(dsc->dbus_interface, interface) != 0) 629 continue; 630 obj_dsc->prop_changed_flags[i] = 0; 631 632 getter_reply = dsc->getter(NULL, obj_dsc->user_data); 633 if (!getter_reply || 634 dbus_message_get_type(getter_reply) == 635 DBUS_MESSAGE_TYPE_ERROR) { 636 wpa_printf(MSG_ERROR, "dbus: %s: Cannot get new value " 637 "of property %s", __func__, 638 dsc->dbus_property); 639 continue; 640 } 641 642 if (!dbus_message_iter_init(getter_reply, &prop_iter) || 643 !dbus_message_iter_open_container(dict_iter, 644 DBUS_TYPE_DICT_ENTRY, 645 NULL, &entry_iter) || 646 !dbus_message_iter_append_basic(&entry_iter, 647 DBUS_TYPE_STRING, 648 &dsc->dbus_property)) 649 goto err; 650 651 recursive_iter_copy(&prop_iter, &entry_iter); 652 653 if (!dbus_message_iter_close_container(dict_iter, &entry_iter)) 654 goto err; 655 656 dbus_message_unref(getter_reply); 657 } 658 659 return; 660 661 err: 662 wpa_printf(MSG_ERROR, "dbus: %s: Cannot construct signal", __func__); 663 } 664 665 666 static void send_prop_changed_signal( 667 DBusConnection *con, const char *path, const char *interface, 668 const struct wpa_dbus_object_desc *obj_dsc) 669 { 670 DBusMessage *msg; 671 DBusMessageIter signal_iter, dict_iter; 672 673 msg = dbus_message_new_signal(path, interface, "PropertiesChanged"); 674 if (msg == NULL) 675 return; 676 677 dbus_message_iter_init_append(msg, &signal_iter); 678 679 if (!dbus_message_iter_open_container(&signal_iter, DBUS_TYPE_ARRAY, 680 "{sv}", &dict_iter)) 681 goto err; 682 683 put_changed_properties(obj_dsc, interface, &dict_iter); 684 685 if (!dbus_message_iter_close_container(&signal_iter, &dict_iter)) 686 goto err; 687 688 dbus_connection_send(con, msg, NULL); 689 690 out: 691 dbus_message_unref(msg); 692 return; 693 694 err: 695 wpa_printf(MSG_DEBUG, "dbus: %s: Failed to construct signal", 696 __func__); 697 goto out; 698 } 699 700 701 static void flush_object_timeout_handler(void *eloop_ctx, void *timeout_ctx) 702 { 703 DBusConnection *con = eloop_ctx; 704 struct wpa_dbus_object_desc *obj_desc = timeout_ctx; 705 706 wpa_printf(MSG_DEBUG, "dbus: %s: Timeout - sending changed properties " 707 "of object %s", __func__, obj_desc->path); 708 wpa_dbus_flush_object_changed_properties(con, obj_desc->path); 709 } 710 711 712 static void recursive_flush_changed_properties(DBusConnection *con, 713 const char *path) 714 { 715 char **objects = NULL; 716 char subobj_path[WPAS_DBUS_OBJECT_PATH_MAX]; 717 int i; 718 719 wpa_dbus_flush_object_changed_properties(con, path); 720 721 if (!dbus_connection_list_registered(con, path, &objects)) 722 goto out; 723 724 for (i = 0; objects[i]; i++) { 725 os_snprintf(subobj_path, WPAS_DBUS_OBJECT_PATH_MAX, 726 "%s/%s", path, objects[i]); 727 recursive_flush_changed_properties(con, subobj_path); 728 } 729 730 out: 731 dbus_free_string_array(objects); 732 } 733 734 735 /** 736 * wpa_dbus_flush_all_changed_properties - Send all PropertiesChanged signals 737 * @con: DBus connection 738 * 739 * Traverses through all registered objects and sends PropertiesChanged for 740 * each properties. 741 */ 742 void wpa_dbus_flush_all_changed_properties(DBusConnection *con) 743 { 744 recursive_flush_changed_properties(con, WPAS_DBUS_NEW_PATH); 745 } 746 747 748 /** 749 * wpa_dbus_flush_object_changed_properties - Send PropertiesChanged for object 750 * @con: DBus connection 751 * @path: path to a DBus object for which PropertiesChanged will be sent. 752 * 753 * Iterates over all properties registered with object and for each interface 754 * containing properties marked as changed, sends a PropertiesChanged signal 755 * containing names and new values of properties that have changed. 756 * 757 * You need to call this function after wpa_dbus_mark_property_changed() 758 * if you want to send PropertiesChanged signal immediately (i.e., without 759 * waiting timeout to expire). PropertiesChanged signal for an object is sent 760 * automatically short time after first marking property as changed. All 761 * PropertiesChanged signals are sent automatically after responding on DBus 762 * message, so if you marked a property changed as a result of DBus call 763 * (e.g., param setter), you usually do not need to call this function. 764 */ 765 void wpa_dbus_flush_object_changed_properties(DBusConnection *con, 766 const char *path) 767 { 768 struct wpa_dbus_object_desc *obj_desc = NULL; 769 const struct wpa_dbus_property_desc *dsc; 770 int i; 771 772 dbus_connection_get_object_path_data(con, path, (void **) &obj_desc); 773 if (!obj_desc) 774 return; 775 eloop_cancel_timeout(flush_object_timeout_handler, con, obj_desc); 776 777 dsc = obj_desc->properties; 778 for (dsc = obj_desc->properties, i = 0; dsc && dsc->dbus_property; 779 dsc++, i++) { 780 if (obj_desc->prop_changed_flags == NULL || 781 !obj_desc->prop_changed_flags[i]) 782 continue; 783 send_prop_changed_signal(con, path, dsc->dbus_interface, 784 obj_desc); 785 } 786 } 787 788 789 #define WPA_DBUS_SEND_PROP_CHANGED_TIMEOUT 5000 790 791 792 /** 793 * wpa_dbus_mark_property_changed - Mark a property as changed and 794 * @iface: dbus priv struct 795 * @path: path to DBus object which property has changed 796 * @interface: interface containing changed property 797 * @property: property name which has changed 798 * 799 * Iterates over all properties registered with an object and marks the one 800 * given in parameters as changed. All parameters registered for an object 801 * within a single interface will be aggregated together and sent in one 802 * PropertiesChanged signal when function 803 * wpa_dbus_flush_object_changed_properties() is called. 804 */ 805 void wpa_dbus_mark_property_changed(struct wpas_dbus_priv *iface, 806 const char *path, const char *interface, 807 const char *property) 808 { 809 struct wpa_dbus_object_desc *obj_desc = NULL; 810 const struct wpa_dbus_property_desc *dsc; 811 int i = 0; 812 813 if (iface == NULL) 814 return; 815 816 dbus_connection_get_object_path_data(iface->con, path, 817 (void **) &obj_desc); 818 if (!obj_desc) { 819 wpa_printf(MSG_ERROR, "dbus: wpa_dbus_property_changed: " 820 "could not obtain object's private data: %s", path); 821 return; 822 } 823 824 for (dsc = obj_desc->properties; dsc && dsc->dbus_property; dsc++, i++) 825 if (os_strcmp(property, dsc->dbus_property) == 0 && 826 os_strcmp(interface, dsc->dbus_interface) == 0) { 827 if (obj_desc->prop_changed_flags) 828 obj_desc->prop_changed_flags[i] = 1; 829 break; 830 } 831 832 if (!dsc || !dsc->dbus_property) { 833 wpa_printf(MSG_ERROR, "dbus: wpa_dbus_property_changed: " 834 "no property %s in object %s", property, path); 835 return; 836 } 837 838 if (!eloop_is_timeout_registered(flush_object_timeout_handler, 839 iface->con, obj_desc->path)) { 840 eloop_register_timeout(0, WPA_DBUS_SEND_PROP_CHANGED_TIMEOUT, 841 flush_object_timeout_handler, 842 iface->con, obj_desc); 843 } 844 } 845 846 847 /** 848 * wpa_dbus_get_object_properties - Put object's properties into dictionary 849 * @iface: dbus priv struct 850 * @path: path to DBus object which properties will be obtained 851 * @interface: interface name which properties will be obtained 852 * @dict_iter: correct, open DBus dictionary iterator. 853 * 854 * Iterates over all properties registered with object and execute getters 855 * of those, which are readable and which interface matches interface 856 * specified as argument. Obtained properties values are stored in 857 * dict_iter dictionary. 858 */ 859 void wpa_dbus_get_object_properties(struct wpas_dbus_priv *iface, 860 const char *path, const char *interface, 861 DBusMessageIter *dict_iter) 862 { 863 struct wpa_dbus_object_desc *obj_desc = NULL; 864 865 dbus_connection_get_object_path_data(iface->con, path, 866 (void **) &obj_desc); 867 if (!obj_desc) { 868 wpa_printf(MSG_ERROR, "dbus: wpa_dbus_get_object_properties: " 869 "could not obtain object's private data: %s", path); 870 return; 871 } 872 873 fill_dict_with_properties(dict_iter, obj_desc->properties, 874 interface, obj_desc->user_data); 875 } 876