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