root/usr/src/cmd/hal/hald/hald_dbus.c
/***************************************************************************
 * CVSID: $Id$
 *
 * dbus.c : D-BUS interface of HAL daemon
 *
 * Copyright (C) 2003 David Zeuthen, <david@fubar.dk>
 *
 * Licensed under the Academic Free License version 2.1
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * 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 St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 **************************************************************************/

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

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdarg.h>
#include <stdint.h>
#include <sys/time.h>

#include <dbus/dbus.h>
#include <dbus/dbus-glib-lowlevel.h>

#include "hald.h"
#include "hald_dbus.h"
#include "device.h"
#include "device_store.h"
#include "device_info.h"
#include "logger.h"
#include "osspec.h"
#include "util.h"
#include "hald_runner.h"

#define HALD_DBUS_ADDRESS "unix:tmpdir=" HALD_SOCKET_DIR

static DBusConnection *dbus_connection = NULL;

static void
raise_error (DBusConnection *connection,
             DBusMessage *in_reply_to,
             const char *error_name,
             char *format, ...) __attribute__((format (printf, 4, 5)));

/**
 * @defgroup DaemonErrors Error conditions
 * @ingroup HalDaemon
 * @brief Various error messages the HAL daemon can raise
 * @{
 */

/** Raise HAL error
 *
 *  @param  connection          D-Bus connection
 *  @param  in_reply_to         message to report error on
 *  @param  error_name          D-Bus error name
 *  @param  format              printf-style format for error message
 */
static void
raise_error (DBusConnection *connection,
             DBusMessage *in_reply_to,
             const char *error_name,
             char *format, ...)
{
        char buf[512];
        DBusMessage *reply;

        va_list args;
        va_start(args, format);
        vsnprintf(buf, sizeof buf, format, args);
        va_end(args);

        HAL_WARNING ((buf));
        reply = dbus_message_new_error (in_reply_to, error_name, buf);
        if (reply == NULL)
                DIE (("No memory"));
        if (!dbus_connection_send (connection, reply, NULL))
                DIE (("No memory"));
        dbus_message_unref (reply);
}

/** Raise the org.freedesktop.Hal.NoSuchDevice error
 *
 *  @param  connection          D-Bus connection
 *  @param  in_reply_to         message to report error on
 *  @param  udi                 Unique device id given
 */
static void
raise_no_such_device (DBusConnection *connection,
                      DBusMessage *in_reply_to, const char *udi)
{
        raise_error (
                connection, in_reply_to,
                "org.freedesktop.Hal.NoSuchDevice",
                "No device with id %s",
                udi
        );
}

/** Raise the org.freedesktop.Hal.NoSuchProperty error
 *
 *  @param  connection          D-Bus connection
 *  @param  in_reply_to         message to report error on
 *  @param  device_id           Id of the device
 *  @param  key                 Key of the property that didn't exist
 */
static void
raise_no_such_property (DBusConnection *connection,
                        DBusMessage *in_reply_to,
                        const char *device_id, const char *key)
{
        raise_error (
                connection, in_reply_to,
                "org.freedesktop.Hal.NoSuchProperty",
                "No property %s on device with id %s",
                key, device_id
        );
}

/** Raise the org.freedesktop.Hal.TypeMismatch error
 *
 *  @param  connection          D-Bus connection
 *  @param  in_reply_to         message to report error on
 *  @param  device_id           Id of the device
 *  @param  key                 Key of the property
 */
static void
raise_property_type_error (DBusConnection *connection,
                           DBusMessage *in_reply_to,
                           const char *device_id, const char *key)
{
        raise_error (
                connection, in_reply_to,
                "org.freedesktop.Hal.TypeMismatch",
                "Type mismatch setting property %s on device with id %s",
                key, device_id
        );
}

/** Raise the org.freedesktop.Hal.SyntaxError error
 *
 *  @param  connection          D-Bus connection
 *  @param  in_reply_to         message to report error on
 *  @param  method_name         Name of the method that was invoked with
 *                              the wrong signature
 */
static void
raise_syntax (DBusConnection *connection,
              DBusMessage *in_reply_to, const char *method_name)
{
        raise_error (
                connection, in_reply_to,
                "org.freedesktop.Hal.SyntaxError",
                "There is a syntax error in the invocation of the method %s",
                method_name
        );
}

/** Raise the org.freedesktop.Hal.DeviceNotLocked error
 *
 *  @param  connection          D-Bus connection
 *  @param  in_reply_to         message to report error on
 *  @param  device              device which isn't locked
 */
static void
raise_device_not_locked (DBusConnection *connection,
                         DBusMessage    *in_reply_to,
                         HalDevice      *device)
{
        raise_error (
                connection, in_reply_to,
                "org.freedesktop.Hal.DeviceNotLocked",
                "The device %s is not locked",
                 hal_device_get_udi (device)
        );
}

/** Raise the org.freedesktop.Hal.DeviceAlreadyLocked error
 *
 *  @param  connection          D-Bus connection
 *  @param  in_reply_to         message to report error on
 *  @param  device              device which isn't locked
 */
static void
raise_device_already_locked (DBusConnection *connection,
                             DBusMessage    *in_reply_to,
                             HalDevice      *device)
{
        DBusMessage *reply;
        const char *reason;

        reason = hal_device_property_get_string (device, "info.locked.reason");
        HAL_WARNING (("Device %s is already locked: %s",
                      hal_device_get_udi (device), reason));


        reply = dbus_message_new_error (in_reply_to,
                                        "org.freedesktop.Hal.DeviceAlreadyLocked",
                                        reason);

        if (reply == NULL || !dbus_connection_send (connection, reply, NULL))
                DIE (("No memory"));

        dbus_message_unref (reply);
}

/** Raise the org.freedesktop.Hal.BranchAlreadyClaimed error
 *
 *  @param  connection          D-Bus connection
 *  @param  in_reply_to         message to report error on
 *  @param  udi                 branch which isn't claimed
 */
static void
raise_branch_already_claimed (DBusConnection *connection,
                             DBusMessage    *in_reply_to,
                             HalDevice      *device)
{
        DBusMessage *reply;
        const char *claim_service;

        claim_service = hal_device_property_get_string (device, "info.claimed.service");
        HAL_WARNING (("Branch %s is already claimed by: %s",
                      hal_device_get_udi (device), claim_service));


        reply = dbus_message_new_error (in_reply_to,
                                        "org.freedesktop.Hal.BranchAlreadyClaimed",
                                        claim_service);

        if (reply == NULL || !dbus_connection_send (connection, reply, NULL))
                DIE (("No memory"));

        dbus_message_unref (reply);
}

/** Raise the org.freedesktop.Hal.BranchNotClaimed error
 *
 *  @param  connection          D-Bus connection
 *  @param  in_reply_to         message to report error on
 *  @param  udi                 branch which isn't claimed
 */
static void
raise_branch_not_claimed (DBusConnection *connection,
                             DBusMessage    *in_reply_to,
                             HalDevice      *device)
{
        raise_error (
                connection, in_reply_to,
                "org.freedesktop.Hal.BranchNotClaimed",
                "The branch %s is not claimed",
                 hal_device_get_udi (device)
        );
}

/** Raise the org.freedesktop.Hal.PermissionDenied error
 *
 *  @param  connection          D-Bus connection
 *  @param  in_reply_to         message to report error on
 *  @param  message             what you're not allowed to do
 */
static void
raise_permission_denied (DBusConnection *connection,
                         DBusMessage    *in_reply_to,
                         const char     *reason)
{
        raise_error (
                connection, in_reply_to,
                "org.freedesktop.Hal.PermissionDenied",
                "Permission denied: %s",
                 reason
        );
}

/** @} */

/**
 * @defgroup ManagerInterface D-BUS interface org.freedesktop.Hal.Manager
 * @ingroup HalDaemon
 * @brief D-BUS interface for querying device objects
 *
 * @{
 */

static gboolean
foreach_device_get_udi (HalDeviceStore *store, HalDevice *device,
                        gpointer user_data)
{
        DBusMessageIter *iter = user_data;
        const char *udi;

        udi = hal_device_get_udi (device);
        dbus_message_iter_append_basic (iter, DBUS_TYPE_STRING, &udi);

        return TRUE;
}

/** Get all devices.
 *
 *  <pre>
 *  array{object_reference} Manager.GetAllDevices()
 *  </pre>
 *
 *  @param  connection          D-BUS connection
 *  @param  message             Message
 *  @return                     What to do with the message
 */
DBusHandlerResult
manager_get_all_devices (DBusConnection * connection,
                         DBusMessage * message)
{
        DBusMessage *reply;
        DBusMessageIter iter;
        DBusMessageIter iter_array;

        reply = dbus_message_new_method_return (message);
        if (reply == NULL)
                DIE (("No memory"));

        dbus_message_iter_init_append (reply, &iter);
        dbus_message_iter_open_container (&iter,
                                          DBUS_TYPE_ARRAY,
                                          DBUS_TYPE_STRING_AS_STRING,
                                          &iter_array);

        hal_device_store_foreach (hald_get_gdl (),
                                  foreach_device_get_udi,
                                  &iter_array);

        dbus_message_iter_close_container (&iter, &iter_array);

        if (!dbus_connection_send (connection, reply, NULL))
                DIE (("No memory"));

        dbus_message_unref (reply);

        return DBUS_HANDLER_RESULT_HANDLED;
}

typedef struct {
        const char *key;
        const char *value;
        DBusMessageIter *iter;
} DeviceMatchInfo;

static gboolean
foreach_device_match_get_udi (HalDeviceStore *store, HalDevice *device,
                              gpointer user_data)
{
        DeviceMatchInfo *info = user_data;
        const char *dev_value;

        if (hal_device_property_get_type (device,
                                          info->key) != DBUS_TYPE_STRING)
                return TRUE;

        dev_value = hal_device_property_get_string (device, info->key);

        if (dev_value != NULL && strcmp (dev_value, info->value) == 0) {
                const char *udi;
                udi =  hal_device_get_udi (device);
                dbus_message_iter_append_basic  (info->iter,
                                                 DBUS_TYPE_STRING,
                                                 &udi);
        }

        return TRUE;
}

static gboolean
foreach_device_match_get_udi_tdl (HalDeviceStore *store, HalDevice *device,
                                  gpointer user_data)
{
        DeviceMatchInfo *info = user_data;
        const char *dev_value;

        /* skip devices in the TDL that hasn't got a real UDI yet */
        if (strncmp (device->udi, "/org/freedesktop/Hal/devices/temp",
                     sizeof ("/org/freedesktop/Hal/devices/temp")) == 0)
                return TRUE;

        if (hal_device_property_get_type (device,
                                          info->key) != DBUS_TYPE_STRING)
                return TRUE;

        dev_value = hal_device_property_get_string (device, info->key);

        if (dev_value != NULL && strcmp (dev_value, info->value) == 0) {
                const char *udi;
                udi = hal_device_get_udi (device);

                dbus_message_iter_append_basic (info->iter,
                                                DBUS_TYPE_STRING,
                                                &udi);
        }

        return TRUE;
}

/** Find devices in the GDL where a single string property matches a given
 *  value. Also returns devices in the TDL that has a non-tmp UDI.
 *
 *  <pre>
 *  array{object_reference} Manager.FindDeviceStringMatch(string key,
 *                                                        string value)
 *  </pre>
 *
 *  @param  connection          D-BUS connection
 *  @param  message             Message
 *  @return                     What to do with the message
 */
DBusHandlerResult
manager_find_device_string_match (DBusConnection * connection,
                                  DBusMessage * message)
{
        DBusMessage *reply;
        DBusMessageIter iter;
        DBusMessageIter iter_array;
        DBusError error;
        const char *key;
        const char *value;
        DeviceMatchInfo info;

        HAL_TRACE (("entering"));

        dbus_error_init (&error);
        if (!dbus_message_get_args (message, &error,
                                    DBUS_TYPE_STRING, &key,
                                    DBUS_TYPE_STRING, &value,
                                    DBUS_TYPE_INVALID)) {
                raise_syntax (connection, message,
                              "Manager.FindDeviceStringMatch");
                return DBUS_HANDLER_RESULT_HANDLED;
        }

        reply = dbus_message_new_method_return (message);
        if (reply == NULL)
                DIE (("No memory"));

        dbus_message_iter_init_append (reply, &iter);
        dbus_message_iter_open_container (&iter,
                                          DBUS_TYPE_ARRAY,
                                          DBUS_TYPE_STRING_AS_STRING,
                                          &iter_array);

        info.key = key;
        info.value = value;
        info.iter = &iter_array;

        hal_device_store_foreach (hald_get_gdl (),
                                  foreach_device_match_get_udi,
                                  &info);

        /* Also returns devices in the TDL that has a non-tmp UDI */
        hal_device_store_foreach (hald_get_tdl (),
                                  foreach_device_match_get_udi_tdl,
                                  &info);

        dbus_message_iter_close_container (&iter, &iter_array);

        if (!dbus_connection_send (connection, reply, NULL))
                DIE (("No memory"));

        dbus_message_unref (reply);

        return DBUS_HANDLER_RESULT_HANDLED;
}

typedef struct {
        const char *capability;
        DBusMessageIter *iter;
} DeviceCapabilityInfo;

static gboolean
foreach_device_by_capability (HalDeviceStore *store, HalDevice *device, gpointer user_data)
{
        DeviceCapabilityInfo *info = (DeviceCapabilityInfo *) user_data;

        if (hal_device_has_capability (device, info->capability)) {
                dbus_message_iter_append_basic (info->iter,
                                                DBUS_TYPE_STRING,
                                                &(device->udi));
        }

        return TRUE;
}

/** Find devices in the GDL with a given capability.
 *
 *  <pre>
 *  array{object_reference} Manager.FindDeviceByCapability(string capability)
 *  </pre>
 *
 *  @param  connection          D-BUS connection
 *  @param  message             Message
 *  @return                     What to do with the message
 */
DBusHandlerResult
manager_find_device_by_capability (DBusConnection * connection,
                                   DBusMessage * message)
{
        DBusMessage *reply;
        DBusMessageIter iter;
        DBusMessageIter iter_array;
        DBusError error;
        const char *capability;
        DeviceCapabilityInfo info;

        HAL_TRACE (("entering"));

        dbus_error_init (&error);
        if (!dbus_message_get_args (message, &error,
                                    DBUS_TYPE_STRING, &capability,
                                    DBUS_TYPE_INVALID)) {
                raise_syntax (connection, message,
                              "Manager.FindDeviceByCapability");
                return DBUS_HANDLER_RESULT_HANDLED;
        }

        reply = dbus_message_new_method_return (message);
        if (reply == NULL)
                DIE (("No memory"));

        dbus_message_iter_init_append (reply, &iter);
        dbus_message_iter_open_container (&iter,
                                          DBUS_TYPE_ARRAY,
                                          DBUS_TYPE_STRING_AS_STRING,
                                          &iter_array);

        info.capability = capability;
        info.iter = &iter_array;

        hal_device_store_foreach (hald_get_gdl (),
                                  foreach_device_by_capability,
                                  &info);

        dbus_message_iter_close_container (&iter, &iter_array);

        if (!dbus_connection_send (connection, reply, NULL))
                DIE (("No memory"));

        dbus_message_unref (reply);

        return DBUS_HANDLER_RESULT_HANDLED;
}


/** Determine if a device exists.
 *
 *  <pre>
 *  bool Manager.DeviceExists(string udi)
 *  </pre>
 *
 *  @param  connection          D-BUS connection
 *  @param  message             Message
 *  @return                     What to do with the message
 */
DBusHandlerResult
manager_device_exists (DBusConnection * connection, DBusMessage * message)
{
        DBusMessage *reply;
        DBusMessageIter iter;
        DBusError error;
        HalDevice *d;
        const char *udi;
        dbus_bool_t b;

        dbus_error_init (&error);
        if (!dbus_message_get_args (message, &error,
                                    DBUS_TYPE_STRING, &udi,
                                    DBUS_TYPE_INVALID)) {
                raise_syntax (connection, message, "Manager.DeviceExists");
                return DBUS_HANDLER_RESULT_HANDLED;
        }

        HAL_TRACE (("entering, udi=%s", udi));

        d = hal_device_store_find (hald_get_gdl (), udi);

        if (d == NULL)
                d = hal_device_store_find (hald_get_tdl (), udi);

        reply = dbus_message_new_method_return (message);
        dbus_message_iter_init_append (reply, &iter);
        b = d != NULL;
        dbus_message_iter_append_basic (&iter, DBUS_TYPE_BOOLEAN, &b);

        if (reply == NULL)
                DIE (("No memory"));

        if (!dbus_connection_send (connection, reply, NULL))
                DIE (("No memory"));

        dbus_message_unref (reply);
        return DBUS_HANDLER_RESULT_HANDLED;
}

/** Send signal DeviceAdded(string udi) on the org.freedesktop.Hal.Manager
 *  interface on the object /org/freedesktop/Hal/Manager.
 *
 *  @param  device              The HalDevice added
 */
void
manager_send_signal_device_added (HalDevice *device)
{
        const char *udi = hal_device_get_udi (device);
        DBusMessage *message;
        DBusMessageIter iter;

        if (dbus_connection == NULL)
                goto out;

        HAL_TRACE (("entering, udi=%s", udi));

        message = dbus_message_new_signal ("/org/freedesktop/Hal/Manager",
                                           "org.freedesktop.Hal.Manager",
                                           "DeviceAdded");

        dbus_message_iter_init_append (message, &iter);
        dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &udi);

        if (!dbus_connection_send (dbus_connection, message, NULL))
                DIE (("error broadcasting message"));

        dbus_message_unref (message);

out:
        ;
}

/** Send signal DeviceRemoved(string udi) on the org.freedesktop.Hal.Manager
 *  interface on the object /org/freedesktop/Hal/Manager.
 *
 *  @param  device              The HalDevice removed
 */
void
manager_send_signal_device_removed (HalDevice *device)
{
        const char *udi = hal_device_get_udi (device);
        DBusMessage *message;
        DBusMessageIter iter;

        if (dbus_connection == NULL)
                goto out;

        HAL_TRACE (("entering, udi=%s", udi));

        message = dbus_message_new_signal ("/org/freedesktop/Hal/Manager",
                                           "org.freedesktop.Hal.Manager",
                                           "DeviceRemoved");

        dbus_message_iter_init_append (message, &iter);
        dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &udi);

        if (!dbus_connection_send (dbus_connection, message, NULL))
                DIE (("error broadcasting message"));

        dbus_message_unref (message);
out:
        ;
}

/** Send signal NewCapability(string udi, string capability) on the
 *  org.freedesktop.Hal.Manager interface on the object
 *  /org/freedesktop/Hal/Manager.
 *
 *  @param  udi                 Unique Device Id
 *  @param  capability          Capability
 */
void
manager_send_signal_new_capability (HalDevice *device,
                                    const char *capability)
{
        const char *udi = hal_device_get_udi (device);
        DBusMessage *message;
        DBusMessageIter iter;

        if (dbus_connection == NULL)
                goto out;

        HAL_TRACE (("entering, udi=%s, cap=%s", udi, capability));

        message = dbus_message_new_signal ("/org/freedesktop/Hal/Manager",
                                           "org.freedesktop.Hal.Manager",
                                           "NewCapability");

        dbus_message_iter_init_append (message, &iter);
        dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &udi);
        dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &capability);

        if (!dbus_connection_send (dbus_connection, message, NULL))
                DIE (("error broadcasting message"));

        dbus_message_unref (message);
out:
        ;
}

/** @} */

/**
 * @defgroup DeviceInterface D-BUS interface org.freedesktop.Hal.Device
 * @ingroup HalDaemon
 * @brief D-BUS interface for generic device operations
 * @{
 */

static gboolean
foreach_property_append (HalDevice *device, HalProperty *p,
                         gpointer user_data)
{
        DBusMessageIter *iter;
        DBusMessageIter iter_dict_entry;
        const char *key;
        int type;

        iter = (DBusMessageIter *)user_data;

        dbus_message_iter_open_container (iter,
                                          DBUS_TYPE_DICT_ENTRY,
                                          NULL,
                                          &iter_dict_entry);

        key = hal_property_get_key (p);
        type = hal_property_get_type (p);

        dbus_message_iter_append_basic (&iter_dict_entry, DBUS_TYPE_STRING, &key);

        switch (type) {
        case HAL_PROPERTY_TYPE_STRING:
        {
                DBusMessageIter iter_var;
                const char *v;

                v = hal_property_get_string (p);

                dbus_message_iter_open_container (&iter_dict_entry,
                                                  DBUS_TYPE_VARIANT,
                                                  DBUS_TYPE_STRING_AS_STRING,
                                                  &iter_var);

                dbus_message_iter_append_basic (&iter_var,
                                                DBUS_TYPE_STRING,
                                                &v);

                dbus_message_iter_close_container (&iter_dict_entry,
                                                   &iter_var);
                break;
        }
        case HAL_PROPERTY_TYPE_INT32:
        {
                DBusMessageIter iter_var;
                dbus_int32_t v;

                v = hal_property_get_int (p);

                dbus_message_iter_open_container (&iter_dict_entry,
                                                  DBUS_TYPE_VARIANT,
                                                  DBUS_TYPE_INT32_AS_STRING,
                                                  &iter_var);

                dbus_message_iter_append_basic (&iter_var,
                                                DBUS_TYPE_INT32,
                                                &v);

                dbus_message_iter_close_container (&iter_dict_entry,
                                                   &iter_var);
                break;
        }
        case HAL_PROPERTY_TYPE_UINT64:
        {
                DBusMessageIter iter_var;
                dbus_uint64_t v;

                v = hal_property_get_uint64 (p);

                dbus_message_iter_open_container (&iter_dict_entry,
                                                  DBUS_TYPE_VARIANT,
                                                  DBUS_TYPE_UINT64_AS_STRING,
                                                  &iter_var);

                dbus_message_iter_append_basic (&iter_var,
                                                DBUS_TYPE_UINT64,
                                                &v);

                dbus_message_iter_close_container (&iter_dict_entry,
                                                   &iter_var);
                break;
        }
        case HAL_PROPERTY_TYPE_DOUBLE:
        {
                DBusMessageIter iter_var;
                double v;

                v = hal_property_get_double (p);

                dbus_message_iter_open_container (&iter_dict_entry,
                                                  DBUS_TYPE_VARIANT,
                                                  DBUS_TYPE_DOUBLE_AS_STRING,
                                                  &iter_var);

                dbus_message_iter_append_basic (&iter_var,
                                                DBUS_TYPE_DOUBLE,
                                                &v);

                dbus_message_iter_close_container (&iter_dict_entry,
                                                   &iter_var);
                break;
        }
        case HAL_PROPERTY_TYPE_BOOLEAN:
        {
                DBusMessageIter iter_var;
                dbus_bool_t v;

                v = hal_property_get_bool (p);

                dbus_message_iter_open_container (&iter_dict_entry,
                                                  DBUS_TYPE_VARIANT,
                                                  DBUS_TYPE_BOOLEAN_AS_STRING,
                                                  &iter_var);

                dbus_message_iter_append_basic (&iter_var,
                                                DBUS_TYPE_BOOLEAN,
                                                &v);

                dbus_message_iter_close_container (&iter_dict_entry,
                                                   &iter_var);
                break;
        }
        case HAL_PROPERTY_TYPE_STRLIST:
        {
                DBusMessageIter iter_var, iter_array;
                GSList *iter;

                dbus_message_iter_open_container (&iter_dict_entry,
                                                  DBUS_TYPE_VARIANT,
                                                  DBUS_TYPE_ARRAY_AS_STRING
                                                  DBUS_TYPE_STRING_AS_STRING,
                                                  &iter_var);

                dbus_message_iter_open_container (&iter_var,
                                                  DBUS_TYPE_ARRAY,
                                                  DBUS_TYPE_STRING_AS_STRING,
                                                  &iter_array);

                for (iter = hal_property_get_strlist (p); iter != NULL; iter = iter->next) {

                        const char *v;
                        v = (const char *) iter->data;

                        dbus_message_iter_append_basic (&iter_array,
                                                        DBUS_TYPE_STRING,
                                                        &v);
                }

                dbus_message_iter_close_container (&iter_var,
                                                   &iter_array);

                dbus_message_iter_close_container (&iter_dict_entry,
                                                   &iter_var);
                break;
        }

        default:
                HAL_WARNING (("Unknown property type 0x%04x", type));
                break;
        }

        dbus_message_iter_close_container (iter, &iter_dict_entry);


        return TRUE;
}



/** Get all properties on a device.
 *
 *  <pre>
 *  map{string, any} Device.GetAllProperties()
 *
 *    raises org.freedesktop.Hal.NoSuchDevice
 *  </pre>
 *
 *  @param  connection          D-BUS connection
 *  @param  message             Message
 *  @return                     What to do with the message
 */
DBusHandlerResult
device_get_all_properties (DBusConnection * connection,
                           DBusMessage * message)
{
        DBusMessage *reply;
        DBusMessageIter iter;
        DBusMessageIter iter_dict;
        HalDevice *d;
        const char *udi;

        udi = dbus_message_get_path (message);

        HAL_TRACE (("entering, udi=%s", udi));

        d = hal_device_store_find (hald_get_gdl (), udi);
        if (d == NULL)
                d = hal_device_store_find (hald_get_tdl (), udi);

        if (d == NULL) {
                raise_no_such_device (connection, message, udi);
                return DBUS_HANDLER_RESULT_HANDLED;
        }

        reply = dbus_message_new_method_return (message);
        if (reply == NULL)
                DIE (("No memory"));

        dbus_message_iter_init_append (reply, &iter);

        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,
                                          &iter_dict);

        hal_device_property_foreach (d,
                                     foreach_property_append,
                                     &iter_dict);

        dbus_message_iter_close_container (&iter, &iter_dict);

        if (!dbus_connection_send (connection, reply, NULL))
                DIE (("No memory"));

        dbus_message_unref (reply);

        return DBUS_HANDLER_RESULT_HANDLED;
}

#ifdef sun
#include <sys/stat.h>
static dbus_bool_t
user_at_console(unsigned long uid)
{
        struct stat st;

        return ((stat("/dev/vt/console_user", &st) == 0) && (st.st_uid == uid));
}
#endif /* sun */

static dbus_bool_t
sender_has_privileges (DBusConnection *connection, DBusMessage *message)
{
        DBusError error;
        unsigned long user_uid;
        const char *user_base_svc;
        dbus_bool_t ret;

        ret = FALSE;

        user_base_svc = dbus_message_get_sender (message);
        if (user_base_svc == NULL) {
                HAL_WARNING (("Cannot determine base service of caller"));
                goto out;
        }

        HAL_DEBUG (("base_svc = %s", user_base_svc));

        dbus_error_init (&error);
        user_uid = dbus_bus_get_unix_user (connection, user_base_svc, &error);
        if (user_uid == (unsigned long) -1 || dbus_error_is_set (&error)) {
                HAL_WARNING (("Could not get uid for connection: %s %s", error.name, error.message));
                dbus_error_free (&error);
                goto out;
        }

        HAL_INFO (("uid for caller is %ld", user_uid));

        if (user_uid != 0 && user_uid != geteuid()) {
#ifdef sun
                if (dbus_message_is_method_call (message,
                                                "org.freedesktop.Hal.Device",
                                                "Rescan")) {
                        if (user_at_console(user_uid)) {
                                ret = TRUE;
                                goto out;
                        }
                }
#endif
                HAL_WARNING (("uid %d is not privileged", user_uid));
                goto out;
        }

        ret = TRUE;

out:
        return ret;
}


/** Set multiple properties on a device in an atomic fashion.
 *
 *  <pre>
 *  Device.GetAllProperties(map{string, any} properties)
 *
 *    raises org.freedesktop.Hal.NoSuchDevice
 *  </pre>
 *
 *  @param  connection          D-BUS connection
 *  @param  message             Message
 *  @return                     What to do with the message
 */
static DBusHandlerResult
device_set_multiple_properties (DBusConnection *connection, DBusMessage *message, dbus_bool_t local_interface)
{
        DBusMessage *reply;
        DBusMessageIter iter;
        DBusMessageIter dict_iter;
        HalDevice *d;
        const char *udi;

        udi = dbus_message_get_path (message);

        HAL_TRACE (("entering, udi=%s", udi));

        d = hal_device_store_find (hald_get_gdl (), udi);
        if (d == NULL)
                d = hal_device_store_find (hald_get_tdl (), udi);

        if (d == NULL) {
                raise_no_such_device (connection, message, udi);
                return DBUS_HANDLER_RESULT_HANDLED;
        }

        if (!local_interface && !sender_has_privileges (connection, message)) {
                raise_permission_denied (connection, message, "SetProperty: not privileged");
                return DBUS_HANDLER_RESULT_HANDLED;
        }

        dbus_message_iter_init (message, &iter);

        if (dbus_message_iter_get_arg_type (&iter) != DBUS_TYPE_ARRAY  &&
            dbus_message_iter_get_element_type (&iter) != DBUS_TYPE_DICT_ENTRY) {
                HAL_ERROR (("error, expecting an array of dict entries", __FILE__, __LINE__));
                raise_syntax (connection, message, udi);
                return DBUS_HANDLER_RESULT_HANDLED;
        }

        dbus_message_iter_recurse (&iter, &dict_iter);

        /* update atomically */
        device_property_atomic_update_begin ();

        while (dbus_message_iter_get_arg_type (&dict_iter) == DBUS_TYPE_DICT_ENTRY)
        {
                DBusMessageIter dict_entry_iter, var_iter, array_iter;
                const char *key;
                int change_type;
                dbus_bool_t rc __unused;

                dbus_message_iter_recurse (&dict_iter, &dict_entry_iter);
                dbus_message_iter_get_basic (&dict_entry_iter, &key);

                dbus_message_iter_next (&dict_entry_iter);
                dbus_message_iter_recurse (&dict_entry_iter, &var_iter);
                change_type = dbus_message_iter_get_arg_type (&var_iter);

                rc = FALSE;

                switch (change_type) {
                case DBUS_TYPE_ARRAY:
                        if (dbus_message_iter_get_element_type (&var_iter) != DBUS_TYPE_STRING) {
                                /* TODO: error */
                        }
                        dbus_message_iter_recurse (&var_iter, &array_iter);

                        hal_device_property_strlist_clear (d, key);

                        while (dbus_message_iter_get_arg_type (&array_iter) == DBUS_TYPE_STRING) {
                                const char *v;
                                dbus_message_iter_get_basic (&array_iter, &v);
                                HAL_INFO ((" strlist elem %s -> %s", key, v));
                                rc = hal_device_property_strlist_append (d, key, v);
                                dbus_message_iter_next (&array_iter);
                        }

                        break;
                case DBUS_TYPE_STRING:
                {
                        const char *v;
                        dbus_message_iter_get_basic (&var_iter, &v);
                        HAL_INFO (("%s -> %s", key, v));
                        rc = hal_device_property_set_string (d, key, v);
                        break;
                }
                case DBUS_TYPE_INT32:
                {
                        dbus_int32_t v;
                        dbus_message_iter_get_basic (&var_iter, &v);
                        HAL_INFO (("%s -> %d", key, v));
                        rc = hal_device_property_set_int (d, key, v);
                        break;
                }
                case DBUS_TYPE_UINT64:
                {
                        dbus_uint64_t v;
                        dbus_message_iter_get_basic (&var_iter, &v);
                        HAL_INFO (("%s -> %lld", key, v));
                        rc = hal_device_property_set_uint64 (d, key, v);
                        break;
                }
                case DBUS_TYPE_DOUBLE:
                {
                        double v;
                        dbus_message_iter_get_basic (&var_iter, &v);
                        HAL_INFO (("%s -> %g", key, v));
                        rc = hal_device_property_set_double (d, key, v);
                        break;
                }
                case DBUS_TYPE_BOOLEAN:
                {
                        gboolean v;
                        dbus_message_iter_get_basic (&var_iter, &v);
                        HAL_INFO (("%s -> %s", key, v ? "True" : "False"));
                        rc = hal_device_property_set_bool (d, key, v);
                        break;
                }
                default:
                        /* TODO: error */
                        break;
                }

                /* TODO: error out on rc==FALSE? */

                dbus_message_iter_next (&dict_iter);
        }

        device_property_atomic_update_end ();


        reply = dbus_message_new_method_return (message);
        if (reply == NULL)
                DIE (("No memory"));

        if (!dbus_connection_send (connection, reply, NULL))
                DIE (("No memory"));

        dbus_message_unref (reply);
        return DBUS_HANDLER_RESULT_HANDLED;
}


/** Get a property on a device.
 *
 *  <pre>
 *  any Device.GetProperty(string key)
 *  string Device.GetPropertyString(string key)
 *  int Device.GetPropertyInteger(string key)
 *  bool Device.GetPropertyBoolean(string key)
 *  double Device.GetPropertyDouble(string key)
 *
 *    raises org.freedesktop.Hal.NoSuchDevice,
 *           org.freedesktop.Hal.NoSuchProperty
 *  </pre>
 *
 *  @param  connection          D-BUS connection
 *  @param  message             Message
 *  @return                     What to do with the message
 */
DBusHandlerResult
device_get_property (DBusConnection * connection, DBusMessage * message)
{
        DBusMessage *reply;
        DBusMessageIter iter;
        DBusError error;
        HalDevice *d;
        const char *udi;
        char *key;
        int type;
        HalProperty *p;

        udi = dbus_message_get_path (message);

        HAL_TRACE (("entering, udi=%s", udi));

        d = hal_device_store_find (hald_get_gdl (), udi);
        if (d == NULL)
                d = hal_device_store_find (hald_get_tdl (), udi);

        if (d == NULL) {
                raise_no_such_device (connection, message, udi);
                return DBUS_HANDLER_RESULT_HANDLED;
        }

        dbus_error_init (&error);
        if (!dbus_message_get_args (message, &error,
                                    DBUS_TYPE_STRING, &key,
                                    DBUS_TYPE_INVALID)) {
                raise_syntax (connection, message, "GetProperty");
                return DBUS_HANDLER_RESULT_HANDLED;
        }

        p = hal_device_property_find (d, key);
        if (p == NULL) {
                raise_no_such_property (connection, message, udi, key);
                return DBUS_HANDLER_RESULT_HANDLED;
        }

        reply = dbus_message_new_method_return (message);
        if (reply == NULL)
                DIE (("No memory"));

        dbus_message_iter_init_append (reply, &iter);

        type = hal_property_get_type (p);
        switch (type) {
        case HAL_PROPERTY_TYPE_STRING:
        {
                const char *s;
                s = hal_property_get_string (p);
                dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &s);
                break;
        }
        case HAL_PROPERTY_TYPE_INT32:
        {
                dbus_int32_t i;
                i = hal_property_get_int (p);
                dbus_message_iter_append_basic (&iter, DBUS_TYPE_INT32, &i);
                break;
        }
        case HAL_PROPERTY_TYPE_UINT64:
        {
                dbus_uint64_t ul;
                ul = hal_property_get_uint64 (p);
                dbus_message_iter_append_basic (&iter, DBUS_TYPE_UINT64, &ul);
                break;
        }
        case HAL_PROPERTY_TYPE_DOUBLE:
        {
                double d;
                d = hal_property_get_double (p);
                dbus_message_iter_append_basic (&iter, DBUS_TYPE_DOUBLE, &d);
                break;
        }
        case HAL_PROPERTY_TYPE_BOOLEAN:
        {
                dbus_bool_t b;
                b = hal_property_get_bool (p);
                dbus_message_iter_append_basic (&iter, DBUS_TYPE_BOOLEAN, &b);
                break;
        }
        case HAL_PROPERTY_TYPE_STRLIST:
        {
                GSList *l;
                DBusMessageIter iter_array;

                dbus_message_iter_open_container (&iter,
                                                  DBUS_TYPE_ARRAY,
                                                  DBUS_TYPE_STRING_AS_STRING,
                                                  &iter_array);

                for (l = hal_property_get_strlist (p); l != NULL; l = g_slist_next (l)) {
                        dbus_message_iter_append_basic (&iter_array, DBUS_TYPE_STRING, &(l->data));
                }

                dbus_message_iter_close_container (&iter, &iter_array);
        }
                break;

        default:
                HAL_WARNING (("Unknown property type %d", type));
                break;
        }

        if (!dbus_connection_send (connection, reply, NULL))
                DIE (("No memory"));

        dbus_message_unref (reply);

        return DBUS_HANDLER_RESULT_HANDLED;
}


/** Get the type of a property on a device.
 *
 *  <pre>
 *  int Device.GetPropertyType(string key)
 *
 *    raises org.freedesktop.Hal.NoSuchDevice,
 *           org.freedesktop.Hal.NoSuchProperty
 *  </pre>
 *
 *  @param  connection          D-BUS connection
 *  @param  message             Message
 *  @return                     What to do with the message
 */
DBusHandlerResult
device_get_property_type (DBusConnection * connection,
                          DBusMessage * message)
{
        DBusMessage *reply;
        DBusMessageIter iter;
        DBusError error;
        HalDevice *d;
        const char *udi;
        char *key;
        HalProperty *p;
        dbus_int32_t i;

        udi = dbus_message_get_path (message);

        HAL_TRACE (("entering, udi=%s", udi));

        d = hal_device_store_find (hald_get_gdl (), udi);
        if (d == NULL)
                d = hal_device_store_find (hald_get_tdl (), udi);

        if (d == NULL) {
                raise_no_such_device (connection, message, udi);
                return DBUS_HANDLER_RESULT_HANDLED;
        }

        dbus_error_init (&error);
        if (!dbus_message_get_args (message, &error,
                                    DBUS_TYPE_STRING, &key,
                                    DBUS_TYPE_INVALID)) {
                raise_syntax (connection, message, "GetPropertyType");
                return DBUS_HANDLER_RESULT_HANDLED;
        }

        p = hal_device_property_find (d, key);
        if (p == NULL) {
                raise_no_such_property (connection, message, udi, key);
                return DBUS_HANDLER_RESULT_HANDLED;
        }

        reply = dbus_message_new_method_return (message);
        if (reply == NULL)
                DIE (("No memory"));

        i = hal_property_get_type (p);
        dbus_message_iter_init_append (reply, &iter);
        dbus_message_iter_append_basic (&iter, DBUS_TYPE_INT32, &i);

        if (!dbus_connection_send (connection, reply, NULL))
                DIE (("No memory"));

        dbus_message_unref (reply);

        return DBUS_HANDLER_RESULT_HANDLED;
}

/** Set a property on a device.
 *
 *  <pre>
 *  void Device.SetProperty(string key, any value)
 *  void Device.SetPropertyString(string key, string value)
 *  void Device.SetPropertyInteger(string key, int value)
 *  void Device.SetPropertyBoolean(string key, bool value)
 *  void Device.SetPropertyDouble(string key, double value)
 *
 *    raises org.freedesktop.Hal.NoSuchDevice,
 *           org.freedesktop.Hal.NoSuchProperty
 *           org.freedesktop.Hal.TypeMismatch
 *  </pre>
 *
 *  @param  connection          D-BUS connection
 *  @param  message             Message
 *  @return                     What to do with the message
 */
DBusHandlerResult
device_set_property (DBusConnection * connection, DBusMessage * message, dbus_bool_t local_interface)
{
        const char *udi;
        char *key;
        int type;
        dbus_bool_t rc;
        HalDevice *device;
        DBusMessageIter iter;
        DBusMessage *reply;

        HAL_TRACE (("entering"));

        udi = dbus_message_get_path (message);

        dbus_message_iter_init (message, &iter);
        type = dbus_message_iter_get_arg_type (&iter);
        if (type != DBUS_TYPE_STRING) {
                raise_syntax (connection, message, "SetProperty");
                return DBUS_HANDLER_RESULT_HANDLED;
        }
        dbus_message_iter_get_basic (&iter, &key);

        if (!local_interface && !sender_has_privileges (connection, message)) {
                raise_permission_denied (connection, message, "SetProperty: not privileged");
                return DBUS_HANDLER_RESULT_HANDLED;
        }

        HAL_DEBUG (("udi=%s, key=%s", udi, key));

        device = hal_device_store_find (hald_get_gdl (), udi);
        if (device == NULL)
                device = hal_device_store_find (hald_get_tdl (), udi);

        if (device == NULL) {
                raise_no_such_device (connection, message, udi);
                return DBUS_HANDLER_RESULT_HANDLED;
        }
        dbus_message_iter_next (&iter);

        /** @todo check permissions of the sender vs property to be modified */

        type = dbus_message_iter_get_arg_type (&iter);
        rc = FALSE;

        switch (type) {
        case DBUS_TYPE_STRING:
        {
                const char *v;
                dbus_message_iter_get_basic (&iter, &v);
                rc = hal_device_property_set_string (device, key, v);
                break;
        }
        case DBUS_TYPE_INT32:
        {
                dbus_int32_t v;
                dbus_message_iter_get_basic (&iter, &v);
                rc = hal_device_property_set_int (device, key, v);
                break;
        }
        case DBUS_TYPE_UINT64:
        {
                dbus_uint64_t v;
                dbus_message_iter_get_basic (&iter, &v);
                rc = hal_device_property_set_uint64 (device, key, v);
                break;
        }
        case DBUS_TYPE_DOUBLE:
        {
                double v;
                dbus_message_iter_get_basic (&iter, &v);
                rc = hal_device_property_set_double (device, key, v);
                break;
        }
        case DBUS_TYPE_BOOLEAN:
        {
                dbus_bool_t v;
                dbus_message_iter_get_basic (&iter, &v);
                rc = hal_device_property_set_bool (device, key, v);
                break;
        }
        default:
                HAL_WARNING (("Unsupported property type %d", type));
                break;
        }

        if (!rc) {
                raise_property_type_error (connection, message, udi, key);
                return DBUS_HANDLER_RESULT_HANDLED;
        }

        reply = dbus_message_new_method_return (message);
        if (reply == NULL)
                DIE (("No memory"));

        if (!dbus_connection_send (connection, reply, NULL))
                DIE (("No memory"));

        dbus_message_unref (reply);
        return DBUS_HANDLER_RESULT_HANDLED;
}

/** This function is used to modify the Capabilities property. The reason
 *  for having a dedicated function is that the HAL daemon will broadcast
 *  a signal on the Manager interface to tell applications that the device
 *  have got a new capability.
 *
 *  This is useful as capabilities can be merged after the device is created.
 *  One example of this is networking cards under Linux 2.6; the net.ethernet
 *  capability is not merged when the device is initially found by looking in
 *  /sys/devices; it is merged when the /sys/classes tree is searched.
 *
 *  Note that the signal is emitted every time this method is invoked even
 *  though the capability already existed. This is useful in the above
 *  scenario when the PCI class says ethernet networking card but we yet
 *  don't have enough information to fill in the net.* and net.ethernet.*
 *  fields since this only happens when we visit the /sys/classes tree.
 *
 *  <pre>
 *  void Device.AddCapability(string capability)
 *
 *    raises org.freedesktop.Hal.NoSuchDevice,
 *    raises org.freedesktop.Hal.PermissionDenied,
 *  </pre>
 *
 *  @param  connection          D-BUS connection
 *  @param  message             Message
 *  @return                     What to do with the message
 */
DBusHandlerResult
device_add_capability (DBusConnection * connection, DBusMessage * message, dbus_bool_t local_interface)
{
        const char *udi;
        const char *capability;
        HalDevice *d;
        DBusMessage *reply;
        DBusError error;

        HAL_TRACE (("entering"));

        if (!local_interface && !sender_has_privileges (connection, message)) {
                raise_permission_denied (connection, message, "AddCapability: not privileged");
                return DBUS_HANDLER_RESULT_HANDLED;
        }

        udi = dbus_message_get_path (message);

        d = hal_device_store_find (hald_get_gdl (), udi);
        if (d == NULL)
                d = hal_device_store_find (hald_get_tdl (), udi);

        if (d == NULL) {
                raise_no_such_device (connection, message, udi);
                return DBUS_HANDLER_RESULT_HANDLED;
        }

        dbus_error_init (&error);
        if (!dbus_message_get_args (message, &error,
                                    DBUS_TYPE_STRING, &capability,
                                    DBUS_TYPE_INVALID)) {
                raise_syntax (connection, message, "AddCapability");
                return DBUS_HANDLER_RESULT_HANDLED;
        }


        hal_device_add_capability (d, capability);

        reply = dbus_message_new_method_return (message);
        if (reply == NULL)
                DIE (("No memory"));

        if (!dbus_connection_send (connection, reply, NULL))
                DIE (("No memory"));

        dbus_message_unref (reply);
        return DBUS_HANDLER_RESULT_HANDLED;
}


/* TODO: docs */
static DBusHandlerResult
device_string_list_append_prepend (DBusConnection * connection, DBusMessage * message, dbus_bool_t do_prepend)
{
        const char *udi;
        const char *key;
        const char *value;
        HalDevice *d;
        DBusMessage *reply;
        DBusError error;
        gboolean ret;

        HAL_TRACE (("entering"));

        udi = dbus_message_get_path (message);

        d = hal_device_store_find (hald_get_gdl (), udi);
        if (d == NULL)
                d = hal_device_store_find (hald_get_tdl (), udi);

        if (d == NULL) {
                raise_no_such_device (connection, message, udi);
                return DBUS_HANDLER_RESULT_HANDLED;
        }

        dbus_error_init (&error);
        if (!dbus_message_get_args (message, &error,
                                    DBUS_TYPE_STRING, &key,
                                    DBUS_TYPE_STRING, &value,
                                    DBUS_TYPE_INVALID)) {
                raise_syntax (connection, message, do_prepend ? "StringListPrepend" : "StringListAppend");
                return DBUS_HANDLER_RESULT_HANDLED;
        }

        if (do_prepend)
                ret = hal_device_property_strlist_prepend (d, key, value);
        else
                ret = hal_device_property_strlist_append (d, key, value);
        if (!ret) {
                raise_property_type_error (connection, message, udi, key);
                return DBUS_HANDLER_RESULT_HANDLED;
        }

        reply = dbus_message_new_method_return (message);
        if (reply == NULL)
                DIE (("No memory"));

        if (!dbus_connection_send (connection, reply, NULL))
                DIE (("No memory"));

        dbus_message_unref (reply);
        return DBUS_HANDLER_RESULT_HANDLED;
}

/* TODO: docs */
static DBusHandlerResult
device_string_list_remove (DBusConnection * connection, DBusMessage * message)
{
        const char *udi;
        const char *key;
        const char *value;
        HalDevice *d;
        DBusMessage *reply;
        DBusError error;
        gboolean ret;

        HAL_TRACE (("entering"));

        udi = dbus_message_get_path (message);

        d = hal_device_store_find (hald_get_gdl (), udi);
        if (d == NULL)
                d = hal_device_store_find (hald_get_tdl (), udi);

        if (d == NULL) {
                raise_no_such_device (connection, message, udi);
                return DBUS_HANDLER_RESULT_HANDLED;
        }

        dbus_error_init (&error);
        if (!dbus_message_get_args (message, &error,
                                    DBUS_TYPE_STRING, &key,
                                    DBUS_TYPE_STRING, &value,
                                    DBUS_TYPE_INVALID)) {
                raise_syntax (connection, message, "StringListRemove");
                return DBUS_HANDLER_RESULT_HANDLED;
        }

        ret = hal_device_property_strlist_remove (d, key, value);
        if (!ret) {
                raise_property_type_error (connection, message, udi, key);
                return DBUS_HANDLER_RESULT_HANDLED;
        }

        reply = dbus_message_new_method_return (message);
        if (reply == NULL)
                DIE (("No memory"));

        if (!dbus_connection_send (connection, reply, NULL))
                DIE (("No memory"));

        dbus_message_unref (reply);
        return DBUS_HANDLER_RESULT_HANDLED;
}



/** Remove a property on a device.
 *
 *  <pre>
 *  void Device.RemoveProperty(string key)
 *
 *    raises org.freedesktop.Hal.NoSuchDevice,
 *           org.freedesktop.Hal.NoSuchProperty
 *           org.freedesktop.Hal.PermissionDenied
 *  </pre>
 *
 *  @param  connection          D-BUS connection
 *  @param  message             Message
 *  @return                     What to do with the message
 */
DBusHandlerResult
device_remove_property (DBusConnection * connection, DBusMessage * message, dbus_bool_t local_interface)
{
        const char *udi;
        char *key;
        HalDevice *d;
        DBusMessage *reply;
        DBusError error;

        HAL_TRACE (("entering"));

        udi = dbus_message_get_path (message);

        if (!local_interface && !sender_has_privileges (connection, message)) {
                raise_permission_denied (connection, message, "RemoveProperty: not privileged");
                return DBUS_HANDLER_RESULT_HANDLED;
        }

        d = hal_device_store_find (hald_get_gdl (), udi);
        if (d == NULL)
                d = hal_device_store_find (hald_get_tdl (), udi);

        if (d == NULL) {
                raise_no_such_device (connection, message, udi);
                return DBUS_HANDLER_RESULT_HANDLED;
        }

        dbus_error_init (&error);
        if (!dbus_message_get_args (message, &error,
                                    DBUS_TYPE_STRING, &key,
                                    DBUS_TYPE_INVALID)) {
                raise_syntax (connection, message, "RemoveProperty");
                return DBUS_HANDLER_RESULT_HANDLED;
        }

        if (!hal_device_property_remove (d, key)) {
                raise_no_such_property (connection, message, udi, key);
                return DBUS_HANDLER_RESULT_HANDLED;
        }


        reply = dbus_message_new_method_return (message);
        if (reply == NULL)
                DIE (("No memory"));

        if (!dbus_connection_send (connection, reply, NULL))
                DIE (("No memory"));

        dbus_message_unref (reply);
        return DBUS_HANDLER_RESULT_HANDLED;
}


/** Determine if a property exists
 *
 *  <pre>
 *  bool Device.PropertyExists(string key)
 *
 *    raises org.freedesktop.Hal.NoSuchDevice,
 *  </pre>
 *
 *  @param  connection          D-BUS connection
 *  @param  message             Message
 *  @return                     What to do with the message
 */
DBusHandlerResult
device_property_exists (DBusConnection * connection, DBusMessage * message)
{
        const char *udi;
        char *key;
        HalDevice *d;
        DBusMessage *reply;
        DBusError error;
        DBusMessageIter iter;
        dbus_bool_t b;

        HAL_TRACE (("entering"));

        udi = dbus_message_get_path (message);

        d = hal_device_store_find (hald_get_gdl (), udi);
        if (d == NULL)
                d = hal_device_store_find (hald_get_tdl (), udi);

        if (d == NULL) {
                raise_no_such_device (connection, message, udi);
                return DBUS_HANDLER_RESULT_HANDLED;
        }

        dbus_error_init (&error);
        if (!dbus_message_get_args (message, &error,
                                    DBUS_TYPE_STRING, &key,
                                    DBUS_TYPE_INVALID)) {
                raise_syntax (connection, message, "RemoveProperty");
                return DBUS_HANDLER_RESULT_HANDLED;
        }

        reply = dbus_message_new_method_return (message);
        if (reply == NULL)
                DIE (("No memory"));

        b =  hal_device_has_property (d, key);
        dbus_message_iter_init_append (reply, &iter);
        dbus_message_iter_append_basic (&iter, DBUS_TYPE_BOOLEAN, &b);

        if (!dbus_connection_send (connection, reply, NULL))
                DIE (("No memory"));

        dbus_message_unref (reply);
        return DBUS_HANDLER_RESULT_HANDLED;
}


/** Determine if a device has a capability
 *
 *  <pre>
 *  bool Device.QueryCapability(string capability_name)
 *
 *    raises org.freedesktop.Hal.NoSuchDevice,
 *  </pre>
 *
 *  @param  connection          D-BUS connection
 *  @param  message             Message
 *  @return                     What to do with the message
 */
DBusHandlerResult
device_query_capability (DBusConnection * connection,
                         DBusMessage * message)
{
        dbus_bool_t rc;
        const char *udi;
        GSList *caps;
        char *capability;
        HalDevice *d;
        DBusMessage *reply;
        DBusError error;
        DBusMessageIter iter;

        HAL_TRACE (("entering"));

        udi = dbus_message_get_path (message);

        d = hal_device_store_find (hald_get_gdl (), udi);
        if (d == NULL)
                d = hal_device_store_find (hald_get_tdl (), udi);

        if (d == NULL) {
                raise_no_such_device (connection, message, udi);
                return DBUS_HANDLER_RESULT_HANDLED;
        }

        dbus_error_init (&error);
        if (!dbus_message_get_args (message, &error,
                                    DBUS_TYPE_STRING, &capability,
                                    DBUS_TYPE_INVALID)) {
                raise_syntax (connection, message, "QueryCapability");
                return DBUS_HANDLER_RESULT_HANDLED;
        }

        reply = dbus_message_new_method_return (message);
        if (reply == NULL)
                DIE (("No memory"));

        rc = FALSE;
        caps = hal_device_property_get_strlist (d, "info.capabilities");
        if (caps != NULL) {
                GSList *iter;

                for (iter = caps; iter != NULL; iter=g_slist_next(iter)) {
                        if (strcmp (iter->data, capability) == 0) {
                                rc = TRUE;
                                break;
                        }
                }
        }

        dbus_message_iter_init_append (reply, &iter);
        dbus_message_iter_append_basic (&iter, DBUS_TYPE_BOOLEAN, &rc);

        if (!dbus_connection_send (connection, reply, NULL))
                DIE (("No memory"));

        dbus_message_unref (reply);
        return DBUS_HANDLER_RESULT_HANDLED;
}

static GHashTable *services_with_locks = NULL;

/** Grab an advisory lock on a device.
 *
 *  <pre>
 *  bool Device.Lock(string reason)
 *
 *    raises org.freedesktop.Hal.NoSuchDevice,
 *           org.freedesktop.Hal.DeviceAlreadyLocked
 *  </pre>
 *
 *  @param  connection          D-BUS connection
 *  @param  message             Message
 *  @return                     What to do with the message
 */
DBusHandlerResult
device_lock (DBusConnection * connection,
             DBusMessage * message)
{
        const char *udi;
        HalDevice *d;
        DBusMessage *reply;
        dbus_bool_t already_locked;
        DBusError error;
        char *reason;
        const char *sender;

        HAL_TRACE (("entering"));

        udi = dbus_message_get_path (message);

        d = hal_device_store_find (hald_get_gdl (), udi);
        if (d == NULL)
                d = hal_device_store_find (hald_get_tdl (), udi);

        if (d == NULL) {
                raise_no_such_device (connection, message, udi);
                return DBUS_HANDLER_RESULT_HANDLED;
        }

        already_locked = hal_device_property_get_bool (d, "info.locked");

        if (already_locked) {
                raise_device_already_locked (connection, message, d);
                return DBUS_HANDLER_RESULT_HANDLED;
        }

        dbus_error_init (&error);
        if (!dbus_message_get_args (message, &error,
                                    DBUS_TYPE_STRING, &reason,
                                    DBUS_TYPE_INVALID)) {
                raise_syntax (connection, message, "Lock");
                return DBUS_HANDLER_RESULT_HANDLED;
        }

        reply = dbus_message_new_method_return (message);
        if (reply == NULL)
                DIE (("No memory"));

        sender = dbus_message_get_sender (message);

        hal_device_property_set_bool (d, "info.locked", TRUE);
        hal_device_property_set_string (d, "info.locked.reason", reason);
        hal_device_property_set_string (d, "info.locked.dbus_name",
                                        sender);

        if (services_with_locks == NULL) {
                services_with_locks =
                        g_hash_table_new_full (g_str_hash,
                                               g_str_equal,
                                               g_free,
                                               g_object_unref);
        }

        g_hash_table_insert (services_with_locks, g_strdup (sender),
                             g_object_ref (d));

        if (!dbus_connection_send (connection, reply, NULL))
                DIE (("No memory"));

        dbus_message_unref (reply);
        return DBUS_HANDLER_RESULT_HANDLED;
}

/** Release an advisory lock on a device.
 *
 *  <pre>
 *  bool Device.Unlock()
 *
 *    raises org.freedesktop.Hal.NoSuchDevice,
 *           org.freedesktop.Hal.DeviceNotLocked,
 *           org.freedesktop.Hal.PermissionDenied
 *  </pre>
 *
 *  @param  connection          D-BUS connection
 *  @param  message             Message
 *  @return                     What to do with the message
 */
DBusHandlerResult
device_unlock (DBusConnection * connection,
               DBusMessage * message)
{
        dbus_bool_t rc;
        const char *udi;
        HalDevice *d;
        DBusMessage *reply;
        DBusError error;
        const char *sender;

        HAL_TRACE (("entering"));

        udi = dbus_message_get_path (message);

        d = hal_device_store_find (hald_get_gdl (), udi);
        if (d == NULL)
                d = hal_device_store_find (hald_get_tdl (), udi);

        if (d == NULL) {
                raise_no_such_device (connection, message, udi);
                return DBUS_HANDLER_RESULT_HANDLED;
        }

        dbus_error_init (&error);
        if (!dbus_message_get_args (message, &error,
                                    DBUS_TYPE_INVALID)) {
                raise_syntax (connection, message, "Unlock");
                return DBUS_HANDLER_RESULT_HANDLED;
        }

        reply = dbus_message_new_method_return (message);
        if (reply == NULL)
                DIE (("No memory"));

        rc = hal_device_property_get_bool (d, "info.locked");

        if (!rc) {
                raise_device_not_locked (connection, message, d);
                return DBUS_HANDLER_RESULT_HANDLED;
        }

        sender = dbus_message_get_sender (message);

        if (strcmp (sender, hal_device_property_get_string (
                            d, "info.locked.dbus_name")) != 0) {
                char *reason;

                reason = g_strdup_printf ("Service '%s' does not own the "
                                          "lock on %s", sender,
                                          hal_device_get_udi (d));

                raise_permission_denied (connection, message, reason);

                g_free (reason);

                return DBUS_HANDLER_RESULT_HANDLED;
        }

        if (g_hash_table_lookup (services_with_locks, sender))
                g_hash_table_remove (services_with_locks, sender);
        else {
                HAL_WARNING (("Service '%s' was not in the list of services "
                              "with locks!", sender));
        }

        hal_device_property_remove (d, "info.locked");
        hal_device_property_remove (d, "info.locked.reason");
        hal_device_property_remove (d, "info.locked.dbus_name");

        /* FIXME?  Pointless? */
        if (!dbus_connection_send (connection, reply, NULL))
                DIE (("No memory"));

        dbus_message_unref (reply);
        return DBUS_HANDLER_RESULT_HANDLED;
}

static GHashTable *services_with_claims = NULL;

/** Claim a branch.
 *
 *  <pre>
 *  bool Manager.ClaimBranch(string udi, string claim_service)
 *  </pre>
 *
 *  @param  connection          D-BUS connection
 *  @param  message             Message
 *  @return                     What to do with the message
 */
DBusHandlerResult
manager_claim_branch (DBusConnection * connection, DBusMessage * message)
{
        DBusMessage *reply;
        DBusMessageIter iter;
        DBusError error;
        HalDevice *d;
        const char *udi;
        const char *claim_service;
        const char *sender;
        dbus_bool_t already_claimed;
        unsigned long uid;

        dbus_error_init (&error);
        if (!dbus_message_get_args (message, &error,
                                    DBUS_TYPE_STRING, &udi,
                                    DBUS_TYPE_STRING, &claim_service,
                                    DBUS_TYPE_INVALID)) {
                raise_syntax (connection, message, "Manager.ClaimBranch");
                return DBUS_HANDLER_RESULT_HANDLED;
        }

        HAL_TRACE (("entering, udi=%s", udi));

        d = hal_device_store_find (hald_get_gdl (), udi);

        if (d == NULL) {
                raise_no_such_device (connection, message, udi);
                return DBUS_HANDLER_RESULT_HANDLED;
        }

        already_claimed = hal_device_property_get_bool (d, "info.claimed");
        if (already_claimed) {
                raise_branch_already_claimed (connection, message, d);
                return DBUS_HANDLER_RESULT_HANDLED;
        }

        reply = dbus_message_new_method_return (message);
        if (reply == NULL)
                DIE (("No memory"));

        sender = dbus_message_get_sender (message);

        dbus_error_init (&error);
        uid = dbus_bus_get_unix_user (connection, sender, &error);
        if (uid == (unsigned long) -1 || dbus_error_is_set (&error)) {
                HAL_WARNING (("Could not get uid for connection: %s %s", error.name, error.message));
                dbus_error_free (&error);
                dbus_message_unref (reply);
                raise_no_such_device (connection, message, udi);
                return DBUS_HANDLER_RESULT_HANDLED;
        }

        hal_util_branch_claim (hald_get_gdl (), d, TRUE, claim_service, uid);
        hal_device_property_set_string (d, "info.claimed.dbus_name", sender);

        if (services_with_claims == NULL) {
                services_with_claims =
                        g_hash_table_new_full (g_str_hash,
                                               g_str_equal,
                                               g_free,
                                               g_object_unref);
        }

        g_hash_table_insert (services_with_claims, g_strdup (sender),
                             g_object_ref (d));

        if (!dbus_connection_send (connection, reply, NULL))
                DIE (("No memory"));

        dbus_message_unref (reply);
        return DBUS_HANDLER_RESULT_HANDLED;
}

/** Unclaim a branch.
 *
 *  <pre>
 *  bool Manager.UnclaimBranch(string udi)
 *  </pre>
 *
 *  @param  connection          D-BUS connection
 *  @param  message             Message
 *  @return                     What to do with the message
 */
DBusHandlerResult
manager_unclaim_branch (DBusConnection * connection, DBusMessage * message)
{
        DBusMessage *reply;
        DBusMessageIter iter;
        DBusError error;
        HalDevice *d;
        const char *udi;
        const char *claim_service;
        const char *sender;
        dbus_bool_t already_claimed;

        dbus_error_init (&error);
        if (!dbus_message_get_args (message, &error,
                                    DBUS_TYPE_STRING, &udi,
                                    DBUS_TYPE_INVALID)) {
                raise_syntax (connection, message, "Manager.UnclaimBranch");
                return DBUS_HANDLER_RESULT_HANDLED;
        }

        HAL_TRACE (("entering, udi=%s", udi));

        d = hal_device_store_find (hald_get_gdl (), udi);

        if (d == NULL) {
                raise_no_such_device (connection, message, udi);
                return DBUS_HANDLER_RESULT_HANDLED;
        }

        already_claimed = hal_device_property_get_bool (d, "info.claimed");
        if (!already_claimed) {
                raise_branch_not_claimed (connection, message, d);
                return DBUS_HANDLER_RESULT_HANDLED;
        }

        reply = dbus_message_new_method_return (message);
        if (reply == NULL)
                DIE (("No memory"));

        if (strcmp (sender, hal_device_property_get_string (
                            d, "info.claimed.dbus_name")) != 0) {
                char *reason;

                reason = g_strdup_printf ("Service '%s' does not own the "
                                          "claim on %s", sender,
                                          hal_device_get_udi (d));

                raise_permission_denied (connection, message, reason);

                g_free (reason);

                return DBUS_HANDLER_RESULT_HANDLED;
        }

        if (g_hash_table_lookup (services_with_claims, sender))
                g_hash_table_remove (services_with_claims, sender);
        else {
                HAL_WARNING (("Service '%s' was not in the list of services "
                              "with claims!", sender));
        }

        hal_util_branch_claim (hald_get_gdl (), d, FALSE, NULL, 0);
        hal_device_property_remove (d, "info.claimed.dbus_name");

        if (!dbus_connection_send (connection, reply, NULL))
                DIE (("No memory"));

        dbus_message_unref (reply);
        return DBUS_HANDLER_RESULT_HANDLED;
}


/** Counter for atomic updating */
static int atomic_count = 0;

/** Number of updates pending */
static int num_pending_updates = 0;

/** Structure for queing updates */
typedef struct PendingUpdate_s {
        char *udi;                    /**< udi of device */
        char *key;                    /**< key of property; free when done */
        dbus_bool_t removed;          /**< true iff property was removed */
        dbus_bool_t added;            /**< true iff property was added */
        struct PendingUpdate_s *next; /**< next update or #NULL */
} PendingUpdate;

static PendingUpdate *pending_updates_head = NULL;

/** Begin an atomic update - this is useful for updating several properties
 *  in one go.
 *
 *  Note that an atomic update is recursive - use with caution!
 */
void
device_property_atomic_update_begin (void)
{
        atomic_count++;
}

/** End an atomic update.
 *
 *  Note that an atomic update is recursive - use with caution!
 */
void
device_property_atomic_update_end (void)
{
        PendingUpdate *pu_iter = NULL;
        PendingUpdate *pu_iter_next = NULL;
        PendingUpdate *pu_iter2 = NULL;

        --atomic_count;

        if (atomic_count < 0) {
                HAL_WARNING (("*** atomic_count = %d < 0 !!", atomic_count));
                atomic_count = 0;
        }

        if (atomic_count == 0 && num_pending_updates > 0) {
                DBusMessage *message;
                DBusMessageIter iter;
                DBusMessageIter iter_array;

                for (pu_iter = pending_updates_head;
                     pu_iter != NULL; pu_iter = pu_iter_next) {
                        int num_updates_this;

                        pu_iter_next = pu_iter->next;

                        /* see if we've already processed this */
                        if (pu_iter->udi == NULL)
                                goto already_processed;

                        /* count number of updates for this device */
                        num_updates_this = 0;
                        for (pu_iter2 = pu_iter; pu_iter2 != NULL; pu_iter2 = pu_iter2->next) {
                                if (strcmp (pu_iter2->udi, pu_iter->udi) == 0)
                                        num_updates_this++;
                        }

                        /* prepare message */
                        message = dbus_message_new_signal (pu_iter->udi,
                                                           "org.freedesktop.Hal.Device",
                                                           "PropertyModified");
                        dbus_message_iter_init_append (message, &iter);
                        dbus_message_iter_append_basic (&iter, DBUS_TYPE_INT32,
                                                        &num_updates_this);

                        dbus_message_iter_open_container (&iter,
                                                          DBUS_TYPE_ARRAY,
                                                          DBUS_STRUCT_BEGIN_CHAR_AS_STRING
                                                          DBUS_TYPE_STRING_AS_STRING
                                                          DBUS_TYPE_BOOLEAN_AS_STRING
                                                          DBUS_TYPE_BOOLEAN_AS_STRING
                                                          DBUS_STRUCT_END_CHAR_AS_STRING,
                                                          &iter_array);

                        for (pu_iter2 = pu_iter; pu_iter2 != NULL;
                             pu_iter2 = pu_iter2->next) {
                                if (strcmp (pu_iter2->udi, pu_iter->udi) == 0) {
                                        DBusMessageIter iter_struct;
                                        dbus_message_iter_open_container (&iter_array,
                                                                          DBUS_TYPE_STRUCT,
                                                                          NULL,
                                                                          &iter_struct);
                                        dbus_message_iter_append_basic
                                            (&iter_struct,
                                             DBUS_TYPE_STRING,
                                             &(pu_iter2->key));
                                        dbus_message_iter_append_basic
                                            (&iter_struct,
                                             DBUS_TYPE_BOOLEAN,
                                             &(pu_iter2->removed));
                                        dbus_message_iter_append_basic
                                            (&iter_struct,
                                             DBUS_TYPE_BOOLEAN,
                                             &(pu_iter2->added));

                                        dbus_message_iter_close_container (&iter_array, &iter_struct);

                                        /* signal this is already processed */
                                        g_free (pu_iter2->key);
                                        if (pu_iter2 != pu_iter) {
                                                g_free (pu_iter2->udi);
                                                pu_iter2->udi = NULL;
                                        }
                                }
                        }

                        g_free (pu_iter->udi);
                        dbus_message_iter_close_container (&iter, &iter_array);

                        if (dbus_connection != NULL) {
                                if (!dbus_connection_send (dbus_connection, message, NULL))
                                        DIE (("error broadcasting message"));
                        }

                        dbus_message_unref (message);

                already_processed:
                        g_free (pu_iter);

                } /* for all updates */

                num_pending_updates = 0;
                pending_updates_head = NULL;
        }
}



void
device_send_signal_property_modified (HalDevice *device, const char *key,
                                      dbus_bool_t added, dbus_bool_t removed)
{
        const char *udi = hal_device_get_udi (device);
        DBusMessage *message;
        DBusMessageIter iter;

/*
    HAL_INFO(("Entering, udi=%s, key=%s, in_gdl=%s, removed=%s added=%s",
              device->udi, key,
              in_gdl ? "true" : "false",
              removed ? "true" : "false",
              added ? "true" : "false"));
*/

        if (atomic_count > 0) {
                PendingUpdate *pu;

                pu = g_new0 (PendingUpdate, 1);
                pu->udi = g_strdup (udi);
                pu->key = g_strdup (key);
                pu->removed = removed;
                pu->added = added;
                pu->next = pending_updates_head;

                pending_updates_head = pu;
                num_pending_updates++;
        } else {
                dbus_int32_t i;
                DBusMessageIter iter_struct;
                DBusMessageIter iter_array;

                if (dbus_connection == NULL)
                        goto out;

                message = dbus_message_new_signal (udi,
                                                  "org.freedesktop.Hal.Device",
                                                   "PropertyModified");

                dbus_message_iter_init_append (message, &iter);
                i = 1;
                dbus_message_iter_append_basic (&iter, DBUS_TYPE_INT32, &i);

                dbus_message_iter_open_container (&iter,
                                                  DBUS_TYPE_ARRAY,
                                                  DBUS_STRUCT_BEGIN_CHAR_AS_STRING
                                                  DBUS_TYPE_STRING_AS_STRING
                                                  DBUS_TYPE_BOOLEAN_AS_STRING
                                                  DBUS_TYPE_BOOLEAN_AS_STRING
                                                  DBUS_STRUCT_END_CHAR_AS_STRING,
                                                  &iter_array);

                dbus_message_iter_open_container (&iter_array,
                                                  DBUS_TYPE_STRUCT,
                                                  NULL,
                                                  &iter_struct);

                dbus_message_iter_append_basic (&iter_struct, DBUS_TYPE_STRING, &key);
                dbus_message_iter_append_basic (&iter_struct, DBUS_TYPE_BOOLEAN, &removed);
                dbus_message_iter_append_basic (&iter_struct, DBUS_TYPE_BOOLEAN, &added);

                dbus_message_iter_close_container (&iter_array, &iter_struct);
                dbus_message_iter_close_container (&iter, &iter_array);

                if (!dbus_connection_send (dbus_connection, message, NULL))
                        DIE (("error broadcasting message"));

                dbus_message_unref (message);
        }
out:
        ;
}

/** Emits a condition on a device; the device has to be in the GDL for
 *  this function to have effect.
 *
 *  Is intended for non-continuous events on the device like
 *  ProcesserOverheating, BlockDeviceGotDevice, e.g. conditions that
 *  are exceptional and may not be inferred by looking at properties
 *  (though some may).
 *
 *  This function accepts a number of parameters that are passed along
 *  in the D-BUS message. The recipient is supposed to extract the parameters
 *  himself, by looking at the HAL specification.
 *
 * @param  udi                  The UDI for this device
 * @param  condition_name       Name of condition
 * @param  first_arg_type       Type of the first argument
 * @param  ...                  value of first argument, list of additional
 *                              type-value pairs. Must be terminated with
 *                              DBUS_TYPE_INVALID
 */
void
device_send_signal_condition (HalDevice *device, const char *condition_name, const char *condition_details)
{
        const char *udi = hal_device_get_udi (device);
        DBusMessage *message;
        DBusMessageIter iter;

        if (dbus_connection == NULL)
                goto out;

        message = dbus_message_new_signal (udi,
                                           "org.freedesktop.Hal.Device",
                                           "Condition");
        dbus_message_iter_init_append (message, &iter);
        dbus_message_iter_append_basic (&iter,
                                        DBUS_TYPE_STRING,
                                        &condition_name);
        dbus_message_iter_append_basic (&iter,
                                        DBUS_TYPE_STRING,
                                        &condition_details);

        if (!dbus_connection_send (dbus_connection, message, NULL))
                DIE (("error broadcasting message"));

        dbus_message_unref (message);
out:
        return;
}



static gboolean
reinit_dbus (gpointer user_data)
{
        HAL_INFO (("entering!"));
        if (hald_dbus_init ())
                return FALSE;
        else
                return TRUE;
}

static void
service_deleted (DBusMessage *message)
{
        char *old_service_name;
        char *new_service_name;
        HalDevice *d;

        if (!dbus_message_get_args (message, NULL,
                                    DBUS_TYPE_STRING, &old_service_name,
                                    DBUS_TYPE_STRING, &new_service_name,
                                    DBUS_TYPE_INVALID)) {
                HAL_ERROR (("Invalid NameOwnerChanged signal from bus!"));
                return;
        }

        if (services_with_locks != NULL) {
                d = g_hash_table_lookup (services_with_locks, new_service_name);

                if (d != NULL) {
                        hal_device_property_remove (d, "info.locked");
                        hal_device_property_remove (d, "info.locked.reason");
                        hal_device_property_remove (d, "info.locked.dbus_name");

                        g_hash_table_remove (services_with_locks, new_service_name);
                }
        }

        if (services_with_claims != NULL) {
                d = g_hash_table_lookup (services_with_claims, new_service_name);

                if (d != NULL) {
                        hal_util_branch_claim (hald_get_gdl (), d, FALSE, NULL, 0);
                        hal_device_property_remove (d, "info.claimed.dbus_name");

                        g_hash_table_remove (services_with_claims, new_service_name);
                }
        }
}

static DBusHandlerResult
device_rescan (DBusConnection * connection, DBusMessage * message, dbus_bool_t local_interface)
{
        const char *udi;
        HalDevice *device;
        DBusMessage *reply;
        DBusMessageIter iter;
        gboolean res;

        HAL_INFO (("entering, local_interface=%d", local_interface));

        udi = dbus_message_get_path (message);

        if (!local_interface && !sender_has_privileges (connection, message)) {
                raise_permission_denied (connection, message, "Rescan: not privileged");
                return DBUS_HANDLER_RESULT_HANDLED;
        }

        HAL_DEBUG (("udi=%s", udi));

        device = hal_device_store_find (hald_get_gdl (), udi);
        if (device == NULL)
                device = hal_device_store_find (hald_get_tdl (), udi);

        if (device == NULL) {
                raise_no_such_device (connection, message, udi);
                return DBUS_HANDLER_RESULT_HANDLED;
        }

        res = osspec_device_rescan (device);

        reply = dbus_message_new_method_return (message);
        if (reply == NULL)
                DIE (("No memory"));
        dbus_message_iter_init_append (reply, &iter);
        dbus_message_iter_append_basic (&iter, DBUS_TYPE_BOOLEAN, &res);

        if (!dbus_connection_send (connection, reply, NULL))
                DIE (("No memory"));

        dbus_message_unref (reply);
        return DBUS_HANDLER_RESULT_HANDLED;
}

static DBusHandlerResult
device_reprobe (DBusConnection * connection, DBusMessage * message, dbus_bool_t local_interface)
{
        const char *udi;
        HalDevice *device;
        DBusMessageIter iter;
        DBusMessage *reply;
        gboolean res;

        HAL_TRACE (("entering"));

        udi = dbus_message_get_path (message);

        if (!local_interface && !sender_has_privileges (connection, message)) {
                raise_permission_denied (connection, message, "Reprobe: not privileged");
                return DBUS_HANDLER_RESULT_HANDLED;
        }

        HAL_DEBUG (("udi=%s", udi));

        device = hal_device_store_find (hald_get_gdl (), udi);
        if (device == NULL)
                device = hal_device_store_find (hald_get_tdl (), udi);

        if (device == NULL) {
                raise_no_such_device (connection, message, udi);
                return DBUS_HANDLER_RESULT_HANDLED;
        }

        res = osspec_device_reprobe (device);

        reply = dbus_message_new_method_return (message);
        if (reply == NULL)
                DIE (("No memory"));
        dbus_message_iter_init_append (reply, &iter);
        dbus_message_iter_append_basic (&iter, DBUS_TYPE_BOOLEAN, &res);

        if (!dbus_connection_send (connection, reply, NULL))
                DIE (("No memory"));

        dbus_message_unref (reply);
        return DBUS_HANDLER_RESULT_HANDLED;
}


static DBusHandlerResult
device_emit_condition (DBusConnection * connection, DBusMessage * message, dbus_bool_t local_interface)
{
        const char *udi;
        HalDevice *device;
        DBusMessageIter iter;
        DBusMessage *reply;
        DBusError error;
        const char *condition_name;
        const char *condition_details;
        dbus_bool_t res;

        HAL_TRACE (("entering"));

        udi = dbus_message_get_path (message);

        if (!local_interface) {
                raise_permission_denied (connection, message, "EmitCondition: only allowed for helpers");
                return DBUS_HANDLER_RESULT_HANDLED;
        }

        HAL_DEBUG (("udi=%s", udi));

        dbus_error_init (&error);
        if (!dbus_message_get_args (message, &error,
                                    DBUS_TYPE_STRING, &condition_name,
                                    DBUS_TYPE_STRING, &condition_details,
                                    DBUS_TYPE_INVALID)) {
                raise_syntax (connection, message, "EmitCondition");
                return DBUS_HANDLER_RESULT_HANDLED;
        }


        device = hal_device_store_find (hald_get_gdl (), udi);
        if (device == NULL)
                device = hal_device_store_find (hald_get_tdl (), udi);

        if (device == NULL) {
                raise_no_such_device (connection, message, udi);
                return DBUS_HANDLER_RESULT_HANDLED;
        }

        device_send_signal_condition (device, condition_name, condition_details);

        res = TRUE;

        reply = dbus_message_new_method_return (message);
        if (reply == NULL)
                DIE (("No memory"));
        dbus_message_iter_init_append (reply, &iter);
        dbus_message_iter_append_basic (&iter, DBUS_TYPE_BOOLEAN, &res);

        if (!dbus_connection_send (connection, reply, NULL))
                DIE (("No memory"));

        dbus_message_unref (reply);
        return DBUS_HANDLER_RESULT_HANDLED;
}

typedef struct
{
        DBusConnection  *connection;
        char            *interface_name;
        char            *introspection_xml;
        char            *udi;
} HelperInterfaceHandler;

static GSList *helper_interface_handlers = NULL;

static DBusHandlerResult
device_claim_interface (DBusConnection * connection, DBusMessage * message, dbus_bool_t local_interface)
{
        const char *udi;
        HalDevice *device;
        DBusMessageIter iter;
        DBusMessage *reply;
        DBusError error;
        const char *interface_name;
        const char *introspection_xml;
        dbus_bool_t res;

        HAL_TRACE (("entering"));

        udi = dbus_message_get_path (message);

        if (!local_interface) {
                raise_permission_denied (connection, message, "ClaimInterface: only allowed for helpers");
                return DBUS_HANDLER_RESULT_HANDLED;
        }

        HAL_DEBUG (("udi=%s", udi));

        dbus_error_init (&error);
        if (!dbus_message_get_args (message, &error,
                                    DBUS_TYPE_STRING, &interface_name,
                                    DBUS_TYPE_STRING, &introspection_xml,
                                    DBUS_TYPE_INVALID)) {
                raise_syntax (connection, message, "ClaimInterface");
                return DBUS_HANDLER_RESULT_HANDLED;
        }

        device = hal_device_store_find (hald_get_gdl (), udi);
        if (device == NULL)
                device = hal_device_store_find (hald_get_tdl (), udi);

        if (device == NULL) {
                raise_no_such_device (connection, message, udi);
                return DBUS_HANDLER_RESULT_HANDLED;
        }

        res = TRUE;

        HAL_INFO (("Local connection 0x%x to handle interface '%s' on udi '%s'",
                   connection,
                   interface_name,
                   udi));

        hal_device_property_strlist_add (device, "info.interfaces", interface_name);

        HelperInterfaceHandler *hih = g_new0 (HelperInterfaceHandler, 1);
        hih->connection = connection;
        hih->interface_name = g_strdup (interface_name);
        hih->introspection_xml = g_strdup (introspection_xml);
        hih->udi = g_strdup (udi);
        helper_interface_handlers = g_slist_append (helper_interface_handlers, hih);


        reply = dbus_message_new_method_return (message);
        if (reply == NULL)
                DIE (("No memory"));
        dbus_message_iter_init_append (reply, &iter);
        dbus_message_iter_append_basic (&iter, DBUS_TYPE_BOOLEAN, &res);

        if (!dbus_connection_send (connection, reply, NULL))
                DIE (("No memory"));

        dbus_message_unref (reply);
        return DBUS_HANDLER_RESULT_HANDLED;
}



static DBusHandlerResult
addon_is_ready (DBusConnection * connection, DBusMessage * message, dbus_bool_t local_interface)
{
        const char *udi;
        HalDevice *device;
        DBusMessageIter iter;
        DBusMessage *reply;
        DBusError error;
        dbus_bool_t res;

        HAL_TRACE (("entering"));

        udi = dbus_message_get_path (message);

        if (!local_interface) {
                raise_permission_denied (connection, message, "AddonIsReady: only allowed for helpers");
                return DBUS_HANDLER_RESULT_HANDLED;
        }

        HAL_DEBUG (("udi=%s", udi));

        dbus_error_init (&error);
        if (!dbus_message_get_args (message, &error,
                                    DBUS_TYPE_INVALID)) {
                raise_syntax (connection, message, "AddonIsReady");
                return DBUS_HANDLER_RESULT_HANDLED;
        }

        device = hal_device_store_find (hald_get_gdl (), udi);
        if (device == NULL)
                device = hal_device_store_find (hald_get_tdl (), udi);

        if (device == NULL) {
                raise_no_such_device (connection, message, udi);
                return DBUS_HANDLER_RESULT_HANDLED;
        }

        if (hal_device_inc_num_ready_addons (device)) {
                if (hal_device_are_all_addons_ready (device)) {
                        manager_send_signal_device_added (device);
                }
        }

        res = TRUE;

        HAL_INFO (("AddonIsReady on udi '%s'", udi));

        reply = dbus_message_new_method_return (message);
        if (reply == NULL)
                DIE (("No memory"));
        dbus_message_iter_init_append (reply, &iter);
        dbus_message_iter_append_basic (&iter, DBUS_TYPE_BOOLEAN, &res);

        if (!dbus_connection_send (connection, reply, NULL))
                DIE (("No memory"));

        dbus_message_unref (reply);
        return DBUS_HANDLER_RESULT_HANDLED;
}


/*
 * Create new device in tdl. Return temporary udi.
 */
DBusHandlerResult
manager_new_device (DBusConnection * connection, DBusMessage * message, dbus_bool_t local_interface)
{
        DBusMessage *reply;
        DBusMessageIter iter;
        DBusError error;
        HalDevice *d;
        gchar *udi;
        int i;
        struct timeval tv;

        dbus_error_init (&error);

        if (!local_interface && !sender_has_privileges (connection, message)) {
                raise_permission_denied (connection, message, "NewDevice: not privileged");
                return DBUS_HANDLER_RESULT_HANDLED;
        }

        reply = dbus_message_new_method_return (message);
        if (reply == NULL)
                DIE (("No memory"));

        dbus_message_iter_init_append (reply, &iter);
        d = hal_device_new ();

        gettimeofday(&tv, NULL);
        for (i = 0; i < 1000000 ; i++) {
                udi = g_strdup_printf ("/org/freedesktop/Hal/devices/tmp%05x", ((unsigned) tv.tv_usec & 0xfffff)) + i;
                if (!hal_device_store_find (hald_get_tdl (), udi)) break;
                g_free (udi);
                udi = NULL;
        }

        if (!udi) {
                raise_error (connection, message, "org.freedesktop.Hal.NoSpace", "NewDevice: no space for device");
                return DBUS_HANDLER_RESULT_HANDLED;
        }

        hal_device_set_udi (d, udi);
        hal_device_property_set_string (d, "info.udi", udi);
        hal_device_store_add (hald_get_tdl (), d);
        dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &udi);
        g_free (udi);

        if (!dbus_connection_send (connection, reply, NULL))
                DIE (("No memory"));

        dbus_message_unref (reply);

        return DBUS_HANDLER_RESULT_HANDLED;
}


/*
 * Callout helper.
 */
static void
manager_remove_done (HalDevice *d, gpointer userdata1, gpointer userdata2)
{
        HAL_INFO (("Remove callouts completed udi=%s", d->udi));

        if (!hal_device_store_remove (hald_get_gdl (), d)) {
                HAL_WARNING (("Error removing device"));
        }
}


/*
 * Remove device. Looks in gdl and tdl.
 */
DBusHandlerResult
manager_remove (DBusConnection * connection, DBusMessage * message, dbus_bool_t local_interface)
{
        DBusMessage *reply;
        DBusMessageIter iter;
        DBusError error;
        HalDevice *d;
        char *udi;
        int in_tdl = 0;

        dbus_error_init (&error);

        if (!local_interface && !sender_has_privileges (connection, message)) {
                raise_permission_denied (connection, message, "Remove: not privileged");
                return DBUS_HANDLER_RESULT_HANDLED;
        }

        if (!dbus_message_get_args (message, &error,
                                    DBUS_TYPE_STRING, &udi,
                                    DBUS_TYPE_INVALID)) {
                raise_syntax (connection, message, "Remove");
                return DBUS_HANDLER_RESULT_HANDLED;
        }

        reply = dbus_message_new_method_return (message);
        if (reply == NULL)
                DIE (("No memory"));

        dbus_message_iter_init_append (reply, &iter);

        d = hal_device_store_find (hald_get_gdl (), udi);
        if (d == NULL) {
                hal_device_store_find (hald_get_tdl (), udi);
                in_tdl = 1;
        }
        if (d == NULL) {
                raise_no_such_device (connection, message, udi);
                return DBUS_HANDLER_RESULT_HANDLED;
        }

        /* FIXME:
         * run "info.callouts.remove" ?
         * delete in gdl ?
         * (auto) stop "info.addons" ?
         */

        if (!in_tdl) {
                hal_util_callout_device_remove (d, manager_remove_done, NULL, NULL);
        }

        hal_device_store_remove (in_tdl ? hald_get_tdl () : hald_get_gdl (), d);
        g_object_unref (d);

        if (!dbus_connection_send (connection, reply, NULL))
                DIE (("No memory"));

        dbus_message_unref (reply);

        return DBUS_HANDLER_RESULT_HANDLED;
}


/*
 * Callout helper.
 */
static void
manager_commit_done (HalDevice *d, gpointer userdata1, gpointer userdata2)
{
        HAL_INFO (("Add callouts completed udi=%s", d->udi));
}

/*
 * Preprobing helper.
 */
static void
manager_commit_preprobing_done (HalDevice *d, gpointer userdata1, gpointer userdata2)
{
        if (hal_device_property_get_bool (d, "info.ignore")) {
                /* Leave the device here with info.ignore==TRUE so we won't pick up children
                 * Also remove category and all capabilities
                 */
                hal_device_property_remove (d, "info.category");
                hal_device_property_remove (d, "info.capabilities");
                hal_device_property_set_string (d, "info.udi", "/org/freedesktop/Hal/devices/ignored-device");
                hal_device_property_set_string (d, "info.product", "Ignored Device");

                HAL_INFO (("Preprobing merged info.ignore==TRUE"));

                return;
        }

        /* Merge properties from .fdi files */
        di_search_and_merge (d, DEVICE_INFO_TYPE_INFORMATION);
        di_search_and_merge (d, DEVICE_INFO_TYPE_POLICY);

        hal_util_callout_device_add (d, manager_commit_done, NULL, NULL);
}


/*
 * Move device from tdl to gdl. Runs helpers and callouts.
 */
DBusHandlerResult
manager_commit_to_gdl (DBusConnection * connection, DBusMessage * message, dbus_bool_t local_interface)
{
        DBusMessage *reply;
        DBusMessageIter iter;
        DBusError error;
        HalDevice *d;
        char udi[256], *udi0, *tmp_udi;

        dbus_error_init (&error);

        if (!local_interface && !sender_has_privileges (connection, message)) {
                raise_permission_denied (connection, message, "CommitToGdl: not privileged");
                return DBUS_HANDLER_RESULT_HANDLED;
        }

        if (!dbus_message_get_args (message, &error,
                                    DBUS_TYPE_STRING, &tmp_udi,
                                    DBUS_TYPE_STRING, &udi0,
                                    DBUS_TYPE_INVALID)) {
                raise_syntax (connection, message, "CommitToGdl");
                return DBUS_HANDLER_RESULT_HANDLED;
        }

        reply = dbus_message_new_method_return (message);
        if (reply == NULL)
                DIE (("No memory"));

        dbus_message_iter_init_append (reply, &iter);

        /* look it up in tdl */

        d = hal_device_store_find (hald_get_tdl (), tmp_udi);
        if (d == NULL) {
                raise_no_such_device (connection, message, tmp_udi);
                return DBUS_HANDLER_RESULT_HANDLED;
        }

        /* sanity check & avoid races */
        hal_util_compute_udi (hald_get_gdl (), udi, sizeof udi, "%s", udi0);

        if (hal_device_store_find (hald_get_gdl (), udi)) {
                /* loose it */
                hal_device_store_remove (hald_get_tdl (), d);
                g_object_unref (d);
                raise_error (connection, message, "org.freedesktop.Hal.DeviceExists", "CommitToGdl: Device exists: %s", udi);
                return DBUS_HANDLER_RESULT_HANDLED;
        }

        /* set new udi */
        hal_device_property_remove (d, "info.udi");
        hal_device_set_udi (d, udi);
        hal_device_property_set_string (d, "info.udi", udi);

        /* FIXME:
         * 'RequireEnable' property?
         * fdi "preprobe"?
         * run "info.callouts.preprobe"?
         * remove "info.ignore" devices?
         * fdi "information"?
         * fdi "policy"?
         * run "info.callouts.add"?
         * tdl -> gdl?
         * (auto) start "info.addons"?
         */

        /* Process preprobe fdi files */
        di_search_and_merge (d, DEVICE_INFO_TYPE_PREPROBE);
        hal_util_callout_device_preprobe (d, manager_commit_preprobing_done, NULL, NULL);

        /* move from tdl to gdl */
        hal_device_store_remove (hald_get_tdl (), d);
        hal_device_store_add (hald_get_gdl (), d);

        if (!dbus_connection_send (connection, reply, NULL))
                DIE (("No memory"));

        dbus_message_unref (reply);

        return DBUS_HANDLER_RESULT_HANDLED;
}

typedef struct {
        char *udi;
        char *execpath;
        char **extra_env;
        char *mstdin;
        char *member;
        char *interface;
        DBusMessage *message;
        DBusConnection *connection;
} MethodInvocation;

static void
hald_exec_method_cb (HalDevice *d, guint32 exit_type,
                     gint return_code, gchar **error,
                     gpointer data1, gpointer data2);


static void
hald_exec_method_free_mi (MethodInvocation *mi)
{
        /* hald_runner_run_method() assumes ownership of mi->message.. so we don't free it here */
        g_free (mi->udi);
        g_free (mi->execpath);
        g_strfreev (mi->extra_env);
        g_free (mi->mstdin);
        g_free (mi->member);
        g_free (mi->interface);
        g_free (mi);
}

/* returns FALSE if we don't actually invoke anything */
static gboolean
hald_exec_method_do_invocation (MethodInvocation *mi)
{
        gboolean ret;
        HalDevice *d;

        ret = FALSE;

        d = hal_device_store_find (hald_get_gdl (), mi->udi);
        if (d == NULL)
                d = hal_device_store_find (hald_get_tdl (), mi->udi);

        if (d != NULL) {
                /* no timeout */
                hald_runner_run_method(d,
                                       mi->execpath,
                                       mi->extra_env,
                                       mi->mstdin,
                                       TRUE,
                                       0,
                                       hald_exec_method_cb,
                                       (gpointer) mi->message,
                                       (gpointer) mi->connection);

                ret = TRUE;
        } else {
                HAL_WARNING (("In-queue method call on non-existant device"));

                raise_no_such_device (mi->connection, mi->message, mi->udi);
        }

        return ret;
}


static GHashTable *udi_to_method_queue = NULL;


gboolean
device_is_executing_method (HalDevice *d, const char *interface_name, const char *method_name)
{
        gpointer origkey;
        gboolean ret;
        GList *queue;

        ret = FALSE;

        if (g_hash_table_lookup_extended (udi_to_method_queue, d->udi, &origkey, (gpointer) &queue)) {

                if (queue != NULL) {
                        MethodInvocation *mi;
                        mi = (MethodInvocation *) queue->data;
                        if ((strcmp (mi->interface, interface_name) == 0) &&
                            (strcmp (mi->member, method_name) == 0)) {
                                ret = TRUE;
                        }
                }

                ret = TRUE;
        }
        return ret;
}

static void
hald_exec_method_process_queue (const char *udi);

static void
hald_exec_method_enqueue (MethodInvocation *mi)
{
        gpointer origkey;
        GList *queue;

        if (udi_to_method_queue == NULL) {
                udi_to_method_queue = g_hash_table_new_full (g_str_hash,
                                                             g_str_equal,
                                                             g_free,
                                                             NULL);
        }

        if (g_hash_table_lookup_extended (udi_to_method_queue, mi->udi, &origkey, (gpointer) &queue)) {
                HAL_INFO (("enqueue"));
                queue = g_list_append (queue, mi);
                g_hash_table_replace (udi_to_method_queue, g_strdup (mi->udi), queue);
        } else {
                HAL_INFO (("no need to enqueue"));
                queue = g_list_append (NULL, mi);
                g_hash_table_insert (udi_to_method_queue, g_strdup (mi->udi), queue);

                hald_exec_method_do_invocation (mi);
        }
}


static void
hald_exec_method_process_queue (const char *udi)
{
        gpointer origkey;
        GList *queue;
        MethodInvocation *mi;

        if (g_hash_table_lookup_extended (udi_to_method_queue, udi, &origkey, (gpointer) &queue)) {

                /* clean the top of the list */
                if (queue != NULL) {
                        mi = (MethodInvocation *) queue->data;
                        queue = g_list_delete_link (queue, queue);
                        if (queue == NULL) {
                                g_hash_table_remove (udi_to_method_queue, udi);
                                HAL_INFO (("No more methods in queue"));
                        }

                        /* if method was Volume.Unmount() then refresh mount state */
                        if (strcmp (mi->interface, "org.freedesktop.Hal.Device.Volume") == 0 &&
                            strcmp (mi->member, "Unmount") == 0) {
                                HalDevice *d;

                                HAL_INFO (("Refreshing mount state for %s since Unmount() completed", mi->udi));

                                d = hal_device_store_find (hald_get_gdl (), mi->udi);
                                if (d == NULL) {
                                        d = hal_device_store_find (hald_get_tdl (), mi->udi);
                                }

                                if (d != NULL) {
                                        osspec_refresh_mount_state_for_block_device (d);
                                } else {
                                        HAL_WARNING ((" Cannot find device object for %s", mi->udi));
                                }
                        }

                        hald_exec_method_free_mi (mi);
                }

                /* process the rest of the list */
                if (queue != NULL) {
                        HAL_INFO (("Execing next method in queue"));
                        g_hash_table_replace (udi_to_method_queue, g_strdup (udi), queue);

                        mi = (MethodInvocation *) queue->data;
                        if (!hald_exec_method_do_invocation (mi)) {
                                /* the device went away before we got to it... */
                                hald_exec_method_process_queue (mi->udi);
                        }
                }
        }
}

static void
hald_exec_method_cb (HalDevice *d, guint32 exit_type,
                     gint return_code, gchar **error,
                     gpointer data1, gpointer data2)
{
        dbus_int32_t result;
        DBusMessage *reply = NULL;
        DBusMessage *message;
        DBusMessageIter iter;
        DBusConnection *conn;
        gchar *exp_name = NULL;
        gchar *exp_detail = NULL;
        gboolean invalid_name = FALSE;

        hald_exec_method_process_queue (d->udi);

        message = (DBusMessage *) data1;
        conn = (DBusConnection *) data2;

        if (exit_type == HALD_RUN_SUCCESS && error != NULL &&
            error[0] != NULL && error[1] != NULL) {
                exp_name = error[0];
                if (error[0] != NULL) {
                        exp_detail = error[1];
                }
                HAL_INFO (("failed with '%s' '%s'", exp_name, exp_detail));
        }

        if (exit_type != HALD_RUN_SUCCESS) {
                reply = dbus_message_new_error (message, "org.freedesktop.Hal.Device.UnknownError", "An unknown error occured");
                if (conn != NULL) {
                        if (!dbus_connection_send (conn, reply, NULL))
                                DIE (("No memory"));
                }
                dbus_message_unref (reply);
        } else if (exp_name != NULL && exp_detail != NULL) {
                if (!is_valid_interface_name (exp_name)) {
                        /*
                         * error name may be invalid,
                         * if so we need a generic HAL error name;
                         * otherwise, dbus will be messed up.
                         */
                        invalid_name = TRUE;
                        exp_detail = g_strconcat (exp_name, " \n ", exp_detail, (void *)NULL);
                        exp_name = "org.freedesktop.Hal.Device.UnknownError";
                }
                reply = dbus_message_new_error (message, exp_name, exp_detail);
                if (reply == NULL) {
                        /* error name may be invalid - assume caller fucked up and use a generic HAL error name */
                        reply = dbus_message_new_error (message, "org.freedesktop.Hal.Device.UnknownError", "An unknown error occured");
                        if (reply == NULL) {
                                DIE (("No memory"));
                        }
                }
                if (conn != NULL) {
                        if (!dbus_connection_send (conn, reply, NULL))
                                DIE (("No memory"));
                }
                dbus_message_unref (reply);

        } else {
                result = (dbus_int32_t) return_code;

                reply = dbus_message_new_method_return (message);
                if (reply == NULL)
                        DIE (("No memory"));

                dbus_message_iter_init_append (reply, &iter);
                dbus_message_iter_append_basic (&iter, DBUS_TYPE_INT32, &result);

                if (conn != NULL) {
                        if (!dbus_connection_send (conn, reply, NULL))
                                DIE (("No memory"));
                }

                dbus_message_unref (reply);
        }

        if (invalid_name)
                g_free (exp_detail);
        dbus_message_unref (message);
}

static DBusHandlerResult
hald_exec_method (HalDevice *d, DBusConnection *connection, dbus_bool_t local_interface,
                  DBusMessage *message, const char *execpath)
{
        int type;
        GString *stdin_str;
        DBusMessageIter iter;
        char *extra_env[3];
        char uid_export[128];
        char sender_export[128];
        MethodInvocation *mi;

        /* add calling uid */
        extra_env[0] = NULL;
        extra_env[1] = NULL;
        if (local_interface) {
                extra_env[0] = "HAL_METHOD_INVOKED_BY_UID=0";
                extra_env[1] = "HAL_METHOD_INVOKED_BY_SYSTEMBUS_CONNECTION_NAME=0";
        } else {
                const char *sender;

                sender = dbus_message_get_sender (message);
                if (sender != NULL) {
                        DBusError error;
                        unsigned long uid;

                        dbus_error_init (&error);
                        uid = dbus_bus_get_unix_user (connection, sender, &error);
                        if (!dbus_error_is_set (&error)) {
                                sprintf (uid_export, "HAL_METHOD_INVOKED_BY_UID=%lu", uid);
                                extra_env[0] = uid_export;
                        }
                        snprintf (sender_export, sizeof (sender_export),
                                  "HAL_METHOD_INVOKED_BY_SYSTEMBUS_CONNECTION_NAME=%s", sender);
                        extra_env[1] = sender_export;
                }
        }

        if (extra_env[0] == NULL)
                extra_env[0] = "HAL_METHOD_INVOKED_BY_UID=nobody";
        if (extra_env[1] == NULL)
                extra_env[1] = "HAL_METHOD_INVOKED_BY_SYSTEMBUS_CONNECTION_NAME=0";


        extra_env[2] = NULL;

        /* prepare stdin with parameters */
        stdin_str = g_string_sized_new (256); /* default size for passing params; can grow */
        dbus_message_iter_init (message, &iter);
        while ((type = dbus_message_iter_get_arg_type (&iter)) != DBUS_TYPE_INVALID) {
                switch (type) {
                case DBUS_TYPE_BYTE:
                {
                        unsigned char value;
                        dbus_message_iter_get_basic (&iter, &value);
                        g_string_append_printf (stdin_str, "%u", value);
                        break;
                }
                case DBUS_TYPE_INT16:
                {
                        dbus_int16_t value;
                        dbus_message_iter_get_basic (&iter, &value);
                        g_string_append_printf (stdin_str, "%d", value);
                        break;
                }
                case DBUS_TYPE_UINT16:
                {
                        dbus_uint16_t value;
                        dbus_message_iter_get_basic (&iter, &value);
                        g_string_append_printf (stdin_str, "%u", value);
                        break;
                }
                case DBUS_TYPE_INT32:
                {
                        dbus_int32_t value;
                        dbus_message_iter_get_basic (&iter, &value);
                        g_string_append_printf (stdin_str, "%d", value);
                        break;
                }
                case DBUS_TYPE_UINT32:
                {
                        dbus_uint32_t value;
                        dbus_message_iter_get_basic (&iter, &value);
                        g_string_append_printf (stdin_str, "%u", value);
                        break;
                }
                case DBUS_TYPE_INT64:
                {
                        dbus_int64_t value;
                        dbus_message_iter_get_basic (&iter, &value);
                        g_string_append_printf (stdin_str, "%lld", (long long int) value);
                        break;
                }
                case DBUS_TYPE_UINT64:
                {
                        dbus_uint64_t value;
                        dbus_message_iter_get_basic (&iter, &value);
                        g_string_append_printf (stdin_str, "%llu", (long long unsigned int) value);
                        break;
                }
                case DBUS_TYPE_DOUBLE:
                {
                        double value;
                        dbus_message_iter_get_basic (&iter, &value);
                        g_string_append_printf (stdin_str, "%g", value);
                        break;
                }
                case DBUS_TYPE_BOOLEAN:
                {
                        dbus_bool_t value;
                        dbus_message_iter_get_basic (&iter, &value);
                        g_string_append (stdin_str, value ? "true" : "false");
                        break;
                }
                case DBUS_TYPE_STRING:
                {
                        char *value;
                        dbus_message_iter_get_basic (&iter, &value);
                        g_string_append (stdin_str, value);
                        break;
                }

                case DBUS_TYPE_ARRAY:
                {
                        DBusMessageIter iter_strlist;
                        if (dbus_message_iter_get_element_type (&iter) != DBUS_TYPE_STRING)
                                goto error;

                        dbus_message_iter_recurse (&iter, &iter_strlist);
                        while (dbus_message_iter_get_arg_type (&iter_strlist) == DBUS_TYPE_STRING) {
                                const char *value;
                                dbus_message_iter_get_basic (&iter_strlist, &value);
                                g_string_append (stdin_str, value);
                                g_string_append (stdin_str, "\t");
                                dbus_message_iter_next(&iter_strlist);
                        }
                        break;
                }

                default:
                        goto error;
                }

                g_string_append_c (stdin_str, '\n');
                dbus_message_iter_next (&iter);
        }

        mi = g_new0 (MethodInvocation, 1);
        mi->udi = g_strdup (d->udi);
        mi->execpath = g_strdup (execpath);
        mi->extra_env = g_strdupv (extra_env);
        mi->mstdin = g_strdup (stdin_str->str);
        mi->message = message;
        mi->connection = connection;
        mi->member = g_strdup (dbus_message_get_member (message));
        mi->interface = g_strdup (dbus_message_get_interface (message));
        hald_exec_method_enqueue (mi);

        dbus_message_ref (message);
        g_string_free (stdin_str, TRUE);

        return DBUS_HANDLER_RESULT_HANDLED;

error:
        g_string_free (stdin_str, TRUE);
        return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
}

static gboolean
foreach_device_get_xml_node (HalDeviceStore *store, HalDevice *device,
                             gpointer user_data)
{
        GString *xml = user_data;
        const char *udi, *name;

        udi = hal_device_get_udi (device);
        name = strrchr(udi, '/')+1;

        xml = g_string_append(xml, "  <node name=\"");
        xml = g_string_append(xml, name);
        xml = g_string_append(xml, "\"/>\n");

        return TRUE;
}

static DBusHandlerResult
do_introspect (DBusConnection  *connection,
               DBusMessage     *message,
               dbus_bool_t      local_interface)
{
        const char *path;
        DBusMessage *reply;
        GString *xml;
        char *xml_string;

        HAL_TRACE (("entering do_introspect"));

        path = dbus_message_get_path (message);

        xml = g_string_new ("<!DOCTYPE node PUBLIC \"-//freedesktop//DTD D-BUS Object Introspection 1.0//EN\"\n"
                            "\"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd\">\n"
                            "<node>\n"
                            "  <interface name=\"org.freedesktop.DBus.Introspectable\">\n"
                            "    <method name=\"Introspect\">\n"
                            "      <arg name=\"data\" direction=\"out\" type=\"s\"/>\n"
                            "    </method>\n"
                            "  </interface>\n");

        if (strcmp (path, "/") == 0) {

                xml = g_string_append (xml,
                                       "  <node name=\"org\"/>\n");

        } else if (strcmp (path, "/org") == 0) {

                xml = g_string_append (xml,
                                       "  <node name=\"freedesktop\"/>\n");

        } else if (strcmp (path, "/org/freedesktop") == 0) {

                xml = g_string_append (xml,
                                       "  <node name=\"Hal\"/>\n");

        } else if (strcmp (path, "/org/freedesktop/Hal") == 0) {

                xml = g_string_append (xml,
                                       "  <node name=\"Manager\"/>\n"
                                       "  <node name=\"devices\"/>\n");

        } else if (strcmp (path, "/org/freedesktop/Hal/devices") == 0) {

                hal_device_store_foreach (hald_get_gdl (),
                                          foreach_device_get_xml_node,
                                          xml);

        } else if (strcmp (path, "/org/freedesktop/Hal/Manager") == 0) {

                xml = g_string_append (xml,
                                       "  <interface name=\"org.freedesktop.Hal.Manager\">\n"
                                       "    <method name=\"GetAllDevices\">\n"
                                       "      <arg name=\"devices\" direction=\"out\" type=\"ao\"/>\n"
                                       "    </method>\n"
                                       "    <method name=\"DeviceExists\">\n"
                                       "      <arg name=\"does_it_exist\" direction=\"out\" type=\"b\"/>\n"
                                       "      <arg name=\"udi\" direction=\"in\" type=\"o\"/>\n"
                                       "    </method>\n"
                                       "    <method name=\"FindDeviceStringMatch\">\n"
                                       "      <arg name=\"devices\" direction=\"out\" type=\"ao\"/>\n"
                                       "      <arg name=\"key\" direction=\"in\" type=\"s\"/>\n"
                                       "      <arg name=\"value\" direction=\"in\" type=\"s\"/>\n"
                                       "    </method>\n"
                                       "    <method name=\"FindDeviceByCapability\">\n"
                                       "      <arg name=\"devices\" direction=\"out\" type=\"ao\"/>\n"
                                       "      <arg name=\"capability\" direction=\"in\" type=\"s\"/>\n"
                                       "    </method>\n"
                                       "    <method name=\"ClaimBranch\">\n"
                                       "      <arg name=\"udi\" direction=\"in\" type=\"o\"/>\n"
                                       "      <arg name=\"claim_service\" direction=\"in\" type=\"s\"/>\n"
                                       "      <arg name=\"result\" direction=\"out\" type=\"b\"/>\n"
                                       "    </method>\n"
                                       "    <method name=\"UnclaimBranch\">\n"
                                       "      <arg name=\"udi\" direction=\"in\" type=\"o\"/>\n"
                                       "      <arg name=\"result\" direction=\"out\" type=\"b\"/>\n"
                                       "    </method>\n"
                                       "    <method name=\"NewDevice\">\n"
                                       "      <arg name=\"temporary_udi\" direction=\"out\" type=\"s\"/>\n"
                                       "    </method>\n"
                                       "    <method name=\"Remove\">\n"
                                       "      <arg name=\"udi\" direction=\"in\" type=\"s\"/>\n"
                                       "    </method>\n"
                                       "    <method name=\"CommitToGdl\">\n"
                                       "      <arg name=\"temporary_udi\" direction=\"in\" type=\"s\"/>\n"
                                       "      <arg name=\"global_udi\" direction=\"in\" type=\"s\"/>\n"
                                       "    </method>\n"
                                       "  </interface>\n");
        } else {
                HalDevice *d;

                d = hal_device_store_find (hald_get_gdl (), path);
                if (d == NULL)
                        d = hal_device_store_find (hald_get_tdl (), path);

                if (d == NULL) {
                        raise_no_such_device (connection, message, path);
                        return DBUS_HANDLER_RESULT_HANDLED;
                }

                xml = g_string_append (xml,
                                       "  <interface name=\"org.freedesktop.Hal.Device\">\n"
                                       "    <method name=\"GetAllProperties\">\n"
                                       "      <arg name=\"properties\" direction=\"out\" type=\"a{sv}\"/>\n"
                                       "    </method>\n"
                                       "    <method name=\"SetMultipleProperties\">\n"
                                       "      <arg name=\"properties\" direction=\"in\" type=\"a{sv}\"/>\n"
                                       "    </method>\n"
                                       "    <method name=\"GetProperty\">\n"
                                       "      <arg name=\"key\" direction=\"in\" type=\"s\"/>\n"
                                       "      <arg name=\"value\" direction=\"out\" type=\"v\"/>\n"
                                       "    </method>\n"
                                       "    <method name=\"GetPropertyString\">\n"
                                       "      <arg name=\"key\" direction=\"in\" type=\"s\"/>\n"
                                       "      <arg name=\"value\" direction=\"out\" type=\"s\"/>\n"
                                       "    </method>\n"
                                       "    <method name=\"GetPropertyStringList\">\n"
                                       "      <arg name=\"key\" direction=\"in\" type=\"s\"/>\n"
                                       "      <arg name=\"value\" direction=\"out\" type=\"as\"/>\n"
                                       "    </method>\n"
                                       "    <method name=\"GetPropertyInteger\">\n"
                                       "      <arg name=\"key\" direction=\"in\" type=\"s\"/>\n"
                                       "      <arg name=\"value\" direction=\"out\" type=\"i\"/>\n"
                                       "    </method>\n"
                                       "    <method name=\"GetPropertyBoolean\">\n"
                                       "      <arg name=\"key\" direction=\"in\" type=\"s\"/>\n"
                                       "      <arg name=\"value\" direction=\"out\" type=\"b\"/>\n"
                                       "    </method>\n"
                                       "    <method name=\"GetPropertyDouble\">\n"
                                       "      <arg name=\"key\" direction=\"in\" type=\"s\"/>\n"
                                       "      <arg name=\"value\" direction=\"out\" type=\"d\"/>\n"
                                       "    </method>\n"
                                       "    <method name=\"SetProperty\">\n"
                                       "      <arg name=\"key\" direction=\"in\" type=\"s\"/>\n"
                                       "      <arg name=\"value\" direction=\"in\" type=\"v\"/>\n"
                                       "    </method>\n"
                                       "    <method name=\"SetPropertyString\">\n"
                                       "      <arg name=\"key\" direction=\"in\" type=\"s\"/>\n"
                                       "      <arg name=\"value\" direction=\"in\" type=\"s\"/>\n"
                                       "    </method>\n"
                                       "    <method name=\"SetPropertyStringList\">\n"
                                       "      <arg name=\"key\" direction=\"in\" type=\"s\"/>\n"
                                       "      <arg name=\"value\" direction=\"in\" type=\"as\"/>\n"
                                       "    </method>\n"
                                       "    <method name=\"SetPropertyInteger\">\n"
                                       "      <arg name=\"key\" direction=\"in\" type=\"s\"/>\n"
                                       "      <arg name=\"value\" direction=\"in\" type=\"i\"/>\n"
                                       "    </method>\n"
                                       "    <method name=\"SetPropertyBoolean\">\n"
                                       "      <arg name=\"key\" direction=\"in\" type=\"s\"/>\n"
                                       "      <arg name=\"value\" direction=\"in\" type=\"b\"/>\n"
                                       "    </method>\n"
                                       "    <method name=\"SetPropertyDouble\">\n"
                                       "      <arg name=\"key\" direction=\"in\" type=\"s\"/>\n"
                                       "      <arg name=\"value\" direction=\"in\" type=\"d\"/>\n"
                                       "    </method>\n"

                                       "    <method name=\"RemoveProperty\">\n"
                                       "      <arg name=\"key\" direction=\"in\" type=\"s\"/>\n"
                                       "    </method>\n"
                                       "    <method name=\"GetPropertyType\">\n"
                                       "      <arg name=\"key\" direction=\"in\" type=\"s\"/>\n"
                                       "      <arg name=\"type\" direction=\"out\" type=\"i\"/>\n"
                                       "    </method>\n"
                                       "    <method name=\"PropertyExists\">\n"
                                       "      <arg name=\"key\" direction=\"in\" type=\"s\"/>\n"
                                       "      <arg name=\"does_it_exist\" direction=\"out\" type=\"b\"/>\n"
                                       "    </method>\n"
                                       "    <method name=\"AddCapability\">\n"
                                       "      <arg name=\"capability\" direction=\"in\" type=\"s\"/>\n"
                                       "    </method>\n"
                                       "    <method name=\"QueryCapability\">\n"
                                       "      <arg name=\"capability\" direction=\"in\" type=\"s\"/>\n"
                                       "      <arg name=\"does_it_have_capability\" direction=\"out\" type=\"b\"/>\n"
                                       "    </method>\n"
                                       "    <method name=\"Lock\">\n"
                                       "      <arg name=\"reason\" direction=\"in\" type=\"s\"/>\n"
                                       "      <arg name=\"acquired_lock\" direction=\"out\" type=\"b\"/>\n"
                                       "    </method>\n"
                                       "    <method name=\"Unlock\">\n"
                                       "      <arg name=\"released_lock\" direction=\"out\" type=\"b\"/>\n"
                                       "    </method>\n"

                                       "    <method name=\"StringListAppend\">\n"
                                       "      <arg name=\"key\" direction=\"in\" type=\"s\"/>\n"
                                       "      <arg name=\"value\" direction=\"in\" type=\"s\"/>\n"
                                       "    </method>\n"
                                       "    <method name=\"StringListPrepend\">\n"
                                       "      <arg name=\"key\" direction=\"in\" type=\"s\"/>\n"
                                       "      <arg name=\"value\" direction=\"in\" type=\"s\"/>\n"
                                       "    </method>\n"
                                       "    <method name=\"StringListRemove\">\n"
                                       "      <arg name=\"key\" direction=\"in\" type=\"s\"/>\n"
                                       "      <arg name=\"value\" direction=\"in\" type=\"s\"/>\n"
                                       "    </method>\n"
                                       "    <method name=\"EmitCondition\">\n"
                                       "      <arg name=\"condition_name\" direction=\"in\" type=\"s\"/>\n"
                                       "      <arg name=\"condition_details\" direction=\"in\" type=\"s\"/>\n"
                                       "      <arg name=\"rc\" direction=\"out\" type=\"b\"/>\n"
                                       "    </method>\n"

                                       "    <method name=\"Rescan\">\n"
                                       "      <arg name=\"call_had_sideeffect\" direction=\"out\" type=\"b\"/>\n"
                                       "    </method>\n"
                                       "    <method name=\"Reprobe\">\n"
                                       "      <arg name=\"call_had_sideeffect\" direction=\"out\" type=\"b\"/>\n"
                                       "    </method>\n"

                                       "    <method name=\"ClaimInterface\">\n"
                                       "      <arg name=\"interface_name\" direction=\"in\" type=\"s\"/>\n"
                                       "      <arg name=\"introspection_xml\" direction=\"in\" type=\"s\"/>\n"
                                       "      <arg name=\"rc\" direction=\"out\" type=\"b\"/>\n"
                                       "    </method>\n"

                                       "    <method name=\"AddonIsReady\">\n"
                                       "      <arg name=\"rc\" direction=\"out\" type=\"b\"/>\n"
                                       "    </method>\n"

                                       "  </interface>\n");

                        GSList *interfaces;
                        GSList *i;

                        interfaces = hal_device_property_get_strlist (d, "info.interfaces");
                        for (i = interfaces; i != NULL; i = g_slist_next (i)) {
                                const char *ifname = (const char *) i->data;
                                char *method_names_prop;
                                char *method_signatures_prop;
                                char *method_argnames_prop;
                                GSList *method_names;
                                GSList *method_signatures;
                                GSList *method_argnames;
                                GSList *j;
                                GSList *k;
                                GSList *l;

                                g_string_append_printf (xml, "  <interface name=\"%s\">\n", ifname);

                                method_names_prop = g_strdup_printf ("%s.method_names", ifname);
                                method_signatures_prop = g_strdup_printf ("%s.method_signatures", ifname);
                                method_argnames_prop = g_strdup_printf ("%s.method_argnames", ifname);

                                method_names = hal_device_property_get_strlist (d, method_names_prop);
                                method_signatures = hal_device_property_get_strlist (d, method_signatures_prop);
                                method_argnames = hal_device_property_get_strlist (d, method_argnames_prop);

                                /* consult local list */
                                if (method_names == NULL) {
                                        GSList *i;

                                        for (i = helper_interface_handlers; i != NULL; i = g_slist_next (i)) {
                                                HelperInterfaceHandler *hih = i->data;
                                                if (strcmp (hih->udi, path) == 0) {
                                                        xml = g_string_append (xml, hih->introspection_xml);
                                                }
                                        }

                                }

                                for (j = method_names, k = method_signatures, l = method_argnames;
                                     j != NULL && k != NULL && l != NULL;
                                     j = g_slist_next (j), k = g_slist_next (k), l = g_slist_next (l)) {
                                        const char *name;
                                        const char *sig;
                                        const char *argnames;
                                        char **args;
                                        unsigned int n;
                                        unsigned int m;

                                        name = j->data;
                                        sig = k->data;
                                        argnames = l->data;

                                        args = g_strsplit (argnames, " ", 0);

                                        g_string_append_printf (xml, "    <method name=\"%s\">\n", name);

                                        for (n = 0, m = 0; n < strlen (sig) && args[m] != NULL; n++, m++) {
                                                switch (sig[n]) {
                                                case 'a':
                                                        if (n == strlen (sig) - 1) {
                                                                HAL_WARNING (("Broken signature for method %s "
                                                                              "on interface %s for object %s",
                                                                              name, ifname, path));
                                                                continue;
                                                        }
                                                        g_string_append_printf (
                                                          xml,
                                                          "      <arg name=\"%s\" direction=\"in\" type=\"a%c\"/>\n",
                                                          args[m], sig[n + 1]);
                                                        n++;
                                                        break;

                                                default:
                                                        g_string_append_printf (
                                                          xml,
                                                          "      <arg name=\"%s\" direction=\"in\" type=\"%c\"/>\n",
                                                          args[m], sig[n]);
                                                        break;
                                                }
                                        }
                                        xml = g_string_append (
                                                xml,
                                                "      <arg name=\"return_code\" direction=\"out\" type=\"i\"/>\n");
                                        xml = g_string_append  (
                                                xml,
                                                "    </method>\n");

                                }


                                xml = g_string_append (xml, "  </interface>\n");

                                g_free (method_names_prop);
                                g_free (method_signatures_prop);
                                g_free (method_argnames_prop);
                        }

        }

        reply = dbus_message_new_method_return (message);

        xml = g_string_append (xml, "</node>\n");
        xml_string = g_string_free (xml, FALSE);

        dbus_message_append_args (reply,
                                  DBUS_TYPE_STRING, &xml_string,
                                  DBUS_TYPE_INVALID);

        g_free (xml_string);

        if (reply == NULL)
                DIE (("No memory"));

        if (!dbus_connection_send (connection, reply, NULL))
                DIE (("No memory"));

        dbus_message_unref (reply);
        return DBUS_HANDLER_RESULT_HANDLED;
}

static void
reply_from_fwd_message (DBusPendingCall *pending_call,
                        void            *user_data)
{
        DBusMessage *reply_from_addon;
        DBusMessage *method_from_caller;
        DBusMessage *reply;

        /*HAL_INFO (("in reply_from_fwd_message : user_data = 0x%x", user_data));*/

        method_from_caller = (DBusMessage *) user_data;
        reply_from_addon = dbus_pending_call_steal_reply (pending_call);

        reply = dbus_message_copy (reply_from_addon);
        dbus_message_set_destination (reply, dbus_message_get_sender (method_from_caller));
        dbus_message_set_reply_serial (reply, dbus_message_get_serial (method_from_caller));

        if (dbus_connection != NULL)
                dbus_connection_send (dbus_connection, reply, NULL);

        dbus_message_unref (reply_from_addon);
        dbus_message_unref (reply);
        dbus_message_unref (method_from_caller);
        dbus_pending_call_unref (pending_call);
}

static DBusHandlerResult
hald_dbus_filter_handle_methods (DBusConnection *connection, DBusMessage *message,
                                 void *user_data, dbus_bool_t local_interface)
{
        /*HAL_INFO (("connection=0x%x obj_path=%s interface=%s method=%s local_interface=%d",
                   connection,
                   dbus_message_get_path (message),
                   dbus_message_get_interface (message),
                   dbus_message_get_member (message),
                   local_interface));*/

        if (dbus_message_is_method_call (message,
                                         "org.freedesktop.Hal.Manager",
                                         "GetAllDevices") &&
                   strcmp (dbus_message_get_path (message),
                           "/org/freedesktop/Hal/Manager") == 0) {
                return manager_get_all_devices (connection, message);
        } else if (dbus_message_is_method_call (message,
                                                "org.freedesktop.Hal.Manager",
                                                "DeviceExists") &&
                   strcmp (dbus_message_get_path (message),
                           "/org/freedesktop/Hal/Manager") == 0) {
                return manager_device_exists (connection, message);
        } else if (dbus_message_is_method_call (message,
                                                "org.freedesktop.Hal.Manager",
                                                "FindDeviceStringMatch") &&
                   strcmp (dbus_message_get_path (message),
                           "/org/freedesktop/Hal/Manager") == 0) {
                return manager_find_device_string_match (connection,
                                                         message);
        } else if (dbus_message_is_method_call
                   (message, "org.freedesktop.Hal.Manager",
                    "FindDeviceByCapability")
                   && strcmp (dbus_message_get_path (message),
                              "/org/freedesktop/Hal/Manager") == 0) {
                return manager_find_device_by_capability (connection,
                                                          message);
        } else if (dbus_message_is_method_call (message,
                                                "org.freedesktop.Hal.Manager",
                                                "ClaimBranch") &&
                   strcmp (dbus_message_get_path (message),
                           "/org/freedesktop/Hal/Manager") == 0) {
                return manager_claim_branch (connection, message);
        } else if (dbus_message_is_method_call (message,
                                                "org.freedesktop.Hal.Manager",
                                                "UnclaimBranch") &&
                   strcmp (dbus_message_get_path (message),
                           "/org/freedesktop/Hal/Manager") == 0) {
                return manager_unclaim_branch (connection, message);
        } else if (dbus_message_is_method_call (message,
                                                "org.freedesktop.Hal.Manager",
                                                "NewDevice") &&
                   strcmp (dbus_message_get_path (message),
                            "/org/freedesktop/Hal/Manager") == 0) {
                return manager_new_device (connection, message, local_interface);
        } else if (dbus_message_is_method_call (message,
                                                "org.freedesktop.Hal.Manager",
                                                "Remove") &&
                   strcmp (dbus_message_get_path (message),
                            "/org/freedesktop/Hal/Manager") == 0) {
                return manager_remove (connection, message, local_interface);
        } else if (dbus_message_is_method_call (message,
                                                "org.freedesktop.Hal.Manager",
                                                "CommitToGdl") &&
                   strcmp (dbus_message_get_path (message),
                            "/org/freedesktop/Hal/Manager") == 0) {
                return manager_commit_to_gdl (connection, message, local_interface);
        } else if (dbus_message_is_method_call (message,
                                              "org.freedesktop.Hal.Device",
                                              "GetAllProperties")) {
                return device_get_all_properties (connection, message);
        } else if (dbus_message_is_method_call (message,
                                              "org.freedesktop.Hal.Device",
                                              "SetMultipleProperties")) {
                return device_set_multiple_properties (connection, message, local_interface);
        } else if (dbus_message_is_method_call (message,
                                                "org.freedesktop.Hal.Device",
                                                "GetProperty")) {
                return device_get_property (connection, message);
        } else if (dbus_message_is_method_call (message,
                                                "org.freedesktop.Hal.Device",
                                                "GetPropertyString")) {
                return device_get_property (connection, message);
        } else if (dbus_message_is_method_call (message,
                                                "org.freedesktop.Hal.Device",
                                                "GetPropertyStringList")) {
                return device_get_property (connection, message);
        } else if (dbus_message_is_method_call (message,
                                                "org.freedesktop.Hal.Device",
                                                "GetPropertyInteger")) {
                return device_get_property (connection, message);
        } else if (dbus_message_is_method_call (message,
                                                "org.freedesktop.Hal.Device",
                                                "GetPropertyBoolean")) {
                return device_get_property (connection, message);
        } else if (dbus_message_is_method_call (message,
                                                "org.freedesktop.Hal.Device",
                                                "GetPropertyDouble")) {
                return device_get_property (connection, message);
        } else if (dbus_message_is_method_call (message,
                                                "org.freedesktop.Hal.Device",
                                                "SetProperty")) {
                return device_set_property (connection, message, local_interface);
        } else if (dbus_message_is_method_call (message,
                                                "org.freedesktop.Hal.Device",
                                                "SetPropertyString")) {
                return device_set_property (connection, message, local_interface);
        } else if (dbus_message_is_method_call (message,
                                                "org.freedesktop.Hal.Device",
                                                "SetPropertyInteger")) {
                return device_set_property (connection, message, local_interface);
        } else if (dbus_message_is_method_call (message,
                                                "org.freedesktop.Hal.Device",
                                                "SetPropertyBoolean")) {
                return device_set_property (connection, message, local_interface);
        } else if (dbus_message_is_method_call (message,
                                                "org.freedesktop.Hal.Device",
                                                "SetPropertyDouble")) {
                return device_set_property (connection, message, local_interface);
        } else if (dbus_message_is_method_call (message,
                                                "org.freedesktop.Hal.Device",
                                                "RemoveProperty")) {
                return device_remove_property (connection, message, local_interface);
        } else if (dbus_message_is_method_call (message,
                                                "org.freedesktop.Hal.Device",
                                                "GetPropertyType")) {
                return device_get_property_type (connection, message);
        } else if (dbus_message_is_method_call (message,
                                                "org.freedesktop.Hal.Device",
                                                "PropertyExists")) {
                return device_property_exists (connection, message);
        } else if (dbus_message_is_method_call (message,
                                                "org.freedesktop.Hal.Device",
                                                "AddCapability")) {
                return device_add_capability (connection, message, local_interface);
        } else if (dbus_message_is_method_call (message,
                                                "org.freedesktop.Hal.Device",
                                                "QueryCapability")) {
                return device_query_capability (connection, message);
        } else if (dbus_message_is_method_call (message,
                                                "org.freedesktop.Hal.Device",
                                                "Lock")) {
                return device_lock (connection, message);
        } else if (dbus_message_is_method_call (message,
                                                "org.freedesktop.Hal.Device",
                                                "Unlock")) {
                return device_unlock (connection, message);
        } else if (dbus_message_is_method_call (message,
                                                "org.freedesktop.Hal.Device",
                                                "StringListAppend")) {
                return device_string_list_append_prepend (connection, message, FALSE);
        } else if (dbus_message_is_method_call (message,
                                                "org.freedesktop.Hal.Device",
                                                "StringListPrepend")) {
                return device_string_list_append_prepend (connection, message, TRUE);
        } else if (dbus_message_is_method_call (message,
                                                "org.freedesktop.Hal.Device",
                                                "StringListRemove")) {
                return device_string_list_remove (connection, message);
        } else if (dbus_message_is_method_call (message,
                                                "org.freedesktop.Hal.Device",
                                                "Rescan")) {
                return device_rescan (connection, message, local_interface);
        } else if (dbus_message_is_method_call (message,
                                                "org.freedesktop.Hal.Device",
                                                "Reprobe")) {
                return device_reprobe (connection, message, local_interface);
        } else if (dbus_message_is_method_call (message,
                                                "org.freedesktop.Hal.Device",
                                                "EmitCondition")) {
                return device_emit_condition (connection, message, local_interface);
        } else if (dbus_message_is_method_call (message,
                                                "org.freedesktop.Hal.Device",
                                                "ClaimInterface")) {
                return device_claim_interface (connection, message, local_interface);
#if 0
        } else if (dbus_message_is_method_call (message,
                                                "org.freedesktop.Hal.Device",
                                                "ReleaseInterface")) {
                return device_release_interface (connection, message, local_interface);
#endif
        } else if (dbus_message_is_method_call (message,
                                                "org.freedesktop.Hal.Device",
                                                "AddonIsReady")) {
                return addon_is_ready (connection, message, local_interface);
        } else if (dbus_message_is_method_call (message,
                                                "org.freedesktop.DBus.Introspectable",
                                                "Introspect")) {
                return do_introspect (connection, message, local_interface);
        } else {
                const char *interface;
                const char *udi;
                const char *method;
                const char *signature;
                HalDevice *d;

                /* check for device-specific interfaces that individual objects may support */

                udi = dbus_message_get_path (message);
                interface = dbus_message_get_interface (message);
                method = dbus_message_get_member (message);
                signature = dbus_message_get_signature (message);

                d = NULL;

                if (udi != NULL) {
                        d = hal_device_store_find (hald_get_gdl (), udi);
                        if (d == NULL)
                                d = hal_device_store_find (hald_get_tdl (), udi);
                }

                if (d != NULL && interface != NULL) {
                        GSList *i;

                        for (i = helper_interface_handlers; i != NULL; i = g_slist_next (i)) {
                                HelperInterfaceHandler *hih = i->data;
                                if (strcmp (hih->udi, udi) == 0 &&
                                    strcmp (hih->interface_name, interface) == 0) {
                                        DBusPendingCall *pending_call;
                                        DBusMessage *copy;

                                        /*HAL_INFO (("forwarding method to connection 0x%x", hih->connection));*/

                                        dbus_message_ref (message);

                                        /* send a copy of the message */
                                        copy = dbus_message_copy (message);
                                        if (!dbus_connection_send_with_reply (hih->connection,
                                                                              copy,
                                                                              &pending_call,
                                                                              /*-1*/ 8000)) {
                                                /* TODO: handle error */
                                        } else {
                                                /*HAL_INFO (("connection=%x message=%x", connection, message));*/
                                                dbus_pending_call_set_notify (pending_call,
                                                                              reply_from_fwd_message,
                                                                              (void *) message,
                                                                              NULL);
                                        }

                                        dbus_message_unref (copy);

                                        return DBUS_HANDLER_RESULT_HANDLED;
                                }
                        }
                }

                if (d != NULL && interface != NULL && method != NULL && signature != NULL) {
                        GSList *interfaces;
                        GSList *i;

                        interfaces = hal_device_property_get_strlist (d, "info.interfaces");
                        for (i = interfaces; i != NULL; i = g_slist_next (i)) {
                                const char *ifname = (const char *) i->data;

                                if (strcmp (ifname, interface) == 0) {
                                        guint num;
                                        GSList *method_names;
                                        char *s;

                                        s = g_strdup_printf ("%s.method_names", interface);
                                        method_names = hal_device_property_get_strlist (d, s);
                                        g_free (s);
                                        for (i = method_names, num = 0; i != NULL; i = g_slist_next (i), num++) {
                                                const char *methodname = (const char *) i->data;
                                                if (strcmp (methodname, method) == 0) {
                                                        const char *execpath;
                                                        const char *sig;

                                                        s = g_strdup_printf ("%s.method_execpaths", interface);
                                                        execpath = hal_device_property_get_strlist_elem (d, s, num);
                                                        g_free (s);
                                                        s = g_strdup_printf ("%s.method_signatures", interface);
                                                        sig = hal_device_property_get_strlist_elem (d, s, num);
                                                        g_free (s);

                                                        if (execpath != NULL && sig != NULL &&
                                                            strcmp (sig, signature) == 0) {

                                                                HAL_INFO (("OK for method '%s' with signature '%s' on interface '%s' for UDI '%s' and execpath '%s'", method, signature, interface, udi, execpath));

                                                                return hald_exec_method (d, connection, local_interface,
                                                                                         message, execpath);
                                                        }

                                                }
                                        }
                                }
                        }

                }
        }

        return osspec_filter_function (connection, message, user_data);
}


/** Message handler for method invocations. All invocations on any object
 *  or interface is routed through this function.
 *
 *  @param  connection          D-BUS connection
 *  @param  message             Message
 *  @param  user_data           User data
 *  @return                     What to do with the message
 */
DBusHandlerResult
hald_dbus_filter_function (DBusConnection * connection,
                           DBusMessage * message, void *user_data)
{
        if (dbus_message_is_signal (message, DBUS_INTERFACE_LOCAL, "Disconnected") &&
            strcmp (dbus_message_get_path (message), DBUS_PATH_LOCAL) == 0) {

                /* this is a local message; e.g. from libdbus in this process */

                HAL_INFO (("Got disconnected from the system message bus; "
                           "retrying to reconnect every 3000 ms"));
                dbus_connection_unref (dbus_connection);
                dbus_connection = NULL;

                g_timeout_add (3000, reinit_dbus, NULL);

        } else if (dbus_message_is_signal (message,
                                           DBUS_INTERFACE_DBUS,
                                           "NameOwnerChanged")) {

                if (services_with_locks != NULL || services_with_claims != NULL)
                        service_deleted (message);
        } else
                return hald_dbus_filter_handle_methods (connection, message, user_data, FALSE);

        return DBUS_HANDLER_RESULT_HANDLED;
}



static DBusHandlerResult
local_server_message_handler (DBusConnection *connection,
                              DBusMessage *message,
                              void *user_data)
{
        /*HAL_INFO (("local_server_message_handler: destination=%s obj_path=%s interface=%s method=%s",
                   dbus_message_get_destination (message),
                   dbus_message_get_path (message),
                   dbus_message_get_interface (message),
                   dbus_message_get_member (message)));*/

        if (dbus_message_is_method_call (message, "org.freedesktop.DBus", "AddMatch")) {
                DBusMessage *reply;

                /* cheat, and handle AddMatch since libhal will try to invoke this method */
                reply = dbus_message_new_method_return (message);
                if (reply == NULL)
                        DIE (("No memory"));
                if (!dbus_connection_send (connection, reply, NULL))
                        DIE (("No memory"));
                dbus_message_unref (reply);
                return DBUS_HANDLER_RESULT_HANDLED;
        } else if (dbus_message_is_signal (message, DBUS_INTERFACE_LOCAL, "Disconnected") &&
                   strcmp (dbus_message_get_path (message), DBUS_PATH_LOCAL) == 0) {
                GSList *i;
                GSList *j;

                HAL_INFO (("Client to local_server was disconnected"));

                for (i = helper_interface_handlers; i != NULL; i = j) {
                        HelperInterfaceHandler *hih = i->data;

                        j = g_slist_next (i);

                        if (hih->connection == connection) {
                                g_free (hih->interface_name);
                                g_free (hih->introspection_xml);
                                g_free (hih->udi);
                                g_free (hih);
                                helper_interface_handlers = g_slist_remove_link (helper_interface_handlers, i);
                        }
                }

                dbus_connection_unref (connection);
                return DBUS_HANDLER_RESULT_HANDLED;
        } else if (dbus_message_get_type (message) == DBUS_MESSAGE_TYPE_SIGNAL) {
                DBusMessage *copy;

                /* it's a signal, just forward it onto the system message bus */
                copy = dbus_message_copy (message);
                if (dbus_connection != NULL) {
                        dbus_connection_send (dbus_connection, copy, NULL);
                }
                dbus_message_unref (copy);
        } else {
                return hald_dbus_filter_handle_methods (connection, message, user_data, TRUE);
        }

        return DBUS_HANDLER_RESULT_HANDLED;
}

static void
local_server_unregister_handler (DBusConnection *connection, void *user_data)
{
        HAL_INFO (("unregistered"));
}

static void
local_server_handle_connection (DBusServer *server,
                          DBusConnection *new_connection,
                          void *data)
{
        DBusObjectPathVTable vtable = { &local_server_unregister_handler,
                                        &local_server_message_handler,
                                        NULL, NULL, NULL, NULL};

        HAL_INFO (("%d: Got a connection", getpid ()));
        HAL_INFO (("dbus_connection_get_is_connected = %d", dbus_connection_get_is_connected (new_connection)));

        /*dbus_connection_add_filter (new_connection, server_filter_function, NULL, NULL);*/

        dbus_connection_register_fallback (new_connection,
                                           "/org/freedesktop",
                                           &vtable,
                                           NULL);
        dbus_connection_ref (new_connection);
        dbus_connection_setup_with_g_main (new_connection, NULL);
}


static DBusServer *local_server = NULL;

char *
hald_dbus_local_server_addr (void)
{
        if (local_server == NULL)
                return NULL;

        return dbus_server_get_address (local_server);
}

gboolean
hald_dbus_local_server_init (void)
{
        gboolean ret;
        DBusError error;
        char *server_addr;

        ret = FALSE;

        /* setup a server listening on a socket so we can do point to point
         * connections for programs spawned by hald
         */
        dbus_error_init (&error);
        if ((local_server = dbus_server_listen (HALD_DBUS_ADDRESS, &error)) == NULL) {
                HAL_ERROR (("Cannot create D-BUS server"));
                goto out;
        }
        server_addr = dbus_server_get_address (local_server);
        HAL_INFO (("local server is listening at %s", server_addr));
        dbus_free (server_addr);
        dbus_server_setup_with_g_main (local_server, NULL);
        dbus_server_set_new_connection_function (local_server, local_server_handle_connection, NULL, NULL);

        ret = TRUE;

out:
        return ret;
}

gboolean
hald_dbus_init (void)
{
        DBusError dbus_error;

        HAL_INFO (("entering"));

        dbus_connection_set_change_sigpipe (TRUE);

        dbus_error_init (&dbus_error);
        dbus_connection = dbus_bus_get (DBUS_BUS_SYSTEM, &dbus_error);
        if (dbus_connection == NULL) {
                HAL_ERROR (("dbus_bus_get(): %s", dbus_error.message));
                return FALSE;
        }

        dbus_connection_setup_with_g_main (dbus_connection, NULL);
        dbus_connection_set_exit_on_disconnect (dbus_connection, FALSE);

        dbus_bus_request_name (dbus_connection, "org.freedesktop.Hal",
                                  0, &dbus_error);
        if (dbus_error_is_set (&dbus_error)) {
                HAL_ERROR (("dbus_bus_request_name(): %s",
                            dbus_error.message));
                return FALSE;
        }

        dbus_connection_add_filter (dbus_connection, hald_dbus_filter_function, NULL, NULL);

        dbus_bus_add_match (dbus_connection,
                            "type='signal'"
                            ",interface='"DBUS_INTERFACE_DBUS"'"
                            ",sender='"DBUS_SERVICE_DBUS"'"
                            ",member='NameOwnerChanged'",
                            NULL);

        return TRUE;
}

/** @} */