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