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