Logo Search packages:      
Sourcecode: libnih version File versions  Download package

dbus_object.c

/* libnih
 *
 * dbus_object.c - D-Bus local object implementation
 *
 * Copyright © 2009 Scott James Remnant <scott@netsplit.com>.
 * Copyright © 2009 Canonical Ltd.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2, as
 * published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */

#ifdef HAVE_CONFIG_H
# include <config.h>
#endif /* HAVE_CONFIG_H */


#include <dbus/dbus.h>

#include <nih/macros.h>
#include <nih/alloc.h>
#include <nih/list.h>
#include <nih/hash.h>
#include <nih/string.h>
#include <nih/logging.h>
#include <nih/error.h>

#include <nih-dbus/dbus_message.h>
#include <nih-dbus/dbus_error.h>
#include <nih-dbus/errors.h>

#include "dbus_object.h"


/* Prototypes for static functions */
static int               nih_dbus_object_destroy      (NihDBusObject *object);
static void              nih_dbus_object_unregister   (DBusConnection *connection,
                                           NihDBusObject *object);
static DBusHandlerResult nih_dbus_object_message      (DBusConnection *connection,
                                           DBusMessage *message,
                                           NihDBusObject *object);
static DBusHandlerResult nih_dbus_object_introspect   (DBusConnection *connection,
                                           DBusMessage *message,
                                           NihDBusObject *object);
static DBusHandlerResult nih_dbus_object_property_get (DBusConnection *connection,
                                           DBusMessage *message,
                                           NihDBusObject *object);
static DBusHandlerResult nih_dbus_object_property_get_all (DBusConnection *connection,
                                             DBusMessage *message,
                                             NihDBusObject *object);
static DBusHandlerResult nih_dbus_object_property_set (DBusConnection *connection,
                                           DBusMessage *message,
                                           NihDBusObject *object);


/**
 * nih_dbus_object_vtable:
 *
 * Table of functions for handling D-Bus objects.
 **/
static const DBusObjectPathVTable nih_dbus_object_vtable = {
      (DBusObjectPathUnregisterFunction)nih_dbus_object_unregister,
      (DBusObjectPathMessageFunction)nih_dbus_object_message,
      NULL,
};


/**
 * nih_dbus_object_new:
 * @parent: parent object for new object,
 * @connection: D-Bus connection to associate with,
 * @path: path of object,
 * @interfaces: interfaces list to attach,
 * @data: data pointer.
 *
 * Creates a new D-Bus object with the attached list of @interfaces which
 * specify the methods, signals and properties that object will export
 * and the C functions that will handle them.
 *
 * @interfaces should be a NULL-terminated array of pointers to
 * NihDBusInterface structures.  Normally this is constructed using pointers
 * to structures defined by nih-dbus-tool which provides all the necessary
 * glue arrays and functions.
 *
 * The object structure is allocated using nih_alloc() and connected to
 * the given @connection, it can be unregistered by freeing it and it will be
 * automatically unregistered should @connection be disconnected.
 *
 * If @parent is not NULL, it should be a pointer to another object which
 * will be used as a parent for the returned object.  When all parents
 * of the returned object are freed, the returned object will also be
 * freed.
 *
 * Returns: new NihDBusObject structure on success, or NULL if
 * insufficient memory.
 **/
NihDBusObject *
nih_dbus_object_new (const void *             parent,
                 DBusConnection *         connection,
                 const char *             path,
                 const NihDBusInterface **interfaces,
                 void *                   data)
{
      NihDBusObject *object;

      nih_assert (connection != NULL);
      nih_assert (path != NULL);
      nih_assert (interfaces != NULL);

      object = nih_new (parent, NihDBusObject);
      if (! object)
            return NULL;

      object->path = nih_strdup (object, path);
      if (! object->path) {
            nih_free (object);
            return NULL;
      }

      /* We don't reference the connection, it's only used to unregister
       * the object when freed directly; in addition, we get called if
       * the connection is freed and discard this object - and don't want
       * to block that happening.
       */
      object->connection = connection;

      object->data = data;
      object->interfaces = interfaces;
      object->registered = FALSE;

      if (! dbus_connection_register_object_path (object->connection,
                                        object->path,
                                        &nih_dbus_object_vtable,
                                        object)) {
            nih_free (object);
            return NULL;
      }

      object->registered = TRUE;
      nih_alloc_set_destructor (object, nih_dbus_object_destroy);

      return object;
}

/**
 * nih_dbus_object_destroy:
 * @object: D-Bus object being destroyed.
 *
 * Destructor function for an NihDBusObject structure, ensures that it
 * is unregistered from the attached D-Bus connection and path.
 *
 * Returns: always zero.
 **/
static int
nih_dbus_object_destroy (NihDBusObject *object)
{
      nih_assert (object != NULL);

      if (object->registered) {
            object->registered = FALSE;
            dbus_connection_unregister_object_path (object->connection,
                                          object->path);
      }

      return 0;
}

/**
 * nih_dbus_object_unregister:
 * @connection: D-Bus connection,
 * @object: D-Bus object to destroy.
 *
 * Called by D-Bus to unregister the @object attached to the D-Bus connection
 * @connection, requires us to free the attached structure.
 **/
static void
nih_dbus_object_unregister (DBusConnection *connection,
                      NihDBusObject * object)
{
      nih_assert (connection != NULL);
      nih_assert (object != NULL);
      nih_assert (object->connection == connection);

      if (object->registered) {
            object->registered = FALSE;
            nih_free (object);
      }
}


/**
 * nih_dbus_object_message:
 * @connection: D-Bus connection,
 * @message: D-Bus message received,
 * @object: Object that received the message.
 *
 * Called by D-Bus when a @message is received for a registered @object.  We
 * handle messages related to introspection and properties ourselves,
 * otherwise the method invoked is located in the @object's interfaces array
 * and the handler function called to handle it.
 *
 * Returns: result of handling the message.
 **/
static DBusHandlerResult
nih_dbus_object_message (DBusConnection *connection,
                   DBusMessage *   message,
                   NihDBusObject * object)
{
      const NihDBusInterface **interface;

      nih_assert (connection != NULL);
      nih_assert (message != NULL);
      nih_assert (object != NULL);
      nih_assert (object->connection == connection);

      /* Handle introspection internally */
      if (dbus_message_is_method_call (
                message, DBUS_INTERFACE_INTROSPECTABLE, "Introspect"))
            return nih_dbus_object_introspect (connection, message, object);

      /* Handle properties semi-internally */
      if (dbus_message_is_method_call (
                message, DBUS_INTERFACE_PROPERTIES, "Get"))
            return nih_dbus_object_property_get (connection, message, object);

      if (dbus_message_is_method_call (
                message, DBUS_INTERFACE_PROPERTIES, "Set"))
            return nih_dbus_object_property_set (connection, message, object);

      if (dbus_message_is_method_call (
                message, DBUS_INTERFACE_PROPERTIES, "GetAll"))
            return nih_dbus_object_property_get_all (connection, message, object);


      /* No built-in handling, locate a handler function in the defined
       * interfaces that can handle it.
       */
      for (interface = object->interfaces; interface && *interface;
           interface++) {
            const NihDBusMethod *method;

            for (method = (*interface)->methods; method && method->name;
                 method++) {
                  nih_assert (method->handler != NULL);

                  if (dbus_message_is_method_call (message,
                                           (*interface)->name,
                                           method->name)) {
                        nih_local NihDBusMessage *msg = NULL;
                        DBusHandlerResult         result;

                        msg = nih_dbus_message_new (NULL,
                                              connection, message);
                        if (! msg)
                              return DBUS_HANDLER_RESULT_NEED_MEMORY;

                        nih_error_push_context ();
                        result = method->handler (object, msg);
                        nih_error_pop_context ();

                        if (result != DBUS_HANDLER_RESULT_NOT_YET_HANDLED)
                              return result;
                  }
            }
      }

      return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
}

/**
 * nih_dbus_object_introspect:
 * @connection: D-Bus connection,
 * @message: D-Bus message received,
 * @object: Object that received the message.
 *
 * Called because the D-Bus introspection method has been invoked on @object,
 * we return an XML description of the object's interfaces, methods, signals
 * and properties based on its interfaces array.
 *
 * Returns: result of handling the message.
 **/
static DBusHandlerResult
nih_dbus_object_introspect (DBusConnection *connection,
                      DBusMessage *   message,
                      NihDBusObject * object)
{
      const NihDBusInterface **interface;
      nih_local char *         xml = NULL;
      char **                  children = NULL;
      char **                  child;
      DBusMessage *            reply = NULL;
      int                      have_props = FALSE;

      nih_assert (connection != NULL);
      nih_assert (message != NULL);
      nih_assert (object != NULL);
      nih_assert (object->connection == connection);

      /* Make sure the message signature was what we expected */
      if (! dbus_message_has_signature (message, "")) {
            reply = dbus_message_new_error (message, DBUS_ERROR_INVALID_ARGS,
                                    _("Invalid arguments to Introspect method"));
            if (! reply)
                  return DBUS_HANDLER_RESULT_NEED_MEMORY;

            if (! dbus_connection_send (connection, reply, NULL)) {
                  dbus_message_unref (reply);
                  return DBUS_HANDLER_RESULT_NEED_MEMORY;
            }

            dbus_message_unref (reply);

            return DBUS_HANDLER_RESULT_HANDLED;
      }

      xml = nih_strdup (NULL, DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE);
      if (! xml)
            return DBUS_HANDLER_RESULT_NEED_MEMORY;

      /* Root node */
      if (! nih_strcat_sprintf (&xml, NULL, "<node name=\"%s\">\n",
                          object->path))
            return DBUS_HANDLER_RESULT_NEED_MEMORY;

      /* Add each interface definition */
      for (interface = object->interfaces; interface && *interface;
           interface++) {
            const NihDBusMethod *  method;
            const NihDBusSignal *  signal;
            const NihDBusProperty *property;

            if (! nih_strcat_sprintf (&xml, NULL,
                                "  <interface name=\"%s\">\n",
                                (*interface)->name))
                  return DBUS_HANDLER_RESULT_NEED_MEMORY;

            for (method = (*interface)->methods; method && method->name;
                 method++) {
                  const NihDBusArg *arg;

                  if (! nih_strcat_sprintf (&xml, NULL,
                                      "    <method name=\"%s\">\n",
                                      method->name))
                        return DBUS_HANDLER_RESULT_NEED_MEMORY;

                  for (arg = method->args; arg && arg->type; arg++) {
                        if (! nih_strcat_sprintf (
                                  &xml, NULL,
                                  "      <arg"))
                              return DBUS_HANDLER_RESULT_NEED_MEMORY;

                        if (arg->name)
                              if (! nih_strcat_sprintf (
                                        &xml, NULL,
                                        " name=\"%s\"",
                                        arg->name))
                                    return DBUS_HANDLER_RESULT_NEED_MEMORY;

                        if (! nih_strcat_sprintf (
                                  &xml, NULL,
                                  " type=\"%s\""
                                  " direction=\"%s\"/>\n",
                                  arg->type,
                                  (arg->dir == NIH_DBUS_ARG_IN ? "in"
                                   : "out")))
                              return DBUS_HANDLER_RESULT_NEED_MEMORY;
                  }

                  if (! nih_strcat (&xml, NULL, "    </method>\n"))
                        return DBUS_HANDLER_RESULT_NEED_MEMORY;
            }

            for (signal = (*interface)->signals; signal && signal->name;
                 signal++) {
                  const NihDBusArg *arg;

                  if (! nih_strcat_sprintf (&xml, NULL,
                                      "    <signal name=\"%s\">\n",
                                      signal->name))
                        return DBUS_HANDLER_RESULT_NEED_MEMORY;

                  for (arg = signal->args; arg && arg->type; arg++) {
                        if (! nih_strcat_sprintf (
                                  &xml, NULL,
                                  "      <arg"))
                              return DBUS_HANDLER_RESULT_NEED_MEMORY;

                        if (arg->name)
                              if (! nih_strcat_sprintf (
                                        &xml, NULL,
                                        " name=\"%s\"",
                                        arg->name))
                                    return DBUS_HANDLER_RESULT_NEED_MEMORY;

                        if (! nih_strcat_sprintf (
                                  &xml, NULL,
                                  " type=\"%s\"/>\n",
                                  arg->type))
                              return DBUS_HANDLER_RESULT_NEED_MEMORY;
                  }

                  if (! nih_strcat (&xml, NULL, "    </signal>\n"))
                        return DBUS_HANDLER_RESULT_NEED_MEMORY;
            }

            for (property = (*interface)->properties;
                 property && property->name; property++) {
                  have_props = TRUE;
                  if (! nih_strcat_sprintf (
                            &xml, NULL,
                            "    <property name=\"%s\" type=\"%s\" "
                            "access=\"%s\"/>\n",
                            property->name, property->type,
                            (property->access == NIH_DBUS_READ ? "read"
                             : (property->access == NIH_DBUS_WRITE
                              ? "write" : "readwrite"))))
                        return DBUS_HANDLER_RESULT_NEED_MEMORY;
            }

            if (! nih_strcat (&xml, NULL, "  </interface>\n"))
                  return DBUS_HANDLER_RESULT_NEED_MEMORY;
      }

      /* We may also support properties, but don't want to announce that
       * unless we really do have some.
       */
      if (have_props)
            if (! nih_strcat_sprintf (
                      &xml, NULL,
                      "  <interface name=\"%s\">\n"
                      "    <method name=\"Get\">\n"
                      "      <arg name=\"interface_name\" type=\"s\" direction=\"in\"/>\n"
                      "      <arg name=\"property_name\" type=\"s\" direction=\"in\"/>\n"
                      "      <arg name=\"value\" type=\"v\" direction=\"out\"/>\n"
                      "    </method>\n"
                      "    <method name=\"Set\">\n"
                      "      <arg name=\"interface_name\" type=\"s\" direction=\"in\"/>\n"
                      "      <arg name=\"property_name\" type=\"s\" direction=\"in\"/>\n"
                      "      <arg name=\"value\" type=\"v\" direction=\"in\"/>\n"
                      "    </method>\n"
                      "    <method name=\"GetAll\">\n"
                      "      <arg name=\"interface_name\" type=\"s\" direction=\"in\"/>\n"
                      "      <arg name=\"props\" type=\"a{sv}\" direction=\"out\"/>\n"
                      "    </method>\n"
                      "  </interface>\n",
                      DBUS_INTERFACE_PROPERTIES))
                  return DBUS_HANDLER_RESULT_NEED_MEMORY;

      /* Obviously we support introspection */
      if (! nih_strcat_sprintf (&xml, NULL,
                          "  <interface name=\"%s\">\n"
                          "    <method name=\"Introspect\">\n"
                          "      <arg name=\"data\" type=\"s\" direction=\"out\"/>\n"
                          "    </method>\n"
                          "  </interface>\n",
                          DBUS_INTERFACE_INTROSPECTABLE))
            return DBUS_HANDLER_RESULT_NEED_MEMORY;

      /* Add node items for children */
      if (! dbus_connection_list_registered (connection, object->path, &children))
            return DBUS_HANDLER_RESULT_NEED_MEMORY;

      for (child = children; *child; child++) {
            if (! nih_strcat_sprintf (&xml, NULL, "  <node name=\"%s\"/>\n",
                                *child)) {
                  dbus_free_string_array (children);
                  return DBUS_HANDLER_RESULT_NEED_MEMORY;
            }
      }

      if (! nih_strcat (&xml, NULL, "</node>\n")) {
            dbus_free_string_array (children);
            return DBUS_HANDLER_RESULT_NEED_MEMORY;
      }

      dbus_free_string_array (children);


      /* Generate and send the reply */
      reply = dbus_message_new_method_return (message);
      if (! reply)
            return DBUS_HANDLER_RESULT_NEED_MEMORY;

      if (! dbus_message_append_args (reply,
                              DBUS_TYPE_STRING, &xml,
                              DBUS_TYPE_INVALID)) {
            dbus_message_unref (reply);
            return DBUS_HANDLER_RESULT_NEED_MEMORY;
      }

      if (! dbus_connection_send (connection, reply, NULL)) {
            dbus_message_unref (reply);
            return DBUS_HANDLER_RESULT_NEED_MEMORY;
      }

      dbus_message_unref (reply);

      return DBUS_HANDLER_RESULT_HANDLED;
}

/**
 * nih_dbus_object_property_get:
 * @connection: D-Bus connection,
 * @message: D-Bus message received,
 * @object: Object that received the message.
 *
 * Called because the D-Bus properties Get method has been invoked on
 * @object  We locate the property in the @object's interfaces array and
 * call the getter function to append a variant onto the reply we
 * generate.
 *
 * Returns: result of handling the message.
 **/
static DBusHandlerResult
nih_dbus_object_property_get (DBusConnection *connection,
                        DBusMessage *   message,
                        NihDBusObject * object)
{
      DBusMessage *            reply;
      DBusMessageIter          iter;
      const char *             interface_name;
      const char *             property_name;
      const NihDBusInterface **interface;

      nih_assert (connection != NULL);
      nih_assert (message != NULL);
      nih_assert (object != NULL);
      nih_assert (object->connection == connection);

      /* Retrieve the requested interface and property names from the
       * method call, first making sure the message signature was what
       * we expected.
       */
      if (! dbus_message_has_signature (message,
                                (DBUS_TYPE_STRING_AS_STRING
                                 DBUS_TYPE_STRING_AS_STRING))) {
            reply = dbus_message_new_error (message, DBUS_ERROR_INVALID_ARGS,
                                    _("Invalid arguments to Get method"));
            if (! reply)
                  return DBUS_HANDLER_RESULT_NEED_MEMORY;

            if (! dbus_connection_send (connection, reply, NULL)) {
                  dbus_message_unref (reply);
                  return DBUS_HANDLER_RESULT_NEED_MEMORY;
            }

            dbus_message_unref (reply);

            return DBUS_HANDLER_RESULT_HANDLED;
      }

      dbus_message_iter_init (message, &iter);
      dbus_message_iter_get_basic (&iter, &interface_name);
      dbus_message_iter_next (&iter);
      dbus_message_iter_get_basic (&iter, &property_name);
      dbus_message_iter_next (&iter);


      /* Locate a getter function in the defined interfaces. */
      for (interface = object->interfaces; interface && *interface;
           interface++) {
            const NihDBusProperty *property;

            for (property = (*interface)->properties;
                 property && property->name;
                 property++) {
                  nih_local NihDBusMessage *msg = NULL;
                  int                       ret;

                  if (strcmp (property->name, property_name)
                      || (strlen (interface_name)
                        && strcmp ((*interface)->name, interface_name)))
                        continue;

                  if (property->getter) {
                        msg = nih_dbus_message_new (NULL,
                                              connection, message);
                        if (! msg)
                              return DBUS_HANDLER_RESULT_NEED_MEMORY;

                        reply = dbus_message_new_method_return (message);
                        if (! reply)
                              return DBUS_HANDLER_RESULT_NEED_MEMORY;

                        dbus_message_iter_init_append (reply, &iter);

                        nih_error_push_context ();
                        ret = property->getter (object, msg, &iter);
                        if (ret < 0) {
                              NihError *err;

                              dbus_message_unref (reply);

                              err = nih_error_get ();
                              if (err->number == ENOMEM) {
                                    nih_free (err);
                                    nih_error_pop_context ();

                                    return DBUS_HANDLER_RESULT_NEED_MEMORY;
                              } else if (err->number == NIH_DBUS_ERROR) {
                                    NihDBusError *dbus_err = (NihDBusError *)err;

                                    reply = NIH_MUST (dbus_message_new_error (
                                                        message,
                                                        dbus_err->name,
                                                        dbus_err->message));
                                    nih_free (err);
                                    nih_error_pop_context ();
                              } else {
                                    reply = NIH_MUST (dbus_message_new_error (
                                                        message,
                                                        DBUS_ERROR_FAILED,
                                                        err->message));
                                    nih_free (err);
                                    nih_error_pop_context ();
                              }
                        } else {
                              nih_error_pop_context ();
                        }
                  } else {
                        reply = dbus_message_new_error_printf (
                              message, DBUS_ERROR_ACCESS_DENIED,
                              _("The %s property is write-only"),
                              property->name);
                        if (! reply)
                              return DBUS_HANDLER_RESULT_NEED_MEMORY;
                  }

                  if (! dbus_connection_send (connection, reply, NULL)) {
                        dbus_message_unref (reply);
                        return DBUS_HANDLER_RESULT_NEED_MEMORY;
                  }

                  dbus_message_unref (reply);

                  return DBUS_HANDLER_RESULT_HANDLED;
            }
      }

      return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
}

/**
 * nih_dbus_object_property_get_all:
 * @connection: D-Bus connection,
 * @message: D-Bus message received,
 * @object: Object that received the message.
 *
 * Called because the D-Bus properties Get method has been invoked on
 * @object  We locate the property in the @object's interfaces array and
 * call the getter function to append a variant onto the reply we
 * generate.
 *
 * Returns: result of handling the message.
 **/
static DBusHandlerResult
nih_dbus_object_property_get_all (DBusConnection *connection,
                          DBusMessage *   message,
                          NihDBusObject * object)
{
      DBusMessage *             reply;
      DBusMessageIter           iter;
      DBusMessageIter           arrayiter;
      const char *              interface_name;
      nih_local NihHash *       name_hash = NULL;
      nih_local NihDBusMessage *msg = NULL;
      const NihDBusInterface ** interface;

      nih_assert (connection != NULL);
      nih_assert (message != NULL);
      nih_assert (object != NULL);
      nih_assert (object->connection == connection);

      /* Retrieve the requested interface name from the method call,
       * first making sure the message signature was what we expected.
       */
      if (! dbus_message_has_signature (message, DBUS_TYPE_STRING_AS_STRING)) {
            reply = dbus_message_new_error (message, DBUS_ERROR_INVALID_ARGS,
                                    _("Invalid arguments to GetAll method"));
            if (! reply)
                  return DBUS_HANDLER_RESULT_NEED_MEMORY;

            if (! dbus_connection_send (connection, reply, NULL)) {
                  dbus_message_unref (reply);
                  return DBUS_HANDLER_RESULT_NEED_MEMORY;
            }

            dbus_message_unref (reply);

            return DBUS_HANDLER_RESULT_HANDLED;
      }

      dbus_message_iter_init (message, &iter);
      dbus_message_iter_get_basic (&iter, &interface_name);
      dbus_message_iter_next (&iter);

      /* D-Bus forbids us from returning multiple properties with the
       * same name in the dictionary, so we actually have to build
       * a dictionary of the properties we've visited.
       */
      name_hash = nih_hash_string_new (NULL, 0);
      if (! name_hash)
            return DBUS_HANDLER_RESULT_NEED_MEMORY;

      /* Use the same NihDBusMessage object for each of the getters we
       * call for efficiency
       */
      msg = nih_dbus_message_new (NULL, connection, message);
      if (! msg)
            return DBUS_HANDLER_RESULT_NEED_MEMORY;

      /* Begin constructing the reply immediately as well */
      reply = dbus_message_new_method_return (message);
      if (! reply)
            return DBUS_HANDLER_RESULT_NEED_MEMORY;

      dbus_message_iter_init_append (reply, &iter);

      if (! dbus_message_iter_open_container (&iter, DBUS_TYPE_ARRAY,
                                    (DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
                                     DBUS_TYPE_STRING_AS_STRING
                                     DBUS_TYPE_VARIANT_AS_STRING
                                     DBUS_DICT_ENTRY_END_CHAR_AS_STRING),
                                    &arrayiter)) {
            dbus_message_unref (reply);
            return DBUS_HANDLER_RESULT_NEED_MEMORY;
      }

      /* Call each of the getter functions for the matching interface,
       * or all of them if it's an empty string.
       */
      for (interface = object->interfaces; interface && *interface;
           interface++) {
            const NihDBusProperty *property;

            if (strlen (interface_name)
                && strcmp ((*interface)->name, interface_name))
                  continue;

            for (property = (*interface)->properties;
                 property && property->name;
                 property++) {
                  if (property->getter
                      && (! nih_hash_lookup (name_hash, property->name))) {
                        NihListEntry *  entry;
                        DBusMessageIter dictiter;
                        int             ret;

                        entry = nih_list_entry_new (name_hash);
                        if (! entry) {
                              dbus_message_iter_abandon_container (&iter, &arrayiter);
                              dbus_message_unref (reply);
                              return DBUS_HANDLER_RESULT_NEED_MEMORY;
                        }

                        entry->str = (char *)property->name;
                        nih_hash_add (name_hash, &entry->entry);

                        if (! dbus_message_iter_open_container (
                                  &arrayiter, DBUS_TYPE_DICT_ENTRY,
                                  NULL, &dictiter)) {
                              dbus_message_iter_abandon_container (&iter, &arrayiter);
                              dbus_message_unref (reply);
                              return DBUS_HANDLER_RESULT_NEED_MEMORY;
                        }

                        if (! dbus_message_iter_append_basic (
                                  &dictiter, DBUS_TYPE_STRING,
                                  &(property->name))) {
                              dbus_message_iter_abandon_container (&arrayiter, &dictiter);
                              dbus_message_iter_abandon_container (&iter, &arrayiter);
                              dbus_message_unref (reply);
                              return DBUS_HANDLER_RESULT_NEED_MEMORY;
                        }

                        nih_error_push_context ();
                        ret = property->getter (object, msg, &dictiter);
                        if (ret < 0) {
                              NihError *err;

                              dbus_message_iter_abandon_container (&arrayiter, &dictiter);
                              dbus_message_iter_abandon_container (&iter, &arrayiter);
                              dbus_message_unref (reply);

                              err = nih_error_get ();
                              if (err->number == ENOMEM) {
                                    nih_free (err);
                                    nih_error_pop_context ();

                                    return DBUS_HANDLER_RESULT_NEED_MEMORY;
                              } else if (err->number == NIH_DBUS_ERROR) {
                                    NihDBusError *dbus_err = (NihDBusError *)err;

                                    reply = NIH_MUST (dbus_message_new_error (
                                                        message,
                                                        dbus_err->name,
                                                        dbus_err->message));
                                    nih_free (err);
                                    nih_error_pop_context ();
                              } else {
                                    reply = NIH_MUST (dbus_message_new_error (
                                                        message,
                                                        DBUS_ERROR_FAILED,
                                                        err->message));
                                    nih_free (err);
                                    nih_error_pop_context ();
                              }

                              goto reply;
                        } else {
                              nih_error_pop_context ();

                              if (! dbus_message_iter_close_container (
                                        &arrayiter, &dictiter)) {
                                    dbus_message_iter_abandon_container (&iter, &arrayiter);
                                    dbus_message_unref (reply);
                                    return DBUS_HANDLER_RESULT_NEED_MEMORY;
                              }
                        }


                  }
            }
      }

      /* Close the array and send the reply */
      if (! dbus_message_iter_close_container (&iter, &arrayiter)) {
            dbus_message_unref (reply);
            return DBUS_HANDLER_RESULT_NEED_MEMORY;
      }

reply:
      if (! dbus_connection_send (connection, reply, NULL)) {
            dbus_message_unref (reply);
            return DBUS_HANDLER_RESULT_NEED_MEMORY;
      }

      dbus_message_unref (reply);

      return DBUS_HANDLER_RESULT_HANDLED;
}

/**
 * nih_dbus_object_property_set:
 * @connection: D-Bus connection,
 * @message: D-Bus message received,
 * @object: Object that received the message.
 *
 * Called because the D-Bus properties Set method has been invoked on
 * @object  We locate the property in the @object's interfaces array and
 * call the setter function to retrieve the variant and generate a reply.
 *
 * Returns: result of handling the message.
 **/
static DBusHandlerResult
nih_dbus_object_property_set (DBusConnection *connection,
                        DBusMessage *   message,
                        NihDBusObject * object)
{
      DBusMessage *            reply;
      DBusMessageIter          iter;
      const char *             interface_name;
      const char *             property_name;
      const NihDBusInterface **interface;

      nih_assert (connection != NULL);
      nih_assert (message != NULL);
      nih_assert (object != NULL);
      nih_assert (object->connection == connection);

      /* Retrieve the requested interface and property names from the
       * method call, first making sure the message signature was what
       * we expected.
       */
      if (! dbus_message_has_signature (message,
                                (DBUS_TYPE_STRING_AS_STRING
                                 DBUS_TYPE_STRING_AS_STRING
                                 DBUS_TYPE_VARIANT_AS_STRING))) {
            reply = dbus_message_new_error (message, DBUS_ERROR_INVALID_ARGS,
                                    _("Invalid arguments to Set method"));
            if (! reply)
                  return DBUS_HANDLER_RESULT_NEED_MEMORY;

            if (! dbus_connection_send (connection, reply, NULL)) {
                  dbus_message_unref (reply);
                  return DBUS_HANDLER_RESULT_NEED_MEMORY;
            }

            dbus_message_unref (reply);

            return DBUS_HANDLER_RESULT_HANDLED;
      }

      dbus_message_iter_init (message, &iter);
      dbus_message_iter_get_basic (&iter, &interface_name);
      dbus_message_iter_next (&iter);
      dbus_message_iter_get_basic (&iter, &property_name);
      dbus_message_iter_next (&iter);


      /* Locate a setter function in the defined interfaces. */
      for (interface = object->interfaces; interface && *interface;
           interface++) {
            const NihDBusProperty *property;

            for (property = (*interface)->properties;
                 property && property->name;
                 property++) {
                  nih_local NihDBusMessage *msg = NULL;
                  DBusMessage *             reply;
                  int                       ret;

                  if (strcmp (property->name, property_name)
                      || (strlen (interface_name)
                        && strcmp ((*interface)->name, interface_name)))
                        continue;

                  if (property->setter) {
                        msg = nih_dbus_message_new (NULL,
                                              connection, message);
                        if (! msg)
                              return DBUS_HANDLER_RESULT_NEED_MEMORY;

                        nih_error_push_context ();
                        ret = property->setter (object, msg, &iter);
                        if (ret < 0) {
                              NihError *err;

                              err = nih_error_get ();
                              if (err->number == ENOMEM) {
                                    nih_free (err);
                                    nih_error_pop_context ();

                                    return DBUS_HANDLER_RESULT_NEED_MEMORY;
                              } else if (err->number == NIH_DBUS_ERROR) {
                                    NihDBusError *dbus_err = (NihDBusError *)err;

                                    reply = NIH_MUST (dbus_message_new_error (
                                                        message,
                                                        dbus_err->name,
                                                        dbus_err->message));
                                    nih_free (err);
                                    nih_error_pop_context ();
                              } else {
                                    reply = NIH_MUST (dbus_message_new_error (
                                                        message,
                                                        DBUS_ERROR_FAILED,
                                                        err->message));
                                    nih_free (err);
                                    nih_error_pop_context ();
                              }
                        } else {
                              nih_error_pop_context ();

                              reply = NIH_MUST (dbus_message_new_method_return (message));
                        }
                  } else {
                        reply = dbus_message_new_error_printf (
                              message, DBUS_ERROR_ACCESS_DENIED,
                              _("The %s property is read-only"),
                              property->name);
                        if (! reply)
                              return DBUS_HANDLER_RESULT_NEED_MEMORY;
                  }

                  NIH_MUST (dbus_connection_send (connection, reply, NULL));
                  dbus_message_unref (reply);

                  return DBUS_HANDLER_RESULT_HANDLED;
            }
      }

      return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
}

Generated by  Doxygen 1.6.0   Back to index