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