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