diff options
-rwxr-xr-x | examples/mandatory-locking-test.py | 20 | ||||
-rw-r--r-- | hald/device.c | 171 | ||||
-rw-r--r-- | hald/device.h | 9 | ||||
-rw-r--r-- | hald/hald_dbus.c | 324 | ||||
-rw-r--r-- | hald/hald_dbus.h | 4 |
5 files changed, 528 insertions, 0 deletions
diff --git a/examples/mandatory-locking-test.py b/examples/mandatory-locking-test.py new file mode 100755 index 00000000..d863814f --- /dev/null +++ b/examples/mandatory-locking-test.py @@ -0,0 +1,20 @@ +#!/usr/bin/python + + +import dbus +import sys +import time +import os + +bus = dbus.Bus(dbus.Bus.TYPE_SYSTEM) +device = dbus.Interface(bus.get_object("org.freedesktop.Hal", + "/org/freedesktop/Hal/devices/computer"), +# "/org/freedesktop/Hal/devices/volume_label_EOS_DIGITAL"), + "org.freedesktop.Hal.Device") + +device.AcquireMandatoryLock("foo") +device.AcquireMandatoryLock("foo2") +time.sleep(2) +device.ReleaseMandatoryLock("foo2") +#device.ReleaseMandatoryLock("foo") +time.sleep(2) diff --git a/hald/device.c b/hald/device.c index a9b67f87..81cfa91c 100644 --- a/hald/device.c +++ b/hald/device.c @@ -37,6 +37,24 @@ #include "logger.h" #include "hald_runner.h" +static GSList *locked_devices = NULL; + +static void +add_to_locked_set (HalDevice *device) +{ + if (g_slist_find (locked_devices, device) != NULL) + return; + + locked_devices = g_slist_prepend (locked_devices, device); +} + + +static void +remove_from_locked_set (HalDevice *device) +{ + locked_devices = g_slist_remove (locked_devices, device); +} + struct _HalProperty { int type; union { @@ -374,6 +392,8 @@ hal_device_finalize (GObject *obj) runner_device_finalized (device); + remove_from_locked_set (device); + #ifdef HALD_MEMLEAK_DBG dbg_hal_device_object_delta--; printf ("************* in finalize for udi=%s\n", device->private->udi); @@ -629,6 +649,29 @@ hal_device_property_get_strlist_length (HalDevice *device, return 0; } +gboolean +hal_device_property_strlist_contains (HalDevice *device, + const char *key, + const char *value) +{ + GSList *i; + GSList *elems; + gboolean ret; + + ret = FALSE; + + elems = hal_device_property_get_strlist (device, key); + for (i = elems; i != NULL; i = g_slist_next (i)) { + if (strcmp (i->data, value) == 0) { + ret = TRUE; + break; + } + } + + return ret; +} + + void hal_device_property_strlist_iter_init (HalDevice *device, const char *key, @@ -1567,3 +1610,131 @@ hal_device_are_all_addons_ready (HalDevice *device) return FALSE; } } + +/** + * hal_device_acquire_lock: + * @device: the device to acquire a lock on + * @lock_name: name of the lock + * @sender: the caller to acquire the lock + * + * Acquires a named lock on a device. + * + * Returns: FALSE if the caller already holds this lock. TRUE if the caller got the lock. + */ +gboolean +hal_device_acquire_lock (HalDevice *device, const char *lock_name, const char *sender) +{ + gboolean ret; + char buf[256]; + + ret = FALSE; + + g_snprintf (buf, sizeof (buf), "info.named_locks.%s.dbus_name", lock_name); + if (hal_device_property_strlist_contains (device, buf, sender)) { + /* already locked */ + goto out; + } + hal_device_property_strlist_append (device, buf, sender); + g_snprintf (buf, sizeof (buf), "info.named_locks.%s.locked", lock_name); + hal_device_property_set_bool (device, buf, TRUE); + + hal_device_property_strlist_append (device, "info.named_locks", lock_name); + + add_to_locked_set (device); + + ret = TRUE; +out: + return ret; +} + +/** + * hal_device_release_lock: + * @device: the device to acquire a lock on + * @lock_name: name of the lock + * @sender: the caller to acquire the lock + * + * Releases a named lock on a device. + * + * Returns: FALSE if the caller didn't hold this lock. True if the lock was removed. + */ +gboolean +hal_device_release_lock (HalDevice *device, const char *lock_name, const char *sender) +{ + gboolean ret; + char buf[256]; + + ret = FALSE; + + HAL_INFO (("Removing lock '%s' from '%s'", lock_name, sender)); + + g_snprintf (buf, sizeof (buf), "info.named_locks.%s.locked", lock_name); + if (!hal_device_property_get_bool (device, buf)) { + /* not locked */ + goto out; + } + + g_snprintf (buf, sizeof (buf), "info.named_locks.%s.dbus_name", lock_name); + if (!hal_device_property_strlist_contains (device, buf, sender)) { + /* not locked by sender */ + goto out; + } + + if (hal_device_property_get_strlist_length (device, buf) == 1) { + /* last one to hold the lock */ + g_snprintf (buf, sizeof (buf), "info.named_locks.%s.locked", lock_name); + hal_device_property_remove (device, buf); + g_snprintf (buf, sizeof (buf), "info.named_locks.%s.dbus_name", lock_name); + hal_device_property_remove (device, buf); + + if (hal_device_property_get_strlist_length (device, "info.named_locks") == 1) { + hal_device_property_remove (device, "info.named_locks"); + remove_from_locked_set (device); + } else { + hal_device_property_strlist_remove (device, "info.named_locks", lock_name); + } + } else { + /* there are additional holders */ + hal_device_property_strlist_remove (device, buf, sender); + } + + ret = TRUE; + +out: + return ret; +} + +/** + * hal_device_client_disconnected: + * @sender: the client that disconnected from the bus + * + * Will remove locks held by this client on locked devices. This is a + * static class method that affects all devices that are locked. + * + */ +void +hal_device_client_disconnected (const char *sender) +{ + GSList *i; + + HAL_INFO (("Removing locks from '%s'", sender)); + + for (i = locked_devices; i != NULL; i = g_slist_next (i)) { + HalDevice *device = i->data; + GSList *locks; + GSList *j; + GSList *k; + + HAL_INFO (("Looking at udi '%s'", device->private->udi)); + + locks = hal_device_property_get_strlist (device, "info.named_locks"); + for (j = locks; j != NULL; j = k) { + char *lock_name = j->data; + k = g_slist_next (j); + + HAL_INFO (("Lock '%s'", lock_name)); + + hal_device_release_lock (device, lock_name, sender); + } + } +} + diff --git a/hald/device.h b/hald/device.h index 5bab299a..774d3dc0 100644 --- a/hald/device.h +++ b/hald/device.h @@ -136,6 +136,9 @@ const char *hal_device_property_get_strlist_elem (HalDevice *device, guint index); guint hal_device_property_get_strlist_length (HalDevice *device, const char *key); +gboolean hal_device_property_strlist_contains (HalDevice *device, + const char *key, + const char *value); char **hal_device_property_dup_strlist_as_strv (HalDevice *device, const char *key); @@ -203,5 +206,11 @@ gboolean hal_device_inc_num_ready_addons (HalDevice *device); gboolean hal_device_are_all_addons_ready (HalDevice *device); +gboolean hal_device_acquire_lock (HalDevice *device, const char *lock_name, const char *sender); + +gboolean hal_device_release_lock (HalDevice *device, const char *lock_name, const char *sender); + +/* static method */ +void hal_device_client_disconnected (const char *sender); #endif /* DEVICE_H */ diff --git a/hald/hald_dbus.c b/hald/hald_dbus.c index effcd449..b69cbb70 100644 --- a/hald/hald_dbus.c +++ b/hald/hald_dbus.c @@ -2072,6 +2072,291 @@ device_query_capability (DBusConnection * connection, return DBUS_HANDLER_RESULT_HANDLED; } +#if 0 +/* map: service name -> list of {lock_name, udi} + */ +static GHashTable *services_with_mlocks = NULL; + +typedef struct +{ + char *lock_name; + char *udi; +} MLockEntry; + +static void +free_mlock_entry (MLockEntry *me) +{ + g_free (me->lock_name); + g_free (me->udi); + g_free (me); +} + +static MLockEntry * +new_mlock_entry (const char *lock_name, const char *udi) +{ + MLockEntry *me; + me = g_new0 (MLockEntry, 1); + me->lock_name = g_strdup (lock_name); + me->udi = g_strdup (udi); + return me; +} + +static void +free_mlock_list (GSList *mlock_list) +{ + g_slist_foreach (mlock_list, (GFunc) free_mlock_entry, NULL); + g_slist_free (mlock_list); +} + +static void +print_mlock_per_service (const char *service, GSList *mlock_list, gpointer userdata) +{ + GSList *i; + HAL_INFO ((" mlocks for service %s", service)); + for (i = mlock_list; i != NULL; i = g_slist_next (i)) { + MLockEntry *me = i->data; + HAL_INFO ((" lock_name=%s udi=%s", me->lock_name, me->udi)); + } +} + +static void +dump_mlocks (void) +{ + HAL_INFO (("--- Dumping mlocks")); + g_hash_table_foreach (services_with_mlocks, (GHFunc) print_mlock_per_service, NULL); + HAL_INFO (("------------------")); +} +#endif + +/** + * device_acquire_mandatory_lock: + * @connection: D-BUS connection + * @message: Message + * + * Returns: What to do with the message + * + * Acquire a named mandatory lock on a device. + * + * <pre> + * void Device.AcquireMandatoryLock(string lock_name) + * + * raises org.freedesktop.Hal.NoSuchDevice, + * org.freedesktop.Hal.DeviceAlreadyLocked (if the caller already got a lock on the device with the given name) + * </pre> + */ +DBusHandlerResult +device_acquire_mandatory_lock (DBusConnection *connection, DBusMessage *message) +{ + const char *udi; + HalDevice *d; + DBusMessage *reply; + DBusError error; + char *lock_name; + 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_STRING, &lock_name, + DBUS_TYPE_INVALID)) { + raise_syntax (connection, message, "AqcuireMandatoryLock"); + return DBUS_HANDLER_RESULT_HANDLED; + } + + sender = dbus_message_get_sender (message); + + if (!hal_device_acquire_lock (d, lock_name, sender)) { + raise_device_already_locked (connection, message, d); + 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; +} + + +#if 0 + reply = dbus_message_new_method_return (message); + if (reply == NULL) + DIE (("No memory")); + + g_snprintf (buf, sizeof (buf), "info.named_locks.%s.dbus_name", lock_name); + if (hal_device_property_strlist_contains (d, buf, sender)) { + raise_device_already_locked (connection, message, d); + return DBUS_HANDLER_RESULT_HANDLED; + } + hal_device_property_strlist_append (d, buf, sender); + g_snprintf (buf, sizeof (buf), "info.named_locks.%s.locked", lock_name); + hal_device_property_set_bool (d, buf, TRUE); + + GSList *mlock_list; + if (services_with_mlocks == NULL) { + services_with_mlocks = g_hash_table_new_full (g_str_hash, + g_str_equal, + g_free, + (GDestroyNotify) free_mlock_list); + mlock_list = g_slist_append (NULL, new_mlock_entry (lock_name, udi)); + g_hash_table_insert (services_with_mlocks, g_strdup (sender), mlock_list); + } else { + mlock_list = g_hash_table_lookup (services_with_mlocks, sender); + if (mlock_list != NULL) { + /* head is guarenteed not to change */ + g_slist_append (mlock_list, new_mlock_entry (lock_name, udi)); + } else { + mlock_list = g_slist_append (NULL, new_mlock_entry (lock_name, udi)); + g_hash_table_insert (services_with_mlocks, g_strdup (sender), mlock_list); + } + } + dump_mlocks (); + + if (!dbus_connection_send (connection, reply, NULL)) + DIE (("No memory")); + + dbus_message_unref (reply); + return DBUS_HANDLER_RESULT_HANDLED; +#endif + + +/** + * device_release_mandatory_lock: + * @connection: D-BUS connection + * @message: Message + * + * Returns: What to do with the message + * + * Released a named mandatory lock on a device. + * + * <pre> + * void Device.ReleaseMandatoryLock(string lock_name) + * + * raises org.freedesktop.Hal.NoSuchDevice, + * org.freedesktop.Hal.DeviceNotLocked (if the caller haven't a lock on the device with the given name) + * </pre> + */ +DBusHandlerResult +device_release_mandatory_lock (DBusConnection *connection, DBusMessage *message) +{ + const char *udi; + HalDevice *d; + DBusMessage *reply; + DBusError error; + const char *sender; + char *lock_name; + + 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, &lock_name, + DBUS_TYPE_INVALID)) { + raise_syntax (connection, message, "ReleaseMandatoryLock"); + return DBUS_HANDLER_RESULT_HANDLED; + } + + sender = dbus_message_get_sender (message); + + if (!hal_device_release_lock (d, lock_name, sender)) { + raise_device_already_locked (connection, message, d); + 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; +} + +#if 0 + reply = dbus_message_new_method_return (message); + if (reply == NULL) + DIE (("No memory")); + + g_snprintf (buf, sizeof (buf), "info.named_locks.%s.locked", lock_name); + if (!hal_device_property_get_bool (d, buf)) { + raise_device_not_locked (connection, message, d); + return DBUS_HANDLER_RESULT_HANDLED; + } + + sender = dbus_message_get_sender (message); + + g_snprintf (buf, sizeof (buf), "info.named_locks.%s.dbus_name", lock_name); + if (!hal_device_property_strlist_contains (d, buf, sender)) { + raise_permission_denied (connection, message, "Not locked by you"); + return DBUS_HANDLER_RESULT_HANDLED; + } + + if (hal_device_property_get_strlist_length (d, buf) == 1) { + g_snprintf (buf, sizeof (buf), "info.named_locks.%s.locked", lock_name); + hal_device_property_remove (d, buf); + g_snprintf (buf, sizeof (buf), "info.named_locks.%s.dbus_name", lock_name); + hal_device_property_remove (d, buf); + } else { + hal_device_property_strlist_remove (d, buf, sender); + } + + GSList *mlock_list; + mlock_list = g_hash_table_lookup (services_with_mlocks, sender); + if (mlock_list != NULL) { + GSList *i; + GSList *new_mlock_list = NULL; + for (i = mlock_list; i != NULL; i = g_slist_next (i)) { + MLockEntry *me = i->data; + if (!(strcmp (me->udi, udi) == 0 && strcmp (me->lock_name, lock_name) == 0)) { + new_mlock_list = g_slist_append (new_mlock_list, new_mlock_entry (lock_name, udi)); + } + } + if (new_mlock_list != NULL) + g_hash_table_replace (services_with_mlocks, g_strdup (sender), new_mlock_list); + else + g_hash_table_remove (services_with_mlocks, sender); + } + dump_mlocks (); + + if (!dbus_connection_send (connection, reply, NULL)) + DIE (("No memory")); + + dbus_message_unref (reply); + return DBUS_HANDLER_RESULT_HANDLED; +} +#endif + +/*------------------------------------------------------------------------*/ + static GHashTable *services_with_locks = NULL; /** @@ -3720,6 +4005,13 @@ do_introspect (DBusConnection *connection, " <arg name=\"released_lock\" direction=\"out\" type=\"b\"/>\n" " </method>\n" + " <method name=\"AcquireMandatoryLock\">\n" + " <arg name=\"lock_name\" direction=\"in\" type=\"s\"/>\n" + " </method>\n" + " <method name=\"ReleaseMandatoryLock\">\n" + " <arg name=\"lock_name\" direction=\"in\" type=\"s\"/>\n" + " </method>\n" + " <method name=\"StringListAppend\">\n" " <arg name=\"key\" direction=\"in\" type=\"s\"/>\n" " <arg name=\"value\" direction=\"in\" type=\"s\"/>\n" @@ -4037,6 +4329,14 @@ hald_dbus_filter_handle_methods (DBusConnection *connection, DBusMessage *messag return device_unlock (connection, message); } else if (dbus_message_is_method_call (message, "org.freedesktop.Hal.Device", + "AcquireMandatoryLock")) { + return device_acquire_mandatory_lock (connection, message); + } else if (dbus_message_is_method_call (message, + "org.freedesktop.Hal.Device", + "ReleaseMandatoryLock")) { + return device_release_mandatory_lock (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, @@ -4295,6 +4595,30 @@ hald_dbus_filter_function (DBusConnection * connection, } } + if (strlen (old_service_name) > 0) + hal_device_client_disconnected (old_service_name); +#if 0 + if (services_with_mlocks != NULL) { + GSList *i; + GSList *mlock_list; + mlock_list = g_hash_table_lookup (services_with_mlocks, new_service_name); + for (i = mlock_list; i != NULL; i = g_slist_next (i)) { +#if 0 + if (hal_device_property_get_strlist_length (d, buf) == 1) { + g_snprintf (buf, sizeof (buf), "info.named_locks.%s.locked", lock_name); + hal_device_property_remove (d, buf); + g_snprintf (buf, sizeof (buf), "info.named_locks.%s.dbus_name", lock_name); + hal_device_property_remove (d, buf); + } else { + hal_device_property_strlist_remove (d, buf, sender); + } +#endif + } + g_hash_table_remove (services_with_mlocks, new_service_name); + dump_mlocks (); + } +#endif + #ifdef HAVE_CONKIT } else if (dbus_message_is_signal (message, "org.freedesktop.ConsoleKit.Session", diff --git a/hald/hald_dbus.h b/hald/hald_dbus.h index d7f9accd..3fba19d7 100644 --- a/hald/hald_dbus.h +++ b/hald/hald_dbus.h @@ -64,6 +64,10 @@ DBusHandlerResult device_lock (DBusConnection *connection, DBusMessage *message); DBusHandlerResult device_unlock (DBusConnection *connection, DBusMessage *message); +DBusHandlerResult device_acquire_mandatory_lock (DBusConnection *connection, + DBusMessage *message); +DBusHandlerResult device_release_mandatory_lock (DBusConnection *connection, + DBusMessage *message); DBusHandlerResult manager_new_device (DBusConnection *connection, DBusMessage *message, dbus_bool_t local_interface); |