LCOV - code coverage report
Current view: top level - wpa_supplicant/dbus - dbus_new_helpers.c (source / functions) Hit Total Coverage
Test: wpa_supplicant/hostapd combined for hwsim test run 1426431149 Lines: 319 368 86.7 %
Date: 2015-03-15 Functions: 27 27 100.0 %

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

Generated by: LCOV version 1.10