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
556	DBusObjectPathVTable vtable = {
557		&free_dbus_object_desc_cb, &message_handler,
558		NULL, NULL, NULL, NULL
559	};
560
561	/* Do nothing if the control interface is not turned on */
562	if (ctrl_iface == NULL)
563		return 0;
564
565	con = ctrl_iface->con;
566	obj_desc->connection = con;
567	obj_desc->path = os_strdup(path);
568
569	/* Register the message handler for the interface functions */
570	if (!dbus_connection_register_object_path(con, path, &vtable,
571						  obj_desc)) {
572		wpa_printf(MSG_ERROR, "dbus: Could not set up message "
573			   "handler for interface %s object %s", ifname, path);
574		return -1;
575	}
576
577	return 0;
578}
579
580
581static void flush_object_timeout_handler(void *eloop_ctx, void *timeout_ctx);
582
583
584/**
585 * wpa_dbus_unregister_object_per_iface - Unregisters DBus object
586 * @ctrl_iface: Pointer to dbus private data
587 * @path: DBus path to object which will be unregistered
588 * Returns: Zero on success and -1 on failure
589 *
590 * Unregisters DBus object given by its path
591 */
592int wpa_dbus_unregister_object_per_iface(
593	struct wpas_dbus_priv *ctrl_iface, const char *path)
594{
595	DBusConnection *con = ctrl_iface->con;
596	struct wpa_dbus_object_desc *obj_desc = NULL;
597
598	dbus_connection_get_object_path_data(con, path, (void **) &obj_desc);
599	if (!obj_desc) {
600		wpa_printf(MSG_ERROR, "dbus: %s: Could not obtain object's "
601			   "private data: %s", __func__, path);
602	} else {
603		eloop_cancel_timeout(flush_object_timeout_handler, con,
604				     obj_desc);
605	}
606
607	if (!dbus_connection_unregister_object_path(con, path))
608		return -1;
609
610	return 0;
611}
612
613
614static void put_changed_properties(const struct wpa_dbus_object_desc *obj_dsc,
615				   const char *interface,
616				   DBusMessageIter *dict_iter)
617{
618	DBusMessage *getter_reply;
619	DBusMessageIter prop_iter, entry_iter;
620	const struct wpa_dbus_property_desc *dsc;
621	int i;
622
623	for (dsc = obj_dsc->properties, i = 0; dsc && dsc->dbus_property;
624	     dsc++, i++) {
625		if (obj_dsc->prop_changed_flags == NULL ||
626		    !obj_dsc->prop_changed_flags[i])
627			continue;
628		if (os_strcmp(dsc->dbus_interface, interface) != 0)
629			continue;
630		obj_dsc->prop_changed_flags[i] = 0;
631
632		getter_reply = dsc->getter(NULL, obj_dsc->user_data);
633		if (!getter_reply ||
634		    dbus_message_get_type(getter_reply) ==
635		    DBUS_MESSAGE_TYPE_ERROR) {
636			wpa_printf(MSG_ERROR, "dbus: %s: Cannot get new value "
637				   "of property %s", __func__,
638				   dsc->dbus_property);
639			continue;
640		}
641
642		if (!dbus_message_iter_init(getter_reply, &prop_iter) ||
643		    !dbus_message_iter_open_container(dict_iter,
644						      DBUS_TYPE_DICT_ENTRY,
645						      NULL, &entry_iter) ||
646		    !dbus_message_iter_append_basic(&entry_iter,
647						    DBUS_TYPE_STRING,
648						    &dsc->dbus_property))
649			goto err;
650
651		recursive_iter_copy(&prop_iter, &entry_iter);
652
653		if (!dbus_message_iter_close_container(dict_iter, &entry_iter))
654			goto err;
655
656		dbus_message_unref(getter_reply);
657	}
658
659	return;
660
661err:
662	wpa_printf(MSG_ERROR, "dbus: %s: Cannot construct signal", __func__);
663}
664
665
666static void send_prop_changed_signal(
667	DBusConnection *con, const char *path, const char *interface,
668	const struct wpa_dbus_object_desc *obj_dsc)
669{
670	DBusMessage *msg;
671	DBusMessageIter signal_iter, dict_iter;
672
673	msg = dbus_message_new_signal(path, interface, "PropertiesChanged");
674	if (msg == NULL)
675		return;
676
677	dbus_message_iter_init_append(msg, &signal_iter);
678
679	if (!dbus_message_iter_open_container(&signal_iter, DBUS_TYPE_ARRAY,
680					      "{sv}", &dict_iter))
681		goto err;
682
683	put_changed_properties(obj_dsc, interface, &dict_iter);
684
685	if (!dbus_message_iter_close_container(&signal_iter, &dict_iter))
686		goto err;
687
688	dbus_connection_send(con, msg, NULL);
689
690out:
691	dbus_message_unref(msg);
692	return;
693
694err:
695	wpa_printf(MSG_DEBUG, "dbus: %s: Failed to construct signal",
696		   __func__);
697	goto out;
698}
699
700
701static void flush_object_timeout_handler(void *eloop_ctx, void *timeout_ctx)
702{
703	DBusConnection *con = eloop_ctx;
704	struct wpa_dbus_object_desc *obj_desc = timeout_ctx;
705
706	wpa_printf(MSG_DEBUG, "dbus: %s: Timeout - sending changed properties "
707		   "of object %s", __func__, obj_desc->path);
708	wpa_dbus_flush_object_changed_properties(con, obj_desc->path);
709}
710
711
712static void recursive_flush_changed_properties(DBusConnection *con,
713					       const char *path)
714{
715	char **objects = NULL;
716	char subobj_path[WPAS_DBUS_OBJECT_PATH_MAX];
717	int i;
718
719	wpa_dbus_flush_object_changed_properties(con, path);
720
721	if (!dbus_connection_list_registered(con, path, &objects))
722		goto out;
723
724	for (i = 0; objects[i]; i++) {
725		os_snprintf(subobj_path, WPAS_DBUS_OBJECT_PATH_MAX,
726			    "%s/%s", path, objects[i]);
727		recursive_flush_changed_properties(con, subobj_path);
728	}
729
730out:
731	dbus_free_string_array(objects);
732}
733
734
735/**
736 * wpa_dbus_flush_all_changed_properties - Send all PropertiesChanged signals
737 * @con: DBus connection
738 *
739 * Traverses through all registered objects and sends PropertiesChanged for
740 * each properties.
741 */
742void wpa_dbus_flush_all_changed_properties(DBusConnection *con)
743{
744	recursive_flush_changed_properties(con, WPAS_DBUS_NEW_PATH);
745}
746
747
748/**
749 * wpa_dbus_flush_object_changed_properties - Send PropertiesChanged for object
750 * @con: DBus connection
751 * @path: path to a DBus object for which PropertiesChanged will be sent.
752 *
753 * Iterates over all properties registered with object and for each interface
754 * containing properties marked as changed, sends a PropertiesChanged signal
755 * containing names and new values of properties that have changed.
756 *
757 * You need to call this function after wpa_dbus_mark_property_changed()
758 * if you want to send PropertiesChanged signal immediately (i.e., without
759 * waiting timeout to expire). PropertiesChanged signal for an object is sent
760 * automatically short time after first marking property as changed. All
761 * PropertiesChanged signals are sent automatically after responding on DBus
762 * message, so if you marked a property changed as a result of DBus call
763 * (e.g., param setter), you usually do not need to call this function.
764 */
765void wpa_dbus_flush_object_changed_properties(DBusConnection *con,
766					      const char *path)
767{
768	struct wpa_dbus_object_desc *obj_desc = NULL;
769	const struct wpa_dbus_property_desc *dsc;
770	int i;
771
772	dbus_connection_get_object_path_data(con, path, (void **) &obj_desc);
773	if (!obj_desc)
774		return;
775	eloop_cancel_timeout(flush_object_timeout_handler, con, obj_desc);
776
777	dsc = obj_desc->properties;
778	for (dsc = obj_desc->properties, i = 0; dsc && dsc->dbus_property;
779	     dsc++, i++) {
780		if (obj_desc->prop_changed_flags == NULL ||
781		    !obj_desc->prop_changed_flags[i])
782			continue;
783		send_prop_changed_signal(con, path, dsc->dbus_interface,
784					 obj_desc);
785	}
786}
787
788
789#define WPA_DBUS_SEND_PROP_CHANGED_TIMEOUT 5000
790
791
792/**
793 * wpa_dbus_mark_property_changed - Mark a property as changed and
794 * @iface: dbus priv struct
795 * @path: path to DBus object which property has changed
796 * @interface: interface containing changed property
797 * @property: property name which has changed
798 *
799 * Iterates over all properties registered with an object and marks the one
800 * given in parameters as changed. All parameters registered for an object
801 * within a single interface will be aggregated together and sent in one
802 * PropertiesChanged signal when function
803 * wpa_dbus_flush_object_changed_properties() is called.
804 */
805void wpa_dbus_mark_property_changed(struct wpas_dbus_priv *iface,
806				    const char *path, const char *interface,
807				    const char *property)
808{
809	struct wpa_dbus_object_desc *obj_desc = NULL;
810	const struct wpa_dbus_property_desc *dsc;
811	int i = 0;
812
813	if (iface == NULL)
814		return;
815
816	dbus_connection_get_object_path_data(iface->con, path,
817					     (void **) &obj_desc);
818	if (!obj_desc) {
819		wpa_printf(MSG_ERROR, "dbus: wpa_dbus_property_changed: "
820			   "could not obtain object's private data: %s", path);
821		return;
822	}
823
824	for (dsc = obj_desc->properties; dsc && dsc->dbus_property; dsc++, i++)
825		if (os_strcmp(property, dsc->dbus_property) == 0 &&
826		    os_strcmp(interface, dsc->dbus_interface) == 0) {
827			if (obj_desc->prop_changed_flags)
828				obj_desc->prop_changed_flags[i] = 1;
829			break;
830		}
831
832	if (!dsc || !dsc->dbus_property) {
833		wpa_printf(MSG_ERROR, "dbus: wpa_dbus_property_changed: "
834			   "no property %s in object %s", property, path);
835		return;
836	}
837
838	if (!eloop_is_timeout_registered(flush_object_timeout_handler,
839					 iface->con, obj_desc->path)) {
840		eloop_register_timeout(0, WPA_DBUS_SEND_PROP_CHANGED_TIMEOUT,
841				       flush_object_timeout_handler,
842				       iface->con, obj_desc);
843	}
844}
845
846
847/**
848 * wpa_dbus_get_object_properties - Put object's properties into dictionary
849 * @iface: dbus priv struct
850 * @path: path to DBus object which properties will be obtained
851 * @interface: interface name which properties will be obtained
852 * @dict_iter: correct, open DBus dictionary iterator.
853 *
854 * Iterates over all properties registered with object and execute getters
855 * of those, which are readable and which interface matches interface
856 * specified as argument. Obtained properties values are stored in
857 * dict_iter dictionary.
858 */
859void wpa_dbus_get_object_properties(struct wpas_dbus_priv *iface,
860				    const char *path, const char *interface,
861				    DBusMessageIter *dict_iter)
862{
863	struct wpa_dbus_object_desc *obj_desc = NULL;
864
865	dbus_connection_get_object_path_data(iface->con, path,
866					     (void **) &obj_desc);
867	if (!obj_desc) {
868		wpa_printf(MSG_ERROR, "dbus: wpa_dbus_get_object_properties: "
869			   "could not obtain object's private data: %s", path);
870		return;
871	}
872
873	fill_dict_with_properties(dict_iter, obj_desc->properties,
874				  interface, obj_desc->user_data);
875}
876