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 software may be distributed under the terms of the BSD license. 7 * See README for more details. 8 */ 9 10#include "utils/includes.h" 11 12#include "utils/common.h" 13#include "utils/eloop.h" 14#include "dbus_common.h" 15#include "dbus_common_i.h" 16#include "dbus_new.h" 17#include "dbus_new_helpers.h" 18#include "dbus_dict_helpers.h" 19 20 21static dbus_bool_t fill_dict_with_properties( 22 DBusMessageIter *dict_iter, 23 const struct wpa_dbus_property_desc *props, 24 const char *interface, void *user_data, DBusError *error) 25{ 26 DBusMessageIter entry_iter; 27 const struct wpa_dbus_property_desc *dsc; 28 29 for (dsc = props; dsc && dsc->dbus_property; dsc++) { 30 /* Only return properties for the requested D-Bus interface */ 31 if (os_strncmp(dsc->dbus_interface, interface, 32 WPAS_DBUS_INTERFACE_MAX) != 0) 33 continue; 34 35 /* Skip write-only properties */ 36 if (dsc->getter == NULL) 37 continue; 38 39 if (!dbus_message_iter_open_container(dict_iter, 40 DBUS_TYPE_DICT_ENTRY, 41 NULL, &entry_iter) || 42 !dbus_message_iter_append_basic(&entry_iter, 43 DBUS_TYPE_STRING, 44 &dsc->dbus_property)) 45 goto error; 46 47 /* An error getting a property fails the request entirely */ 48 if (!dsc->getter(&entry_iter, error, user_data)) 49 return FALSE; 50 51 if (!dbus_message_iter_close_container(dict_iter, &entry_iter)) 52 goto error; 53 } 54 55 return TRUE; 56 57error: 58 dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory"); 59 return FALSE; 60} 61 62 63/** 64 * get_all_properties - Responds for GetAll properties calls on object 65 * @message: Message with GetAll call 66 * @interface: interface name which properties will be returned 67 * @property_dsc: list of object's properties 68 * Returns: Message with dict of variants as argument with properties values 69 * 70 * Iterates over all properties registered with object and execute getters 71 * of those, which are readable and which interface matches interface 72 * specified as argument. Returned message contains one dict argument 73 * with properties names as keys and theirs values as values. 74 */ 75static DBusMessage * get_all_properties(DBusMessage *message, char *interface, 76 struct wpa_dbus_object_desc *obj_dsc) 77{ 78 DBusMessage *reply; 79 DBusMessageIter iter, dict_iter; 80 DBusError error; 81 82 reply = dbus_message_new_method_return(message); 83 if (reply == NULL) { 84 wpa_printf(MSG_ERROR, "%s: out of memory creating dbus reply", 85 __func__); 86 return NULL; 87 } 88 89 dbus_message_iter_init_append(reply, &iter); 90 if (!wpa_dbus_dict_open_write(&iter, &dict_iter)) { 91 wpa_printf(MSG_ERROR, "%s: out of memory creating reply", 92 __func__); 93 dbus_message_unref(reply); 94 reply = dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY, 95 "out of memory"); 96 return reply; 97 } 98 99 dbus_error_init(&error); 100 if (!fill_dict_with_properties(&dict_iter, obj_dsc->properties, 101 interface, obj_dsc->user_data, &error)) 102 { 103 dbus_message_unref(reply); 104 reply = wpas_dbus_reply_new_from_error(message, &error, 105 DBUS_ERROR_INVALID_ARGS, 106 "No readable properties" 107 " in this interface"); 108 dbus_error_free(&error); 109 return reply; 110 } 111 112 if (!wpa_dbus_dict_close_write(&iter, &dict_iter)) { 113 dbus_message_unref(reply); 114 return dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY, 115 "out of memory"); 116 } 117 118 return reply; 119} 120 121 122static int is_signature_correct(DBusMessage *message, 123 const struct wpa_dbus_method_desc *method_dsc) 124{ 125 /* According to DBus documentation max length of signature is 255 */ 126#define MAX_SIG_LEN 256 127 char registered_sig[MAX_SIG_LEN], *pos; 128 const char *sig = dbus_message_get_signature(message); 129 int ret; 130 const struct wpa_dbus_argument *arg; 131 132 pos = registered_sig; 133 *pos = '\0'; 134 135 for (arg = method_dsc->args; arg && arg->name; arg++) { 136 if (arg->dir == ARG_IN) { 137 size_t blen = registered_sig + MAX_SIG_LEN - pos; 138 ret = os_snprintf(pos, blen, "%s", arg->type); 139 if (ret < 0 || (size_t) ret >= blen) 140 return 0; 141 pos += ret; 142 } 143 } 144 145 return !os_strncmp(registered_sig, sig, MAX_SIG_LEN); 146} 147 148 149static DBusMessage * properties_get_all(DBusMessage *message, char *interface, 150 struct wpa_dbus_object_desc *obj_dsc) 151{ 152 if (os_strcmp(dbus_message_get_signature(message), "s") != 0) 153 return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS, 154 NULL); 155 156 return get_all_properties(message, interface, obj_dsc); 157} 158 159 160static DBusMessage * properties_get(DBusMessage *message, 161 const struct wpa_dbus_property_desc *dsc, 162 void *user_data) 163{ 164 DBusMessage *reply; 165 DBusMessageIter iter; 166 DBusError error; 167 168 if (os_strcmp(dbus_message_get_signature(message), "ss")) { 169 return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS, 170 NULL); 171 } 172 173 if (dsc->getter == NULL) { 174 return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS, 175 "Property is write-only"); 176 } 177 178 reply = dbus_message_new_method_return(message); 179 dbus_message_iter_init_append(reply, &iter); 180 181 dbus_error_init(&error); 182 if (dsc->getter(&iter, &error, user_data) == FALSE) { 183 dbus_message_unref(reply); 184 reply = wpas_dbus_reply_new_from_error( 185 message, &error, DBUS_ERROR_FAILED, 186 "Failed to read property"); 187 dbus_error_free(&error); 188 } 189 190 return reply; 191} 192 193 194static DBusMessage * properties_set(DBusMessage *message, 195 const struct wpa_dbus_property_desc *dsc, 196 void *user_data) 197{ 198 DBusMessage *reply; 199 DBusMessageIter iter; 200 DBusError error; 201 202 if (os_strcmp(dbus_message_get_signature(message), "ssv")) { 203 return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS, 204 NULL); 205 } 206 207 if (dsc->setter == NULL) { 208 return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS, 209 "Property is read-only"); 210 } 211 212 dbus_message_iter_init(message, &iter); 213 /* Skip the interface name and the property name */ 214 dbus_message_iter_next(&iter); 215 dbus_message_iter_next(&iter); 216 217 /* Iter will now point to the property's new value */ 218 dbus_error_init(&error); 219 if (dsc->setter(&iter, &error, user_data) == TRUE) { 220 /* Success */ 221 reply = dbus_message_new_method_return(message); 222 } else { 223 reply = wpas_dbus_reply_new_from_error( 224 message, &error, DBUS_ERROR_FAILED, 225 "Failed to set property"); 226 dbus_error_free(&error); 227 } 228 229 return reply; 230} 231 232 233static DBusMessage * 234properties_get_or_set(DBusMessage *message, DBusMessageIter *iter, 235 char *interface, 236 struct wpa_dbus_object_desc *obj_dsc) 237{ 238 const struct wpa_dbus_property_desc *property_dsc; 239 char *property; 240 const char *method; 241 242 method = dbus_message_get_member(message); 243 property_dsc = obj_dsc->properties; 244 245 /* Second argument: property name (DBUS_TYPE_STRING) */ 246 if (!dbus_message_iter_next(iter) || 247 dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_STRING) { 248 return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS, 249 NULL); 250 } 251 dbus_message_iter_get_basic(iter, &property); 252 253 while (property_dsc && property_dsc->dbus_property) { 254 /* compare property names and 255 * interfaces */ 256 if (!os_strncmp(property_dsc->dbus_property, property, 257 WPAS_DBUS_METHOD_SIGNAL_PROP_MAX) && 258 !os_strncmp(property_dsc->dbus_interface, interface, 259 WPAS_DBUS_INTERFACE_MAX)) 260 break; 261 262 property_dsc++; 263 } 264 if (property_dsc == NULL || property_dsc->dbus_property == NULL) { 265 wpa_printf(MSG_DEBUG, "no property handler for %s.%s on %s", 266 interface, property, 267 dbus_message_get_path(message)); 268 return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS, 269 "No such property"); 270 } 271 272 if (os_strncmp(WPA_DBUS_PROPERTIES_GET, method, 273 WPAS_DBUS_METHOD_SIGNAL_PROP_MAX) == 0) 274 return properties_get(message, property_dsc, 275 obj_dsc->user_data); 276 277 return properties_set(message, property_dsc, obj_dsc->user_data); 278} 279 280 281static DBusMessage * properties_handler(DBusMessage *message, 282 struct wpa_dbus_object_desc *obj_dsc) 283{ 284 DBusMessageIter iter; 285 char *interface; 286 const char *method; 287 288 method = dbus_message_get_member(message); 289 dbus_message_iter_init(message, &iter); 290 291 if (!os_strncmp(WPA_DBUS_PROPERTIES_GET, method, 292 WPAS_DBUS_METHOD_SIGNAL_PROP_MAX) || 293 !os_strncmp(WPA_DBUS_PROPERTIES_SET, method, 294 WPAS_DBUS_METHOD_SIGNAL_PROP_MAX) || 295 !os_strncmp(WPA_DBUS_PROPERTIES_GETALL, method, 296 WPAS_DBUS_METHOD_SIGNAL_PROP_MAX)) { 297 /* First argument: interface name (DBUS_TYPE_STRING) */ 298 if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING) 299 { 300 return dbus_message_new_error(message, 301 DBUS_ERROR_INVALID_ARGS, 302 NULL); 303 } 304 305 dbus_message_iter_get_basic(&iter, &interface); 306 307 if (!os_strncmp(WPA_DBUS_PROPERTIES_GETALL, method, 308 WPAS_DBUS_METHOD_SIGNAL_PROP_MAX)) { 309 /* GetAll */ 310 return properties_get_all(message, interface, obj_dsc); 311 } 312 /* Get or Set */ 313 return properties_get_or_set(message, &iter, interface, 314 obj_dsc); 315 } 316 return dbus_message_new_error(message, DBUS_ERROR_UNKNOWN_METHOD, 317 NULL); 318} 319 320 321static DBusMessage * msg_method_handler(DBusMessage *message, 322 struct wpa_dbus_object_desc *obj_dsc) 323{ 324 const struct wpa_dbus_method_desc *method_dsc = obj_dsc->methods; 325 const char *method; 326 const char *msg_interface; 327 328 method = dbus_message_get_member(message); 329 msg_interface = dbus_message_get_interface(message); 330 331 /* try match call to any registered method */ 332 while (method_dsc && method_dsc->dbus_method) { 333 /* compare method names and interfaces */ 334 if (!os_strncmp(method_dsc->dbus_method, method, 335 WPAS_DBUS_METHOD_SIGNAL_PROP_MAX) && 336 !os_strncmp(method_dsc->dbus_interface, msg_interface, 337 WPAS_DBUS_INTERFACE_MAX)) 338 break; 339 340 method_dsc++; 341 } 342 if (method_dsc == NULL || method_dsc->dbus_method == NULL) { 343 wpa_printf(MSG_DEBUG, "no method handler for %s.%s on %s", 344 msg_interface, method, 345 dbus_message_get_path(message)); 346 return dbus_message_new_error(message, 347 DBUS_ERROR_UNKNOWN_METHOD, NULL); 348 } 349 350 if (!is_signature_correct(message, method_dsc)) { 351 return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS, 352 NULL); 353 } 354 355 return method_dsc->method_handler(message, 356 obj_dsc->user_data); 357} 358 359 360/** 361 * message_handler - Handles incoming DBus messages 362 * @connection: DBus connection on which message was received 363 * @message: Received message 364 * @user_data: pointer to description of object to which message was sent 365 * Returns: Returns information whether message was handled or not 366 * 367 * Reads message interface and method name, then checks if they matches one 368 * of the special cases i.e. introspection call or properties get/getall/set 369 * methods and handles it. Else it iterates over registered methods list 370 * and tries to match method's name and interface to those read from message 371 * If appropriate method was found its handler function is called and 372 * response is sent. Otherwise, the DBUS_ERROR_UNKNOWN_METHOD error message 373 * will be sent. 374 */ 375static DBusHandlerResult message_handler(DBusConnection *connection, 376 DBusMessage *message, void *user_data) 377{ 378 struct wpa_dbus_object_desc *obj_dsc = user_data; 379 const char *method; 380 const char *path; 381 const char *msg_interface; 382 DBusMessage *reply; 383 384 /* get method, interface and path the message is addressed to */ 385 method = dbus_message_get_member(message); 386 path = dbus_message_get_path(message); 387 msg_interface = dbus_message_get_interface(message); 388 if (!method || !path || !msg_interface) 389 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; 390 391 wpa_printf(MSG_MSGDUMP, "dbus: %s.%s (%s)", 392 msg_interface, method, path); 393 394 /* if message is introspection method call */ 395 if (!os_strncmp(WPA_DBUS_INTROSPECTION_METHOD, method, 396 WPAS_DBUS_METHOD_SIGNAL_PROP_MAX) && 397 !os_strncmp(WPA_DBUS_INTROSPECTION_INTERFACE, msg_interface, 398 WPAS_DBUS_INTERFACE_MAX)) { 399#ifdef CONFIG_CTRL_IFACE_DBUS_INTRO 400 reply = wpa_dbus_introspect(message, obj_dsc); 401#else /* CONFIG_CTRL_IFACE_DBUS_INTRO */ 402 reply = dbus_message_new_error( 403 message, DBUS_ERROR_UNKNOWN_METHOD, 404 "wpa_supplicant was compiled without " 405 "introspection support."); 406#endif /* CONFIG_CTRL_IFACE_DBUS_INTRO */ 407 } else if (!os_strncmp(WPA_DBUS_PROPERTIES_INTERFACE, msg_interface, 408 WPAS_DBUS_INTERFACE_MAX)) { 409 /* if message is properties method call */ 410 reply = properties_handler(message, obj_dsc); 411 } else { 412 reply = msg_method_handler(message, obj_dsc); 413 } 414 415 /* If handler succeed returning NULL, reply empty message */ 416 if (!reply) 417 reply = dbus_message_new_method_return(message); 418 if (reply) { 419 if (!dbus_message_get_no_reply(message)) 420 dbus_connection_send(connection, reply, NULL); 421 dbus_message_unref(reply); 422 } 423 424 wpa_dbus_flush_all_changed_properties(connection); 425 426 return DBUS_HANDLER_RESULT_HANDLED; 427} 428 429 430/** 431 * free_dbus_object_desc - Frees object description data structure 432 * @connection: DBus connection 433 * @obj_dsc: Object description to free 434 * 435 * Frees each of properties, methods and signals description lists and 436 * the object description structure itself. 437 */ 438void free_dbus_object_desc(struct wpa_dbus_object_desc *obj_dsc) 439{ 440 if (!obj_dsc) 441 return; 442 443 /* free handler's argument */ 444 if (obj_dsc->user_data_free_func) 445 obj_dsc->user_data_free_func(obj_dsc->user_data); 446 447 os_free(obj_dsc->path); 448 os_free(obj_dsc->prop_changed_flags); 449 os_free(obj_dsc); 450} 451 452 453static void free_dbus_object_desc_cb(DBusConnection *connection, void *obj_dsc) 454{ 455 free_dbus_object_desc(obj_dsc); 456} 457 458/** 459 * wpa_dbus_ctrl_iface_init - Initialize dbus control interface 460 * @application_data: Pointer to application specific data structure 461 * @dbus_path: DBus path to interface object 462 * @dbus_service: DBus service name to register with 463 * @messageHandler: a pointer to function which will handle dbus messages 464 * coming on interface 465 * Returns: 0 on success, -1 on failure 466 * 467 * Initialize the dbus control interface and start receiving commands from 468 * external programs over the bus. 469 */ 470int wpa_dbus_ctrl_iface_init(struct wpas_dbus_priv *iface, 471 char *dbus_path, char *dbus_service, 472 struct wpa_dbus_object_desc *obj_desc) 473{ 474 DBusError error; 475 int ret = -1; 476 DBusObjectPathVTable wpa_vtable = { 477 &free_dbus_object_desc_cb, &message_handler, 478 NULL, NULL, NULL, NULL 479 }; 480 481 obj_desc->connection = iface->con; 482 obj_desc->path = os_strdup(dbus_path); 483 484 /* Register the message handler for the global dbus interface */ 485 if (!dbus_connection_register_object_path(iface->con, 486 dbus_path, &wpa_vtable, 487 obj_desc)) { 488 wpa_printf(MSG_ERROR, "dbus: Could not set up message " 489 "handler"); 490 return -1; 491 } 492 493 /* Register our service with the message bus */ 494 dbus_error_init(&error); 495 switch (dbus_bus_request_name(iface->con, dbus_service, 496 0, &error)) { 497 case DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER: 498 ret = 0; 499 break; 500 case DBUS_REQUEST_NAME_REPLY_EXISTS: 501 case DBUS_REQUEST_NAME_REPLY_IN_QUEUE: 502 case DBUS_REQUEST_NAME_REPLY_ALREADY_OWNER: 503 wpa_printf(MSG_ERROR, "dbus: Could not request service name: " 504 "already registered"); 505 break; 506 default: 507 wpa_printf(MSG_ERROR, "dbus: Could not request service name: " 508 "%s %s", error.name, error.message); 509 break; 510 } 511 dbus_error_free(&error); 512 513 if (ret != 0) 514 return -1; 515 516 wpa_printf(MSG_DEBUG, "Providing DBus service '%s'.", dbus_service); 517 518 return 0; 519} 520 521 522/** 523 * wpa_dbus_register_object_per_iface - Register a new object with dbus 524 * @ctrl_iface: pointer to dbus private data 525 * @path: DBus path to object 526 * @ifname: interface name 527 * @obj_desc: description of object's methods, signals and properties 528 * Returns: 0 on success, -1 on error 529 * 530 * Registers a new interface with dbus and assigns it a dbus object path. 531 */ 532int wpa_dbus_register_object_per_iface( 533 struct wpas_dbus_priv *ctrl_iface, 534 const char *path, const char *ifname, 535 struct wpa_dbus_object_desc *obj_desc) 536{ 537 DBusConnection *con; 538 DBusError error; 539 540 DBusObjectPathVTable vtable = { 541 &free_dbus_object_desc_cb, &message_handler, 542 NULL, NULL, NULL, NULL 543 }; 544 545 /* Do nothing if the control interface is not turned on */ 546 if (ctrl_iface == NULL) 547 return 0; 548 549 con = ctrl_iface->con; 550 obj_desc->connection = con; 551 obj_desc->path = os_strdup(path); 552 553 dbus_error_init(&error); 554 /* Register the message handler for the interface functions */ 555 if (!dbus_connection_try_register_object_path(con, path, &vtable, 556 obj_desc, &error)) { 557 if (!os_strcmp(error.name, DBUS_ERROR_OBJECT_PATH_IN_USE)) { 558 wpa_printf(MSG_DEBUG, "dbus: %s", error.message); 559 } else { 560 wpa_printf(MSG_ERROR, "dbus: Could not set up message " 561 "handler for interface %s object %s", 562 ifname, path); 563 wpa_printf(MSG_ERROR, "dbus error: %s", error.name); 564 wpa_printf(MSG_ERROR, "dbus: %s", error.message); 565 } 566 dbus_error_free(&error); 567 return -1; 568 } 569 570 dbus_error_free(&error); 571 return 0; 572} 573 574 575static void flush_object_timeout_handler(void *eloop_ctx, void *timeout_ctx); 576 577 578/** 579 * wpa_dbus_unregister_object_per_iface - Unregisters DBus object 580 * @ctrl_iface: Pointer to dbus private data 581 * @path: DBus path to object which will be unregistered 582 * Returns: Zero on success and -1 on failure 583 * 584 * Unregisters DBus object given by its path 585 */ 586int wpa_dbus_unregister_object_per_iface( 587 struct wpas_dbus_priv *ctrl_iface, const char *path) 588{ 589 DBusConnection *con = ctrl_iface->con; 590 struct wpa_dbus_object_desc *obj_desc = NULL; 591 592 dbus_connection_get_object_path_data(con, path, (void **) &obj_desc); 593 if (!obj_desc) { 594 wpa_printf(MSG_ERROR, "dbus: %s: Could not obtain object's " 595 "private data: %s", __func__, path); 596 return 0; 597 } 598 599 eloop_cancel_timeout(flush_object_timeout_handler, con, obj_desc); 600 601 if (!dbus_connection_unregister_object_path(con, path)) 602 return -1; 603 604 return 0; 605} 606 607 608static dbus_bool_t put_changed_properties( 609 const struct wpa_dbus_object_desc *obj_dsc, const char *interface, 610 DBusMessageIter *dict_iter, int clear_changed) 611{ 612 DBusMessageIter entry_iter; 613 const struct wpa_dbus_property_desc *dsc; 614 int i; 615 DBusError error; 616 617 for (dsc = obj_dsc->properties, i = 0; dsc && dsc->dbus_property; 618 dsc++, i++) { 619 if (obj_dsc->prop_changed_flags == NULL || 620 !obj_dsc->prop_changed_flags[i]) 621 continue; 622 if (os_strcmp(dsc->dbus_interface, interface) != 0) 623 continue; 624 if (clear_changed) 625 obj_dsc->prop_changed_flags[i] = 0; 626 627 if (!dbus_message_iter_open_container(dict_iter, 628 DBUS_TYPE_DICT_ENTRY, 629 NULL, &entry_iter)) 630 return FALSE; 631 632 if (!dbus_message_iter_append_basic(&entry_iter, 633 DBUS_TYPE_STRING, 634 &dsc->dbus_property)) 635 return FALSE; 636 637 dbus_error_init(&error); 638 if (!dsc->getter(&entry_iter, &error, obj_dsc->user_data)) { 639 if (dbus_error_is_set (&error)) { 640 wpa_printf(MSG_ERROR, "dbus: %s: Cannot get " 641 "new value of property %s: (%s) %s", 642 __func__, dsc->dbus_property, 643 error.name, error.message); 644 } else { 645 wpa_printf(MSG_ERROR, "dbus: %s: Cannot get " 646 "new value of property %s", 647 __func__, dsc->dbus_property); 648 } 649 dbus_error_free(&error); 650 return FALSE; 651 } 652 653 if (!dbus_message_iter_close_container(dict_iter, &entry_iter)) 654 return FALSE; 655 } 656 657 return TRUE; 658} 659 660 661static void do_send_prop_changed_signal( 662 DBusConnection *con, const char *path, const char *interface, 663 const struct wpa_dbus_object_desc *obj_dsc) 664{ 665 DBusMessage *msg; 666 DBusMessageIter signal_iter, dict_iter; 667 668 msg = dbus_message_new_signal(path, DBUS_INTERFACE_PROPERTIES, 669 "PropertiesChanged"); 670 if (msg == NULL) 671 return; 672 673 dbus_message_iter_init_append(msg, &signal_iter); 674 675 if (!dbus_message_iter_append_basic(&signal_iter, DBUS_TYPE_STRING, 676 &interface)) 677 goto err; 678 679 /* Changed properties dict */ 680 if (!dbus_message_iter_open_container(&signal_iter, DBUS_TYPE_ARRAY, 681 "{sv}", &dict_iter)) 682 goto err; 683 684 if (!put_changed_properties(obj_dsc, interface, &dict_iter, 0)) 685 goto err; 686 687 if (!dbus_message_iter_close_container(&signal_iter, &dict_iter)) 688 goto err; 689 690 /* Invalidated properties array (empty) */ 691 if (!dbus_message_iter_open_container(&signal_iter, DBUS_TYPE_ARRAY, 692 "s", &dict_iter)) 693 goto err; 694 695 if (!dbus_message_iter_close_container(&signal_iter, &dict_iter)) 696 goto err; 697 698 dbus_connection_send(con, msg, NULL); 699 700out: 701 dbus_message_unref(msg); 702 return; 703 704err: 705 wpa_printf(MSG_DEBUG, "dbus: %s: Failed to construct signal", 706 __func__); 707 goto out; 708} 709 710 711static void do_send_deprecated_prop_changed_signal( 712 DBusConnection *con, const char *path, const char *interface, 713 const struct wpa_dbus_object_desc *obj_dsc) 714{ 715 DBusMessage *msg; 716 DBusMessageIter signal_iter, dict_iter; 717 718 msg = dbus_message_new_signal(path, interface, "PropertiesChanged"); 719 if (msg == NULL) 720 return; 721 722 dbus_message_iter_init_append(msg, &signal_iter); 723 724 if (!dbus_message_iter_open_container(&signal_iter, DBUS_TYPE_ARRAY, 725 "{sv}", &dict_iter)) 726 goto err; 727 728 if (!put_changed_properties(obj_dsc, interface, &dict_iter, 1)) 729 goto err; 730 731 if (!dbus_message_iter_close_container(&signal_iter, &dict_iter)) 732 goto err; 733 734 dbus_connection_send(con, msg, NULL); 735 736out: 737 dbus_message_unref(msg); 738 return; 739 740err: 741 wpa_printf(MSG_DEBUG, "dbus: %s: Failed to construct signal", 742 __func__); 743 goto out; 744} 745 746 747static void send_prop_changed_signal( 748 DBusConnection *con, const char *path, const char *interface, 749 const struct wpa_dbus_object_desc *obj_dsc) 750{ 751 /* 752 * First, send property change notification on the standardized 753 * org.freedesktop.DBus.Properties interface. This call will not 754 * clear the property change bits, so that they are preserved for 755 * the call that follows. 756 */ 757 do_send_prop_changed_signal(con, path, interface, obj_dsc); 758 759 /* 760 * Now send PropertiesChanged on our own interface for backwards 761 * compatibility. This is deprecated and will be removed in a future 762 * release. 763 */ 764 do_send_deprecated_prop_changed_signal(con, path, interface, obj_dsc); 765 766 /* Property change bits have now been cleared. */ 767} 768 769 770static void flush_object_timeout_handler(void *eloop_ctx, void *timeout_ctx) 771{ 772 DBusConnection *con = eloop_ctx; 773 struct wpa_dbus_object_desc *obj_desc = timeout_ctx; 774 775 wpa_printf(MSG_DEBUG, "dbus: %s: Timeout - sending changed properties " 776 "of object %s", __func__, obj_desc->path); 777 wpa_dbus_flush_object_changed_properties(con, obj_desc->path); 778} 779 780 781static void recursive_flush_changed_properties(DBusConnection *con, 782 const char *path) 783{ 784 char **objects = NULL; 785 char subobj_path[WPAS_DBUS_OBJECT_PATH_MAX]; 786 int i; 787 788 wpa_dbus_flush_object_changed_properties(con, path); 789 790 if (!dbus_connection_list_registered(con, path, &objects)) 791 goto out; 792 793 for (i = 0; objects[i]; i++) { 794 os_snprintf(subobj_path, WPAS_DBUS_OBJECT_PATH_MAX, 795 "%s/%s", path, objects[i]); 796 recursive_flush_changed_properties(con, subobj_path); 797 } 798 799out: 800 dbus_free_string_array(objects); 801} 802 803 804/** 805 * wpa_dbus_flush_all_changed_properties - Send all PropertiesChanged signals 806 * @con: DBus connection 807 * 808 * Traverses through all registered objects and sends PropertiesChanged for 809 * each properties. 810 */ 811void wpa_dbus_flush_all_changed_properties(DBusConnection *con) 812{ 813 recursive_flush_changed_properties(con, WPAS_DBUS_NEW_PATH); 814} 815 816 817/** 818 * wpa_dbus_flush_object_changed_properties - Send PropertiesChanged for object 819 * @con: DBus connection 820 * @path: path to a DBus object for which PropertiesChanged will be sent. 821 * 822 * Iterates over all properties registered with object and for each interface 823 * containing properties marked as changed, sends a PropertiesChanged signal 824 * containing names and new values of properties that have changed. 825 * 826 * You need to call this function after wpa_dbus_mark_property_changed() 827 * if you want to send PropertiesChanged signal immediately (i.e., without 828 * waiting timeout to expire). PropertiesChanged signal for an object is sent 829 * automatically short time after first marking property as changed. All 830 * PropertiesChanged signals are sent automatically after responding on DBus 831 * message, so if you marked a property changed as a result of DBus call 832 * (e.g., param setter), you usually do not need to call this function. 833 */ 834void wpa_dbus_flush_object_changed_properties(DBusConnection *con, 835 const char *path) 836{ 837 struct wpa_dbus_object_desc *obj_desc = NULL; 838 const struct wpa_dbus_property_desc *dsc; 839 int i; 840 841 dbus_connection_get_object_path_data(con, path, (void **) &obj_desc); 842 if (!obj_desc) 843 return; 844 eloop_cancel_timeout(flush_object_timeout_handler, con, obj_desc); 845 846 for (dsc = obj_desc->properties, i = 0; dsc && dsc->dbus_property; 847 dsc++, i++) { 848 if (obj_desc->prop_changed_flags == NULL || 849 !obj_desc->prop_changed_flags[i]) 850 continue; 851 send_prop_changed_signal(con, path, dsc->dbus_interface, 852 obj_desc); 853 } 854} 855 856 857#define WPA_DBUS_SEND_PROP_CHANGED_TIMEOUT 5000 858 859 860/** 861 * wpa_dbus_mark_property_changed - Mark a property as changed and 862 * @iface: dbus priv struct 863 * @path: path to DBus object which property has changed 864 * @interface: interface containing changed property 865 * @property: property name which has changed 866 * 867 * Iterates over all properties registered with an object and marks the one 868 * given in parameters as changed. All parameters registered for an object 869 * within a single interface will be aggregated together and sent in one 870 * PropertiesChanged signal when function 871 * wpa_dbus_flush_object_changed_properties() is called. 872 */ 873void wpa_dbus_mark_property_changed(struct wpas_dbus_priv *iface, 874 const char *path, const char *interface, 875 const char *property) 876{ 877 struct wpa_dbus_object_desc *obj_desc = NULL; 878 const struct wpa_dbus_property_desc *dsc; 879 int i = 0; 880 881 if (iface == NULL) 882 return; 883 884 dbus_connection_get_object_path_data(iface->con, path, 885 (void **) &obj_desc); 886 if (!obj_desc) { 887 wpa_printf(MSG_ERROR, "dbus: wpa_dbus_property_changed: " 888 "could not obtain object's private data: %s", path); 889 return; 890 } 891 892 for (dsc = obj_desc->properties; dsc && dsc->dbus_property; dsc++, i++) 893 if (os_strcmp(property, dsc->dbus_property) == 0 && 894 os_strcmp(interface, dsc->dbus_interface) == 0) { 895 if (obj_desc->prop_changed_flags) 896 obj_desc->prop_changed_flags[i] = 1; 897 break; 898 } 899 900 if (!dsc || !dsc->dbus_property) { 901 wpa_printf(MSG_ERROR, "dbus: wpa_dbus_property_changed: " 902 "no property %s in object %s", property, path); 903 return; 904 } 905 906 if (!eloop_is_timeout_registered(flush_object_timeout_handler, 907 iface->con, obj_desc->path)) { 908 eloop_register_timeout(0, WPA_DBUS_SEND_PROP_CHANGED_TIMEOUT, 909 flush_object_timeout_handler, 910 iface->con, obj_desc); 911 } 912} 913 914 915/** 916 * wpa_dbus_get_object_properties - Put object's properties into dictionary 917 * @iface: dbus priv struct 918 * @path: path to DBus object which properties will be obtained 919 * @interface: interface name which properties will be obtained 920 * @iter: DBus message iter at which to append property dictionary. 921 * 922 * Iterates over all properties registered with object and execute getters 923 * of those, which are readable and which interface matches interface 924 * specified as argument. Obtained properties values are stored in 925 * dict_iter dictionary. 926 */ 927dbus_bool_t wpa_dbus_get_object_properties(struct wpas_dbus_priv *iface, 928 const char *path, 929 const char *interface, 930 DBusMessageIter *iter) 931{ 932 struct wpa_dbus_object_desc *obj_desc = NULL; 933 DBusMessageIter dict_iter; 934 DBusError error; 935 936 dbus_connection_get_object_path_data(iface->con, path, 937 (void **) &obj_desc); 938 if (!obj_desc) { 939 wpa_printf(MSG_ERROR, "dbus: %s: could not obtain object's " 940 "private data: %s", __func__, path); 941 return FALSE; 942 } 943 944 if (!wpa_dbus_dict_open_write(iter, &dict_iter)) { 945 wpa_printf(MSG_ERROR, "dbus: %s: failed to open message dict", 946 __func__); 947 return FALSE; 948 } 949 950 dbus_error_init(&error); 951 if (!fill_dict_with_properties(&dict_iter, obj_desc->properties, 952 interface, obj_desc->user_data, 953 &error)) { 954 wpa_printf(MSG_ERROR, "dbus: %s: failed to get object" 955 " properties: (%s) %s", __func__, 956 dbus_error_is_set(&error) ? error.name : "none", 957 dbus_error_is_set(&error) ? error.message : "none"); 958 dbus_error_free(&error); 959 return FALSE; 960 } 961 962 return wpa_dbus_dict_close_write(iter, &dict_iter); 963} 964 965/** 966 * wpas_dbus_new_decompose_object_path - Decompose an interface object path into parts 967 * @path: The dbus object path 968 * @p2p_persistent_group: indicates whether to parse the path as a P2P 969 * persistent group object 970 * @network: (out) the configured network this object path refers to, if any 971 * @bssid: (out) the scanned bssid this object path refers to, if any 972 * Returns: The object path of the network interface this path refers to 973 * 974 * For a given object path, decomposes the object path into object id, network, 975 * and BSSID parts, if those parts exist. 976 */ 977char *wpas_dbus_new_decompose_object_path(const char *path, 978 int p2p_persistent_group, 979 char **network, 980 char **bssid) 981{ 982 const unsigned int dev_path_prefix_len = 983 os_strlen(WPAS_DBUS_NEW_PATH_INTERFACES "/"); 984 char *obj_path_only; 985 char *next_sep; 986 987 /* Be a bit paranoid about path */ 988 if (!path || os_strncmp(path, WPAS_DBUS_NEW_PATH_INTERFACES "/", 989 dev_path_prefix_len)) 990 return NULL; 991 992 /* Ensure there's something at the end of the path */ 993 if ((path + dev_path_prefix_len)[0] == '\0') 994 return NULL; 995 996 obj_path_only = os_strdup(path); 997 if (obj_path_only == NULL) 998 return NULL; 999 1000 next_sep = os_strchr(obj_path_only + dev_path_prefix_len, '/'); 1001 if (next_sep != NULL) { 1002 const char *net_part = os_strstr( 1003 next_sep, p2p_persistent_group ? 1004 WPAS_DBUS_NEW_PERSISTENT_GROUPS_PART "/" : 1005 WPAS_DBUS_NEW_NETWORKS_PART "/"); 1006 const char *bssid_part = os_strstr( 1007 next_sep, WPAS_DBUS_NEW_BSSIDS_PART "/"); 1008 1009 if (network && net_part) { 1010 /* Deal with a request for a configured network */ 1011 const char *net_name = net_part + 1012 os_strlen(p2p_persistent_group ? 1013 WPAS_DBUS_NEW_PERSISTENT_GROUPS_PART 1014 "/" : 1015 WPAS_DBUS_NEW_NETWORKS_PART "/"); 1016 *network = NULL; 1017 if (os_strlen(net_name)) 1018 *network = os_strdup(net_name); 1019 } else if (bssid && bssid_part) { 1020 /* Deal with a request for a scanned BSSID */ 1021 const char *bssid_name = bssid_part + 1022 os_strlen(WPAS_DBUS_NEW_BSSIDS_PART "/"); 1023 if (os_strlen(bssid_name)) 1024 *bssid = os_strdup(bssid_name); 1025 else 1026 *bssid = NULL; 1027 } 1028 1029 /* Cut off interface object path before "/" */ 1030 *next_sep = '\0'; 1031 } 1032 1033 return obj_path_only; 1034} 1035 1036 1037/** 1038 * wpas_dbus_reply_new_from_error - Create a new D-Bus error message from a 1039 * dbus error structure 1040 * @message: The original request message for which the error is a reply 1041 * @error: The error containing a name and a descriptive error cause 1042 * @fallback_name: A generic error name if @error was not set 1043 * @fallback_string: A generic error string if @error was not set 1044 * Returns: A new D-Bus error message 1045 * 1046 * Given a DBusMessage structure, creates a new D-Bus error message using 1047 * the error name and string contained in that structure. 1048 */ 1049DBusMessage * wpas_dbus_reply_new_from_error(DBusMessage *message, 1050 DBusError *error, 1051 const char *fallback_name, 1052 const char *fallback_string) 1053{ 1054 if (error && error->name && error->message) { 1055 return dbus_message_new_error(message, error->name, 1056 error->message); 1057 } 1058 if (fallback_name && fallback_string) { 1059 return dbus_message_new_error(message, fallback_name, 1060 fallback_string); 1061 } 1062 return NULL; 1063} 1064