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