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