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