1 /* 2 * wpa_supplicant - D-Bus introspection 3 * Copyright (c) 2006, Dan Williams <dcbw@redhat.com> and Red Hat, Inc. 4 * Copyright (c) 2009, Witold Sowa <witold.sowa@gmail.com> 5 * Copyright (c) 2010, Jouni Malinen <j@w1.fi> 6 * 7 * This software may be distributed under the terms of the BSD license. 8 * See README for more details. 9 */ 10 11 #include "utils/includes.h" 12 13 #include "utils/common.h" 14 #include "utils/list.h" 15 #include "utils/wpabuf.h" 16 #include "dbus_common_i.h" 17 #include "dbus_new_helpers.h" 18 19 20 struct interfaces { 21 struct dl_list list; 22 char *dbus_interface; 23 struct wpabuf *xml; 24 }; 25 26 27 static struct interfaces * add_interface(struct dl_list *list, 28 const char *dbus_interface) 29 { 30 struct interfaces *iface; 31 32 dl_list_for_each(iface, list, struct interfaces, list) { 33 if (os_strcmp(iface->dbus_interface, dbus_interface) == 0) 34 return iface; /* already in the list */ 35 } 36 37 iface = os_zalloc(sizeof(struct interfaces)); 38 if (!iface) 39 return NULL; 40 iface->dbus_interface = os_strdup(dbus_interface); 41 iface->xml = wpabuf_alloc(15000); 42 if (iface->dbus_interface == NULL || iface->xml == NULL) { 43 os_free(iface->dbus_interface); 44 wpabuf_free(iface->xml); 45 os_free(iface); 46 return NULL; 47 } 48 wpabuf_printf(iface->xml, "<interface name=\"%s\">", dbus_interface); 49 dl_list_add_tail(list, &iface->list); 50 return iface; 51 } 52 53 54 static void add_arg(struct wpabuf *xml, const char *name, const char *type, 55 const char *direction) 56 { 57 wpabuf_printf(xml, "<arg name=\"%s\"", name); 58 if (type) 59 wpabuf_printf(xml, " type=\"%s\"", type); 60 if (direction) 61 wpabuf_printf(xml, " direction=\"%s\"", direction); 62 wpabuf_put_str(xml, "/>"); 63 } 64 65 66 static void add_entry(struct wpabuf *xml, const char *type, const char *name, 67 const struct wpa_dbus_argument *args, int include_dir) 68 { 69 const struct wpa_dbus_argument *arg; 70 71 if (args == NULL || args->name == NULL) { 72 wpabuf_printf(xml, "<%s name=\"%s\"/>", type, name); 73 return; 74 } 75 wpabuf_printf(xml, "<%s name=\"%s\">", type, name); 76 for (arg = args; arg && arg->name; arg++) { 77 add_arg(xml, arg->name, arg->type, 78 include_dir ? (arg->dir == ARG_IN ? "in" : "out") : 79 NULL); 80 } 81 wpabuf_printf(xml, "</%s>", type); 82 } 83 84 85 static void add_property(struct wpabuf *xml, 86 const struct wpa_dbus_property_desc *dsc) 87 { 88 wpabuf_printf(xml, "<property name=\"%s\" type=\"%s\" " 89 "access=\"%s%s\"/>", 90 dsc->dbus_property, dsc->type, 91 dsc->getter ? "read" : "", 92 dsc->setter ? "write" : ""); 93 } 94 95 96 static void extract_interfaces_methods( 97 struct dl_list *list, const struct wpa_dbus_method_desc *methods) 98 { 99 const struct wpa_dbus_method_desc *dsc; 100 struct interfaces *iface; 101 102 for (dsc = methods; dsc && dsc->dbus_method; dsc++) { 103 iface = add_interface(list, dsc->dbus_interface); 104 if (iface) 105 add_entry(iface->xml, "method", dsc->dbus_method, 106 dsc->args, 1); 107 } 108 } 109 110 111 static void extract_interfaces_signals( 112 struct dl_list *list, const struct wpa_dbus_signal_desc *signals) 113 { 114 const struct wpa_dbus_signal_desc *dsc; 115 struct interfaces *iface; 116 117 for (dsc = signals; dsc && dsc->dbus_signal; dsc++) { 118 iface = add_interface(list, dsc->dbus_interface); 119 if (iface) 120 add_entry(iface->xml, "signal", dsc->dbus_signal, 121 dsc->args, 0); 122 } 123 } 124 125 126 static void extract_interfaces_properties( 127 struct dl_list *list, const struct wpa_dbus_property_desc *properties) 128 { 129 const struct wpa_dbus_property_desc *dsc; 130 struct interfaces *iface; 131 132 for (dsc = properties; dsc && dsc->dbus_property; dsc++) { 133 iface = add_interface(list, dsc->dbus_interface); 134 if (iface) 135 add_property(iface->xml, dsc); 136 } 137 } 138 139 140 /** 141 * extract_interfaces - Extract interfaces from methods, signals and props 142 * @list: Interface list to be filled 143 * @obj_dsc: Description of object from which interfaces will be extracted 144 * 145 * Iterates over all methods, signals, and properties registered with an 146 * object and collects all declared DBus interfaces and create interfaces' 147 * node in XML root node for each. Returned list elements contain interface 148 * name and XML node of corresponding interface. 149 */ 150 static void extract_interfaces(struct dl_list *list, 151 struct wpa_dbus_object_desc *obj_dsc) 152 { 153 extract_interfaces_methods(list, obj_dsc->methods); 154 extract_interfaces_signals(list, obj_dsc->signals); 155 extract_interfaces_properties(list, obj_dsc->properties); 156 } 157 158 159 static void add_interfaces(struct dl_list *list, struct wpabuf *xml) 160 { 161 struct interfaces *iface, *n; 162 163 dl_list_for_each_safe(iface, n, list, struct interfaces, list) { 164 if (wpabuf_len(iface->xml) + 20 < wpabuf_tailroom(xml)) { 165 wpabuf_put_buf(xml, iface->xml); 166 wpabuf_put_str(xml, "</interface>"); 167 } else { 168 wpa_printf(MSG_DEBUG, 169 "dbus: Not enough room for add_interfaces inspect data: tailroom %u, add %u", 170 (unsigned int) wpabuf_tailroom(xml), 171 (unsigned int) wpabuf_len(iface->xml)); 172 } 173 dl_list_del(&iface->list); 174 wpabuf_free(iface->xml); 175 os_free(iface->dbus_interface); 176 os_free(iface); 177 } 178 } 179 180 181 static void add_child_nodes(struct wpabuf *xml, DBusConnection *con, 182 const char *path) 183 { 184 char **children; 185 int i; 186 187 /* add child nodes to introspection tree */ 188 dbus_connection_list_registered(con, path, &children); 189 for (i = 0; children[i]; i++) 190 wpabuf_printf(xml, "<node name=\"%s\"/>", children[i]); 191 dbus_free_string_array(children); 192 } 193 194 195 static void add_introspectable_interface(struct wpabuf *xml) 196 { 197 wpabuf_printf(xml, "<interface name=\"%s\">" 198 "<method name=\"%s\">" 199 "<arg name=\"data\" type=\"s\" direction=\"out\"/>" 200 "</method>" 201 "</interface>", 202 WPA_DBUS_INTROSPECTION_INTERFACE, 203 WPA_DBUS_INTROSPECTION_METHOD); 204 } 205 206 207 static void add_properties_interface(struct wpabuf *xml) 208 { 209 wpabuf_printf(xml, "<interface name=\"%s\">", 210 WPA_DBUS_PROPERTIES_INTERFACE); 211 212 wpabuf_printf(xml, "<method name=\"%s\">", WPA_DBUS_PROPERTIES_GET); 213 add_arg(xml, "interface", "s", "in"); 214 add_arg(xml, "propname", "s", "in"); 215 add_arg(xml, "value", "v", "out"); 216 wpabuf_put_str(xml, "</method>"); 217 218 wpabuf_printf(xml, "<method name=\"%s\">", WPA_DBUS_PROPERTIES_GETALL); 219 add_arg(xml, "interface", "s", "in"); 220 add_arg(xml, "props", "a{sv}", "out"); 221 wpabuf_put_str(xml, "</method>"); 222 223 wpabuf_printf(xml, "<method name=\"%s\">", WPA_DBUS_PROPERTIES_SET); 224 add_arg(xml, "interface", "s", "in"); 225 add_arg(xml, "propname", "s", "in"); 226 add_arg(xml, "value", "v", "in"); 227 wpabuf_put_str(xml, "</method>"); 228 229 wpabuf_put_str(xml, "</interface>"); 230 } 231 232 233 static void add_wpas_interfaces(struct wpabuf *xml, 234 struct wpa_dbus_object_desc *obj_dsc) 235 { 236 struct dl_list ifaces; 237 238 dl_list_init(&ifaces); 239 extract_interfaces(&ifaces, obj_dsc); 240 add_interfaces(&ifaces, xml); 241 } 242 243 244 /** 245 * wpa_dbus_introspect - Responds for Introspect calls on object 246 * @message: Message with Introspect call 247 * @obj_dsc: Object description on which Introspect was called 248 * Returns: Message with introspection result XML string as only argument 249 * 250 * Iterates over all methods, signals and properties registered with 251 * object and generates introspection data for the object as XML string. 252 */ 253 DBusMessage * wpa_dbus_introspect(DBusMessage *message, 254 struct wpa_dbus_object_desc *obj_dsc) 255 { 256 257 DBusMessage *reply; 258 struct wpabuf *xml; 259 260 xml = wpabuf_alloc(30000); 261 if (xml == NULL) 262 return NULL; 263 264 wpabuf_put_str(xml, "<?xml version=\"1.0\"?>\n"); 265 wpabuf_put_str(xml, DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE); 266 wpabuf_put_str(xml, "<node>"); 267 268 add_introspectable_interface(xml); 269 add_properties_interface(xml); 270 add_wpas_interfaces(xml, obj_dsc); 271 add_child_nodes(xml, obj_dsc->connection, 272 dbus_message_get_path(message)); 273 274 wpabuf_put_str(xml, "</node>\n"); 275 276 reply = dbus_message_new_method_return(message); 277 if (reply) { 278 const char *intro_str = wpabuf_head(xml); 279 280 dbus_message_append_args(reply, DBUS_TYPE_STRING, &intro_str, 281 DBUS_TYPE_INVALID); 282 } 283 wpabuf_free(xml); 284 285 return reply; 286 } 287