]>
Commit | Line | Data |
---|---|---|
8fc2fb56 WS |
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 | * | |
c5a3cebf JM |
6 | * This software may be distributed under the terms of the BSD license. |
7 | * See README for more details. | |
8fc2fb56 WS |
8 | */ |
9 | ||
abd7a4e3 | 10 | #include "utils/includes.h" |
8fc2fb56 | 11 | |
abd7a4e3 WS |
12 | #include "utils/common.h" |
13 | #include "utils/eloop.h" | |
8ddef94b JM |
14 | #include "dbus_common.h" |
15 | #include "dbus_common_i.h" | |
abd7a4e3 | 16 | #include "dbus_new.h" |
a206a29a | 17 | #include "dbus_new_helpers.h" |
a0caebf3 | 18 | #include "dbus_new_handlers.h" |
6aeeb6fa | 19 | #include "dbus_dict_helpers.h" |
8fc2fb56 | 20 | |
8fc2fb56 | 21 | |
6aeeb6fa DW |
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) | |
8fc2fb56 | 26 | { |
6aeeb6fa | 27 | DBusMessageIter entry_iter; |
e90bd80c | 28 | const struct wpa_dbus_property_desc *dsc; |
e376f119 | 29 | |
e90bd80c | 30 | for (dsc = props; dsc && dsc->dbus_property; dsc++) { |
6aeeb6fa DW |
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; | |
e376f119 | 35 | |
33206664 JM |
36 | /* Skip write-only properties */ |
37 | if (dsc->getter == NULL) | |
6aeeb6fa | 38 | continue; |
e376f119 | 39 | |
6aeeb6fa DW |
40 | if (!dbus_message_iter_open_container(dict_iter, |
41 | DBUS_TYPE_DICT_ENTRY, | |
25be28a3 JM |
42 | NULL, &entry_iter) || |
43 | !dbus_message_iter_append_basic(&entry_iter, | |
6aeeb6fa | 44 | DBUS_TYPE_STRING, |
25be28a3 JM |
45 | &dsc->dbus_property)) |
46 | goto error; | |
e376f119 | 47 | |
6aeeb6fa | 48 | /* An error getting a property fails the request entirely */ |
1aa0fb77 | 49 | if (!dsc->getter(dsc, &entry_iter, error, user_data)) { |
e8181e26 JM |
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); | |
6aeeb6fa | 54 | return FALSE; |
e8181e26 | 55 | } |
e376f119 | 56 | |
25be28a3 JM |
57 | if (!dbus_message_iter_close_container(dict_iter, &entry_iter)) |
58 | goto error; | |
e376f119 WS |
59 | } |
60 | ||
6aeeb6fa | 61 | return TRUE; |
25be28a3 JM |
62 | |
63 | error: | |
64 | dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory"); | |
65 | return FALSE; | |
e376f119 WS |
66 | } |
67 | ||
68 | ||
8fc2fb56 WS |
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 | */ | |
6aeeb6fa | 81 | static DBusMessage * get_all_properties(DBusMessage *message, char *interface, |
38279bdb | 82 | struct wpa_dbus_object_desc *obj_dsc) |
8fc2fb56 | 83 | { |
6aeeb6fa | 84 | DBusMessage *reply; |
e376f119 | 85 | DBusMessageIter iter, dict_iter; |
6aeeb6fa | 86 | DBusError error; |
8fc2fb56 | 87 | |
6aeeb6fa | 88 | reply = dbus_message_new_method_return(message); |
a0caebf3 JM |
89 | if (reply == NULL) |
90 | return wpas_dbus_error_no_memory(message); | |
8fc2fb56 | 91 | |
6aeeb6fa DW |
92 | dbus_message_iter_init_append(reply, &iter); |
93 | if (!wpa_dbus_dict_open_write(&iter, &dict_iter)) { | |
6aeeb6fa | 94 | dbus_message_unref(reply); |
a0caebf3 | 95 | return wpas_dbus_error_no_memory(message); |
6aeeb6fa | 96 | } |
8fc2fb56 | 97 | |
6aeeb6fa DW |
98 | dbus_error_init(&error); |
99 | if (!fill_dict_with_properties(&dict_iter, obj_dsc->properties, | |
38279bdb | 100 | interface, obj_dsc->user_data, &error)) { |
8fc2fb56 | 101 | dbus_message_unref(reply); |
38279bdb JM |
102 | reply = wpas_dbus_reply_new_from_error( |
103 | message, &error, DBUS_ERROR_INVALID_ARGS, | |
104 | "No readable properties in this interface"); | |
6aeeb6fa DW |
105 | dbus_error_free(&error); |
106 | return reply; | |
8fc2fb56 WS |
107 | } |
108 | ||
25be28a3 JM |
109 | if (!wpa_dbus_dict_close_write(&iter, &dict_iter)) { |
110 | dbus_message_unref(reply); | |
a0caebf3 | 111 | return wpas_dbus_error_no_memory(message); |
25be28a3 JM |
112 | } |
113 | ||
8fc2fb56 WS |
114 | return reply; |
115 | } | |
116 | ||
117 | ||
68e7cb49 | 118 | static int is_signature_correct(DBusMessage *message, |
e90bd80c | 119 | const struct wpa_dbus_method_desc *method_dsc) |
8fc2fb56 WS |
120 | { |
121 | /* According to DBus documentation max length of signature is 255 */ | |
68e7cb49 JM |
122 | #define MAX_SIG_LEN 256 |
123 | char registered_sig[MAX_SIG_LEN], *pos; | |
8fc2fb56 | 124 | const char *sig = dbus_message_get_signature(message); |
e90bd80c JM |
125 | int ret; |
126 | const struct wpa_dbus_argument *arg; | |
8fc2fb56 | 127 | |
68e7cb49 JM |
128 | pos = registered_sig; |
129 | *pos = '\0'; | |
8fc2fb56 | 130 | |
e90bd80c JM |
131 | for (arg = method_dsc->args; arg && arg->name; arg++) { |
132 | if (arg->dir == ARG_IN) { | |
68e7cb49 | 133 | size_t blen = registered_sig + MAX_SIG_LEN - pos; |
38279bdb | 134 | |
e90bd80c | 135 | ret = os_snprintf(pos, blen, "%s", arg->type); |
d85e1fc8 | 136 | if (os_snprintf_error(blen, ret)) |
68e7cb49 JM |
137 | return 0; |
138 | pos += ret; | |
139 | } | |
8fc2fb56 WS |
140 | } |
141 | ||
142 | return !os_strncmp(registered_sig, sig, MAX_SIG_LEN); | |
143 | } | |
144 | ||
145 | ||
5a464ff8 JM |
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 | ||
1fa5995b | 153 | return get_all_properties(message, interface, obj_dsc); |
5a464ff8 JM |
154 | } |
155 | ||
156 | ||
157 | static DBusMessage * properties_get(DBusMessage *message, | |
e90bd80c | 158 | const struct wpa_dbus_property_desc *dsc, |
1fa5995b | 159 | void *user_data) |
5a464ff8 | 160 | { |
6aeeb6fa DW |
161 | DBusMessage *reply; |
162 | DBusMessageIter iter; | |
163 | DBusError error; | |
164 | ||
165 | if (os_strcmp(dbus_message_get_signature(message), "ss")) { | |
5a464ff8 JM |
166 | return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS, |
167 | NULL); | |
6aeeb6fa | 168 | } |
5a464ff8 | 169 | |
33206664 | 170 | if (dsc->getter == NULL) { |
6aeeb6fa DW |
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); | |
5a464ff8 | 177 | |
6aeeb6fa | 178 | dbus_error_init(&error); |
1aa0fb77 | 179 | if (dsc->getter(dsc, &iter, &error, user_data) == FALSE) { |
6aeeb6fa DW |
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; | |
5a464ff8 JM |
188 | } |
189 | ||
190 | ||
191 | static DBusMessage * properties_set(DBusMessage *message, | |
e90bd80c | 192 | const struct wpa_dbus_property_desc *dsc, |
1fa5995b | 193 | void *user_data) |
5a464ff8 | 194 | { |
6aeeb6fa DW |
195 | DBusMessage *reply; |
196 | DBusMessageIter iter; | |
197 | DBusError error; | |
198 | ||
199 | if (os_strcmp(dbus_message_get_signature(message), "ssv")) { | |
5a464ff8 JM |
200 | return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS, |
201 | NULL); | |
6aeeb6fa DW |
202 | } |
203 | ||
33206664 | 204 | if (dsc->setter == NULL) { |
6aeeb6fa DW |
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); | |
5a464ff8 | 213 | |
6aeeb6fa DW |
214 | /* Iter will now point to the property's new value */ |
215 | dbus_error_init(&error); | |
1aa0fb77 | 216 | if (dsc->setter(dsc, &iter, &error, user_data) == TRUE) { |
6aeeb6fa DW |
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 | } | |
5a464ff8 | 225 | |
6aeeb6fa | 226 | return reply; |
5a464ff8 JM |
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 | { | |
e90bd80c | 235 | const struct wpa_dbus_property_desc *property_dsc; |
5a464ff8 JM |
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 | ||
e90bd80c | 250 | while (property_dsc && property_dsc->dbus_property) { |
5a464ff8 JM |
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 | ||
e90bd80c | 259 | property_dsc++; |
5a464ff8 | 260 | } |
e90bd80c | 261 | if (property_dsc == NULL || property_dsc->dbus_property == NULL) { |
5a464ff8 JM |
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, | |
88ce7938 JM |
270 | WPAS_DBUS_METHOD_SIGNAL_PROP_MAX) == 0) { |
271 | wpa_printf(MSG_MSGDUMP, "%s: Get(%s)", __func__, property); | |
1fa5995b WS |
272 | return properties_get(message, property_dsc, |
273 | obj_dsc->user_data); | |
88ce7938 | 274 | } |
5a464ff8 | 275 | |
88ce7938 | 276 | wpa_printf(MSG_MSGDUMP, "%s: Set(%s)", __func__, property); |
1fa5995b | 277 | return properties_set(message, property_dsc, obj_dsc->user_data); |
5a464ff8 JM |
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) */ | |
38279bdb | 298 | if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING) { |
5a464ff8 JM |
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 | { | |
e90bd80c | 323 | const struct wpa_dbus_method_desc *method_dsc = obj_dsc->methods; |
5a464ff8 JM |
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 */ | |
e90bd80c | 331 | while (method_dsc && method_dsc->dbus_method) { |
5a464ff8 JM |
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 | ||
e90bd80c | 339 | method_dsc++; |
5a464ff8 | 340 | } |
e90bd80c | 341 | if (method_dsc == NULL || method_dsc->dbus_method == NULL) { |
5a464ff8 JM |
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 | ||
38279bdb | 354 | return method_dsc->method_handler(message, obj_dsc->user_data); |
5a464ff8 JM |
355 | } |
356 | ||
357 | ||
8fc2fb56 WS |
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 | |
5a464ff8 JM |
369 | * If appropriate method was found its handler function is called and |
370 | * response is sent. Otherwise, the DBUS_ERROR_UNKNOWN_METHOD error message | |
8fc2fb56 WS |
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; | |
5a464ff8 | 380 | DBusMessage *reply; |
8fc2fb56 WS |
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) | |
5a464ff8 JM |
387 | return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; |
388 | ||
5485798f JM |
389 | wpa_printf(MSG_MSGDUMP, "dbus: %s.%s (%s) [%s]", |
390 | msg_interface, method, path, | |
391 | dbus_message_get_signature(message)); | |
8fc2fb56 WS |
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, | |
04551ee6 JM |
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, | |
38279bdb | 403 | "wpa_supplicant was compiled without introspection support."); |
04551ee6 JM |
404 | #endif /* CONFIG_CTRL_IFACE_DBUS_INTRO */ |
405 | } else if (!os_strncmp(WPA_DBUS_PROPERTIES_INTERFACE, msg_interface, | |
5a464ff8 | 406 | WPAS_DBUS_INTERFACE_MAX)) { |
8fc2fb56 | 407 | /* if message is properties method call */ |
5a464ff8 | 408 | reply = properties_handler(message, obj_dsc); |
8fc2fb56 | 409 | } else { |
5a464ff8 | 410 | reply = msg_method_handler(message, obj_dsc); |
8fc2fb56 WS |
411 | } |
412 | ||
5a464ff8 JM |
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 | } | |
abd7a4e3 WS |
421 | |
422 | wpa_dbus_flush_all_changed_properties(connection); | |
423 | ||
5a464ff8 | 424 | return DBUS_HANDLER_RESULT_HANDLED; |
8fc2fb56 WS |
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 | { | |
8fc2fb56 WS |
438 | if (!obj_dsc) |
439 | return; | |
440 | ||
1fa5995b WS |
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 | ||
abd7a4e3 WS |
445 | os_free(obj_dsc->path); |
446 | os_free(obj_dsc->prop_changed_flags); | |
8fc2fb56 WS |
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 | ||
38279bdb | 456 | |
8fc2fb56 WS |
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 | |
8ddef94b | 464 | * Returns: 0 on success, -1 on failure |
8fc2fb56 WS |
465 | * |
466 | * Initialize the dbus control interface and start receiving commands from | |
467 | * external programs over the bus. | |
468 | */ | |
8ddef94b JM |
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) | |
8fc2fb56 | 472 | { |
8fc2fb56 WS |
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 | ||
8fc2fb56 | 480 | obj_desc->connection = iface->con; |
abd7a4e3 | 481 | obj_desc->path = os_strdup(dbus_path); |
8fc2fb56 | 482 | |
8fc2fb56 | 483 | /* Register the message handler for the global dbus interface */ |
38279bdb JM |
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"); | |
8ddef94b | 487 | return -1; |
8fc2fb56 WS |
488 | } |
489 | ||
490 | /* Register our service with the message bus */ | |
491 | dbus_error_init(&error); | |
38279bdb | 492 | switch (dbus_bus_request_name(iface->con, dbus_service, 0, &error)) { |
8fc2fb56 WS |
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: | |
38279bdb JM |
499 | wpa_printf(MSG_ERROR, |
500 | "dbus: Could not request service name: already registered"); | |
8fc2fb56 WS |
501 | break; |
502 | default: | |
38279bdb JM |
503 | wpa_printf(MSG_ERROR, |
504 | "dbus: Could not request service name: %s %s", | |
505 | error.name, error.message); | |
8fc2fb56 WS |
506 | break; |
507 | } | |
508 | dbus_error_free(&error); | |
509 | ||
510 | if (ret != 0) | |
8ddef94b | 511 | return -1; |
8fc2fb56 WS |
512 | |
513 | wpa_printf(MSG_DEBUG, "Providing DBus service '%s'.", dbus_service); | |
514 | ||
8ddef94b | 515 | return 0; |
8fc2fb56 WS |
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 | */ | |
38279bdb JM |
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) | |
8fc2fb56 WS |
532 | { |
533 | DBusConnection *con; | |
d750b7e6 | 534 | DBusError error; |
8fc2fb56 WS |
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; | |
abd7a4e3 | 546 | obj_desc->path = os_strdup(path); |
8fc2fb56 | 547 | |
d750b7e6 | 548 | dbus_error_init(&error); |
8fc2fb56 | 549 | /* Register the message handler for the interface functions */ |
d750b7e6 JB |
550 | if (!dbus_connection_try_register_object_path(con, path, &vtable, |
551 | obj_desc, &error)) { | |
38279bdb | 552 | if (os_strcmp(error.name, DBUS_ERROR_OBJECT_PATH_IN_USE) == 0) { |
d750b7e6 JB |
553 | wpa_printf(MSG_DEBUG, "dbus: %s", error.message); |
554 | } else { | |
38279bdb JM |
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); | |
d750b7e6 JB |
558 | } |
559 | dbus_error_free(&error); | |
8fc2fb56 WS |
560 | return -1; |
561 | } | |
562 | ||
d750b7e6 | 563 | dbus_error_free(&error); |
8fc2fb56 WS |
564 | return 0; |
565 | } | |
566 | ||
567 | ||
abd7a4e3 WS |
568 | static void flush_object_timeout_handler(void *eloop_ctx, void *timeout_ctx); |
569 | ||
570 | ||
8fc2fb56 WS |
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( | |
8ddef94b | 580 | struct wpas_dbus_priv *ctrl_iface, const char *path) |
8fc2fb56 WS |
581 | { |
582 | DBusConnection *con = ctrl_iface->con; | |
e30020c7 | 583 | struct wpa_dbus_object_desc *obj_desc = NULL; |
abd7a4e3 | 584 | |
e30020c7 JM |
585 | dbus_connection_get_object_path_data(con, path, (void **) &obj_desc); |
586 | if (!obj_desc) { | |
38279bdb JM |
587 | wpa_printf(MSG_ERROR, |
588 | "dbus: %s: Could not obtain object's private data: %s", | |
589 | __func__, path); | |
731ef436 | 590 | return 0; |
e30020c7 | 591 | } |
abd7a4e3 | 592 | |
731ef436 JM |
593 | eloop_cancel_timeout(flush_object_timeout_handler, con, obj_desc); |
594 | ||
8fc2fb56 WS |
595 | if (!dbus_connection_unregister_object_path(con, path)) |
596 | return -1; | |
597 | ||
598 | return 0; | |
599 | } | |
600 | ||
601 | ||
6aeeb6fa DW |
602 | static dbus_bool_t put_changed_properties( |
603 | const struct wpa_dbus_object_desc *obj_dsc, const char *interface, | |
4483f23e | 604 | DBusMessageIter *dict_iter, int clear_changed) |
8fc2fb56 | 605 | { |
6aeeb6fa | 606 | DBusMessageIter entry_iter; |
abd7a4e3 WS |
607 | const struct wpa_dbus_property_desc *dsc; |
608 | int i; | |
6aeeb6fa | 609 | DBusError error; |
abd7a4e3 WS |
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; | |
4483f23e DD |
618 | if (clear_changed) |
619 | obj_dsc->prop_changed_flags[i] = 0; | |
abd7a4e3 | 620 | |
6aeeb6fa | 621 | if (!dbus_message_iter_open_container(dict_iter, |
abd7a4e3 | 622 | DBUS_TYPE_DICT_ENTRY, |
e3c4f0b5 JM |
623 | NULL, &entry_iter) || |
624 | !dbus_message_iter_append_basic(&entry_iter, | |
abd7a4e3 WS |
625 | DBUS_TYPE_STRING, |
626 | &dsc->dbus_property)) | |
6aeeb6fa DW |
627 | return FALSE; |
628 | ||
629 | dbus_error_init(&error); | |
1aa0fb77 DW |
630 | if (!dsc->getter(dsc, &entry_iter, &error, obj_dsc->user_data)) |
631 | { | |
38279bdb JM |
632 | if (dbus_error_is_set(&error)) { |
633 | wpa_printf(MSG_ERROR, | |
634 | "dbus: %s: Cannot get new value of property %s: (%s) %s", | |
635 | __func__, dsc->dbus_property, | |
636 | error.name, error.message); | |
6aeeb6fa | 637 | } else { |
38279bdb JM |
638 | wpa_printf(MSG_ERROR, |
639 | "dbus: %s: Cannot get new value of property %s", | |
6aeeb6fa DW |
640 | __func__, dsc->dbus_property); |
641 | } | |
642 | dbus_error_free(&error); | |
643 | return FALSE; | |
644 | } | |
8fc2fb56 | 645 | |
abd7a4e3 | 646 | if (!dbus_message_iter_close_container(dict_iter, &entry_iter)) |
6aeeb6fa | 647 | return FALSE; |
8fc2fb56 WS |
648 | } |
649 | ||
6aeeb6fa | 650 | return TRUE; |
abd7a4e3 WS |
651 | } |
652 | ||
653 | ||
4483f23e DD |
654 | static void do_send_prop_changed_signal( |
655 | DBusConnection *con, const char *path, const char *interface, | |
656 | const struct wpa_dbus_object_desc *obj_dsc) | |
657 | { | |
658 | DBusMessage *msg; | |
659 | DBusMessageIter signal_iter, dict_iter; | |
660 | ||
661 | msg = dbus_message_new_signal(path, DBUS_INTERFACE_PROPERTIES, | |
662 | "PropertiesChanged"); | |
663 | if (msg == NULL) | |
664 | return; | |
665 | ||
666 | dbus_message_iter_init_append(msg, &signal_iter); | |
667 | ||
668 | if (!dbus_message_iter_append_basic(&signal_iter, DBUS_TYPE_STRING, | |
e3c4f0b5 JM |
669 | &interface) || |
670 | /* Changed properties dict */ | |
671 | !dbus_message_iter_open_container(&signal_iter, DBUS_TYPE_ARRAY, | |
672 | "{sv}", &dict_iter) || | |
673 | !put_changed_properties(obj_dsc, interface, &dict_iter, 0) || | |
674 | !dbus_message_iter_close_container(&signal_iter, &dict_iter) || | |
675 | /* Invalidated properties array (empty) */ | |
676 | !dbus_message_iter_open_container(&signal_iter, DBUS_TYPE_ARRAY, | |
677 | "s", &dict_iter) || | |
38279bdb JM |
678 | !dbus_message_iter_close_container(&signal_iter, &dict_iter)) { |
679 | wpa_printf(MSG_DEBUG, "dbus: %s: Failed to construct signal", | |
680 | __func__); | |
681 | } else { | |
682 | dbus_connection_send(con, msg, NULL); | |
683 | } | |
4483f23e | 684 | |
4483f23e | 685 | dbus_message_unref(msg); |
4483f23e DD |
686 | } |
687 | ||
688 | ||
689 | static void do_send_deprecated_prop_changed_signal( | |
abd7a4e3 WS |
690 | DBusConnection *con, const char *path, const char *interface, |
691 | const struct wpa_dbus_object_desc *obj_dsc) | |
692 | { | |
693 | DBusMessage *msg; | |
694 | DBusMessageIter signal_iter, dict_iter; | |
695 | ||
696 | msg = dbus_message_new_signal(path, interface, "PropertiesChanged"); | |
697 | if (msg == NULL) | |
8fc2fb56 | 698 | return; |
8fc2fb56 | 699 | |
88ba1f72 | 700 | dbus_message_iter_init_append(msg, &signal_iter); |
8fc2fb56 WS |
701 | |
702 | if (!dbus_message_iter_open_container(&signal_iter, DBUS_TYPE_ARRAY, | |
e3c4f0b5 JM |
703 | "{sv}", &dict_iter) || |
704 | !put_changed_properties(obj_dsc, interface, &dict_iter, 1) || | |
38279bdb JM |
705 | !dbus_message_iter_close_container(&signal_iter, &dict_iter)) { |
706 | wpa_printf(MSG_DEBUG, "dbus: %s: Failed to construct signal", | |
707 | __func__); | |
708 | } else { | |
709 | dbus_connection_send(con, msg, NULL); | |
710 | } | |
8fc2fb56 | 711 | |
88ba1f72 | 712 | dbus_message_unref(msg); |
8fc2fb56 | 713 | } |
e376f119 WS |
714 | |
715 | ||
4483f23e DD |
716 | static void send_prop_changed_signal( |
717 | DBusConnection *con, const char *path, const char *interface, | |
718 | const struct wpa_dbus_object_desc *obj_dsc) | |
719 | { | |
720 | /* | |
721 | * First, send property change notification on the standardized | |
722 | * org.freedesktop.DBus.Properties interface. This call will not | |
723 | * clear the property change bits, so that they are preserved for | |
724 | * the call that follows. | |
725 | */ | |
726 | do_send_prop_changed_signal(con, path, interface, obj_dsc); | |
727 | ||
728 | /* | |
729 | * Now send PropertiesChanged on our own interface for backwards | |
730 | * compatibility. This is deprecated and will be removed in a future | |
731 | * release. | |
732 | */ | |
733 | do_send_deprecated_prop_changed_signal(con, path, interface, obj_dsc); | |
734 | ||
735 | /* Property change bits have now been cleared. */ | |
736 | } | |
737 | ||
738 | ||
abd7a4e3 WS |
739 | static void flush_object_timeout_handler(void *eloop_ctx, void *timeout_ctx) |
740 | { | |
741 | DBusConnection *con = eloop_ctx; | |
e30020c7 | 742 | struct wpa_dbus_object_desc *obj_desc = timeout_ctx; |
abd7a4e3 | 743 | |
38279bdb JM |
744 | wpa_printf(MSG_DEBUG, |
745 | "dbus: %s: Timeout - sending changed properties of object %s", | |
746 | __func__, obj_desc->path); | |
e30020c7 | 747 | wpa_dbus_flush_object_changed_properties(con, obj_desc->path); |
abd7a4e3 WS |
748 | } |
749 | ||
750 | ||
751 | static void recursive_flush_changed_properties(DBusConnection *con, | |
752 | const char *path) | |
753 | { | |
754 | char **objects = NULL; | |
755 | char subobj_path[WPAS_DBUS_OBJECT_PATH_MAX]; | |
756 | int i; | |
757 | ||
758 | wpa_dbus_flush_object_changed_properties(con, path); | |
759 | ||
760 | if (!dbus_connection_list_registered(con, path, &objects)) | |
761 | goto out; | |
762 | ||
763 | for (i = 0; objects[i]; i++) { | |
764 | os_snprintf(subobj_path, WPAS_DBUS_OBJECT_PATH_MAX, | |
765 | "%s/%s", path, objects[i]); | |
766 | recursive_flush_changed_properties(con, subobj_path); | |
767 | } | |
768 | ||
769 | out: | |
770 | dbus_free_string_array(objects); | |
771 | } | |
772 | ||
773 | ||
774 | /** | |
775 | * wpa_dbus_flush_all_changed_properties - Send all PropertiesChanged signals | |
776 | * @con: DBus connection | |
777 | * | |
778 | * Traverses through all registered objects and sends PropertiesChanged for | |
779 | * each properties. | |
780 | */ | |
781 | void wpa_dbus_flush_all_changed_properties(DBusConnection *con) | |
782 | { | |
783 | recursive_flush_changed_properties(con, WPAS_DBUS_NEW_PATH); | |
784 | } | |
785 | ||
786 | ||
787 | /** | |
788 | * wpa_dbus_flush_object_changed_properties - Send PropertiesChanged for object | |
789 | * @con: DBus connection | |
790 | * @path: path to a DBus object for which PropertiesChanged will be sent. | |
791 | * | |
792 | * Iterates over all properties registered with object and for each interface | |
793 | * containing properties marked as changed, sends a PropertiesChanged signal | |
794 | * containing names and new values of properties that have changed. | |
795 | * | |
796 | * You need to call this function after wpa_dbus_mark_property_changed() | |
797 | * if you want to send PropertiesChanged signal immediately (i.e., without | |
798 | * waiting timeout to expire). PropertiesChanged signal for an object is sent | |
799 | * automatically short time after first marking property as changed. All | |
800 | * PropertiesChanged signals are sent automatically after responding on DBus | |
801 | * message, so if you marked a property changed as a result of DBus call | |
802 | * (e.g., param setter), you usually do not need to call this function. | |
803 | */ | |
804 | void wpa_dbus_flush_object_changed_properties(DBusConnection *con, | |
805 | const char *path) | |
806 | { | |
807 | struct wpa_dbus_object_desc *obj_desc = NULL; | |
808 | const struct wpa_dbus_property_desc *dsc; | |
809 | int i; | |
810 | ||
abd7a4e3 | 811 | dbus_connection_get_object_path_data(con, path, (void **) &obj_desc); |
abd7a4e3 WS |
812 | if (!obj_desc) |
813 | return; | |
e30020c7 | 814 | eloop_cancel_timeout(flush_object_timeout_handler, con, obj_desc); |
abd7a4e3 | 815 | |
abd7a4e3 WS |
816 | for (dsc = obj_desc->properties, i = 0; dsc && dsc->dbus_property; |
817 | dsc++, i++) { | |
818 | if (obj_desc->prop_changed_flags == NULL || | |
819 | !obj_desc->prop_changed_flags[i]) | |
820 | continue; | |
821 | send_prop_changed_signal(con, path, dsc->dbus_interface, | |
822 | obj_desc); | |
823 | } | |
824 | } | |
825 | ||
826 | ||
827 | #define WPA_DBUS_SEND_PROP_CHANGED_TIMEOUT 5000 | |
828 | ||
829 | ||
830 | /** | |
831 | * wpa_dbus_mark_property_changed - Mark a property as changed and | |
832 | * @iface: dbus priv struct | |
833 | * @path: path to DBus object which property has changed | |
834 | * @interface: interface containing changed property | |
835 | * @property: property name which has changed | |
836 | * | |
837 | * Iterates over all properties registered with an object and marks the one | |
838 | * given in parameters as changed. All parameters registered for an object | |
839 | * within a single interface will be aggregated together and sent in one | |
840 | * PropertiesChanged signal when function | |
841 | * wpa_dbus_flush_object_changed_properties() is called. | |
842 | */ | |
843 | void wpa_dbus_mark_property_changed(struct wpas_dbus_priv *iface, | |
844 | const char *path, const char *interface, | |
845 | const char *property) | |
846 | { | |
847 | struct wpa_dbus_object_desc *obj_desc = NULL; | |
848 | const struct wpa_dbus_property_desc *dsc; | |
849 | int i = 0; | |
850 | ||
c3299a8b JM |
851 | if (iface == NULL) |
852 | return; | |
853 | ||
abd7a4e3 WS |
854 | dbus_connection_get_object_path_data(iface->con, path, |
855 | (void **) &obj_desc); | |
856 | if (!obj_desc) { | |
38279bdb JM |
857 | wpa_printf(MSG_ERROR, |
858 | "dbus: wpa_dbus_property_changed: could not obtain object's private data: %s", | |
859 | path); | |
abd7a4e3 WS |
860 | return; |
861 | } | |
862 | ||
863 | for (dsc = obj_desc->properties; dsc && dsc->dbus_property; dsc++, i++) | |
864 | if (os_strcmp(property, dsc->dbus_property) == 0 && | |
865 | os_strcmp(interface, dsc->dbus_interface) == 0) { | |
866 | if (obj_desc->prop_changed_flags) | |
867 | obj_desc->prop_changed_flags[i] = 1; | |
868 | break; | |
869 | } | |
870 | ||
871 | if (!dsc || !dsc->dbus_property) { | |
38279bdb JM |
872 | wpa_printf(MSG_ERROR, |
873 | "dbus: wpa_dbus_property_changed: no property %s in object %s", | |
874 | property, path); | |
abd7a4e3 WS |
875 | return; |
876 | } | |
877 | ||
878 | if (!eloop_is_timeout_registered(flush_object_timeout_handler, | |
7fbe56b0 | 879 | iface->con, obj_desc)) { |
abd7a4e3 WS |
880 | eloop_register_timeout(0, WPA_DBUS_SEND_PROP_CHANGED_TIMEOUT, |
881 | flush_object_timeout_handler, | |
e30020c7 | 882 | iface->con, obj_desc); |
abd7a4e3 WS |
883 | } |
884 | } | |
885 | ||
886 | ||
e376f119 WS |
887 | /** |
888 | * wpa_dbus_get_object_properties - Put object's properties into dictionary | |
889 | * @iface: dbus priv struct | |
890 | * @path: path to DBus object which properties will be obtained | |
891 | * @interface: interface name which properties will be obtained | |
6aeeb6fa | 892 | * @iter: DBus message iter at which to append property dictionary. |
e376f119 WS |
893 | * |
894 | * Iterates over all properties registered with object and execute getters | |
895 | * of those, which are readable and which interface matches interface | |
896 | * specified as argument. Obtained properties values are stored in | |
897 | * dict_iter dictionary. | |
898 | */ | |
6aeeb6fa DW |
899 | dbus_bool_t wpa_dbus_get_object_properties(struct wpas_dbus_priv *iface, |
900 | const char *path, | |
901 | const char *interface, | |
902 | DBusMessageIter *iter) | |
e376f119 WS |
903 | { |
904 | struct wpa_dbus_object_desc *obj_desc = NULL; | |
6aeeb6fa DW |
905 | DBusMessageIter dict_iter; |
906 | DBusError error; | |
e376f119 WS |
907 | |
908 | dbus_connection_get_object_path_data(iface->con, path, | |
909 | (void **) &obj_desc); | |
910 | if (!obj_desc) { | |
38279bdb JM |
911 | wpa_printf(MSG_ERROR, |
912 | "dbus: %s: could not obtain object's private data: %s", | |
913 | __func__, path); | |
6aeeb6fa DW |
914 | return FALSE; |
915 | } | |
916 | ||
917 | if (!wpa_dbus_dict_open_write(iter, &dict_iter)) { | |
918 | wpa_printf(MSG_ERROR, "dbus: %s: failed to open message dict", | |
919 | __func__); | |
920 | return FALSE; | |
e376f119 WS |
921 | } |
922 | ||
6aeeb6fa DW |
923 | dbus_error_init(&error); |
924 | if (!fill_dict_with_properties(&dict_iter, obj_desc->properties, | |
925 | interface, obj_desc->user_data, | |
926 | &error)) { | |
38279bdb JM |
927 | wpa_printf(MSG_ERROR, |
928 | "dbus: %s: failed to get object properties: (%s) %s", | |
929 | __func__, | |
930 | dbus_error_is_set(&error) ? error.name : "none", | |
931 | dbus_error_is_set(&error) ? error.message : "none"); | |
6aeeb6fa DW |
932 | dbus_error_free(&error); |
933 | return FALSE; | |
934 | } | |
935 | ||
936 | return wpa_dbus_dict_close_write(iter, &dict_iter); | |
e376f119 | 937 | } |
58f80e2a FM |
938 | |
939 | /** | |
940 | * wpas_dbus_new_decompose_object_path - Decompose an interface object path into parts | |
941 | * @path: The dbus object path | |
054dc313 JM |
942 | * @sep: Separating part (e.g., "Networks" or "PersistentGroups") |
943 | * @item: (out) The part following the specified separator, if any | |
944 | * Returns: The object path of the interface this path refers to | |
58f80e2a | 945 | * |
054dc313 JM |
946 | * For a given object path, decomposes the object path into object id and |
947 | * requested part, if those parts exist. The caller is responsible for freeing | |
948 | * the returned value. The *item pointer points to that allocated value and must | |
949 | * not be freed separately. | |
950 | * | |
951 | * As an example, path = "/fi/w1/wpa_supplicant1/Interfaces/1/Networks/0" and | |
952 | * sep = "Networks" would result in "/fi/w1/wpa_supplicant1/Interfaces/1" | |
953 | * getting returned and *items set to point to "0". | |
58f80e2a | 954 | */ |
054dc313 JM |
955 | char * wpas_dbus_new_decompose_object_path(const char *path, const char *sep, |
956 | char **item) | |
58f80e2a FM |
957 | { |
958 | const unsigned int dev_path_prefix_len = | |
959 | os_strlen(WPAS_DBUS_NEW_PATH_INTERFACES "/"); | |
960 | char *obj_path_only; | |
054dc313 JM |
961 | char *pos; |
962 | size_t sep_len; | |
58f80e2a | 963 | |
054dc313 JM |
964 | *item = NULL; |
965 | ||
966 | /* Verify that this starts with our interface prefix */ | |
967 | if (os_strncmp(path, WPAS_DBUS_NEW_PATH_INTERFACES "/", | |
968 | dev_path_prefix_len) != 0) | |
969 | return NULL; /* not our path */ | |
58f80e2a FM |
970 | |
971 | /* Ensure there's something at the end of the path */ | |
972 | if ((path + dev_path_prefix_len)[0] == '\0') | |
973 | return NULL; | |
974 | ||
975 | obj_path_only = os_strdup(path); | |
976 | if (obj_path_only == NULL) | |
977 | return NULL; | |
978 | ||
054dc313 JM |
979 | pos = obj_path_only + dev_path_prefix_len; |
980 | pos = os_strchr(pos, '/'); | |
981 | if (pos == NULL) | |
982 | return obj_path_only; /* no next item on the path */ | |
58f80e2a | 983 | |
054dc313 JM |
984 | /* Separate network interface prefix from the path */ |
985 | *pos++ = '\0'; | |
986 | ||
987 | sep_len = os_strlen(sep); | |
988 | if (os_strncmp(pos, sep, sep_len) != 0 || pos[sep_len] != '/') | |
989 | return obj_path_only; /* no match */ | |
58f80e2a | 990 | |
054dc313 JM |
991 | /* return a pointer to the requested item */ |
992 | *item = pos + sep_len + 1; | |
58f80e2a FM |
993 | return obj_path_only; |
994 | } | |
6aeeb6fa DW |
995 | |
996 | ||
997 | /** | |
998 | * wpas_dbus_reply_new_from_error - Create a new D-Bus error message from a | |
999 | * dbus error structure | |
1000 | * @message: The original request message for which the error is a reply | |
1001 | * @error: The error containing a name and a descriptive error cause | |
1002 | * @fallback_name: A generic error name if @error was not set | |
1003 | * @fallback_string: A generic error string if @error was not set | |
1004 | * Returns: A new D-Bus error message | |
1005 | * | |
1006 | * Given a DBusMessage structure, creates a new D-Bus error message using | |
1007 | * the error name and string contained in that structure. | |
1008 | */ | |
1009 | DBusMessage * wpas_dbus_reply_new_from_error(DBusMessage *message, | |
1010 | DBusError *error, | |
1011 | const char *fallback_name, | |
1012 | const char *fallback_string) | |
1013 | { | |
1014 | if (error && error->name && error->message) { | |
1015 | return dbus_message_new_error(message, error->name, | |
1016 | error->message); | |
1017 | } | |
1018 | if (fallback_name && fallback_string) { | |
1019 | return dbus_message_new_error(message, fallback_name, | |
1020 | fallback_string); | |
1021 | } | |
1022 | return NULL; | |
1023 | } |