Home | History | Annotate | Line # | 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 software may be distributed under the terms of the BSD license.
      7  * See README for more details.
      8  */
      9 
     10 #include "utils/includes.h"
     11 
     12 #include "utils/common.h"
     13 #include "utils/eloop.h"
     14 #include "drivers/driver.h"
     15 #include "dbus_common.h"
     16 #include "dbus_common_i.h"
     17 #include "dbus_new.h"
     18 #include "dbus_new_helpers.h"
     19 #include "dbus_new_handlers.h"
     20 #include "dbus_dict_helpers.h"
     21 
     22 
     23 static dbus_bool_t fill_dict_with_properties(
     24 	DBusMessageIter *dict_iter,
     25 	const struct wpa_dbus_property_desc *props,
     26 	const char *interface, void *user_data, DBusError *error)
     27 {
     28 	DBusMessageIter entry_iter;
     29 	const struct wpa_dbus_property_desc *dsc;
     30 
     31 	for (dsc = props; dsc && dsc->dbus_property; dsc++) {
     32 		/* Only return properties for the requested D-Bus interface */
     33 		if (os_strncmp(dsc->dbus_interface, interface,
     34 			       WPAS_DBUS_INTERFACE_MAX) != 0)
     35 			continue;
     36 
     37 		/* Skip write-only properties */
     38 		if (dsc->getter == NULL)
     39 			continue;
     40 
     41 		if (!dbus_message_iter_open_container(dict_iter,
     42 						      DBUS_TYPE_DICT_ENTRY,
     43 						      NULL, &entry_iter) ||
     44 		    !dbus_message_iter_append_basic(&entry_iter,
     45 						    DBUS_TYPE_STRING,
     46 						    &dsc->dbus_property))
     47 			goto error;
     48 
     49 		/* An error getting a property fails the request entirely */
     50 		if (!dsc->getter(dsc, &entry_iter, error, user_data)) {
     51 			wpa_printf(MSG_INFO,
     52 				   "dbus: %s dbus_interface=%s dbus_property=%s getter failed",
     53 				   __func__, dsc->dbus_interface,
     54 				   dsc->dbus_property);
     55 			return FALSE;
     56 		}
     57 
     58 		if (!dbus_message_iter_close_container(dict_iter, &entry_iter))
     59 			goto error;
     60 	}
     61 
     62 	return TRUE;
     63 
     64 error:
     65 	dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory");
     66 	return FALSE;
     67 }
     68 
     69 
     70 /**
     71  * get_all_properties - Responds for GetAll properties calls on object
     72  * @message: Message with GetAll call
     73  * @interface: interface name which properties will be returned
     74  * @property_dsc: list of object's properties
     75  * Returns: Message with dict of variants as argument with properties values
     76  *
     77  * Iterates over all properties registered with object and execute getters
     78  * of those, which are readable and which interface matches interface
     79  * specified as argument. Returned message contains one dict argument
     80  * with properties names as keys and theirs values as values.
     81  */
     82 static DBusMessage * get_all_properties(DBusMessage *message, char *interface,
     83 					struct wpa_dbus_object_desc *obj_dsc)
     84 {
     85 	DBusMessage *reply;
     86 	DBusMessageIter iter, dict_iter;
     87 	DBusError error;
     88 
     89 	reply = dbus_message_new_method_return(message);
     90 	if (reply == NULL)
     91 		return wpas_dbus_error_no_memory(message);
     92 
     93 	dbus_message_iter_init_append(reply, &iter);
     94 	if (!wpa_dbus_dict_open_write(&iter, &dict_iter)) {
     95 		dbus_message_unref(reply);
     96 		return wpas_dbus_error_no_memory(message);
     97 	}
     98 
     99 	dbus_error_init(&error);
    100 	if (!fill_dict_with_properties(&dict_iter, obj_dsc->properties,
    101 				       interface, obj_dsc->user_data, &error)) {
    102 		wpa_dbus_dict_close_write(&iter, &dict_iter);
    103 		dbus_message_unref(reply);
    104 		reply = wpas_dbus_reply_new_from_error(
    105 			message, &error, DBUS_ERROR_INVALID_ARGS,
    106 			"No readable properties in this interface");
    107 		dbus_error_free(&error);
    108 		return reply;
    109 	}
    110 
    111 	if (!wpa_dbus_dict_close_write(&iter, &dict_iter)) {
    112 		dbus_message_unref(reply);
    113 		return wpas_dbus_error_no_memory(message);
    114 	}
    115 
    116 	return reply;
    117 }
    118 
    119 
    120 static int is_signature_correct(DBusMessage *message,
    121 				const struct wpa_dbus_method_desc *method_dsc)
    122 {
    123 	/* According to DBus documentation max length of signature is 255 */
    124 #define MAX_SIG_LEN 256
    125 	char registered_sig[MAX_SIG_LEN], *pos;
    126 	const char *sig = dbus_message_get_signature(message);
    127 	int ret;
    128 	const struct wpa_dbus_argument *arg;
    129 
    130 	pos = registered_sig;
    131 	*pos = '\0';
    132 
    133 	for (arg = method_dsc->args; arg && arg->name; arg++) {
    134 		if (arg->dir == ARG_IN) {
    135 			size_t blen = registered_sig + MAX_SIG_LEN - pos;
    136 
    137 			ret = os_snprintf(pos, blen, "%s", arg->type);
    138 			if (os_snprintf_error(blen, ret))
    139 				return 0;
    140 			pos += ret;
    141 		}
    142 	}
    143 
    144 	return !os_strncmp(registered_sig, sig, MAX_SIG_LEN);
    145 }
    146 
    147 
    148 static DBusMessage * properties_get_all(DBusMessage *message, char *interface,
    149 					struct wpa_dbus_object_desc *obj_dsc)
    150 {
    151 	if (os_strcmp(dbus_message_get_signature(message), "s") != 0)
    152 		return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS,
    153 					      NULL);
    154 
    155 	return get_all_properties(message, interface, obj_dsc);
    156 }
    157 
    158 
    159 static DBusMessage * properties_get(DBusMessage *message,
    160 				    const struct wpa_dbus_property_desc *dsc,
    161 				    void *user_data)
    162 {
    163 	DBusMessage *reply;
    164 	DBusMessageIter iter;
    165 	DBusError error;
    166 
    167 	if (os_strcmp(dbus_message_get_signature(message), "ss")) {
    168 		return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS,
    169 					      NULL);
    170 	}
    171 
    172 	if (dsc->getter == NULL) {
    173 		return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS,
    174 					      "Property is write-only");
    175 	}
    176 
    177 	reply = dbus_message_new_method_return(message);
    178 	dbus_message_iter_init_append(reply, &iter);
    179 
    180 	dbus_error_init(&error);
    181 	if (dsc->getter(dsc, &iter, &error, user_data) == FALSE) {
    182 		dbus_message_unref(reply);
    183 		reply = wpas_dbus_reply_new_from_error(
    184 			message, &error, DBUS_ERROR_FAILED,
    185 			"Failed to read property");
    186 		dbus_error_free(&error);
    187 	}
    188 
    189 	return reply;
    190 }
    191 
    192 
    193 static DBusMessage * properties_set(DBusMessage *message,
    194 				    const struct wpa_dbus_property_desc *dsc,
    195 				    void *user_data)
    196 {
    197 	DBusMessage *reply;
    198 	DBusMessageIter iter;
    199 	DBusError error;
    200 
    201 	if (os_strcmp(dbus_message_get_signature(message), "ssv")) {
    202 		return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS,
    203 					      NULL);
    204 	}
    205 
    206 	if (dsc->setter == NULL) {
    207 		return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS,
    208 					      "Property is read-only");
    209 	}
    210 
    211 	dbus_message_iter_init(message, &iter);
    212 	/* Skip the interface name and the property name */
    213 	dbus_message_iter_next(&iter);
    214 	dbus_message_iter_next(&iter);
    215 
    216 	/* Iter will now point to the property's new value */
    217 	dbus_error_init(&error);
    218 	if (dsc->setter(dsc, &iter, &error, user_data) == TRUE) {
    219 		/* Success */
    220 		reply = dbus_message_new_method_return(message);
    221 	} else {
    222 		reply = wpas_dbus_reply_new_from_error(
    223 			message, &error, DBUS_ERROR_FAILED,
    224 			"Failed to set property");
    225 		dbus_error_free(&error);
    226 	}
    227 
    228 	return reply;
    229 }
    230 
    231 
    232 static DBusMessage *
    233 properties_get_or_set(DBusMessage *message, DBusMessageIter *iter,
    234 		      char *interface,
    235 		      struct wpa_dbus_object_desc *obj_dsc)
    236 {
    237 	const struct wpa_dbus_property_desc *property_dsc;
    238 	char *property;
    239 	const char *method;
    240 
    241 	method = dbus_message_get_member(message);
    242 	property_dsc = obj_dsc->properties;
    243 
    244 	/* Second argument: property name (DBUS_TYPE_STRING) */
    245 	if (!dbus_message_iter_next(iter) ||
    246 	    dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_STRING) {
    247 		return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS,
    248 					      NULL);
    249 	}
    250 	dbus_message_iter_get_basic(iter, &property);
    251 
    252 	while (property_dsc && property_dsc->dbus_property) {
    253 		/* compare property names and
    254 		 * interfaces */
    255 		if (!os_strncmp(property_dsc->dbus_property, property,
    256 				WPAS_DBUS_METHOD_SIGNAL_PROP_MAX) &&
    257 		    !os_strncmp(property_dsc->dbus_interface, interface,
    258 				WPAS_DBUS_INTERFACE_MAX))
    259 			break;
    260 
    261 		property_dsc++;
    262 	}
    263 	if (property_dsc == NULL || property_dsc->dbus_property == NULL) {
    264 		wpa_printf(MSG_DEBUG, "no property handler for %s.%s on %s",
    265 			   interface, property,
    266 			   dbus_message_get_path(message));
    267 		return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS,
    268 					      "No such property");
    269 	}
    270 
    271 	if (os_strncmp(WPA_DBUS_PROPERTIES_GET, method,
    272 		       WPAS_DBUS_METHOD_SIGNAL_PROP_MAX) == 0) {
    273 		wpa_printf(MSG_MSGDUMP, "%s: Get(%s)", __func__, property);
    274 		return properties_get(message, property_dsc,
    275 				      obj_dsc->user_data);
    276 	}
    277 
    278 	wpa_printf(MSG_MSGDUMP, "%s: Set(%s)", __func__, property);
    279 	return properties_set(message, property_dsc, obj_dsc->user_data);
    280 }
    281 
    282 
    283 static DBusMessage * properties_handler(DBusMessage *message,
    284 					struct wpa_dbus_object_desc *obj_dsc)
    285 {
    286 	DBusMessageIter iter;
    287 	char *interface;
    288 	const char *method;
    289 
    290 	method = dbus_message_get_member(message);
    291 	dbus_message_iter_init(message, &iter);
    292 
    293 	if (!os_strncmp(WPA_DBUS_PROPERTIES_GET, method,
    294 			WPAS_DBUS_METHOD_SIGNAL_PROP_MAX) ||
    295 	    !os_strncmp(WPA_DBUS_PROPERTIES_SET, method,
    296 			WPAS_DBUS_METHOD_SIGNAL_PROP_MAX) ||
    297 	    !os_strncmp(WPA_DBUS_PROPERTIES_GETALL, method,
    298 			WPAS_DBUS_METHOD_SIGNAL_PROP_MAX)) {
    299 		/* First argument: interface name (DBUS_TYPE_STRING) */
    300 		if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING) {
    301 			return dbus_message_new_error(message,
    302 						      DBUS_ERROR_INVALID_ARGS,
    303 						      NULL);
    304 		}
    305 
    306 		dbus_message_iter_get_basic(&iter, &interface);
    307 
    308 		if (!os_strncmp(WPA_DBUS_PROPERTIES_GETALL, method,
    309 				WPAS_DBUS_METHOD_SIGNAL_PROP_MAX)) {
    310 			/* GetAll */
    311 			return properties_get_all(message, interface, obj_dsc);
    312 		}
    313 		/* Get or Set */
    314 		return properties_get_or_set(message, &iter, interface,
    315 					     obj_dsc);
    316 	}
    317 	return dbus_message_new_error(message, DBUS_ERROR_UNKNOWN_METHOD,
    318 				      NULL);
    319 }
    320 
    321 
    322 static DBusMessage * msg_method_handler(DBusMessage *message,
    323 					struct wpa_dbus_object_desc *obj_dsc)
    324 {
    325 	const struct wpa_dbus_method_desc *method_dsc = obj_dsc->methods;
    326 	const char *method;
    327 	const char *msg_interface;
    328 
    329 	method = dbus_message_get_member(message);
    330 	msg_interface = dbus_message_get_interface(message);
    331 
    332 	/* try match call to any registered method */
    333 	while (method_dsc && method_dsc->dbus_method) {
    334 		/* compare method names and interfaces */
    335 		if (!os_strncmp(method_dsc->dbus_method, method,
    336 				WPAS_DBUS_METHOD_SIGNAL_PROP_MAX) &&
    337 		    !os_strncmp(method_dsc->dbus_interface, msg_interface,
    338 				WPAS_DBUS_INTERFACE_MAX))
    339 			break;
    340 
    341 		method_dsc++;
    342 	}
    343 	if (method_dsc == NULL || method_dsc->dbus_method == NULL) {
    344 		wpa_printf(MSG_DEBUG, "no method handler for %s.%s on %s",
    345 			   msg_interface, method,
    346 			   dbus_message_get_path(message));
    347 		return dbus_message_new_error(message,
    348 					      DBUS_ERROR_UNKNOWN_METHOD, NULL);
    349 	}
    350 
    351 	if (!is_signature_correct(message, method_dsc)) {
    352 		return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS,
    353 					      NULL);
    354 	}
    355 
    356 	return method_dsc->method_handler(message, obj_dsc->user_data);
    357 }
    358 
    359 
    360 /**
    361  * message_handler - Handles incoming DBus messages
    362  * @connection: DBus connection on which message was received
    363  * @message: Received message
    364  * @user_data: pointer to description of object to which message was sent
    365  * Returns: Returns information whether message was handled or not
    366  *
    367  * Reads message interface and method name, then checks if they matches one
    368  * of the special cases i.e. introspection call or properties get/getall/set
    369  * methods and handles it. Else it iterates over registered methods list
    370  * and tries to match method's name and interface to those read from message
    371  * If appropriate method was found its handler function is called and
    372  * response is sent. Otherwise, the DBUS_ERROR_UNKNOWN_METHOD error message
    373  * will be sent.
    374  */
    375 static DBusHandlerResult message_handler(DBusConnection *connection,
    376 					 DBusMessage *message, void *user_data)
    377 {
    378 	struct wpa_dbus_object_desc *obj_dsc = user_data;
    379 	const char *method;
    380 	const char *path;
    381 	const char *msg_interface;
    382 	DBusMessage *reply;
    383 
    384 	/* get method, interface and path the message is addressed to */
    385 	method = dbus_message_get_member(message);
    386 	path = dbus_message_get_path(message);
    387 	msg_interface = dbus_message_get_interface(message);
    388 	if (!method || !path || !msg_interface)
    389 		return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
    390 
    391 	wpa_printf(MSG_MSGDUMP, "dbus: %s.%s (%s) [%s]",
    392 		   msg_interface, method, path,
    393 		   dbus_message_get_signature(message));
    394 
    395 	/* if message is introspection method call */
    396 	if (!os_strncmp(WPA_DBUS_INTROSPECTION_METHOD, method,
    397 			WPAS_DBUS_METHOD_SIGNAL_PROP_MAX) &&
    398 	    !os_strncmp(WPA_DBUS_INTROSPECTION_INTERFACE, msg_interface,
    399 			WPAS_DBUS_INTERFACE_MAX)) {
    400 #ifdef CONFIG_CTRL_IFACE_DBUS_INTRO
    401 		reply = wpa_dbus_introspect(message, obj_dsc);
    402 #else /* CONFIG_CTRL_IFACE_DBUS_INTRO */
    403 		reply = dbus_message_new_error(
    404 			message, DBUS_ERROR_UNKNOWN_METHOD,
    405 			"wpa_supplicant was compiled without introspection support.");
    406 #endif /* CONFIG_CTRL_IFACE_DBUS_INTRO */
    407 	} else if (!os_strncmp(WPA_DBUS_PROPERTIES_INTERFACE, msg_interface,
    408 			     WPAS_DBUS_INTERFACE_MAX)) {
    409 		/* if message is properties method call */
    410 		reply = properties_handler(message, obj_dsc);
    411 	} else {
    412 		reply = msg_method_handler(message, obj_dsc);
    413 	}
    414 
    415 	/* If handler succeed returning NULL, reply empty message */
    416 	if (!reply)
    417 		reply = dbus_message_new_method_return(message);
    418 	if (reply) {
    419 		if (!dbus_message_get_no_reply(message))
    420 			dbus_connection_send(connection, reply, NULL);
    421 		dbus_message_unref(reply);
    422 	}
    423 
    424 	wpa_dbus_flush_all_changed_properties(connection);
    425 
    426 	return DBUS_HANDLER_RESULT_HANDLED;
    427 }
    428 
    429 
    430 /**
    431  * free_dbus_object_desc - Frees object description data structure
    432  * @connection: DBus connection
    433  * @obj_dsc: Object description to free
    434  *
    435  * Frees each of properties, methods and signals description lists and
    436  * the object description structure itself.
    437  */
    438 void free_dbus_object_desc(struct wpa_dbus_object_desc *obj_dsc)
    439 {
    440 	if (!obj_dsc)
    441 		return;
    442 
    443 	/* free handler's argument */
    444 	if (obj_dsc->user_data_free_func)
    445 		obj_dsc->user_data_free_func(obj_dsc->user_data);
    446 
    447 	os_free(obj_dsc->path);
    448 	os_free(obj_dsc->prop_changed_flags);
    449 	os_free(obj_dsc);
    450 }
    451 
    452 
    453 static void free_dbus_object_desc_cb(DBusConnection *connection, void *obj_dsc)
    454 {
    455 	free_dbus_object_desc(obj_dsc);
    456 }
    457 
    458 
    459 /**
    460  * wpa_dbus_ctrl_iface_init - Initialize dbus control interface
    461  * @application_data: Pointer to application specific data structure
    462  * @dbus_path: DBus path to interface object
    463  * @dbus_service: DBus service name to register with
    464  * @messageHandler: a pointer to function which will handle dbus messages
    465  * coming on interface
    466  * Returns: 0 on success, -1 on failure
    467  *
    468  * Initialize the dbus control interface and start receiving commands from
    469  * external programs over the bus.
    470  */
    471 int wpa_dbus_ctrl_iface_init(struct wpas_dbus_priv *iface,
    472 			     char *dbus_path, char *dbus_service,
    473 			     struct wpa_dbus_object_desc *obj_desc)
    474 {
    475 	DBusError error;
    476 	int ret = -1;
    477 	DBusObjectPathVTable wpa_vtable = {
    478 		&free_dbus_object_desc_cb, &message_handler,
    479 		NULL, NULL, NULL, NULL
    480 	};
    481 
    482 	obj_desc->connection = iface->con;
    483 	obj_desc->path = os_strdup(dbus_path);
    484 
    485 	/* Register the message handler for the global dbus interface */
    486 	if (!dbus_connection_register_object_path(iface->con, dbus_path,
    487 						  &wpa_vtable, obj_desc)) {
    488 		wpa_printf(MSG_ERROR, "dbus: Could not set up message handler");
    489 		return -1;
    490 	}
    491 
    492 	/* Register our service with the message bus */
    493 	dbus_error_init(&error);
    494 	switch (dbus_bus_request_name(iface->con, dbus_service, 0, &error)) {
    495 	case DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER:
    496 		ret = 0;
    497 		break;
    498 	case DBUS_REQUEST_NAME_REPLY_EXISTS:
    499 	case DBUS_REQUEST_NAME_REPLY_IN_QUEUE:
    500 	case DBUS_REQUEST_NAME_REPLY_ALREADY_OWNER:
    501 		wpa_printf(MSG_ERROR,
    502 			   "dbus: Could not request service name: already registered");
    503 		break;
    504 	default:
    505 		wpa_printf(MSG_ERROR,
    506 			   "dbus: Could not request service name: %s %s",
    507 			   error.name, error.message);
    508 		break;
    509 	}
    510 	dbus_error_free(&error);
    511 
    512 	if (ret != 0)
    513 		return -1;
    514 
    515 	wpa_printf(MSG_DEBUG, "Providing DBus service '%s'.", dbus_service);
    516 
    517 	return 0;
    518 }
    519 
    520 
    521 /**
    522  * wpa_dbus_register_object_per_iface - Register a new object with dbus
    523  * @ctrl_iface: pointer to dbus private data
    524  * @path: DBus path to object
    525  * @ifname: interface name
    526  * @obj_desc: description of object's methods, signals and properties
    527  * Returns: 0 on success, -1 on error
    528  *
    529  * Registers a new interface with dbus and assigns it a dbus object path.
    530  */
    531 int wpa_dbus_register_object_per_iface(struct wpas_dbus_priv *ctrl_iface,
    532 				       const char *path, const char *ifname,
    533 				       struct wpa_dbus_object_desc *obj_desc)
    534 {
    535 	DBusConnection *con;
    536 	DBusError error;
    537 	DBusObjectPathVTable vtable = {
    538 		&free_dbus_object_desc_cb, &message_handler,
    539 		NULL, NULL, NULL, NULL
    540 	};
    541 
    542 	/* Do nothing if the control interface is not turned on */
    543 	if (ctrl_iface == NULL)
    544 		return 0;
    545 
    546 	con = ctrl_iface->con;
    547 	obj_desc->connection = con;
    548 	obj_desc->path = os_strdup(path);
    549 
    550 	dbus_error_init(&error);
    551 	/* Register the message handler for the interface functions */
    552 	if (!dbus_connection_try_register_object_path(con, path, &vtable,
    553 						      obj_desc, &error)) {
    554 		if (os_strcmp(error.name, DBUS_ERROR_OBJECT_PATH_IN_USE) == 0) {
    555 			wpa_printf(MSG_DEBUG, "dbus: %s", error.message);
    556 		} else {
    557 			wpa_printf(MSG_ERROR,
    558 				   "dbus: Could not set up message handler for interface %s object %s (error: %s message: %s)",
    559 				   ifname, path, error.name, error.message);
    560 		}
    561 		dbus_error_free(&error);
    562 		return -1;
    563 	}
    564 
    565 	dbus_error_free(&error);
    566 	return 0;
    567 }
    568 
    569 
    570 static void flush_object_timeout_handler(void *eloop_ctx, void *timeout_ctx);
    571 
    572 
    573 /**
    574  * wpa_dbus_unregister_object_per_iface - Unregisters DBus object
    575  * @ctrl_iface: Pointer to dbus private data
    576  * @path: DBus path to object which will be unregistered
    577  * Returns: Zero on success and -1 on failure
    578  *
    579  * Unregisters DBus object given by its path
    580  */
    581 int wpa_dbus_unregister_object_per_iface(
    582 	struct wpas_dbus_priv *ctrl_iface, const char *path)
    583 {
    584 	DBusConnection *con = ctrl_iface->con;
    585 	struct wpa_dbus_object_desc *obj_desc = NULL;
    586 
    587 	dbus_connection_get_object_path_data(con, path, (void **) &obj_desc);
    588 	if (!obj_desc) {
    589 		wpa_printf(MSG_ERROR,
    590 			   "dbus: %s: Could not obtain object's private data: %s",
    591 			   __func__, path);
    592 		return 0;
    593 	}
    594 
    595 	eloop_cancel_timeout(flush_object_timeout_handler, con, obj_desc);
    596 
    597 	if (!dbus_connection_unregister_object_path(con, path))
    598 		return -1;
    599 
    600 	return 0;
    601 }
    602 
    603 
    604 static dbus_bool_t put_changed_properties(
    605 	const struct wpa_dbus_object_desc *obj_dsc, const char *interface,
    606 	DBusMessageIter *dict_iter, int clear_changed)
    607 {
    608 	DBusMessageIter entry_iter;
    609 	const struct wpa_dbus_property_desc *dsc;
    610 	int i;
    611 	DBusError error;
    612 
    613 	for (dsc = obj_dsc->properties, i = 0; dsc && dsc->dbus_property;
    614 	     dsc++, i++) {
    615 		if (obj_dsc->prop_changed_flags == NULL ||
    616 		    !obj_dsc->prop_changed_flags[i])
    617 			continue;
    618 		if (os_strcmp(dsc->dbus_interface, interface) != 0)
    619 			continue;
    620 		if (clear_changed)
    621 			obj_dsc->prop_changed_flags[i] = 0;
    622 
    623 		if (!dbus_message_iter_open_container(dict_iter,
    624 						      DBUS_TYPE_DICT_ENTRY,
    625 						      NULL, &entry_iter) ||
    626 		    !dbus_message_iter_append_basic(&entry_iter,
    627 						    DBUS_TYPE_STRING,
    628 						    &dsc->dbus_property))
    629 			return FALSE;
    630 
    631 		dbus_error_init(&error);
    632 		if (!dsc->getter(dsc, &entry_iter, &error, obj_dsc->user_data))
    633 		{
    634 			if (dbus_error_is_set(&error)) {
    635 				wpa_printf(MSG_ERROR,
    636 					   "dbus: %s: Cannot get new value of property %s: (%s) %s",
    637 					   __func__, dsc->dbus_property,
    638 					   error.name, error.message);
    639 			} else {
    640 				wpa_printf(MSG_ERROR,
    641 					   "dbus: %s: Cannot get new value of property %s",
    642 					   __func__, dsc->dbus_property);
    643 			}
    644 			dbus_error_free(&error);
    645 			return FALSE;
    646 		}
    647 
    648 		if (!dbus_message_iter_close_container(dict_iter, &entry_iter))
    649 			return FALSE;
    650 	}
    651 
    652 	return TRUE;
    653 }
    654 
    655 
    656 static void do_send_prop_changed_signal(
    657 	DBusConnection *con, const char *path, const char *interface,
    658 	const struct wpa_dbus_object_desc *obj_dsc)
    659 {
    660 	DBusMessage *msg;
    661 	DBusMessageIter signal_iter, dict_iter;
    662 
    663 	msg = dbus_message_new_signal(path, DBUS_INTERFACE_PROPERTIES,
    664 				      "PropertiesChanged");
    665 	if (msg == NULL)
    666 		return;
    667 
    668 	dbus_message_iter_init_append(msg, &signal_iter);
    669 
    670 	if (!dbus_message_iter_append_basic(&signal_iter, DBUS_TYPE_STRING,
    671 					    &interface) ||
    672 	    /* Changed properties dict */
    673 	    !dbus_message_iter_open_container(&signal_iter, DBUS_TYPE_ARRAY,
    674 					      "{sv}", &dict_iter))
    675 		goto fail;
    676 	if (!put_changed_properties(obj_dsc, interface, &dict_iter, 0)) {
    677 		dbus_message_iter_close_container(&signal_iter, &dict_iter);
    678 		goto fail;
    679 	}
    680 	if (!dbus_message_iter_close_container(&signal_iter, &dict_iter) ||
    681 	    /* Invalidated properties array (empty) */
    682 	    !dbus_message_iter_open_container(&signal_iter, DBUS_TYPE_ARRAY,
    683 					      "s", &dict_iter) ||
    684 	    !dbus_message_iter_close_container(&signal_iter, &dict_iter))
    685 		goto fail;
    686 
    687 	dbus_connection_send(con, msg, NULL);
    688 
    689 out:
    690 	dbus_message_unref(msg);
    691 	return;
    692 fail:
    693 	wpa_printf(MSG_DEBUG, "dbus: %s: Failed to construct signal", __func__);
    694 	goto out;
    695 }
    696 
    697 
    698 static void do_send_deprecated_prop_changed_signal(
    699 	DBusConnection *con, const char *path, const char *interface,
    700 	const struct wpa_dbus_object_desc *obj_dsc)
    701 {
    702 	DBusMessage *msg;
    703 	DBusMessageIter signal_iter, dict_iter;
    704 
    705 	msg = dbus_message_new_signal(path, interface, "PropertiesChanged");
    706 	if (msg == NULL)
    707 		return;
    708 
    709 	dbus_message_iter_init_append(msg, &signal_iter);
    710 
    711 	if (!dbus_message_iter_open_container(&signal_iter, DBUS_TYPE_ARRAY,
    712 					      "{sv}", &dict_iter))
    713 		goto fail;
    714 	if (!put_changed_properties(obj_dsc, interface, &dict_iter, 1)) {
    715 		dbus_message_iter_close_container(&signal_iter, &dict_iter);
    716 		goto fail;
    717 	}
    718 	if (!dbus_message_iter_close_container(&signal_iter, &dict_iter))
    719 		goto fail;
    720 
    721 	dbus_connection_send(con, msg, NULL);
    722 
    723 out:
    724 	dbus_message_unref(msg);
    725 	return;
    726 fail:
    727 	wpa_printf(MSG_DEBUG, "dbus: %s: Failed to construct signal", __func__);
    728 	goto out;
    729 }
    730 
    731 
    732 static void send_prop_changed_signal(
    733 	DBusConnection *con, const char *path, const char *interface,
    734 	const struct wpa_dbus_object_desc *obj_dsc)
    735 {
    736 	/*
    737 	 * First, send property change notification on the standardized
    738 	 * org.freedesktop.DBus.Properties interface. This call will not
    739 	 * clear the property change bits, so that they are preserved for
    740 	 * the call that follows.
    741 	 */
    742 	do_send_prop_changed_signal(con, path, interface, obj_dsc);
    743 
    744 	/*
    745 	 * Now send PropertiesChanged on our own interface for backwards
    746 	 * compatibility. This is deprecated and will be removed in a future
    747 	 * release.
    748 	 */
    749 	do_send_deprecated_prop_changed_signal(con, path, interface, obj_dsc);
    750 
    751 	/* Property change bits have now been cleared. */
    752 }
    753 
    754 
    755 static void flush_object_timeout_handler(void *eloop_ctx, void *timeout_ctx)
    756 {
    757 	DBusConnection *con = eloop_ctx;
    758 	struct wpa_dbus_object_desc *obj_desc = timeout_ctx;
    759 
    760 	wpa_printf(MSG_MSGDUMP,
    761 		   "dbus: %s: Timeout - sending changed properties of object %s",
    762 		   __func__, obj_desc->path);
    763 	wpa_dbus_flush_object_changed_properties(con, obj_desc->path);
    764 }
    765 
    766 
    767 static void recursive_flush_changed_properties(DBusConnection *con,
    768 					       const char *path)
    769 {
    770 	char **objects = NULL;
    771 	char subobj_path[WPAS_DBUS_OBJECT_PATH_MAX];
    772 	int i;
    773 
    774 	wpa_dbus_flush_object_changed_properties(con, path);
    775 
    776 	if (!dbus_connection_list_registered(con, path, &objects))
    777 		goto out;
    778 
    779 	for (i = 0; objects[i]; i++) {
    780 		os_snprintf(subobj_path, WPAS_DBUS_OBJECT_PATH_MAX,
    781 			    "%s/%s", path, objects[i]);
    782 		recursive_flush_changed_properties(con, subobj_path);
    783 	}
    784 
    785 out:
    786 	dbus_free_string_array(objects);
    787 }
    788 
    789 
    790 /**
    791  * wpa_dbus_flush_all_changed_properties - Send all PropertiesChanged signals
    792  * @con: DBus connection
    793  *
    794  * Traverses through all registered objects and sends PropertiesChanged for
    795  * each properties.
    796  */
    797 void wpa_dbus_flush_all_changed_properties(DBusConnection *con)
    798 {
    799 	recursive_flush_changed_properties(con, WPAS_DBUS_NEW_PATH);
    800 }
    801 
    802 
    803 /**
    804  * wpa_dbus_flush_object_changed_properties - Send PropertiesChanged for object
    805  * @con: DBus connection
    806  * @path: path to a DBus object for which PropertiesChanged will be sent.
    807  *
    808  * Iterates over all properties registered with object and for each interface
    809  * containing properties marked as changed, sends a PropertiesChanged signal
    810  * containing names and new values of properties that have changed.
    811  *
    812  * You need to call this function after wpa_dbus_mark_property_changed()
    813  * if you want to send PropertiesChanged signal immediately (i.e., without
    814  * waiting timeout to expire). PropertiesChanged signal for an object is sent
    815  * automatically short time after first marking property as changed. All
    816  * PropertiesChanged signals are sent automatically after responding on DBus
    817  * message, so if you marked a property changed as a result of DBus call
    818  * (e.g., param setter), you usually do not need to call this function.
    819  */
    820 void wpa_dbus_flush_object_changed_properties(DBusConnection *con,
    821 					      const char *path)
    822 {
    823 	struct wpa_dbus_object_desc *obj_desc = NULL;
    824 	const struct wpa_dbus_property_desc *dsc;
    825 	int i;
    826 
    827 	dbus_connection_get_object_path_data(con, path, (void **) &obj_desc);
    828 	if (!obj_desc)
    829 		return;
    830 	eloop_cancel_timeout(flush_object_timeout_handler, con, obj_desc);
    831 
    832 	for (dsc = obj_desc->properties, i = 0; dsc && dsc->dbus_property;
    833 	     dsc++, i++) {
    834 		if (obj_desc->prop_changed_flags == NULL ||
    835 		    !obj_desc->prop_changed_flags[i])
    836 			continue;
    837 		send_prop_changed_signal(con, path, dsc->dbus_interface,
    838 					 obj_desc);
    839 	}
    840 }
    841 
    842 
    843 #define WPA_DBUS_SEND_PROP_CHANGED_TIMEOUT 5000
    844 
    845 
    846 /**
    847  * wpa_dbus_mark_property_changed - Mark a property as changed and
    848  * @iface: dbus priv struct
    849  * @path: path to DBus object which property has changed
    850  * @interface: interface containing changed property
    851  * @property: property name which has changed
    852  *
    853  * Iterates over all properties registered with an object and marks the one
    854  * given in parameters as changed. All parameters registered for an object
    855  * within a single interface will be aggregated together and sent in one
    856  * PropertiesChanged signal when function
    857  * wpa_dbus_flush_object_changed_properties() is called.
    858  */
    859 void wpa_dbus_mark_property_changed(struct wpas_dbus_priv *iface,
    860 				    const char *path, const char *interface,
    861 				    const char *property)
    862 {
    863 	struct wpa_dbus_object_desc *obj_desc = NULL;
    864 	const struct wpa_dbus_property_desc *dsc;
    865 	int i = 0;
    866 
    867 	if (iface == NULL)
    868 		return;
    869 
    870 	dbus_connection_get_object_path_data(iface->con, path,
    871 					     (void **) &obj_desc);
    872 	if (!obj_desc) {
    873 		wpa_printf(MSG_ERROR,
    874 			   "dbus: wpa_dbus_property_changed: could not obtain object's private data: %s",
    875 			   path);
    876 		return;
    877 	}
    878 
    879 	for (dsc = obj_desc->properties; dsc && dsc->dbus_property; dsc++, i++)
    880 		if (os_strcmp(property, dsc->dbus_property) == 0 &&
    881 		    os_strcmp(interface, dsc->dbus_interface) == 0) {
    882 			if (obj_desc->prop_changed_flags)
    883 				obj_desc->prop_changed_flags[i] = 1;
    884 			break;
    885 		}
    886 
    887 	if (!dsc || !dsc->dbus_property) {
    888 		wpa_printf(MSG_ERROR,
    889 			   "dbus: wpa_dbus_property_changed: no property %s in object %s",
    890 			   property, path);
    891 		return;
    892 	}
    893 
    894 	if (!eloop_is_timeout_registered(flush_object_timeout_handler,
    895 					 iface->con, obj_desc)) {
    896 		eloop_register_timeout(0, WPA_DBUS_SEND_PROP_CHANGED_TIMEOUT,
    897 				       flush_object_timeout_handler,
    898 				       iface->con, obj_desc);
    899 	}
    900 }
    901 
    902 
    903 /**
    904  * wpa_dbus_get_object_properties - Put object's properties into dictionary
    905  * @iface: dbus priv struct
    906  * @path: path to DBus object which properties will be obtained
    907  * @interface: interface name which properties will be obtained
    908  * @iter: DBus message iter at which to append property dictionary.
    909  *
    910  * Iterates over all properties registered with object and execute getters
    911  * of those, which are readable and which interface matches interface
    912  * specified as argument. Obtained properties values are stored in
    913  * dict_iter dictionary.
    914  */
    915 dbus_bool_t wpa_dbus_get_object_properties(struct wpas_dbus_priv *iface,
    916 					   const char *path,
    917 					   const char *interface,
    918 					   DBusMessageIter *iter)
    919 {
    920 	struct wpa_dbus_object_desc *obj_desc = NULL;
    921 	DBusMessageIter dict_iter;
    922 	DBusError error;
    923 
    924 	dbus_connection_get_object_path_data(iface->con, path,
    925 					     (void **) &obj_desc);
    926 	if (!obj_desc) {
    927 		wpa_printf(MSG_ERROR,
    928 			   "dbus: %s: could not obtain object's private data: %s",
    929 			   __func__, path);
    930 		return FALSE;
    931 	}
    932 
    933 	if (!wpa_dbus_dict_open_write(iter, &dict_iter)) {
    934 		wpa_printf(MSG_ERROR, "dbus: %s: failed to open message dict",
    935 			   __func__);
    936 		return FALSE;
    937 	}
    938 
    939 	dbus_error_init(&error);
    940 	if (!fill_dict_with_properties(&dict_iter, obj_desc->properties,
    941 				       interface, obj_desc->user_data,
    942 				       &error)) {
    943 		wpa_printf(MSG_ERROR,
    944 			   "dbus: %s: failed to get object properties: (%s) %s",
    945 			   __func__,
    946 			   dbus_error_is_set(&error) ? error.name : "none",
    947 			   dbus_error_is_set(&error) ? error.message : "none");
    948 		dbus_error_free(&error);
    949 		wpa_dbus_dict_close_write(iter, &dict_iter);
    950 		return FALSE;
    951 	}
    952 
    953 	return wpa_dbus_dict_close_write(iter, &dict_iter);
    954 }
    955 
    956 /**
    957  * wpas_dbus_new_decompose_object_path - Decompose an interface object path into parts
    958  * @path: The dbus object path
    959  * @sep: Separating part (e.g., "Networks" or "PersistentGroups")
    960  * @item: (out) The part following the specified separator, if any
    961  * Returns: The object path of the interface this path refers to
    962  *
    963  * For a given object path, decomposes the object path into object id and
    964  * requested part, if those parts exist. The caller is responsible for freeing
    965  * the returned value. The *item pointer points to that allocated value and must
    966  * not be freed separately.
    967  *
    968  * As an example, path = "/fi/w1/wpa_supplicant1/Interfaces/1/Networks/0" and
    969  * sep = "Networks" would result in "/fi/w1/wpa_supplicant1/Interfaces/1"
    970  * getting returned and *items set to point to "0".
    971  */
    972 char * wpas_dbus_new_decompose_object_path(const char *path, const char *sep,
    973 					   char **item)
    974 {
    975 	const unsigned int dev_path_prefix_len =
    976 		os_strlen(WPAS_DBUS_NEW_PATH_INTERFACES "/");
    977 	char *obj_path_only;
    978 	char *pos;
    979 	size_t sep_len;
    980 
    981 	*item = NULL;
    982 
    983 	/* Verify that this starts with our interface prefix */
    984 	if (os_strncmp(path, WPAS_DBUS_NEW_PATH_INTERFACES "/",
    985 		       dev_path_prefix_len) != 0)
    986 		return NULL; /* not our path */
    987 
    988 	/* Ensure there's something at the end of the path */
    989 	if ((path + dev_path_prefix_len)[0] == '\0')
    990 		return NULL;
    991 
    992 	obj_path_only = os_strdup(path);
    993 	if (obj_path_only == NULL)
    994 		return NULL;
    995 
    996 	pos = obj_path_only + dev_path_prefix_len;
    997 	pos = os_strchr(pos, '/');
    998 	if (pos == NULL)
    999 		return obj_path_only; /* no next item on the path */
   1000 
   1001 	 /* Separate network interface prefix from the path */
   1002 	*pos++ = '\0';
   1003 
   1004 	sep_len = os_strlen(sep);
   1005 	if (os_strncmp(pos, sep, sep_len) != 0 || pos[sep_len] != '/')
   1006 		return obj_path_only; /* no match */
   1007 
   1008 	 /* return a pointer to the requested item */
   1009 	*item = pos + sep_len + 1;
   1010 	return obj_path_only;
   1011 }
   1012 
   1013 
   1014 /**
   1015  * wpas_dbus_reply_new_from_error - Create a new D-Bus error message from a
   1016  *   dbus error structure
   1017  * @message: The original request message for which the error is a reply
   1018  * @error: The error containing a name and a descriptive error cause
   1019  * @fallback_name: A generic error name if @error was not set
   1020  * @fallback_string: A generic error string if @error was not set
   1021  * Returns: A new D-Bus error message
   1022  *
   1023  * Given a DBusMessage structure, creates a new D-Bus error message using
   1024  * the error name and string contained in that structure.
   1025  */
   1026 DBusMessage * wpas_dbus_reply_new_from_error(DBusMessage *message,
   1027 					     DBusError *error,
   1028 					     const char *fallback_name,
   1029 					     const char *fallback_string)
   1030 {
   1031 	if (error && error->name && error->message) {
   1032 		return dbus_message_new_error(message, error->name,
   1033 					      error->message);
   1034 	}
   1035 	if (fallback_name && fallback_string) {
   1036 		return dbus_message_new_error(message, fallback_name,
   1037 					      fallback_string);
   1038 	}
   1039 	return NULL;
   1040 }
   1041 
   1042 
   1043 static double guard_interval_to_double(enum guard_interval value)
   1044 {
   1045 	switch (value) {
   1046 	case GUARD_INTERVAL_0_4:
   1047 		return 0.4;
   1048 	case GUARD_INTERVAL_0_8:
   1049 		return 0.8;
   1050 	case GUARD_INTERVAL_1_6:
   1051 		return 1.6;
   1052 	case GUARD_INTERVAL_3_2:
   1053 		return 3.2;
   1054 	default:
   1055 		return 0;
   1056 	}
   1057 }
   1058 
   1059 
   1060 /**
   1061  * wpas_dbus_new_from_signal_information - Adds a wpa_signal_info
   1062  * to a DBusMessage.
   1063  * @msg: Pointer to message to append fields to
   1064  * @si: Pointer to wpa_signal_info to add to the message
   1065  * Returns: 0 on success, otherwise, an errorcode
   1066  *
   1067  * Adds all the pertinent fields from a wpa_signal_info to a DBusMessage.
   1068  * The same logic is useful in both responding to signal_poll calls, and
   1069  * sending signal_change signals.
   1070  */
   1071 int wpas_dbus_new_from_signal_information(DBusMessageIter *iter,
   1072 					  struct wpa_signal_info *si)
   1073 {
   1074 	DBusMessageIter iter_dict, variant_iter;
   1075 
   1076 	if (!dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT,
   1077 					      "a{sv}", &variant_iter) ||
   1078 	    !wpa_dbus_dict_open_write(&variant_iter, &iter_dict) ||
   1079 	    !wpa_dbus_dict_append_int32(&iter_dict, "rssi",
   1080 					si->data.signal) ||
   1081 	    !wpa_dbus_dict_append_uint32(&iter_dict, "linkspeed",
   1082 					si->data.current_tx_rate / 1000) ||
   1083 	    !wpa_dbus_dict_append_int32(&iter_dict, "noise",
   1084 					si->current_noise) ||
   1085 	    !wpa_dbus_dict_append_uint32(&iter_dict, "frequency",
   1086 					 si->frequency) ||
   1087 	    (si->chanwidth != CHAN_WIDTH_UNKNOWN &&
   1088 	     !wpa_dbus_dict_append_string(
   1089 		     &iter_dict, "width",
   1090 		     channel_width_to_string(si->chanwidth))) ||
   1091 	    (si->center_frq1 > 0 && si->center_frq2 > 0 &&
   1092 	     (!wpa_dbus_dict_append_int32(&iter_dict, "center-frq1",
   1093 					  si->center_frq1) ||
   1094 	      !wpa_dbus_dict_append_int32(&iter_dict, "center-frq2",
   1095 					  si->center_frq2))) ||
   1096 	    (si->data.avg_signal &&
   1097 	     !wpa_dbus_dict_append_int32(&iter_dict, "avg-rssi",
   1098 					 si->data.avg_signal)) ||
   1099 	    (si->data.rx_bytes &&
   1100 	     !wpa_dbus_dict_append_uint64(&iter_dict, "rx-bytes",
   1101 					  si->data.rx_bytes)) ||
   1102 	    (si->data.tx_bytes &&
   1103 	     !wpa_dbus_dict_append_uint64(&iter_dict, "tx-bytes",
   1104 					  si->data.tx_bytes)) ||
   1105 	    (si->data.rx_packets &&
   1106 	     !wpa_dbus_dict_append_uint32(&iter_dict, "rx-packets",
   1107 					  si->data.rx_packets)) ||
   1108 	    (si->data.tx_packets &&
   1109 	     !wpa_dbus_dict_append_uint32(&iter_dict, "tx-packets",
   1110 					  si->data.tx_packets)) ||
   1111 	    (si->data.beacons_count &&
   1112 	     !wpa_dbus_dict_append_uint64(&iter_dict, "beacons",
   1113 					  si->data.beacons_count)) ||
   1114 	    (si->data.current_rx_rate &&
   1115 	     !wpa_dbus_dict_append_uint32(&iter_dict, "linkrxspeed",
   1116 					  si->data.current_rx_rate)) ||
   1117 	    (si->data.current_rx_rate &&
   1118 	     !wpa_dbus_dict_append_uint32(&iter_dict, "linktxspeed",
   1119 					  si->data.current_tx_rate)) ||
   1120 	    (si->data.inactive_msec &&
   1121 	     !wpa_dbus_dict_append_uint32(&iter_dict, "inactive-time",
   1122 					 si->data.inactive_msec)) ||
   1123 	    (si->data.tx_retry_failed &&
   1124 	     !wpa_dbus_dict_append_uint32(&iter_dict, "retries-failed",
   1125 					  si->data.tx_retry_failed)) ||
   1126 	    (si->data.tx_retry_count &&
   1127 	     !wpa_dbus_dict_append_uint32(&iter_dict, "retries",
   1128 					  si->data.tx_retry_count)) ||
   1129 	    (si->data.last_ack_rssi &&
   1130 	     !wpa_dbus_dict_append_int32(&iter_dict, "last-ack-rssi",
   1131 					 si->data.last_ack_rssi)) ||
   1132 	    (si->data.fcs_error_count &&
   1133 	     !wpa_dbus_dict_append_uint32(&iter_dict, "fcs-errors",
   1134 					  si->data.fcs_error_count)) ||
   1135 	    (si->data.beacon_loss_count &&
   1136 	     !wpa_dbus_dict_append_uint32(&iter_dict, "beacon-losses",
   1137 					  si->data.beacon_loss_count)) ||
   1138 	    (si->data.expected_throughput &&
   1139 	     !wpa_dbus_dict_append_uint32(&iter_dict, "expected-throughput",
   1140 					  si->data.expected_throughput)) ||
   1141 	    (si->data.rx_drop_misc &&
   1142 	     !wpa_dbus_dict_append_uint32(&iter_dict, "rx-drop-misc",
   1143 					  si->data.rx_drop_misc)) ||
   1144 	    (si->data.rx_mpdus &&
   1145 	     !wpa_dbus_dict_append_uint32(&iter_dict, "rx-mpdus",
   1146 					  si->data.rx_mpdus)) ||
   1147 	    (si->data.rx_hemcs &&
   1148 	     !wpa_dbus_dict_append_uint32(&iter_dict, "rx-he-mcs",
   1149 					  si->data.rx_hemcs)) ||
   1150 	    (si->data.tx_hemcs &&
   1151 	     !wpa_dbus_dict_append_uint32(&iter_dict, "tx-he-mcs",
   1152 					  si->data.tx_hemcs)) ||
   1153 	    (si->data.rx_vhtmcs &&
   1154 	     !wpa_dbus_dict_append_uint32(&iter_dict, "rx-vht-mcs",
   1155 					  si->data.rx_vhtmcs)) ||
   1156 	    (si->data.tx_vhtmcs &&
   1157 	     !wpa_dbus_dict_append_uint32(&iter_dict, "tx-vht-mcs",
   1158 					  si->data.tx_vhtmcs)) ||
   1159 	    (si->data.rx_mcs &&
   1160 	     !wpa_dbus_dict_append_uint32(&iter_dict, "rx-mcs",
   1161 					  si->data.rx_mcs)) ||
   1162 	    (si->data.tx_mcs &&
   1163 	     !wpa_dbus_dict_append_uint32(&iter_dict, "tx-mcs",
   1164 					  si->data.tx_mcs)) ||
   1165 	    (si->data.rx_he_nss &&
   1166 	     !wpa_dbus_dict_append_uint32(&iter_dict, "rx-he-nss",
   1167 					  si->data.rx_he_nss)) ||
   1168 	    (si->data.tx_he_nss &&
   1169 	     !wpa_dbus_dict_append_uint32(&iter_dict, "tx-he-nss",
   1170 					  si->data.tx_he_nss)) ||
   1171 	    (si->data.rx_vht_nss &&
   1172 	     !wpa_dbus_dict_append_uint32(&iter_dict, "rx-vht-nss",
   1173 					  si->data.rx_vht_nss)) ||
   1174 	    (si->data.tx_vht_nss &&
   1175 	     !wpa_dbus_dict_append_uint32(&iter_dict, "tx-vht-nss",
   1176 					  si->data.tx_vht_nss)) ||
   1177 	    (si->data.avg_beacon_signal &&
   1178 	     !wpa_dbus_dict_append_int32(&iter_dict, "avg-beacon-rssi",
   1179 					 si->data.avg_beacon_signal)) ||
   1180 	    (si->data.avg_ack_signal &&
   1181 	     !wpa_dbus_dict_append_int32(&iter_dict, "avg-ack-rssi",
   1182 					 si->data.avg_ack_signal)) ||
   1183 	    (si->data.rx_guard_interval &&
   1184 	     !wpa_dbus_dict_append_double(
   1185 		     &iter_dict, "rx-guard-interval",
   1186 		     guard_interval_to_double(si->data.rx_guard_interval))) ||
   1187 	    (si->data.tx_guard_interval &&
   1188 	     !wpa_dbus_dict_append_double(
   1189 		     &iter_dict, "tx-guard-interval",
   1190 		     guard_interval_to_double(si->data.tx_guard_interval))) ||
   1191 	    ((si->data.flags & STA_DRV_DATA_RX_HE_DCM) &&
   1192 	     !wpa_dbus_dict_append_bool(&iter_dict, "rx-dcm",
   1193 					si->data.rx_dcm)) ||
   1194 	    ((si->data.flags & STA_DRV_DATA_TX_HE_DCM) &&
   1195 	     !wpa_dbus_dict_append_bool(&iter_dict, "tx-dcm",
   1196 					si->data.tx_dcm)) ||
   1197 	    !wpa_dbus_dict_close_write(&variant_iter, &iter_dict) ||
   1198 	    !dbus_message_iter_close_container(iter, &variant_iter))
   1199 		return -ENOMEM;
   1200 
   1201 	return 0;
   1202 }
   1203