Home | History | Annotate | Download | only in dbus
      1 /*
      2  * WPA Supplicant / dbus-based control interface
      3  * Copyright (c) 2006, Dan Williams <dcbw (at) redhat.com> and Red Hat, Inc.
      4  * Copyright (c) 2009, Witold Sowa <witold.sowa (at) 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  */
     35 static 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 
     88 static 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  */
    145 static 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 
    180 static 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 
    207 static 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 
    218 static 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 
    234 static 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 
    250 static DBusMessage *
    251 properties_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 
    298 static 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 
    338 static 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  */
    392 static 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  */
    455 void 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 
    470 static 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  */
    487 int 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  */
    549 int 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 
    592 static 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  */
    603 int 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 
    625 static 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 
    672 err:
    673 	wpa_printf(MSG_ERROR, "dbus: %s: Cannot construct signal", __func__);
    674 }
    675 
    676 
    677 static 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 
    701 out:
    702 	dbus_message_unref(msg);
    703 	return;
    704 
    705 err:
    706 	wpa_printf(MSG_DEBUG, "dbus: %s: Failed to construct signal",
    707 		   __func__);
    708 	goto out;
    709 }
    710 
    711 
    712 static 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 
    723 static 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 
    741 out:
    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  */
    753 void 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  */
    776 void 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  */
    816 void 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  */
    870 void 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  */
    900 char *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