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

Generated by: LCOV version 1.10