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