1/* 2 * WPA Supplicant / dbus-based control interface 3 * Copyright (c) 2006, Dan Williams <dcbw@redhat.com> and Red Hat, Inc. 4 * Copyright (c) 2009, Witold Sowa <witold.sowa@gmail.com> 5 * |
6 * This software may be distributed under the terms of the BSD license. 7 * See README for more details. |
8 */ 9 10#include "utils/includes.h" 11 12#include "utils/common.h" 13#include "utils/eloop.h" 14#include "dbus_common.h" 15#include "dbus_common_i.h" 16#include "dbus_new.h" 17#include "dbus_new_helpers.h" |
18#include "dbus_dict_helpers.h" |
19 20 |
21static dbus_bool_t fill_dict_with_properties( 22 DBusMessageIter *dict_iter, 23 const struct wpa_dbus_property_desc *props, 24 const char *interface, void *user_data, DBusError *error) |
25{ |
26 DBusMessageIter entry_iter; |
27 const struct wpa_dbus_property_desc *dsc; 28 29 for (dsc = props; dsc && dsc->dbus_property; dsc++) { |
30 /* Only return properties for the requested D-Bus interface */ 31 if (os_strncmp(dsc->dbus_interface, interface, 32 WPAS_DBUS_INTERFACE_MAX) != 0) 33 continue; |
34 |
35 /* Skip write-only properties */ 36 if (dsc->getter == NULL) 37 continue; |
38 |
39 if (!dbus_message_iter_open_container(dict_iter, 40 DBUS_TYPE_DICT_ENTRY, 41 NULL, &entry_iter)) { 42 dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, 43 "no memory"); 44 return FALSE; 45 } 46 if (!dbus_message_iter_append_basic(&entry_iter, 47 DBUS_TYPE_STRING, 48 &dsc->dbus_property)) { 49 dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, 50 "no memory"); 51 return FALSE; 52 } |
53 |
54 /* An error getting a property fails the request entirely */ 55 if (!dsc->getter(&entry_iter, error, user_data)) 56 return FALSE; |
57 |
58 dbus_message_iter_close_container(dict_iter, &entry_iter); |
59 } 60 |
61 return TRUE; |
62} 63 64 65/** 66 * get_all_properties - Responds for GetAll properties calls on object 67 * @message: Message with GetAll call 68 * @interface: interface name which properties will be returned 69 * @property_dsc: list of object's properties 70 * Returns: Message with dict of variants as argument with properties values 71 * 72 * Iterates over all properties registered with object and execute getters 73 * of those, which are readable and which interface matches interface 74 * specified as argument. Returned message contains one dict argument 75 * with properties names as keys and theirs values as values. 76 */ |
77static DBusMessage * get_all_properties(DBusMessage *message, char *interface, 78 struct wpa_dbus_object_desc *obj_dsc) |
79{ |
80 DBusMessage *reply; |
81 DBusMessageIter iter, dict_iter; |
82 DBusError error; |
83 |
84 reply = dbus_message_new_method_return(message); 85 if (reply == NULL) { 86 wpa_printf(MSG_ERROR, "%s: out of memory creating dbus reply", 87 __func__); 88 return NULL; 89 } 90 |
91 dbus_message_iter_init_append(reply, &iter); |
92 if (!wpa_dbus_dict_open_write(&iter, &dict_iter)) { 93 wpa_printf(MSG_ERROR, "%s: out of memory creating reply", 94 __func__); 95 dbus_message_unref(reply); 96 reply = dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY, 97 "out of memory"); 98 return reply; 99 } |
100 |
101 dbus_error_init(&error); 102 if (!fill_dict_with_properties(&dict_iter, obj_dsc->properties, 103 interface, obj_dsc->user_data, &error)) 104 { |
105 dbus_message_unref(reply); |
106 reply = wpas_dbus_reply_new_from_error(message, &error, 107 DBUS_ERROR_INVALID_ARGS, 108 "No readable properties" 109 " in this interface"); 110 dbus_error_free(&error); 111 return reply; |
112 } 113 |
114 wpa_dbus_dict_close_write(&iter, &dict_iter); |
115 return reply; 116} 117 118 119static 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 */ --- 30 unchanged lines hidden (view full) --- 153 return get_all_properties(message, interface, obj_dsc); 154} 155 156 157static 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 191static 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 230static DBusMessage * 231properties_get_or_set(DBusMessage *message, DBusMessageIter *iter, 232 char *interface, 233 struct wpa_dbus_object_desc *obj_dsc) 234{ --- 292 unchanged lines hidden (view full) --- 527 * Registers a new interface with dbus and assigns it a dbus object path. 528 */ 529int wpa_dbus_register_object_per_iface( 530 struct wpas_dbus_priv *ctrl_iface, 531 const char *path, const char *ifname, 532 struct wpa_dbus_object_desc *obj_desc) 533{ 534 DBusConnection *con; |
535 DBusError error; |
536 537 DBusObjectPathVTable vtable = { 538 &free_dbus_object_desc_cb, &message_handler, 539 NULL, NULL, NULL, NULL 540 }; 541 542 /* Do nothing if the control interface is not turned on */ 543 if (ctrl_iface == NULL) 544 return 0; 545 546 con = ctrl_iface->con; 547 obj_desc->connection = con; 548 obj_desc->path = os_strdup(path); 549 |
550 dbus_error_init(&error); |
551 /* Register the message handler for the interface functions */ |
552 if (!dbus_connection_try_register_object_path(con, path, &vtable, 553 obj_desc, &error)) { 554 if (!os_strcmp(error.name, DBUS_ERROR_OBJECT_PATH_IN_USE)) { 555 wpa_printf(MSG_DEBUG, "dbus: %s", error.message); 556 } else { 557 wpa_printf(MSG_ERROR, "dbus: Could not set up message " 558 "handler for interface %s object %s", 559 ifname, path); 560 wpa_printf(MSG_ERROR, "dbus error: %s", error.name); 561 wpa_printf(MSG_ERROR, "dbus: %s", error.message); 562 } 563 dbus_error_free(&error); |
564 return -1; 565 } 566 |
567 dbus_error_free(&error); |
568 return 0; 569} 570 571 572static void flush_object_timeout_handler(void *eloop_ctx, void *timeout_ctx); 573 574 575/** --- 21 unchanged lines hidden (view full) --- 597 598 if (!dbus_connection_unregister_object_path(con, path)) 599 return -1; 600 601 return 0; 602} 603 604 |
605static dbus_bool_t put_changed_properties( 606 const struct wpa_dbus_object_desc *obj_dsc, const char *interface, 607 DBusMessageIter *dict_iter, int clear_changed) |
608{ |
609 DBusMessageIter entry_iter; |
610 const struct wpa_dbus_property_desc *dsc; 611 int i; |
612 DBusError error; |
613 614 for (dsc = obj_dsc->properties, i = 0; dsc && dsc->dbus_property; 615 dsc++, i++) { 616 if (obj_dsc->prop_changed_flags == NULL || 617 !obj_dsc->prop_changed_flags[i]) 618 continue; 619 if (os_strcmp(dsc->dbus_interface, interface) != 0) 620 continue; |
621 if (clear_changed) 622 obj_dsc->prop_changed_flags[i] = 0; |
623 |
624 if (!dbus_message_iter_open_container(dict_iter, |
625 DBUS_TYPE_DICT_ENTRY, |
626 NULL, &entry_iter)) 627 return FALSE; 628 629 if (!dbus_message_iter_append_basic(&entry_iter, |
630 DBUS_TYPE_STRING, 631 &dsc->dbus_property)) |
632 return FALSE; |
633 |
634 dbus_error_init(&error); 635 if (!dsc->getter(&entry_iter, &error, obj_dsc->user_data)) { 636 if (dbus_error_is_set (&error)) { 637 wpa_printf(MSG_ERROR, "dbus: %s: Cannot get " 638 "new value of property %s: (%s) %s", 639 __func__, dsc->dbus_property, 640 error.name, error.message); 641 } else { 642 wpa_printf(MSG_ERROR, "dbus: %s: Cannot get " 643 "new value of property %s", 644 __func__, dsc->dbus_property); 645 } 646 dbus_error_free(&error); 647 return FALSE; 648 } |
649 650 if (!dbus_message_iter_close_container(dict_iter, &entry_iter)) |
651 return FALSE; |
652 } 653 |
654 return TRUE; 655} 656 657 658static void do_send_prop_changed_signal( 659 DBusConnection *con, const char *path, const char *interface, 660 const struct wpa_dbus_object_desc *obj_dsc) 661{ 662 DBusMessage *msg; 663 DBusMessageIter signal_iter, dict_iter; 664 665 msg = dbus_message_new_signal(path, DBUS_INTERFACE_PROPERTIES, 666 "PropertiesChanged"); 667 if (msg == NULL) 668 return; 669 670 dbus_message_iter_init_append(msg, &signal_iter); 671 672 if (!dbus_message_iter_append_basic(&signal_iter, DBUS_TYPE_STRING, 673 &interface)) 674 goto err; 675 676 /* Changed properties dict */ 677 if (!dbus_message_iter_open_container(&signal_iter, DBUS_TYPE_ARRAY, 678 "{sv}", &dict_iter)) 679 goto err; 680 681 if (!put_changed_properties(obj_dsc, interface, &dict_iter, 0)) 682 goto err; 683 684 if (!dbus_message_iter_close_container(&signal_iter, &dict_iter)) 685 goto err; 686 687 /* Invalidated properties array (empty) */ 688 if (!dbus_message_iter_open_container(&signal_iter, DBUS_TYPE_ARRAY, 689 "s", &dict_iter)) 690 goto err; 691 692 if (!dbus_message_iter_close_container(&signal_iter, &dict_iter)) 693 goto err; 694 695 dbus_connection_send(con, msg, NULL); 696 697out: 698 dbus_message_unref(msg); |
699 return; 700 701err: |
702 wpa_printf(MSG_DEBUG, "dbus: %s: Failed to construct signal", 703 __func__); 704 goto out; |
705} 706 707 |
708static void do_send_deprecated_prop_changed_signal( |
709 DBusConnection *con, const char *path, const char *interface, 710 const struct wpa_dbus_object_desc *obj_dsc) 711{ 712 DBusMessage *msg; 713 DBusMessageIter signal_iter, dict_iter; 714 715 msg = dbus_message_new_signal(path, interface, "PropertiesChanged"); 716 if (msg == NULL) 717 return; 718 719 dbus_message_iter_init_append(msg, &signal_iter); 720 721 if (!dbus_message_iter_open_container(&signal_iter, DBUS_TYPE_ARRAY, 722 "{sv}", &dict_iter)) 723 goto err; 724 |
725 if (!put_changed_properties(obj_dsc, interface, &dict_iter, 1)) 726 goto err; |
727 728 if (!dbus_message_iter_close_container(&signal_iter, &dict_iter)) 729 goto err; 730 731 dbus_connection_send(con, msg, NULL); 732 733out: 734 dbus_message_unref(msg); 735 return; 736 737err: 738 wpa_printf(MSG_DEBUG, "dbus: %s: Failed to construct signal", 739 __func__); 740 goto out; 741} 742 743 |
744static void send_prop_changed_signal( 745 DBusConnection *con, const char *path, const char *interface, 746 const struct wpa_dbus_object_desc *obj_dsc) 747{ 748 /* 749 * First, send property change notification on the standardized 750 * org.freedesktop.DBus.Properties interface. This call will not 751 * clear the property change bits, so that they are preserved for 752 * the call that follows. 753 */ 754 do_send_prop_changed_signal(con, path, interface, obj_dsc); 755 756 /* 757 * Now send PropertiesChanged on our own interface for backwards 758 * compatibility. This is deprecated and will be removed in a future 759 * release. 760 */ 761 do_send_deprecated_prop_changed_signal(con, path, interface, obj_dsc); 762 763 /* Property change bits have now been cleared. */ 764} 765 766 |
767static void flush_object_timeout_handler(void *eloop_ctx, void *timeout_ctx) 768{ 769 DBusConnection *con = eloop_ctx; 770 struct wpa_dbus_object_desc *obj_desc = timeout_ctx; 771 772 wpa_printf(MSG_DEBUG, "dbus: %s: Timeout - sending changed properties " 773 "of object %s", __func__, obj_desc->path); 774 wpa_dbus_flush_object_changed_properties(con, obj_desc->path); --- 135 unchanged lines hidden (view full) --- 910} 911 912 913/** 914 * wpa_dbus_get_object_properties - Put object's properties into dictionary 915 * @iface: dbus priv struct 916 * @path: path to DBus object which properties will be obtained 917 * @interface: interface name which properties will be obtained |
918 * @iter: DBus message iter at which to append property dictionary. |
919 * 920 * Iterates over all properties registered with object and execute getters 921 * of those, which are readable and which interface matches interface 922 * specified as argument. Obtained properties values are stored in 923 * dict_iter dictionary. 924 */ |
925dbus_bool_t wpa_dbus_get_object_properties(struct wpas_dbus_priv *iface, 926 const char *path, 927 const char *interface, 928 DBusMessageIter *iter) |
929{ 930 struct wpa_dbus_object_desc *obj_desc = NULL; |
931 DBusMessageIter dict_iter; 932 DBusError error; |
933 934 dbus_connection_get_object_path_data(iface->con, path, 935 (void **) &obj_desc); 936 if (!obj_desc) { |
937 wpa_printf(MSG_ERROR, "dbus: %s: could not obtain object's " 938 "private data: %s", __func__, path); 939 return FALSE; |
940 } 941 |
942 if (!wpa_dbus_dict_open_write(iter, &dict_iter)) { 943 wpa_printf(MSG_ERROR, "dbus: %s: failed to open message dict", 944 __func__); 945 return FALSE; 946 } 947 948 dbus_error_init(&error); 949 if (!fill_dict_with_properties(&dict_iter, obj_desc->properties, 950 interface, obj_desc->user_data, 951 &error)) { 952 wpa_printf(MSG_ERROR, "dbus: %s: failed to get object" 953 " properties: (%s) %s", __func__, 954 dbus_error_is_set(&error) ? error.name : "none", 955 dbus_error_is_set(&error) ? error.message : "none"); 956 dbus_error_free(&error); 957 return FALSE; 958 } 959 960 return wpa_dbus_dict_close_write(iter, &dict_iter); |
961} |
962 963/** 964 * wpas_dbus_new_decompose_object_path - Decompose an interface object path into parts 965 * @path: The dbus object path 966 * @p2p_persistent_group: indicates whether to parse the path as a P2P 967 * persistent group object 968 * @network: (out) the configured network this object path refers to, if any 969 * @bssid: (out) the scanned bssid this object path refers to, if any 970 * Returns: The object path of the network interface this path refers to 971 * 972 * For a given object path, decomposes the object path into object id, network, 973 * and BSSID parts, if those parts exist. 974 */ 975char *wpas_dbus_new_decompose_object_path(const char *path, 976 int p2p_persistent_group, 977 char **network, 978 char **bssid) 979{ 980 const unsigned int dev_path_prefix_len = 981 os_strlen(WPAS_DBUS_NEW_PATH_INTERFACES "/"); 982 char *obj_path_only; 983 char *next_sep; 984 985 /* Be a bit paranoid about path */ 986 if (!path || os_strncmp(path, WPAS_DBUS_NEW_PATH_INTERFACES "/", 987 dev_path_prefix_len)) 988 return NULL; 989 990 /* Ensure there's something at the end of the path */ 991 if ((path + dev_path_prefix_len)[0] == '\0') 992 return NULL; 993 994 obj_path_only = os_strdup(path); 995 if (obj_path_only == NULL) 996 return NULL; 997 998 next_sep = os_strchr(obj_path_only + dev_path_prefix_len, '/'); 999 if (next_sep != NULL) { 1000 const char *net_part = os_strstr( 1001 next_sep, p2p_persistent_group ? 1002 WPAS_DBUS_NEW_PERSISTENT_GROUPS_PART "/" : 1003 WPAS_DBUS_NEW_NETWORKS_PART "/"); 1004 const char *bssid_part = os_strstr( 1005 next_sep, WPAS_DBUS_NEW_BSSIDS_PART "/"); 1006 1007 if (network && net_part) { 1008 /* Deal with a request for a configured network */ 1009 const char *net_name = net_part + 1010 os_strlen(p2p_persistent_group ? 1011 WPAS_DBUS_NEW_PERSISTENT_GROUPS_PART 1012 "/" : 1013 WPAS_DBUS_NEW_NETWORKS_PART "/"); 1014 *network = NULL; 1015 if (os_strlen(net_name)) 1016 *network = os_strdup(net_name); 1017 } else if (bssid && bssid_part) { 1018 /* Deal with a request for a scanned BSSID */ 1019 const char *bssid_name = bssid_part + 1020 os_strlen(WPAS_DBUS_NEW_BSSIDS_PART "/"); 1021 if (os_strlen(bssid_name)) 1022 *bssid = os_strdup(bssid_name); 1023 else 1024 *bssid = NULL; 1025 } 1026 1027 /* Cut off interface object path before "/" */ 1028 *next_sep = '\0'; 1029 } 1030 1031 return obj_path_only; 1032} 1033 1034 1035/** 1036 * wpas_dbus_reply_new_from_error - Create a new D-Bus error message from a 1037 * dbus error structure 1038 * @message: The original request message for which the error is a reply 1039 * @error: The error containing a name and a descriptive error cause 1040 * @fallback_name: A generic error name if @error was not set 1041 * @fallback_string: A generic error string if @error was not set 1042 * Returns: A new D-Bus error message 1043 * 1044 * Given a DBusMessage structure, creates a new D-Bus error message using 1045 * the error name and string contained in that structure. 1046 */ 1047DBusMessage * wpas_dbus_reply_new_from_error(DBusMessage *message, 1048 DBusError *error, 1049 const char *fallback_name, 1050 const char *fallback_string) 1051{ 1052 if (error && error->name && error->message) { 1053 return dbus_message_new_error(message, error->name, 1054 error->message); 1055 } 1056 if (fallback_name && fallback_string) { 1057 return dbus_message_new_error(message, fallback_name, 1058 fallback_string); 1059 } 1060 return NULL; 1061} |