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 1475438200 Lines: 324 369 87.8 %
Date: 2016-10-02 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        6453 : 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       63614 :         for (dsc = props; dsc && dsc->dbus_property; dsc++) {
      31             :                 /* Only return properties for the requested D-Bus interface */
      32       57188 :                 if (os_strncmp(dsc->dbus_interface, interface,
      33             :                                WPAS_DBUS_INTERFACE_MAX) != 0)
      34        1947 :                         continue;
      35             : 
      36             :                 /* Skip write-only properties */
      37       55241 :                 if (dsc->getter == NULL)
      38           0 :                         continue;
      39             : 
      40       55241 :                 if (!dbus_message_iter_open_container(dict_iter,
      41             :                                                       DBUS_TYPE_DICT_ENTRY,
      42       55241 :                                                       NULL, &entry_iter) ||
      43       55241 :                     !dbus_message_iter_append_basic(&entry_iter,
      44             :                                                     DBUS_TYPE_STRING,
      45       55241 :                                                     &dsc->dbus_property))
      46             :                         goto error;
      47             : 
      48             :                 /* An error getting a property fails the request entirely */
      49       55241 :                 if (!dsc->getter(dsc, &entry_iter, error, user_data)) {
      50          27 :                         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          27 :                         return FALSE;
      55             :                 }
      56             : 
      57       55214 :                 if (!dbus_message_iter_close_container(dict_iter, &entry_iter))
      58           0 :                         goto error;
      59             :         }
      60             : 
      61        6426 :         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          75 : 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          75 :         reply = dbus_message_new_method_return(message);
      89          75 :         if (reply == NULL)
      90           0 :                 return wpas_dbus_error_no_memory(message);
      91             : 
      92          75 :         dbus_message_iter_init_append(reply, &iter);
      93          75 :         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          75 :         dbus_error_init(&error);
      99          75 :         if (!fill_dict_with_properties(&dict_iter, obj_dsc->properties,
     100             :                                        interface, obj_dsc->user_data, &error)) {
     101          24 :                 dbus_message_unref(reply);
     102          24 :                 reply = wpas_dbus_reply_new_from_error(
     103             :                         message, &error, DBUS_ERROR_INVALID_ARGS,
     104             :                         "No readable properties in this interface");
     105          24 :                 dbus_error_free(&error);
     106          24 :                 return reply;
     107             :         }
     108             : 
     109          51 :         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          51 :         return reply;
     115             : }
     116             : 
     117             : 
     118         860 : 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         860 :         const char *sig = dbus_message_get_signature(message);
     125             :         int ret;
     126             :         const struct wpa_dbus_argument *arg;
     127             : 
     128         860 :         pos = registered_sig;
     129         860 :         *pos = '\0';
     130             : 
     131        1835 :         for (arg = method_dsc->args; arg && arg->name; arg++) {
     132         975 :                 if (arg->dir == ARG_IN) {
     133         664 :                         size_t blen = registered_sig + MAX_SIG_LEN - pos;
     134             : 
     135         664 :                         ret = os_snprintf(pos, blen, "%s", arg->type);
     136         664 :                         if (os_snprintf_error(blen, ret))
     137           0 :                                 return 0;
     138         664 :                         pos += ret;
     139             :                 }
     140             :         }
     141             : 
     142         860 :         return !os_strncmp(registered_sig, sig, MAX_SIG_LEN);
     143             : }
     144             : 
     145             : 
     146          75 : static DBusMessage * properties_get_all(DBusMessage *message, char *interface,
     147             :                                         struct wpa_dbus_object_desc *obj_dsc)
     148             : {
     149          75 :         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          75 :         return get_all_properties(message, interface, obj_dsc);
     154             : }
     155             : 
     156             : 
     157          97 : 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          97 :         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          97 :         if (dsc->getter == NULL) {
     171           0 :                 return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS,
     172             :                                               "Property is write-only");
     173             :         }
     174             : 
     175          97 :         reply = dbus_message_new_method_return(message);
     176          97 :         dbus_message_iter_init_append(reply, &iter);
     177             : 
     178          97 :         dbus_error_init(&error);
     179          97 :         if (dsc->getter(dsc, &iter, &error, user_data) == FALSE) {
     180          16 :                 dbus_message_unref(reply);
     181          16 :                 reply = wpas_dbus_reply_new_from_error(
     182             :                         message, &error, DBUS_ERROR_FAILED,
     183             :                         "Failed to read property");
     184          16 :                 dbus_error_free(&error);
     185             :         }
     186             : 
     187          97 :         return reply;
     188             : }
     189             : 
     190             : 
     191          70 : 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          70 :         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          70 :         if (dsc->setter == NULL) {
     205           1 :                 return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS,
     206             :                                               "Property is read-only");
     207             :         }
     208             : 
     209          69 :         dbus_message_iter_init(message, &iter);
     210             :         /* Skip the interface name and the property name */
     211          69 :         dbus_message_iter_next(&iter);
     212          69 :         dbus_message_iter_next(&iter);
     213             : 
     214             :         /* Iter will now point to the property's new value */
     215          69 :         dbus_error_init(&error);
     216          69 :         if (dsc->setter(dsc, &iter, &error, user_data) == TRUE) {
     217             :                 /* Success */
     218          41 :                 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          69 :         return reply;
     227             : }
     228             : 
     229             : 
     230             : static DBusMessage *
     231         169 : 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         169 :         method = dbus_message_get_member(message);
     240         169 :         property_dsc = obj_dsc->properties;
     241             : 
     242             :         /* Second argument: property name (DBUS_TYPE_STRING) */
     243         338 :         if (!dbus_message_iter_next(iter) ||
     244         169 :             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         168 :         dbus_message_iter_get_basic(iter, &property);
     249             : 
     250        2575 :         while (property_dsc && property_dsc->dbus_property) {
     251             :                 /* compare property names and
     252             :                  * interfaces */
     253        2406 :                 if (!os_strncmp(property_dsc->dbus_property, property,
     254         168 :                                 WPAS_DBUS_METHOD_SIGNAL_PROP_MAX) &&
     255         168 :                     !os_strncmp(property_dsc->dbus_interface, interface,
     256             :                                 WPAS_DBUS_INTERFACE_MAX))
     257         167 :                         break;
     258             : 
     259        2239 :                 property_dsc++;
     260             :         }
     261         168 :         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         167 :         if (os_strncmp(WPA_DBUS_PROPERTIES_GET, method,
     270             :                        WPAS_DBUS_METHOD_SIGNAL_PROP_MAX) == 0) {
     271          97 :                 wpa_printf(MSG_MSGDUMP, "%s: Get(%s)", __func__, property);
     272          97 :                 return properties_get(message, property_dsc,
     273             :                                       obj_dsc->user_data);
     274             :         }
     275             : 
     276          70 :         wpa_printf(MSG_MSGDUMP, "%s: Set(%s)", __func__, property);
     277          70 :         return properties_set(message, property_dsc, obj_dsc->user_data);
     278             : }
     279             : 
     280             : 
     281         246 : 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         246 :         method = dbus_message_get_member(message);
     289         246 :         dbus_message_iter_init(message, &iter);
     290             : 
     291         246 :         if (!os_strncmp(WPA_DBUS_PROPERTIES_GET, method,
     292         146 :                         WPAS_DBUS_METHOD_SIGNAL_PROP_MAX) ||
     293         146 :             !os_strncmp(WPA_DBUS_PROPERTIES_SET, method,
     294          76 :                         WPAS_DBUS_METHOD_SIGNAL_PROP_MAX) ||
     295          76 :             !os_strncmp(WPA_DBUS_PROPERTIES_GETALL, method,
     296             :                         WPAS_DBUS_METHOD_SIGNAL_PROP_MAX)) {
     297             :                 /* First argument: interface name (DBUS_TYPE_STRING) */
     298         245 :                 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         244 :                 dbus_message_iter_get_basic(&iter, &interface);
     305             : 
     306         244 :                 if (!os_strncmp(WPA_DBUS_PROPERTIES_GETALL, method,
     307             :                                 WPAS_DBUS_METHOD_SIGNAL_PROP_MAX)) {
     308             :                         /* GetAll */
     309          75 :                         return properties_get_all(message, interface, obj_dsc);
     310             :                 }
     311             :                 /* Get or Set */
     312         169 :                 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         861 : static DBusMessage * msg_method_handler(DBusMessage *message,
     321             :                                         struct wpa_dbus_object_desc *obj_dsc)
     322             : {
     323         861 :         const struct wpa_dbus_method_desc *method_dsc = obj_dsc->methods;
     324             :         const char *method;
     325             :         const char *msg_interface;
     326             : 
     327         861 :         method = dbus_message_get_member(message);
     328         861 :         msg_interface = dbus_message_get_interface(message);
     329             : 
     330             :         /* try match call to any registered method */
     331       11355 :         while (method_dsc && method_dsc->dbus_method) {
     332             :                 /* compare method names and interfaces */
     333       10493 :                 if (!os_strncmp(method_dsc->dbus_method, method,
     334         876 :                                 WPAS_DBUS_METHOD_SIGNAL_PROP_MAX) &&
     335         876 :                     !os_strncmp(method_dsc->dbus_interface, msg_interface,
     336             :                                 WPAS_DBUS_INTERFACE_MAX))
     337         860 :                         break;
     338             : 
     339        9633 :                 method_dsc++;
     340             :         }
     341         861 :         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         860 :         if (!is_signature_correct(message, method_dsc)) {
     350           2 :                 return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS,
     351             :                                               NULL);
     352             :         }
     353             : 
     354         858 :         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        1301 : static DBusHandlerResult message_handler(DBusConnection *connection,
     374             :                                          DBusMessage *message, void *user_data)
     375             : {
     376        1301 :         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        1301 :         method = dbus_message_get_member(message);
     384        1301 :         path = dbus_message_get_path(message);
     385        1301 :         msg_interface = dbus_message_get_interface(message);
     386        1301 :         if (!method || !path || !msg_interface)
     387           0 :                 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
     388             : 
     389        1301 :         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        1301 :         if (!os_strncmp(WPA_DBUS_INTROSPECTION_METHOD, method,
     395         194 :                         WPAS_DBUS_METHOD_SIGNAL_PROP_MAX) &&
     396         194 :             !os_strncmp(WPA_DBUS_INTROSPECTION_INTERFACE, msg_interface,
     397             :                         WPAS_DBUS_INTERFACE_MAX)) {
     398             : #ifdef CONFIG_CTRL_IFACE_DBUS_INTRO
     399         194 :                 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        1107 :         } else if (!os_strncmp(WPA_DBUS_PROPERTIES_INTERFACE, msg_interface,
     406             :                              WPAS_DBUS_INTERFACE_MAX)) {
     407             :                 /* if message is properties method call */
     408         246 :                 reply = properties_handler(message, obj_dsc);
     409             :         } else {
     410         861 :                 reply = msg_method_handler(message, obj_dsc);
     411             :         }
     412             : 
     413             :         /* If handler succeed returning NULL, reply empty message */
     414        1301 :         if (!reply)
     415         416 :                 reply = dbus_message_new_method_return(message);
     416        1301 :         if (reply) {
     417        1301 :                 if (!dbus_message_get_no_reply(message))
     418        1301 :                         dbus_connection_send(connection, reply, NULL);
     419        1301 :                 dbus_message_unref(reply);
     420             :         }
     421             : 
     422        1301 :         wpa_dbus_flush_all_changed_properties(connection);
     423             : 
     424        1301 :         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        6744 : void free_dbus_object_desc(struct wpa_dbus_object_desc *obj_dsc)
     437             : {
     438        6744 :         if (!obj_dsc)
     439        6746 :                 return;
     440             : 
     441             :         /* free handler's argument */
     442        6742 :         if (obj_dsc->user_data_free_func)
     443        6209 :                 obj_dsc->user_data_free_func(obj_dsc->user_data);
     444             : 
     445        6742 :         os_free(obj_dsc->path);
     446        6742 :         os_free(obj_dsc->prop_changed_flags);
     447        6742 :         os_free(obj_dsc);
     448             : }
     449             : 
     450             : 
     451        6741 : static void free_dbus_object_desc_cb(DBusConnection *connection, void *obj_dsc)
     452             : {
     453        6741 :         free_dbus_object_desc(obj_dsc);
     454        6741 : }
     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        6735 : 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        6735 :         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        6735 :         if (ctrl_iface == NULL)
     542           0 :                 return 0;
     543             : 
     544        6735 :         con = ctrl_iface->con;
     545        6735 :         obj_desc->connection = con;
     546        6735 :         obj_desc->path = os_strdup(path);
     547             : 
     548        6735 :         dbus_error_init(&error);
     549             :         /* Register the message handler for the interface functions */
     550        6735 :         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        6735 :         dbus_error_free(&error);
     564        6735 :         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        6739 : int wpa_dbus_unregister_object_per_iface(
     580             :         struct wpas_dbus_priv *ctrl_iface, const char *path)
     581             : {
     582        6739 :         DBusConnection *con = ctrl_iface->con;
     583        6739 :         struct wpa_dbus_object_desc *obj_desc = NULL;
     584             : 
     585        6739 :         dbus_connection_get_object_path_data(con, path, (void **) &obj_desc);
     586        6739 :         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        6735 :         eloop_cancel_timeout(flush_object_timeout_handler, con, obj_desc);
     594             : 
     595        6735 :         if (!dbus_connection_unregister_object_path(con, path))
     596           0 :                 return -1;
     597             : 
     598        6735 :         return 0;
     599             : }
     600             : 
     601             : 
     602       62110 : 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     7476888 :         for (dsc = obj_dsc->properties, i = 0; dsc && dsc->dbus_property;
     612     7352668 :              dsc++, i++) {
     613    14705344 :                 if (obj_dsc->prop_changed_flags == NULL ||
     614     7352672 :                     !obj_dsc->prop_changed_flags[i])
     615     7253030 :                         continue;
     616       99642 :                 if (os_strcmp(dsc->dbus_interface, interface) != 0)
     617           0 :                         continue;
     618       99642 :                 if (clear_changed)
     619       49821 :                         obj_dsc->prop_changed_flags[i] = 0;
     620             : 
     621       99642 :                 if (!dbus_message_iter_open_container(dict_iter,
     622             :                                                       DBUS_TYPE_DICT_ENTRY,
     623       99642 :                                                       NULL, &entry_iter) ||
     624       99642 :                     !dbus_message_iter_append_basic(&entry_iter,
     625             :                                                     DBUS_TYPE_STRING,
     626       99642 :                                                     &dsc->dbus_property))
     627           0 :                         return FALSE;
     628             : 
     629       99642 :                 dbus_error_init(&error);
     630       99642 :                 if (!dsc->getter(dsc, &entry_iter, &error, obj_dsc->user_data))
     631             :                 {
     632           4 :                         if (dbus_error_is_set(&error)) {
     633           4 :                                 wpa_printf(MSG_ERROR,
     634             :                                            "dbus: %s: Cannot get new value of property %s: (%s) %s",
     635             :                                            __func__, dsc->dbus_property,
     636             :                                            error.name, error.message);
     637             :                         } else {
     638           0 :                                 wpa_printf(MSG_ERROR,
     639             :                                            "dbus: %s: Cannot get new value of property %s",
     640             :                                            __func__, dsc->dbus_property);
     641             :                         }
     642           4 :                         dbus_error_free(&error);
     643           4 :                         return FALSE;
     644             :                 }
     645             : 
     646       99638 :                 if (!dbus_message_iter_close_container(dict_iter, &entry_iter))
     647           0 :                         return FALSE;
     648             :         }
     649             : 
     650       62106 :         return TRUE;
     651             : }
     652             : 
     653             : 
     654       31055 : static void do_send_prop_changed_signal(
     655             :         DBusConnection *con, const char *path, const char *interface,
     656             :         const struct wpa_dbus_object_desc *obj_dsc)
     657             : {
     658             :         DBusMessage *msg;
     659             :         DBusMessageIter signal_iter, dict_iter;
     660             : 
     661       31055 :         msg = dbus_message_new_signal(path, DBUS_INTERFACE_PROPERTIES,
     662             :                                       "PropertiesChanged");
     663       31055 :         if (msg == NULL)
     664       31055 :                 return;
     665             : 
     666       31055 :         dbus_message_iter_init_append(msg, &signal_iter);
     667             : 
     668       31055 :         if (!dbus_message_iter_append_basic(&signal_iter, DBUS_TYPE_STRING,
     669       31055 :                                             &interface) ||
     670             :             /* Changed properties dict */
     671       31055 :             !dbus_message_iter_open_container(&signal_iter, DBUS_TYPE_ARRAY,
     672       31055 :                                               "{sv}", &dict_iter) ||
     673       62108 :             !put_changed_properties(obj_dsc, interface, &dict_iter, 0) ||
     674       62106 :             !dbus_message_iter_close_container(&signal_iter, &dict_iter) ||
     675             :             /* Invalidated properties array (empty) */
     676       31053 :             !dbus_message_iter_open_container(&signal_iter, DBUS_TYPE_ARRAY,
     677       31053 :                                               "s", &dict_iter) ||
     678       31053 :             !dbus_message_iter_close_container(&signal_iter, &dict_iter)) {
     679           2 :                 wpa_printf(MSG_DEBUG, "dbus: %s: Failed to construct signal",
     680             :                            __func__);
     681             :         } else {
     682       31053 :                 dbus_connection_send(con, msg, NULL);
     683             :         }
     684             : 
     685       31055 :         dbus_message_unref(msg);
     686             : }
     687             : 
     688             : 
     689       31055 : static void do_send_deprecated_prop_changed_signal(
     690             :         DBusConnection *con, const char *path, const char *interface,
     691             :         const struct wpa_dbus_object_desc *obj_dsc)
     692             : {
     693             :         DBusMessage *msg;
     694             :         DBusMessageIter signal_iter, dict_iter;
     695             : 
     696       31055 :         msg = dbus_message_new_signal(path, interface, "PropertiesChanged");
     697       31055 :         if (msg == NULL)
     698       31055 :                 return;
     699             : 
     700       31055 :         dbus_message_iter_init_append(msg, &signal_iter);
     701             : 
     702       31055 :         if (!dbus_message_iter_open_container(&signal_iter, DBUS_TYPE_ARRAY,
     703       31055 :                                               "{sv}", &dict_iter) ||
     704       62108 :             !put_changed_properties(obj_dsc, interface, &dict_iter, 1) ||
     705       31053 :             !dbus_message_iter_close_container(&signal_iter, &dict_iter)) {
     706           2 :                 wpa_printf(MSG_DEBUG, "dbus: %s: Failed to construct signal",
     707             :                            __func__);
     708             :         } else {
     709       31053 :                 dbus_connection_send(con, msg, NULL);
     710             :         }
     711             : 
     712       31055 :         dbus_message_unref(msg);
     713             : }
     714             : 
     715             : 
     716       31055 : static void send_prop_changed_signal(
     717             :         DBusConnection *con, const char *path, const char *interface,
     718             :         const struct wpa_dbus_object_desc *obj_dsc)
     719             : {
     720             :         /*
     721             :          * First, send property change notification on the standardized
     722             :          * org.freedesktop.DBus.Properties interface. This call will not
     723             :          * clear the property change bits, so that they are preserved for
     724             :          * the call that follows.
     725             :          */
     726       31055 :         do_send_prop_changed_signal(con, path, interface, obj_dsc);
     727             : 
     728             :         /*
     729             :          * Now send PropertiesChanged on our own interface for backwards
     730             :          * compatibility. This is deprecated and will be removed in a future
     731             :          * release.
     732             :          */
     733       31055 :         do_send_deprecated_prop_changed_signal(con, path, interface, obj_dsc);
     734             : 
     735             :         /* Property change bits have now been cleared. */
     736       31055 : }
     737             : 
     738             : 
     739       27498 : static void flush_object_timeout_handler(void *eloop_ctx, void *timeout_ctx)
     740             : {
     741       27498 :         DBusConnection *con = eloop_ctx;
     742       27498 :         struct wpa_dbus_object_desc *obj_desc = timeout_ctx;
     743             : 
     744       27498 :         wpa_printf(MSG_DEBUG,
     745             :                    "dbus: %s: Timeout - sending changed properties of object %s",
     746             :                    __func__, obj_desc->path);
     747       27498 :         wpa_dbus_flush_object_changed_properties(con, obj_desc->path);
     748       27498 : }
     749             : 
     750             : 
     751       42103 : static void recursive_flush_changed_properties(DBusConnection *con,
     752             :                                                const char *path)
     753             : {
     754       42103 :         char **objects = NULL;
     755             :         char subobj_path[WPAS_DBUS_OBJECT_PATH_MAX];
     756             :         int i;
     757             : 
     758       42103 :         wpa_dbus_flush_object_changed_properties(con, path);
     759             : 
     760       42103 :         if (!dbus_connection_list_registered(con, path, &objects))
     761           0 :                 goto out;
     762             : 
     763       82905 :         for (i = 0; objects[i]; i++) {
     764       40802 :                 os_snprintf(subobj_path, WPAS_DBUS_OBJECT_PATH_MAX,
     765       40802 :                             "%s/%s", path, objects[i]);
     766       40802 :                 recursive_flush_changed_properties(con, subobj_path);
     767             :         }
     768             : 
     769             : out:
     770       42103 :         dbus_free_string_array(objects);
     771       42103 : }
     772             : 
     773             : 
     774             : /**
     775             :  * wpa_dbus_flush_all_changed_properties - Send all PropertiesChanged signals
     776             :  * @con: DBus connection
     777             :  *
     778             :  * Traverses through all registered objects and sends PropertiesChanged for
     779             :  * each properties.
     780             :  */
     781        1301 : void wpa_dbus_flush_all_changed_properties(DBusConnection *con)
     782             : {
     783        1301 :         recursive_flush_changed_properties(con, WPAS_DBUS_NEW_PATH);
     784        1301 : }
     785             : 
     786             : 
     787             : /**
     788             :  * wpa_dbus_flush_object_changed_properties - Send PropertiesChanged for object
     789             :  * @con: DBus connection
     790             :  * @path: path to a DBus object for which PropertiesChanged will be sent.
     791             :  *
     792             :  * Iterates over all properties registered with object and for each interface
     793             :  * containing properties marked as changed, sends a PropertiesChanged signal
     794             :  * containing names and new values of properties that have changed.
     795             :  *
     796             :  * You need to call this function after wpa_dbus_mark_property_changed()
     797             :  * if you want to send PropertiesChanged signal immediately (i.e., without
     798             :  * waiting timeout to expire). PropertiesChanged signal for an object is sent
     799             :  * automatically short time after first marking property as changed. All
     800             :  * PropertiesChanged signals are sent automatically after responding on DBus
     801             :  * message, so if you marked a property changed as a result of DBus call
     802             :  * (e.g., param setter), you usually do not need to call this function.
     803             :  */
     804       72896 : void wpa_dbus_flush_object_changed_properties(DBusConnection *con,
     805             :                                               const char *path)
     806             : {
     807       72896 :         struct wpa_dbus_object_desc *obj_desc = NULL;
     808             :         const struct wpa_dbus_property_desc *dsc;
     809             :         int i;
     810             : 
     811       72896 :         dbus_connection_get_object_path_data(con, path, (void **) &obj_desc);
     812       72896 :         if (!obj_desc)
     813      111088 :                 return;
     814       34704 :         eloop_cancel_timeout(flush_object_timeout_handler, con, obj_desc);
     815             : 
     816     3925824 :         for (dsc = obj_desc->properties, i = 0; dsc && dsc->dbus_property;
     817     3856416 :              dsc++, i++) {
     818     7712695 :                 if (obj_desc->prop_changed_flags == NULL ||
     819     3856279 :                     !obj_desc->prop_changed_flags[i])
     820     3825361 :                         continue;
     821       31055 :                 send_prop_changed_signal(con, path, dsc->dbus_interface,
     822             :                                          obj_desc);
     823             :         }
     824             : }
     825             : 
     826             : 
     827             : #define WPA_DBUS_SEND_PROP_CHANGED_TIMEOUT 5000
     828             : 
     829             : 
     830             : /**
     831             :  * wpa_dbus_mark_property_changed - Mark a property as changed and
     832             :  * @iface: dbus priv struct
     833             :  * @path: path to DBus object which property has changed
     834             :  * @interface: interface containing changed property
     835             :  * @property: property name which has changed
     836             :  *
     837             :  * Iterates over all properties registered with an object and marks the one
     838             :  * given in parameters as changed. All parameters registered for an object
     839             :  * within a single interface will be aggregated together and sent in one
     840             :  * PropertiesChanged signal when function
     841             :  * wpa_dbus_flush_object_changed_properties() is called.
     842             :  */
     843       53744 : void wpa_dbus_mark_property_changed(struct wpas_dbus_priv *iface,
     844             :                                     const char *path, const char *interface,
     845             :                                     const char *property)
     846             : {
     847       53744 :         struct wpa_dbus_object_desc *obj_desc = NULL;
     848             :         const struct wpa_dbus_property_desc *dsc;
     849       53744 :         int i = 0;
     850             : 
     851       53744 :         if (iface == NULL)
     852           0 :                 return;
     853             : 
     854       53744 :         dbus_connection_get_object_path_data(iface->con, path,
     855             :                                              (void **) &obj_desc);
     856       53744 :         if (!obj_desc) {
     857           0 :                 wpa_printf(MSG_ERROR,
     858             :                            "dbus: wpa_dbus_property_changed: could not obtain object's private data: %s",
     859             :                            path);
     860           0 :                 return;
     861             :         }
     862             : 
     863      418453 :         for (dsc = obj_desc->properties; dsc && dsc->dbus_property; dsc++, i++)
     864      472197 :                 if (os_strcmp(property, dsc->dbus_property) == 0 &&
     865       53744 :                     os_strcmp(interface, dsc->dbus_interface) == 0) {
     866       53744 :                         if (obj_desc->prop_changed_flags)
     867       53743 :                                 obj_desc->prop_changed_flags[i] = 1;
     868       53744 :                         break;
     869             :                 }
     870             : 
     871       53744 :         if (!dsc || !dsc->dbus_property) {
     872           0 :                 wpa_printf(MSG_ERROR,
     873             :                            "dbus: wpa_dbus_property_changed: no property %s in object %s",
     874             :                            property, path);
     875           0 :                 return;
     876             :         }
     877             : 
     878      107488 :         if (!eloop_is_timeout_registered(flush_object_timeout_handler,
     879       53744 :                                          iface->con, obj_desc)) {
     880       62442 :                 eloop_register_timeout(0, WPA_DBUS_SEND_PROP_CHANGED_TIMEOUT,
     881             :                                        flush_object_timeout_handler,
     882       31221 :                                        iface->con, obj_desc);
     883             :         }
     884             : }
     885             : 
     886             : 
     887             : /**
     888             :  * wpa_dbus_get_object_properties - Put object's properties into dictionary
     889             :  * @iface: dbus priv struct
     890             :  * @path: path to DBus object which properties will be obtained
     891             :  * @interface: interface name which properties will be obtained
     892             :  * @iter: DBus message iter at which to append property dictionary.
     893             :  *
     894             :  * Iterates over all properties registered with object and execute getters
     895             :  * of those, which are readable and which interface matches interface
     896             :  * specified as argument. Obtained properties values are stored in
     897             :  * dict_iter dictionary.
     898             :  */
     899        6378 : dbus_bool_t wpa_dbus_get_object_properties(struct wpas_dbus_priv *iface,
     900             :                                            const char *path,
     901             :                                            const char *interface,
     902             :                                            DBusMessageIter *iter)
     903             : {
     904        6378 :         struct wpa_dbus_object_desc *obj_desc = NULL;
     905             :         DBusMessageIter dict_iter;
     906             :         DBusError error;
     907             : 
     908        6378 :         dbus_connection_get_object_path_data(iface->con, path,
     909             :                                              (void **) &obj_desc);
     910        6378 :         if (!obj_desc) {
     911           0 :                 wpa_printf(MSG_ERROR,
     912             :                            "dbus: %s: could not obtain object's private data: %s",
     913             :                            __func__, path);
     914           0 :                 return FALSE;
     915             :         }
     916             : 
     917        6378 :         if (!wpa_dbus_dict_open_write(iter, &dict_iter)) {
     918           0 :                 wpa_printf(MSG_ERROR, "dbus: %s: failed to open message dict",
     919             :                            __func__);
     920           0 :                 return FALSE;
     921             :         }
     922             : 
     923        6378 :         dbus_error_init(&error);
     924        6378 :         if (!fill_dict_with_properties(&dict_iter, obj_desc->properties,
     925        6378 :                                        interface, obj_desc->user_data,
     926             :                                        &error)) {
     927           6 :                 wpa_printf(MSG_ERROR,
     928             :                            "dbus: %s: failed to get object properties: (%s) %s",
     929             :                            __func__,
     930           3 :                            dbus_error_is_set(&error) ? error.name : "none",
     931           3 :                            dbus_error_is_set(&error) ? error.message : "none");
     932           3 :                 dbus_error_free(&error);
     933           3 :                 return FALSE;
     934             :         }
     935             : 
     936        6375 :         return wpa_dbus_dict_close_write(iter, &dict_iter);
     937             : }
     938             : 
     939             : /**
     940             :  * wpas_dbus_new_decompose_object_path - Decompose an interface object path into parts
     941             :  * @path: The dbus object path
     942             :  * @sep: Separating part (e.g., "Networks" or "PersistentGroups")
     943             :  * @item: (out) The part following the specified separator, if any
     944             :  * Returns: The object path of the interface this path refers to
     945             :  *
     946             :  * For a given object path, decomposes the object path into object id and
     947             :  * requested part, if those parts exist. The caller is responsible for freeing
     948             :  * the returned value. The *item pointer points to that allocated value and must
     949             :  * not be freed separately.
     950             :  *
     951             :  * As an example, path = "/fi/w1/wpa_supplicant1/Interfaces/1/Networks/0" and
     952             :  * sep = "Networks" would result in "/fi/w1/wpa_supplicant1/Interfaces/1"
     953             :  * getting returned and *items set to point to "0".
     954             :  */
     955         142 : char * wpas_dbus_new_decompose_object_path(const char *path, const char *sep,
     956             :                                            char **item)
     957             : {
     958         142 :         const unsigned int dev_path_prefix_len =
     959             :                 os_strlen(WPAS_DBUS_NEW_PATH_INTERFACES "/");
     960             :         char *obj_path_only;
     961             :         char *pos;
     962             :         size_t sep_len;
     963             : 
     964         142 :         *item = NULL;
     965             : 
     966             :         /* Verify that this starts with our interface prefix */
     967         142 :         if (os_strncmp(path, WPAS_DBUS_NEW_PATH_INTERFACES "/",
     968             :                        dev_path_prefix_len) != 0)
     969           1 :                 return NULL; /* not our path */
     970             : 
     971             :         /* Ensure there's something at the end of the path */
     972         141 :         if ((path + dev_path_prefix_len)[0] == '\0')
     973           0 :                 return NULL;
     974             : 
     975         141 :         obj_path_only = os_strdup(path);
     976         141 :         if (obj_path_only == NULL)
     977           1 :                 return NULL;
     978             : 
     979         140 :         pos = obj_path_only + dev_path_prefix_len;
     980         140 :         pos = os_strchr(pos, '/');
     981         140 :         if (pos == NULL)
     982           3 :                 return obj_path_only; /* no next item on the path */
     983             : 
     984             :          /* Separate network interface prefix from the path */
     985         137 :         *pos++ = '\0';
     986             : 
     987         137 :         sep_len = os_strlen(sep);
     988         137 :         if (os_strncmp(pos, sep, sep_len) != 0 || pos[sep_len] != '/')
     989           1 :                 return obj_path_only; /* no match */
     990             : 
     991             :          /* return a pointer to the requested item */
     992         136 :         *item = pos + sep_len + 1;
     993         136 :         return obj_path_only;
     994             : }
     995             : 
     996             : 
     997             : /**
     998             :  * wpas_dbus_reply_new_from_error - Create a new D-Bus error message from a
     999             :  *   dbus error structure
    1000             :  * @message: The original request message for which the error is a reply
    1001             :  * @error: The error containing a name and a descriptive error cause
    1002             :  * @fallback_name: A generic error name if @error was not set
    1003             :  * @fallback_string: A generic error string if @error was not set
    1004             :  * Returns: A new D-Bus error message
    1005             :  *
    1006             :  * Given a DBusMessage structure, creates a new D-Bus error message using
    1007             :  * the error name and string contained in that structure.
    1008             :  */
    1009          78 : DBusMessage * wpas_dbus_reply_new_from_error(DBusMessage *message,
    1010             :                                              DBusError *error,
    1011             :                                              const char *fallback_name,
    1012             :                                              const char *fallback_string)
    1013             : {
    1014          78 :         if (error && error->name && error->message) {
    1015          72 :                 return dbus_message_new_error(message, error->name,
    1016             :                                               error->message);
    1017             :         }
    1018           6 :         if (fallback_name && fallback_string) {
    1019           6 :                 return dbus_message_new_error(message, fallback_name,
    1020             :                                               fallback_string);
    1021             :         }
    1022           0 :         return NULL;
    1023             : }

Generated by: LCOV version 1.10