dbus_new_helpers.c revision 75ecf5267604f166b85a7ee2cf0d9cb682966680
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 program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2 as
8 * published by the Free Software Foundation.
9 *
10 * Alternatively, this software may be distributed under the terms of BSD
11 * license.
12 *
13 * See README and COPYING for more details.
14 */
15
16#include "utils/includes.h"
17
18#include "utils/common.h"
19#include "utils/eloop.h"
20#include "dbus_common.h"
21#include "dbus_common_i.h"
22#include "dbus_new.h"
23#include "dbus_new_helpers.h"
24
25
26/**
27 * recursive_iter_copy - Reads arguments from one iterator and
28 * writes to another recursively
29 * @from: iterator to read from
30 * @to: iterator to write to
31 *
32 * Copies one iterator's elements to another. If any element in
33 * iterator is of container type, its content is copied recursively
34 */
35static void recursive_iter_copy(DBusMessageIter *from, DBusMessageIter *to)
36{
37
38	char *subtype = NULL;
39	int type;
40
41	/* iterate over iterator to copy */
42	while ((type = dbus_message_iter_get_arg_type(from)) !=
43	       DBUS_TYPE_INVALID) {
44
45		/* simply copy basic type entries */
46		if (dbus_type_is_basic(type)) {
47			if (dbus_type_is_fixed(type)) {
48				/*
49				 * According to DBus documentation all
50				 * fixed-length types are guaranteed to fit
51				 * 8 bytes
52				 */
53				dbus_uint64_t v;
54				dbus_message_iter_get_basic(from, &v);
55				dbus_message_iter_append_basic(to, type, &v);
56			} else {
57				char *v;
58				dbus_message_iter_get_basic(from, &v);
59				dbus_message_iter_append_basic(to, type, &v);
60			}
61		} else {
62			/* recursively copy container type entries */
63			DBusMessageIter write_subiter, read_subiter;
64
65			dbus_message_iter_recurse(from, &read_subiter);
66
67			if (type == DBUS_TYPE_VARIANT ||
68			    type == DBUS_TYPE_ARRAY) {
69				subtype = dbus_message_iter_get_signature(
70					&read_subiter);
71			}
72
73			dbus_message_iter_open_container(to, type, subtype,
74							 &write_subiter);
75
76			recursive_iter_copy(&read_subiter, &write_subiter);
77
78			dbus_message_iter_close_container(to, &write_subiter);
79			if (subtype)
80				dbus_free(subtype);
81		}
82
83		dbus_message_iter_next(from);
84	}
85}
86
87
88static unsigned int fill_dict_with_properties(
89	DBusMessageIter *dict_iter, const struct wpa_dbus_property_desc *props,
90	const char *interface, const void *user_data)
91{
92	DBusMessage *reply;
93	DBusMessageIter entry_iter, ret_iter;
94	unsigned int counter = 0;
95	const struct wpa_dbus_property_desc *dsc;
96
97	for (dsc = props; dsc && dsc->dbus_property; dsc++) {
98		if (!os_strncmp(dsc->dbus_interface, interface,
99				WPAS_DBUS_INTERFACE_MAX) &&
100		    dsc->access != W && dsc->getter) {
101			reply = dsc->getter(NULL, user_data);
102			if (!reply)
103				continue;
104
105			if (dbus_message_get_type(reply) ==
106			    DBUS_MESSAGE_TYPE_ERROR) {
107				dbus_message_unref(reply);
108				continue;
109			}
110
111			dbus_message_iter_init(reply, &ret_iter);
112
113			dbus_message_iter_open_container(dict_iter,
114							 DBUS_TYPE_DICT_ENTRY,
115							 NULL, &entry_iter);
116			dbus_message_iter_append_basic(
117				&entry_iter, DBUS_TYPE_STRING,
118				&dsc->dbus_property);
119
120			recursive_iter_copy(&ret_iter, &entry_iter);
121
122			dbus_message_iter_close_container(dict_iter,
123							  &entry_iter);
124			dbus_message_unref(reply);
125			counter++;
126		}
127	}
128
129	return counter;
130}
131
132
133/**
134 * get_all_properties - Responds for GetAll properties calls on object
135 * @message: Message with GetAll call
136 * @interface: interface name which properties will be returned
137 * @property_dsc: list of object's properties
138 * Returns: Message with dict of variants as argument with properties values
139 *
140 * Iterates over all properties registered with object and execute getters
141 * of those, which are readable and which interface matches interface
142 * specified as argument. Returned message contains one dict argument
143 * with properties names as keys and theirs values as values.
144 */
145static DBusMessage * get_all_properties(
146	DBusMessage *message, char *interface,
147	struct wpa_dbus_object_desc *obj_dsc)
148{
149	/* Create and initialize the return message */
150	DBusMessage *reply = dbus_message_new_method_return(message);
151	DBusMessageIter iter, dict_iter;
152	int props_num;
153
154	dbus_message_iter_init_append(reply, &iter);
155
156	dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
157					 DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
158					 DBUS_TYPE_STRING_AS_STRING
159					 DBUS_TYPE_VARIANT_AS_STRING
160					 DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
161					 &dict_iter);
162
163	props_num = fill_dict_with_properties(&dict_iter, obj_dsc->properties,
164					      interface, obj_dsc->user_data);
165
166	dbus_message_iter_close_container(&iter, &dict_iter);
167
168	if (props_num == 0) {
169		dbus_message_unref(reply);
170		reply = dbus_message_new_error(message,
171					       DBUS_ERROR_INVALID_ARGS,
172					       "No readable properties in "
173					       "this interface");
174	}
175
176	return reply;
177}
178
179
180static int is_signature_correct(DBusMessage *message,
181				const struct wpa_dbus_method_desc *method_dsc)
182{
183	/* According to DBus documentation max length of signature is 255 */
184#define MAX_SIG_LEN 256
185	char registered_sig[MAX_SIG_LEN], *pos;
186	const char *sig = dbus_message_get_signature(message);
187	int ret;
188	const struct wpa_dbus_argument *arg;
189
190	pos = registered_sig;
191	*pos = '\0';
192
193	for (arg = method_dsc->args; arg && arg->name; arg++) {
194		if (arg->dir == ARG_IN) {
195			size_t blen = registered_sig + MAX_SIG_LEN - pos;
196			ret = os_snprintf(pos, blen, "%s", arg->type);
197			if (ret < 0 || (size_t) ret >= blen)
198				return 0;
199			pos += ret;
200		}
201	}
202
203	return !os_strncmp(registered_sig, sig, MAX_SIG_LEN);
204}
205
206
207static DBusMessage * properties_get_all(DBusMessage *message, char *interface,
208					struct wpa_dbus_object_desc *obj_dsc)
209{
210	if (os_strcmp(dbus_message_get_signature(message), "s") != 0)
211		return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS,
212					      NULL);
213
214	return get_all_properties(message, interface, obj_dsc);
215}
216
217
218static DBusMessage * properties_get(DBusMessage *message,
219				    const struct wpa_dbus_property_desc *dsc,
220				    void *user_data)
221{
222	if (os_strcmp(dbus_message_get_signature(message), "ss"))
223		return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS,
224					      NULL);
225
226	if (dsc->access != W && dsc->getter)
227		return dsc->getter(message, user_data);
228
229	return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS,
230				      "Property is write-only");
231}
232
233
234static DBusMessage * properties_set(DBusMessage *message,
235				    const struct wpa_dbus_property_desc *dsc,
236				    void *user_data)
237{
238	if (os_strcmp(dbus_message_get_signature(message), "ssv"))
239		return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS,
240					      NULL);
241
242	if (dsc->access != R && dsc->setter)
243		return dsc->setter(message, user_data);
244
245	return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS,
246				      "Property is read-only");
247}
248
249
250static DBusMessage *
251properties_get_or_set(DBusMessage *message, DBusMessageIter *iter,
252		      char *interface,
253		      struct wpa_dbus_object_desc *obj_dsc)
254{
255	const struct wpa_dbus_property_desc *property_dsc;
256	char *property;
257	const char *method;
258
259	method = dbus_message_get_member(message);
260	property_dsc = obj_dsc->properties;
261
262	/* Second argument: property name (DBUS_TYPE_STRING) */
263	if (!dbus_message_iter_next(iter) ||
264	    dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_STRING) {
265		return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS,
266					      NULL);
267	}
268	dbus_message_iter_get_basic(iter, &property);
269
270	while (property_dsc && property_dsc->dbus_property) {
271		/* compare property names and
272		 * interfaces */
273		if (!os_strncmp(property_dsc->dbus_property, property,
274				WPAS_DBUS_METHOD_SIGNAL_PROP_MAX) &&
275		    !os_strncmp(property_dsc->dbus_interface, interface,
276				WPAS_DBUS_INTERFACE_MAX))
277			break;
278
279		property_dsc++;
280	}
281	if (property_dsc == NULL || property_dsc->dbus_property == NULL) {
282		wpa_printf(MSG_DEBUG, "no property handler for %s.%s on %s",
283			   interface, property,
284			   dbus_message_get_path(message));
285		return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS,
286					      "No such property");
287	}
288
289	if (os_strncmp(WPA_DBUS_PROPERTIES_GET, method,
290		       WPAS_DBUS_METHOD_SIGNAL_PROP_MAX) == 0)
291		return properties_get(message, property_dsc,
292				      obj_dsc->user_data);
293
294	return properties_set(message, property_dsc, obj_dsc->user_data);
295}
296
297
298static DBusMessage * properties_handler(DBusMessage *message,
299					struct wpa_dbus_object_desc *obj_dsc)
300{
301	DBusMessageIter iter;
302	char *interface;
303	const char *method;
304
305	method = dbus_message_get_member(message);
306	dbus_message_iter_init(message, &iter);
307
308	if (!os_strncmp(WPA_DBUS_PROPERTIES_GET, method,
309			WPAS_DBUS_METHOD_SIGNAL_PROP_MAX) ||
310	    !os_strncmp(WPA_DBUS_PROPERTIES_SET, method,
311			WPAS_DBUS_METHOD_SIGNAL_PROP_MAX) ||
312	    !os_strncmp(WPA_DBUS_PROPERTIES_GETALL, method,
313			WPAS_DBUS_METHOD_SIGNAL_PROP_MAX)) {
314		/* First argument: interface name (DBUS_TYPE_STRING) */
315		if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
316		{
317			return dbus_message_new_error(message,
318						      DBUS_ERROR_INVALID_ARGS,
319						      NULL);
320		}
321
322		dbus_message_iter_get_basic(&iter, &interface);
323
324		if (!os_strncmp(WPA_DBUS_PROPERTIES_GETALL, method,
325				WPAS_DBUS_METHOD_SIGNAL_PROP_MAX)) {
326			/* GetAll */
327			return properties_get_all(message, interface, obj_dsc);
328		}
329		/* Get or Set */
330		return properties_get_or_set(message, &iter, interface,
331					     obj_dsc);
332	}
333	return dbus_message_new_error(message, DBUS_ERROR_UNKNOWN_METHOD,
334				      NULL);
335}
336
337
338static DBusMessage * msg_method_handler(DBusMessage *message,
339					struct wpa_dbus_object_desc *obj_dsc)
340{
341	const struct wpa_dbus_method_desc *method_dsc = obj_dsc->methods;
342	const char *method;
343	const char *msg_interface;
344
345	method = dbus_message_get_member(message);
346	msg_interface = dbus_message_get_interface(message);
347
348	/* try match call to any registered method */
349	while (method_dsc && method_dsc->dbus_method) {
350		/* compare method names and interfaces */
351		if (!os_strncmp(method_dsc->dbus_method, method,
352				WPAS_DBUS_METHOD_SIGNAL_PROP_MAX) &&
353		    !os_strncmp(method_dsc->dbus_interface, msg_interface,
354				WPAS_DBUS_INTERFACE_MAX))
355			break;
356
357		method_dsc++;
358	}
359	if (method_dsc == NULL || method_dsc->dbus_method == NULL) {
360		wpa_printf(MSG_DEBUG, "no method handler for %s.%s on %s",
361			   msg_interface, method,
362			   dbus_message_get_path(message));
363		return dbus_message_new_error(message,
364					      DBUS_ERROR_UNKNOWN_METHOD, NULL);
365	}
366
367	if (!is_signature_correct(message, method_dsc)) {
368		return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS,
369					      NULL);
370	}
371
372	return method_dsc->method_handler(message,
373					  obj_dsc->user_data);
374}
375
376
377/**
378 * message_handler - Handles incoming DBus messages
379 * @connection: DBus connection on which message was received
380 * @message: Received message
381 * @user_data: pointer to description of object to which message was sent
382 * Returns: Returns information whether message was handled or not
383 *
384 * Reads message interface and method name, then checks if they matches one
385 * of the special cases i.e. introspection call or properties get/getall/set
386 * methods and handles it. Else it iterates over registered methods list
387 * and tries to match method's name and interface to those read from message
388 * If appropriate method was found its handler function is called and
389 * response is sent. Otherwise, the DBUS_ERROR_UNKNOWN_METHOD error message
390 * will be sent.
391 */
392static DBusHandlerResult message_handler(DBusConnection *connection,
393					 DBusMessage *message, void *user_data)
394{
395	struct wpa_dbus_object_desc *obj_dsc = user_data;
396	const char *method;
397	const char *path;
398	const char *msg_interface;
399	DBusMessage *reply;
400
401	/* get method, interface and path the message is addressed to */
402	method = dbus_message_get_member(message);
403	path = dbus_message_get_path(message);
404	msg_interface = dbus_message_get_interface(message);
405	if (!method || !path || !msg_interface)
406		return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
407
408	wpa_printf(MSG_MSGDUMP, "dbus: %s.%s (%s)",
409		   msg_interface, method, path);
410
411	/* if message is introspection method call */
412	if (!os_strncmp(WPA_DBUS_INTROSPECTION_METHOD, method,
413			WPAS_DBUS_METHOD_SIGNAL_PROP_MAX) &&
414	    !os_strncmp(WPA_DBUS_INTROSPECTION_INTERFACE, msg_interface,
415			WPAS_DBUS_INTERFACE_MAX)) {
416#ifdef CONFIG_CTRL_IFACE_DBUS_INTRO
417		reply = wpa_dbus_introspect(message, obj_dsc);
418#else /* CONFIG_CTRL_IFACE_DBUS_INTRO */
419		reply = dbus_message_new_error(
420			message, DBUS_ERROR_UNKNOWN_METHOD,
421			"wpa_supplicant was compiled without "
422			"introspection support.");
423#endif /* CONFIG_CTRL_IFACE_DBUS_INTRO */
424	} else if (!os_strncmp(WPA_DBUS_PROPERTIES_INTERFACE, msg_interface,
425			     WPAS_DBUS_INTERFACE_MAX)) {
426		/* if message is properties method call */
427		reply = properties_handler(message, obj_dsc);
428	} else {
429		reply = msg_method_handler(message, obj_dsc);
430	}
431
432	/* If handler succeed returning NULL, reply empty message */
433	if (!reply)
434		reply = dbus_message_new_method_return(message);
435	if (reply) {
436		if (!dbus_message_get_no_reply(message))
437			dbus_connection_send(connection, reply, NULL);
438		dbus_message_unref(reply);
439	}
440
441	wpa_dbus_flush_all_changed_properties(connection);
442
443	return DBUS_HANDLER_RESULT_HANDLED;
444}
445
446
447/**
448 * free_dbus_object_desc - Frees object description data structure
449 * @connection: DBus connection
450 * @obj_dsc: Object description to free
451 *
452 * Frees each of properties, methods and signals description lists and
453 * the object description structure itself.
454 */
455void free_dbus_object_desc(struct wpa_dbus_object_desc *obj_dsc)
456{
457	if (!obj_dsc)
458		return;
459
460	/* free handler's argument */
461	if (obj_dsc->user_data_free_func)
462		obj_dsc->user_data_free_func(obj_dsc->user_data);
463
464	os_free(obj_dsc->path);
465	os_free(obj_dsc->prop_changed_flags);
466	os_free(obj_dsc);
467}
468
469
470static void free_dbus_object_desc_cb(DBusConnection *connection, void *obj_dsc)
471{
472	free_dbus_object_desc(obj_dsc);
473}
474
475/**
476 * wpa_dbus_ctrl_iface_init - Initialize dbus control interface
477 * @application_data: Pointer to application specific data structure
478 * @dbus_path: DBus path to interface object
479 * @dbus_service: DBus service name to register with
480 * @messageHandler: a pointer to function which will handle dbus messages
481 * coming on interface
482 * Returns: 0 on success, -1 on failure
483 *
484 * Initialize the dbus control interface and start receiving commands from
485 * external programs over the bus.
486 */
487int wpa_dbus_ctrl_iface_init(struct wpas_dbus_priv *iface,
488			     char *dbus_path, char *dbus_service,
489			     struct wpa_dbus_object_desc *obj_desc)
490{
491	DBusError error;
492	int ret = -1;
493	DBusObjectPathVTable wpa_vtable = {
494		&free_dbus_object_desc_cb, &message_handler,
495		NULL, NULL, NULL, NULL
496	};
497
498	obj_desc->connection = iface->con;
499	obj_desc->path = os_strdup(dbus_path);
500
501	/* Register the message handler for the global dbus interface */
502	if (!dbus_connection_register_object_path(iface->con,
503						  dbus_path, &wpa_vtable,
504						  obj_desc)) {
505		wpa_printf(MSG_ERROR, "dbus: Could not set up message "
506			   "handler");
507		return -1;
508	}
509
510	/* Register our service with the message bus */
511	dbus_error_init(&error);
512	switch (dbus_bus_request_name(iface->con, dbus_service,
513				      0, &error)) {
514	case DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER:
515		ret = 0;
516		break;
517	case DBUS_REQUEST_NAME_REPLY_EXISTS:
518	case DBUS_REQUEST_NAME_REPLY_IN_QUEUE:
519	case DBUS_REQUEST_NAME_REPLY_ALREADY_OWNER:
520		wpa_printf(MSG_ERROR, "dbus: Could not request service name: "
521			   "already registered");
522		break;
523	default:
524		wpa_printf(MSG_ERROR, "dbus: Could not request service name: "
525			   "%s %s", error.name, error.message);
526		break;
527	}
528	dbus_error_free(&error);
529
530	if (ret != 0)
531		return -1;
532
533	wpa_printf(MSG_DEBUG, "Providing DBus service '%s'.", dbus_service);
534
535	return 0;
536}
537
538
539/**
540 * wpa_dbus_register_object_per_iface - Register a new object with dbus
541 * @ctrl_iface: pointer to dbus private data
542 * @path: DBus path to object
543 * @ifname: interface name
544 * @obj_desc: description of object's methods, signals and properties
545 * Returns: 0 on success, -1 on error
546 *
547 * Registers a new interface with dbus and assigns it a dbus object path.
548 */
549int wpa_dbus_register_object_per_iface(
550	struct wpas_dbus_priv *ctrl_iface,
551	const char *path, const char *ifname,
552	struct wpa_dbus_object_desc *obj_desc)
553{
554	DBusConnection *con;
555	DBusError error;
556
557	DBusObjectPathVTable vtable = {
558		&free_dbus_object_desc_cb, &message_handler,
559		NULL, NULL, NULL, NULL
560	};
561
562	/* Do nothing if the control interface is not turned on */
563	if (ctrl_iface == NULL)
564		return 0;
565
566	con = ctrl_iface->con;
567	obj_desc->connection = con;
568	obj_desc->path = os_strdup(path);
569
570	dbus_error_init(&error);
571	/* Register the message handler for the interface functions */
572	if (!dbus_connection_try_register_object_path(con, path, &vtable,
573						      obj_desc, &error)) {
574		if (!os_strcmp(error.name, DBUS_ERROR_OBJECT_PATH_IN_USE)) {
575			wpa_printf(MSG_DEBUG, "dbus: %s", error.message);
576		} else {
577			wpa_printf(MSG_ERROR, "dbus: Could not set up message "
578				   "handler for interface %s object %s",
579				   ifname, path);
580			wpa_printf(MSG_ERROR, "dbus error: %s", error.name);
581			wpa_printf(MSG_ERROR, "dbus: %s", error.message);
582		}
583		dbus_error_free(&error);
584		return -1;
585	}
586
587	dbus_error_free(&error);
588	return 0;
589}
590
591
592static void flush_object_timeout_handler(void *eloop_ctx, void *timeout_ctx);
593
594
595/**
596 * wpa_dbus_unregister_object_per_iface - Unregisters DBus object
597 * @ctrl_iface: Pointer to dbus private data
598 * @path: DBus path to object which will be unregistered
599 * Returns: Zero on success and -1 on failure
600 *
601 * Unregisters DBus object given by its path
602 */
603int wpa_dbus_unregister_object_per_iface(
604	struct wpas_dbus_priv *ctrl_iface, const char *path)
605{
606	DBusConnection *con = ctrl_iface->con;
607	struct wpa_dbus_object_desc *obj_desc = NULL;
608
609	dbus_connection_get_object_path_data(con, path, (void **) &obj_desc);
610	if (!obj_desc) {
611		wpa_printf(MSG_ERROR, "dbus: %s: Could not obtain object's "
612			   "private data: %s", __func__, path);
613	} else {
614		eloop_cancel_timeout(flush_object_timeout_handler, con,
615				     obj_desc);
616	}
617
618	if (!dbus_connection_unregister_object_path(con, path))
619		return -1;
620
621	return 0;
622}
623
624
625static void put_changed_properties(const struct wpa_dbus_object_desc *obj_dsc,
626				   const char *interface,
627				   DBusMessageIter *dict_iter)
628{
629	DBusMessage *getter_reply;
630	DBusMessageIter prop_iter, entry_iter;
631	const struct wpa_dbus_property_desc *dsc;
632	int i;
633
634	for (dsc = obj_dsc->properties, i = 0; dsc && dsc->dbus_property;
635	     dsc++, i++) {
636		if (obj_dsc->prop_changed_flags == NULL ||
637		    !obj_dsc->prop_changed_flags[i])
638			continue;
639		if (os_strcmp(dsc->dbus_interface, interface) != 0)
640			continue;
641		obj_dsc->prop_changed_flags[i] = 0;
642
643		getter_reply = dsc->getter(NULL, obj_dsc->user_data);
644		if (!getter_reply ||
645		    dbus_message_get_type(getter_reply) ==
646		    DBUS_MESSAGE_TYPE_ERROR) {
647			wpa_printf(MSG_ERROR, "dbus: %s: Cannot get new value "
648				   "of property %s", __func__,
649				   dsc->dbus_property);
650			continue;
651		}
652
653		if (!dbus_message_iter_init(getter_reply, &prop_iter) ||
654		    !dbus_message_iter_open_container(dict_iter,
655						      DBUS_TYPE_DICT_ENTRY,
656						      NULL, &entry_iter) ||
657		    !dbus_message_iter_append_basic(&entry_iter,
658						    DBUS_TYPE_STRING,
659						    &dsc->dbus_property))
660			goto err;
661
662		recursive_iter_copy(&prop_iter, &entry_iter);
663
664		if (!dbus_message_iter_close_container(dict_iter, &entry_iter))
665			goto err;
666
667		dbus_message_unref(getter_reply);
668	}
669
670	return;
671
672err:
673	wpa_printf(MSG_ERROR, "dbus: %s: Cannot construct signal", __func__);
674}
675
676
677static void send_prop_changed_signal(
678	DBusConnection *con, const char *path, const char *interface,
679	const struct wpa_dbus_object_desc *obj_dsc)
680{
681	DBusMessage *msg;
682	DBusMessageIter signal_iter, dict_iter;
683
684	msg = dbus_message_new_signal(path, interface, "PropertiesChanged");
685	if (msg == NULL)
686		return;
687
688	dbus_message_iter_init_append(msg, &signal_iter);
689
690	if (!dbus_message_iter_open_container(&signal_iter, DBUS_TYPE_ARRAY,
691					      "{sv}", &dict_iter))
692		goto err;
693
694	put_changed_properties(obj_dsc, interface, &dict_iter);
695
696	if (!dbus_message_iter_close_container(&signal_iter, &dict_iter))
697		goto err;
698
699	dbus_connection_send(con, msg, NULL);
700
701out:
702	dbus_message_unref(msg);
703	return;
704
705err:
706	wpa_printf(MSG_DEBUG, "dbus: %s: Failed to construct signal",
707		   __func__);
708	goto out;
709}
710
711
712static void flush_object_timeout_handler(void *eloop_ctx, void *timeout_ctx)
713{
714	DBusConnection *con = eloop_ctx;
715	struct wpa_dbus_object_desc *obj_desc = timeout_ctx;
716
717	wpa_printf(MSG_DEBUG, "dbus: %s: Timeout - sending changed properties "
718		   "of object %s", __func__, obj_desc->path);
719	wpa_dbus_flush_object_changed_properties(con, obj_desc->path);
720}
721
722
723static void recursive_flush_changed_properties(DBusConnection *con,
724					       const char *path)
725{
726	char **objects = NULL;
727	char subobj_path[WPAS_DBUS_OBJECT_PATH_MAX];
728	int i;
729
730	wpa_dbus_flush_object_changed_properties(con, path);
731
732	if (!dbus_connection_list_registered(con, path, &objects))
733		goto out;
734
735	for (i = 0; objects[i]; i++) {
736		os_snprintf(subobj_path, WPAS_DBUS_OBJECT_PATH_MAX,
737			    "%s/%s", path, objects[i]);
738		recursive_flush_changed_properties(con, subobj_path);
739	}
740
741out:
742	dbus_free_string_array(objects);
743}
744
745
746/**
747 * wpa_dbus_flush_all_changed_properties - Send all PropertiesChanged signals
748 * @con: DBus connection
749 *
750 * Traverses through all registered objects and sends PropertiesChanged for
751 * each properties.
752 */
753void wpa_dbus_flush_all_changed_properties(DBusConnection *con)
754{
755	recursive_flush_changed_properties(con, WPAS_DBUS_NEW_PATH);
756}
757
758
759/**
760 * wpa_dbus_flush_object_changed_properties - Send PropertiesChanged for object
761 * @con: DBus connection
762 * @path: path to a DBus object for which PropertiesChanged will be sent.
763 *
764 * Iterates over all properties registered with object and for each interface
765 * containing properties marked as changed, sends a PropertiesChanged signal
766 * containing names and new values of properties that have changed.
767 *
768 * You need to call this function after wpa_dbus_mark_property_changed()
769 * if you want to send PropertiesChanged signal immediately (i.e., without
770 * waiting timeout to expire). PropertiesChanged signal for an object is sent
771 * automatically short time after first marking property as changed. All
772 * PropertiesChanged signals are sent automatically after responding on DBus
773 * message, so if you marked a property changed as a result of DBus call
774 * (e.g., param setter), you usually do not need to call this function.
775 */
776void wpa_dbus_flush_object_changed_properties(DBusConnection *con,
777					      const char *path)
778{
779	struct wpa_dbus_object_desc *obj_desc = NULL;
780	const struct wpa_dbus_property_desc *dsc;
781	int i;
782
783	dbus_connection_get_object_path_data(con, path, (void **) &obj_desc);
784	if (!obj_desc)
785		return;
786	eloop_cancel_timeout(flush_object_timeout_handler, con, obj_desc);
787
788	dsc = obj_desc->properties;
789	for (dsc = obj_desc->properties, i = 0; dsc && dsc->dbus_property;
790	     dsc++, i++) {
791		if (obj_desc->prop_changed_flags == NULL ||
792		    !obj_desc->prop_changed_flags[i])
793			continue;
794		send_prop_changed_signal(con, path, dsc->dbus_interface,
795					 obj_desc);
796	}
797}
798
799
800#define WPA_DBUS_SEND_PROP_CHANGED_TIMEOUT 5000
801
802
803/**
804 * wpa_dbus_mark_property_changed - Mark a property as changed and
805 * @iface: dbus priv struct
806 * @path: path to DBus object which property has changed
807 * @interface: interface containing changed property
808 * @property: property name which has changed
809 *
810 * Iterates over all properties registered with an object and marks the one
811 * given in parameters as changed. All parameters registered for an object
812 * within a single interface will be aggregated together and sent in one
813 * PropertiesChanged signal when function
814 * wpa_dbus_flush_object_changed_properties() is called.
815 */
816void wpa_dbus_mark_property_changed(struct wpas_dbus_priv *iface,
817				    const char *path, const char *interface,
818				    const char *property)
819{
820	struct wpa_dbus_object_desc *obj_desc = NULL;
821	const struct wpa_dbus_property_desc *dsc;
822	int i = 0;
823
824	if (iface == NULL)
825		return;
826
827	dbus_connection_get_object_path_data(iface->con, path,
828					     (void **) &obj_desc);
829	if (!obj_desc) {
830		wpa_printf(MSG_ERROR, "dbus: wpa_dbus_property_changed: "
831			   "could not obtain object's private data: %s", path);
832		return;
833	}
834
835	for (dsc = obj_desc->properties; dsc && dsc->dbus_property; dsc++, i++)
836		if (os_strcmp(property, dsc->dbus_property) == 0 &&
837		    os_strcmp(interface, dsc->dbus_interface) == 0) {
838			if (obj_desc->prop_changed_flags)
839				obj_desc->prop_changed_flags[i] = 1;
840			break;
841		}
842
843	if (!dsc || !dsc->dbus_property) {
844		wpa_printf(MSG_ERROR, "dbus: wpa_dbus_property_changed: "
845			   "no property %s in object %s", property, path);
846		return;
847	}
848
849	if (!eloop_is_timeout_registered(flush_object_timeout_handler,
850					 iface->con, obj_desc->path)) {
851		eloop_register_timeout(0, WPA_DBUS_SEND_PROP_CHANGED_TIMEOUT,
852				       flush_object_timeout_handler,
853				       iface->con, obj_desc);
854	}
855}
856
857
858/**
859 * wpa_dbus_get_object_properties - Put object's properties into dictionary
860 * @iface: dbus priv struct
861 * @path: path to DBus object which properties will be obtained
862 * @interface: interface name which properties will be obtained
863 * @dict_iter: correct, open DBus dictionary iterator.
864 *
865 * Iterates over all properties registered with object and execute getters
866 * of those, which are readable and which interface matches interface
867 * specified as argument. Obtained properties values are stored in
868 * dict_iter dictionary.
869 */
870void wpa_dbus_get_object_properties(struct wpas_dbus_priv *iface,
871				    const char *path, const char *interface,
872				    DBusMessageIter *dict_iter)
873{
874	struct wpa_dbus_object_desc *obj_desc = NULL;
875
876	dbus_connection_get_object_path_data(iface->con, path,
877					     (void **) &obj_desc);
878	if (!obj_desc) {
879		wpa_printf(MSG_ERROR, "dbus: wpa_dbus_get_object_properties: "
880			   "could not obtain object's private data: %s", path);
881		return;
882	}
883
884	fill_dict_with_properties(dict_iter, obj_desc->properties,
885				  interface, obj_desc->user_data);
886}
887
888/**
889 * wpas_dbus_new_decompose_object_path - Decompose an interface object path into parts
890 * @path: The dbus object path
891 * @p2p_persistent_group: indicates whether to parse the path as a P2P
892 *                        persistent group object
893 * @network: (out) the configured network this object path refers to, if any
894 * @bssid: (out) the scanned bssid this object path refers to, if any
895 * Returns: The object path of the network interface this path refers to
896 *
897 * For a given object path, decomposes the object path into object id, network,
898 * and BSSID parts, if those parts exist.
899 */
900char *wpas_dbus_new_decompose_object_path(const char *path,
901					   int p2p_persistent_group,
902					   char **network,
903					   char **bssid)
904{
905	const unsigned int dev_path_prefix_len =
906		os_strlen(WPAS_DBUS_NEW_PATH_INTERFACES "/");
907	char *obj_path_only;
908	char *next_sep;
909
910	/* Be a bit paranoid about path */
911	if (!path || os_strncmp(path, WPAS_DBUS_NEW_PATH_INTERFACES "/",
912				dev_path_prefix_len))
913		return NULL;
914
915	/* Ensure there's something at the end of the path */
916	if ((path + dev_path_prefix_len)[0] == '\0')
917		return NULL;
918
919	obj_path_only = os_strdup(path);
920	if (obj_path_only == NULL)
921		return NULL;
922
923	next_sep = os_strchr(obj_path_only + dev_path_prefix_len, '/');
924	if (next_sep != NULL) {
925		const char *net_part = os_strstr(
926			next_sep, p2p_persistent_group ?
927			WPAS_DBUS_NEW_PERSISTENT_GROUPS_PART "/" :
928			WPAS_DBUS_NEW_NETWORKS_PART "/");
929		const char *bssid_part = os_strstr(
930			next_sep, WPAS_DBUS_NEW_BSSIDS_PART "/");
931
932		if (network && net_part) {
933			/* Deal with a request for a configured network */
934			const char *net_name = net_part +
935				os_strlen(p2p_persistent_group ?
936					  WPAS_DBUS_NEW_PERSISTENT_GROUPS_PART
937					  "/" :
938					  WPAS_DBUS_NEW_NETWORKS_PART "/");
939			*network = NULL;
940			if (os_strlen(net_name))
941				*network = os_strdup(net_name);
942		} else if (bssid && bssid_part) {
943			/* Deal with a request for a scanned BSSID */
944			const char *bssid_name = bssid_part +
945				os_strlen(WPAS_DBUS_NEW_BSSIDS_PART "/");
946			if (os_strlen(bssid_name))
947				*bssid = os_strdup(bssid_name);
948			else
949				*bssid = NULL;
950		}
951
952		/* Cut off interface object path before "/" */
953		*next_sep = '\0';
954	}
955
956	return obj_path_only;
957}
958