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