hal: Branch 'master'

David Zeuthen david at kemper.freedesktop.org
Sun Mar 25 15:25:34 PDT 2007


 examples/interface-locking-test.py |   51 ++++
 examples/mandatory-locking-test.py |   20 -
 hald/access-check.c                |  113 ++++++++++
 hald/access-check.h                |    4 
 hald/device.c                      |   36 +++
 hald/device.h                      |    4 
 hald/hald_dbus.c                   |  400 +++++++++++++++++++++++++++++++------
 hald/hald_dbus.h                   |    4 
 libhal/libhal.c                    |  243 ++++++++++++++++++++++
 libhal/libhal.h                    |   30 ++
 tools/hal-storage-mount.c          |    2 
 tools/hal-storage-shared.c         |   23 ++
 tools/hal-storage-shared.h         |    2 
 13 files changed, 843 insertions(+), 89 deletions(-)

New commits:
diff-tree ce1a29366fa816ade302159db0f64efe8000d310 (from f7cb7e7828ae6d54f06dad49b63b0f8c0a553a41)
Author: David Zeuthen <davidz at redhat.com>
Date:   Sun Mar 25 18:25:32 2007 -0400

    add interface locking and remove generic locking API committed yesterday
    
    Now to write some docs how all this works...

diff --git a/examples/interface-locking-test.py b/examples/interface-locking-test.py
new file mode 100755
index 0000000..ff0c7dd
--- /dev/null
+++ b/examples/interface-locking-test.py
@@ -0,0 +1,51 @@
+#!/usr/bin/python
+
+
+import dbus
+import sys
+import time
+import os
+
+# this is just various test code
+
+bus = dbus.Bus(dbus.Bus.TYPE_SYSTEM)
+
+manager = dbus.Interface(bus.get_object("org.freedesktop.Hal",
+                                         "/org/freedesktop/Hal/Manager"),
+                          "org.freedesktop.Hal.Manager")
+
+computer = dbus.Interface(bus.get_object("org.freedesktop.Hal",
+                                         "/org/freedesktop/Hal/devices/computer"),
+                          "org.freedesktop.Hal.Device")
+
+#                                       "/org/freedesktop/Hal/devices/volume_uuid_456C_AAA8"),
+
+device = dbus.Interface(bus.get_object("org.freedesktop.Hal",
+                                       "/org/freedesktop/Hal/devices/macbook_pro_keyboard_backlight"),
+                        "org.freedesktop.Hal.Device")
+device2 = dbus.Interface(bus.get_object("org.freedesktop.Hal",
+                                        "/org/freedesktop/Hal/devices/macbook_pro_keyboard_backlight"),
+                         "org.freedesktop.Hal.Device.KeyboardBacklight")
+
+device3 = dbus.Interface(bus.get_object("org.freedesktop.Hal",
+                                       "/org/freedesktop/Hal/devices/storage_serial_Kingston_DataTraveler_2_0_07F0E4611101494D"),
+                        "org.freedesktop.Hal.Device")
+
+manager.AcquireGlobalInterfaceLock("org.freedesktop.Hal.Device.Storage", True)
+time.sleep(10000)
+
+#device3.AcquireInterfaceLock("org.freedesktop.Hal.Device.Storage", True)
+#time.sleep(100000)
+
+#manager.AcquireGlobalInterfaceLock("org.freedesktop.Hal.Device.KeyboardBacklight")
+#device.AcquireInterfaceLock("org.freedesktop.Hal.Device.KeyboardBacklight")
+#n = 0
+#while True:
+#    time.sleep(1)
+#    device2.SetBrightness (n)
+#    n = n + 10
+#    if (n > 200):
+#        n = 0
+#manager.ReleaseGlobalInterfaceLock("org.freedesktop.Hal.Device.KeyboardBacklight")
+#device.ReleaseInterfaceLock("org.freedesktop.Hal.Device.KeyboardBacklight")
+
diff --git a/examples/mandatory-locking-test.py b/examples/mandatory-locking-test.py
deleted file mode 100755
index d863814..0000000
--- a/examples/mandatory-locking-test.py
+++ /dev/null
@@ -1,20 +0,0 @@
-#!/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/access-check.c b/hald/access-check.c
index b001eea..d96ddaa 100644
--- a/hald/access-check.c
+++ b/hald/access-check.c
@@ -38,6 +38,7 @@
 #include <dbus/dbus.h>
 #include <glib.h>
 
+#include "hald.h"
 #include "logger.h"
 #include "access-check.h"
 
@@ -107,6 +108,7 @@ out:
 
 /**
  * access_check_caller_have_access_to_device:
+ * @cit: the CITracker object
  * @device: The device to check for
  * @caller_unique_sysbus_name: The unique system bus connection name (e.g. ":1.43") of the caller
  *
@@ -169,3 +171,114 @@ out:
         return TRUE;
 }
 #endif
+
+/**
+ * access_check_caller_locked_out:
+ * @cit: the CITracker object
+ * @device: The device to check for
+ * @caller_unique_sysbus_name: The unique system bus connection name (e.g. ":1.43") of the caller
+ * @interface_name: the interface to check for
+ *
+ * This method determines if a caller is locked out to access a given
+ * interface on a given device. A caller is locked out when:
+ *
+ * 1. Another caller is holding a lock on the interface on the device
+ *    non-withstanding that the caller to check for holds the lock
+ *    himself.
+ *
+ * 2. Another caller is holding the global lock for the interface on
+ *    the root computer device object and that other caller has
+ *    access to the device in question.
+ *
+ *    (In other words, a client Foo can grab a lock on the root
+ *    computer device object, but that doesn't mean Foo can lock
+ *    other clients out of devices that Foo doesn't have access to.)
+ *
+ * Specifically a caller is not locked out if he has locked the
+ * interface and he is the only one holding the lock. However, if two
+ * clients have a lock on a device neither of them can access the
+ * device.
+ * 
+ * Returns: TRUE iff the caller is locked out
+ */
+gboolean
+access_check_caller_locked_out (CITracker   *cit,
+                                HalDevice   *device,
+                                const char  *caller_unique_sysbus_name,
+                                const char  *interface_name)
+{
+        int n;
+        gboolean ret;
+        char *global_lock_name;
+        char **holders;
+        char **global_holders;
+        HalDevice *computer;
+
+        global_lock_name = NULL;
+        holders = NULL;
+        global_holders = NULL;
+        ret = TRUE;
+
+	computer = hal_device_store_find (hald_get_gdl (), "/org/freedesktop/Hal/devices/computer");
+	if (computer == NULL)
+		computer = hal_device_store_find (hald_get_tdl (), "/org/freedesktop/Hal/devices/computer");
+	if (computer == NULL)
+		goto out;
+
+        global_lock_name = g_strdup_printf ("Global.%s", interface_name);
+
+        holders = hal_device_get_lock_holders (device, interface_name);
+        global_holders = hal_device_get_lock_holders (computer, global_lock_name);
+
+        /* check if there are other holders than us - these are
+         * assumed to have access to the device since they got to hold
+         * the lock in the first place. 
+         */
+        if (holders != NULL) {
+                for (n = 0; holders[n] != NULL; n++) {
+                        if (strcmp (holders[n], caller_unique_sysbus_name) != 0) {
+                                /* Yup, there's someone else... can't do it Sally */
+                                HAL_INFO (("Caller '%s' is locked out of interface '%s' on device '%s' "
+                                           "because caller '%s' got a lock on the interface on the device",
+                                           caller_unique_sysbus_name,
+                                           interface_name,
+                                           hal_device_get_udi (device),
+                                           holders[n]));
+                                goto out;
+                        }
+                }
+        }
+
+        if (global_holders != NULL) {
+                for (n = 0; global_holders[n] != NULL; n++) {
+                        if (strcmp (global_holders[n], caller_unique_sysbus_name) != 0) {
+                                /* Someone else is holding the global
+                                 * lock.. check if that someone actually have
+                                 * access to the device...
+                                 */
+                                if (access_check_caller_have_access_to_device (cit, device, global_holders[n])) {
+                                        /* They certainly do. Give up. */
+                                        
+                                        HAL_INFO (("Caller '%s' is locked out of interface '%s' on device '%s' "
+                                                   "because caller '%s' got a lock on the global interface and "
+                                                   "have access to the device",
+                                                   caller_unique_sysbus_name,
+                                                   interface_name,
+                                                   hal_device_get_udi (device),
+                                                   global_holders[n]));
+                                        goto out;
+                                }
+                        }
+                }
+        }
+
+        /* done all the checks so we're not locked out */
+        ret = FALSE;
+
+out:
+        g_strfreev (global_holders);
+        g_strfreev (holders);
+        g_free (global_lock_name);
+        return ret;
+}
+
diff --git a/hald/access-check.h b/hald/access-check.h
index 8514824..ab05f07 100644
--- a/hald/access-check.h
+++ b/hald/access-check.h
@@ -36,5 +36,9 @@ gboolean access_check_message_caller_is_
 gboolean access_check_caller_have_access_to_device  (CITracker   *cit,
                                                      HalDevice   *device,
                                                      const char  *caller_unique_sysbus_name);
+gboolean access_check_caller_locked_out             (CITracker   *cit,
+                                                     HalDevice   *device,
+                                                     const char  *caller_unique_sysbus_name,
+                                                     const char  *interface_name);
 
 #endif /* ACCESS_CHECK_H */
diff --git a/hald/device.c b/hald/device.c
index 81cfa91..a471d2c 100644
--- a/hald/device.c
+++ b/hald/device.c
@@ -1622,23 +1622,31 @@ hal_device_are_all_addons_ready (HalDevi
  * 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)
+hal_device_acquire_lock (HalDevice *device, const char *lock_name, gboolean exclusive, const char *sender)
 {
         gboolean ret;
         char buf[256];
 
         ret = FALSE;
 
+	g_snprintf (buf, sizeof (buf), "info.named_locks.%s.exclusive", lock_name);
+	if (hal_device_property_get_bool (device, buf) == TRUE) {
+            /* exclusively locked */
+            goto out;
+        }
+	hal_device_property_set_bool (device, buf, exclusive);
+
 	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);
+	hal_device_property_strlist_add (device, "info.named_locks", lock_name);
 
         add_to_locked_set (device);
 
@@ -1681,6 +1689,8 @@ hal_device_release_lock (HalDevice *devi
 
 	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.exclusive", lock_name);
+		hal_device_property_remove (device, buf);
 		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);
@@ -1704,6 +1714,28 @@ out:
 }
 
 /**
+ * hal_device_get_lock_holders:
+ * @device: the device to check for
+ * @lock_name: the lock name
+ *
+ * Get the lock holders on a device.
+ *
+ * Returns: NULL if there are no lock holders; otherwise a
+ * NULL-terminated array of strings with the dbus names of the
+ * holders. Caller must free this with g_strfreev().
+ */
+char **
+hal_device_get_lock_holders (HalDevice *device, const char *lock_name)
+{
+        char **ret;
+        char buf[256];
+        
+        g_snprintf (buf, sizeof (buf), "info.named_locks.%s.dbus_name", lock_name);
+        ret = hal_device_property_dup_strlist_as_strv (device, buf);
+        return ret;
+}
+
+/**
  * hal_device_client_disconnected:
  * @sender: the client that disconnected from the bus
  *
diff --git a/hald/device.h b/hald/device.h
index 774d3dc..5dd11f5 100644
--- a/hald/device.h
+++ b/hald/device.h
@@ -206,10 +206,12 @@ gboolean      hal_device_inc_num_ready_a
 
 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_acquire_lock (HalDevice *device, const char *lock_name, gboolean exclusive, const char *sender);
 
 gboolean      hal_device_release_lock (HalDevice *device, const char *lock_name, const char *sender);
 
+char        **hal_device_get_lock_holders (HalDevice *device, const char *lock_name);
+
 /* static method */
 void          hal_device_client_disconnected (const char *sender);
 
diff --git a/hald/hald_dbus.c b/hald/hald_dbus.c
index 798e093..2df8e0d 100644
--- a/hald/hald_dbus.c
+++ b/hald/hald_dbus.c
@@ -223,13 +223,16 @@ raise_device_already_locked (DBusConnect
 	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));
+        if (reason != NULL) {
+                HAL_WARNING (("Device %s is already locked: %s", hal_device_get_udi (device), reason));
+        } else {
+                HAL_WARNING (("Device %s is already locked", hal_device_get_udi (device)));
+        }
 
 
 	reply = dbus_message_new_error (in_reply_to,
 					"org.freedesktop.Hal.DeviceAlreadyLocked",
-					reason);
+					reason != NULL ? reason : "Device is already locked");
 
 	if (reply == NULL || !dbus_connection_send (connection, reply, NULL))
 		DIE (("No memory"));
@@ -237,7 +240,7 @@ raise_device_already_locked (DBusConnect
 	dbus_message_unref (reply);
 }
 
-/** 
+/**
  *  raise_permission_denied:
  *  @connection:         D-Bus connection
  *  @in_reply_to:        message to report error on
@@ -259,6 +262,64 @@ raise_permission_denied (DBusConnection 
 }
 
 
+/**
+ *  raise_interface_locked:
+ *  @connection:         D-Bus connection
+ *  @in_reply_to:        message to report error on
+ *  @interface:          the interface that is locked
+ *
+ *  Raise the org.freedesktop.Hal.Device.InterfaceLocked error 
+ */
+static void
+raise_interface_locked (DBusConnection *connection,
+                        DBusMessage    *in_reply_to,
+                        const char     *interface)
+{
+	raise_error (connection, in_reply_to,
+                     "org.freedesktop.Hal.Device.InterfaceLocked",
+                     "The interface %s is locked by someone else",
+                     interface);
+}
+
+/**
+ *  raise_interface_locked:
+ *  @connection:         D-Bus connection
+ *  @in_reply_to:        message to report error on
+ *  @interface:          the interface that is locked
+ *
+ *  Raise the org.freedesktop.Hal.Device.InterfaceLocked error 
+ */
+static void
+raise_interface_already_locked (DBusConnection *connection,
+                                DBusMessage    *in_reply_to,
+                                const char     *interface)
+{
+	raise_error (connection, in_reply_to,
+                     "org.freedesktop.Hal.Device.InterfaceAlreadyLocked",
+                     "The interface %s is already exclusively locked either by someone else or it's already locked by yourself",
+                     interface);
+}
+
+/**
+ *  raise_interface_locked:
+ *  @connection:         D-Bus connection
+ *  @in_reply_to:        message to report error on
+ *  @interface:          the interface that is locked
+ *
+ *  Raise the org.freedesktop.Hal.Device.InterfaceLocked error 
+ */
+static void
+raise_interface_not_locked (DBusConnection *connection,
+                                DBusMessage    *in_reply_to,
+                                const char     *interface)
+{
+	raise_error (connection, in_reply_to,
+                     "org.freedesktop.Hal.Device.InterfaceNotLocked",
+                     "The interface %s is not locked by you",
+                     interface);
+}
+
+
 static gboolean
 foreach_device_get_udi (HalDeviceStore *store, HalDevice *device,
 			gpointer user_data)
@@ -1814,31 +1875,17 @@ device_query_capability (DBusConnection 
 	return DBUS_HANDLER_RESULT_HANDLED;
 }
 
-/**  
- *  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)
+/*------------------------------------------------------------------------*/
+static DBusHandlerResult
+device_acquire_interface_lock (DBusConnection *connection, DBusMessage *message, dbus_bool_t local_interface)
 {
 	const char *udi;
 	HalDevice *d;
 	DBusMessage *reply;
 	DBusError error;
-	char *lock_name;
+	char *interface_name;
 	const char *sender;
+        gboolean exclusive;
 
 	HAL_TRACE (("entering"));
 
@@ -1853,18 +1900,82 @@ device_acquire_mandatory_lock (DBusConne
 		return DBUS_HANDLER_RESULT_HANDLED;
 	}
 
+	sender = dbus_message_get_sender (message);
+
+        if (!local_interface) {
+                if (!access_check_caller_have_access_to_device (ci_tracker, d, sender)) {
+                        raise_permission_denied (connection, message, "AcquireInterfaceLock: no access to device");
+                        return DBUS_HANDLER_RESULT_HANDLED;
+                }
+        }
+
 	dbus_error_init (&error);
 	if (!dbus_message_get_args (message, &error,
-				    DBUS_TYPE_STRING, &lock_name,
+				    DBUS_TYPE_STRING, &interface_name,
+				    DBUS_TYPE_BOOLEAN, &exclusive,
 				    DBUS_TYPE_INVALID)) {
-		raise_syntax (connection, message, "AqcuireMandatoryLock");
+		raise_syntax (connection, message, "AqcuireInterfaceLock");
+		return DBUS_HANDLER_RESULT_HANDLED;
+	}
+
+        if (!hal_device_acquire_lock (d, interface_name, exclusive, sender)) {
+		raise_interface_already_locked (connection, message, interface_name);
+		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;
+}
+
+static DBusHandlerResult
+device_release_interface_lock (DBusConnection *connection, DBusMessage *message, dbus_bool_t local_interface)
+{
+	const char *udi;
+	HalDevice *d;
+	DBusMessage *reply;
+	DBusError error;
+	const char *sender;
+	char *interface_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;
 	}
 
 	sender = dbus_message_get_sender (message);
 
-        if (!hal_device_acquire_lock (d, lock_name, sender)) {
-		raise_device_already_locked (connection, message, d);
+        if (!local_interface) {
+                if (!access_check_caller_have_access_to_device (ci_tracker, d, sender)) {
+                        raise_permission_denied (connection, message, "ReleaseInterfaceLock: no access to device");
+                        return DBUS_HANDLER_RESULT_HANDLED;
+                }
+        }
+
+	dbus_error_init (&error);
+	if (!dbus_message_get_args (message, &error,
+				    DBUS_TYPE_STRING, &interface_name,
+				    DBUS_TYPE_INVALID)) {
+		raise_syntax (connection, message, "ReleaseInterfaceLock");
+		return DBUS_HANDLER_RESULT_HANDLED;
+	}
+
+        if (!hal_device_release_lock (d, interface_name, sender)) {
+		raise_interface_not_locked (connection, message, interface_name);
 		return DBUS_HANDLER_RESULT_HANDLED;
         }
 
@@ -1879,31 +1990,19 @@ device_acquire_mandatory_lock (DBusConne
 	return DBUS_HANDLER_RESULT_HANDLED;
 }
 
-/** 
- *  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)
+
+static DBusHandlerResult
+device_is_caller_locked_out (DBusConnection *connection, DBusMessage *message, dbus_bool_t local_interface)
 {
 	const char *udi;
 	HalDevice *d;
 	DBusMessage *reply;
 	DBusError error;
 	const char *sender;
-	char *lock_name;
+	char *interface_name;
+	char *caller_sysbus_name;
+        dbus_bool_t result;
+        DBusMessageIter iter;
 
 	HAL_TRACE (("entering"));
 
@@ -1918,20 +2017,154 @@ device_release_mandatory_lock (DBusConne
 		return DBUS_HANDLER_RESULT_HANDLED;
 	}
 
+	sender = dbus_message_get_sender (message);
+
+        /* only allow HAL helpers to ask this question */
+        if (!local_interface) {
+                raise_permission_denied (connection, message, "IsCallerLockedOut: not privileged");
+        }
+
+	dbus_error_init (&error);
+	if (!dbus_message_get_args (message, &error,
+				    DBUS_TYPE_STRING, &interface_name,
+				    DBUS_TYPE_STRING, &caller_sysbus_name,
+				    DBUS_TYPE_INVALID)) {
+		raise_syntax (connection, message, "ReleaseInterfaceLock");
+		return DBUS_HANDLER_RESULT_HANDLED;
+	}
+
+        result = access_check_caller_locked_out (ci_tracker, d, caller_sysbus_name, interface_name);
+
+	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, &result);
+
+	if (!dbus_connection_send (connection, reply, NULL))
+		DIE (("No memory"));
+
+	dbus_message_unref (reply);
+	return DBUS_HANDLER_RESULT_HANDLED;
+}
+
+/*------------------------------------------------------------------------*/
+
+static DBusHandlerResult
+device_acquire_global_interface_lock (DBusConnection *connection, DBusMessage *message, dbus_bool_t local_interface)
+{
+	const char *udi;
+	HalDevice *d;
+	DBusMessage *reply;
+	DBusError error;
+	char *interface_name;
+	char *lock_name;
+	const char *sender;
+        gboolean exclusive;
+
+	HAL_TRACE (("entering"));
+
+	udi = "/org/freedesktop/Hal/devices/computer";
+	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;
+	}
+
+	sender = dbus_message_get_sender (message);
+
+        /* no security checks; global locks are special; anyone can
+         * take them but it might not have any effect... a global lock
+         * will only have effect if a lock owner have access to the
+         * device - see the function
+         *
+         *  hald/access-check.c:access_check_caller_locked_out()
+         *
+         * for details.
+         */
+
 	dbus_error_init (&error);
 	if (!dbus_message_get_args (message, &error,
-				    DBUS_TYPE_STRING, &lock_name,
+				    DBUS_TYPE_STRING, &interface_name,
+				    DBUS_TYPE_BOOLEAN, &exclusive,
 				    DBUS_TYPE_INVALID)) {
-		raise_syntax (connection, message, "ReleaseMandatoryLock");
+		raise_syntax (connection, message, "AqcuireGlobalInterfaceLock");
+		return DBUS_HANDLER_RESULT_HANDLED;
+	}
+
+        lock_name = g_strdup_printf ("Global.%s", interface_name);
+        if (!hal_device_acquire_lock (d, lock_name, exclusive, sender)) {
+		raise_interface_already_locked (connection, message, interface_name);
+                g_free (lock_name);
+		return DBUS_HANDLER_RESULT_HANDLED;
+        }
+        g_free (lock_name);
+
+	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;
+}
+
+static DBusHandlerResult
+device_release_global_interface_lock (DBusConnection *connection, DBusMessage *message, dbus_bool_t local_interface)
+{
+	const char *udi;
+	HalDevice *d;
+	DBusMessage *reply;
+	DBusError error;
+	const char *sender;
+	char *interface_name;
+        char *lock_name;
+
+	HAL_TRACE (("entering"));
+
+	udi = "/org/freedesktop/Hal/devices/computer";
+	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;
 	}
 
 	sender = dbus_message_get_sender (message);
 
-        if (!hal_device_release_lock (d, lock_name, sender)) {
-		raise_device_already_locked (connection, message, d);
+        /* no security checks; global locks are special; anyone can
+         * take them but it might not have any effect... a global lock
+         * will only have effect if a lock owner have access to the
+         * device - see the function
+         *
+         *  hald/access-check.c:access_check_caller_locked_out()
+         *
+         * for details.
+         */
+
+	dbus_error_init (&error);
+	if (!dbus_message_get_args (message, &error,
+				    DBUS_TYPE_STRING, &interface_name,
+				    DBUS_TYPE_INVALID)) {
+		raise_syntax (connection, message, "ReleaseGlobalInterfaceLock");
+		return DBUS_HANDLER_RESULT_HANDLED;
+	}
+
+        lock_name = g_strdup_printf ("Global.%s", interface_name);
+        if (!hal_device_release_lock (d, interface_name, sender)) {
+		raise_interface_not_locked (connection, message, interface_name);
+                g_free (lock_name);
 		return DBUS_HANDLER_RESULT_HANDLED;
         }
+        g_free (lock_name);
 
 	reply = dbus_message_new_method_return (message);
 	if (reply == NULL)
@@ -3464,6 +3697,14 @@ do_introspect (DBusConnection  *connecti
 				       "      <arg name=\"global_udi\" direction=\"in\" type=\"s\"/>\n"
 				       "    </method>\n"
 
+				       "    <method name=\"AcquireGlobalInterfaceLock\">\n"
+				       "      <arg name=\"interface_name\" direction=\"in\" type=\"s\"/>\n"
+				       "      <arg name=\"exclusive\" direction=\"in\" type=\"b\"/>\n"
+				       "    </method>\n"
+				       "    <method name=\"ReleaseGlobalInterfaceLock\">\n"
+				       "      <arg name=\"interface_name\" direction=\"in\" type=\"s\"/>\n"
+				       "    </method>\n"
+
 				       "    <signal name=\"DeviceAdded\">\n"
 				       "      <arg name=\"udi\" type=\"s\"/>\n"
 				       "    </signal>\n"
@@ -3571,11 +3812,17 @@ do_introspect (DBusConnection  *connecti
 				       "      <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 name=\"AcquireInterfaceLock\">\n"
+				       "      <arg name=\"interface_name\" direction=\"in\" type=\"s\"/>\n"
+				       "      <arg name=\"exclusive\" direction=\"in\" type=\"b\"/>\n"
 				       "    </method>\n"
-				       "    <method name=\"ReleaseMandatoryLock\">\n"
-				       "      <arg name=\"lock_name\" direction=\"in\" type=\"s\"/>\n"
+				       "    <method name=\"ReleaseInterfaceLock\">\n"
+				       "      <arg name=\"interface_name\" direction=\"in\" type=\"s\"/>\n"
+				       "    </method>\n"
+				       "    <method name=\"IsCallerLockedOut\">\n"
+				       "      <arg name=\"interface_name\" direction=\"in\" type=\"s\"/>\n"
+				       "      <arg name=\"caller_sysbus_name\" direction=\"in\" type=\"s\"/>\n"
+				       "      <arg name=\"whether_caller_is_locked_out\" direction=\"out\" type=\"b\"/>\n"
 				       "    </method>\n"
 
 				       "    <method name=\"StringListAppend\">\n"
@@ -3822,6 +4069,31 @@ hald_dbus_filter_handle_methods (DBusCon
 		   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.Manager",
+						"AcquireGlobalInterfaceLock") &&
+                   strcmp (dbus_message_get_path (message), "/org/freedesktop/Hal/Manager") == 0) {
+		return device_acquire_global_interface_lock (connection, message, local_interface);
+	} else if (dbus_message_is_method_call (message,
+						"org.freedesktop.Hal.Manager",
+						"ReleaseGlobalInterfaceLock") &&
+                   strcmp (dbus_message_get_path (message), "/org/freedesktop/Hal/Manager") == 0) {
+		return device_release_global_interface_lock (connection, message, local_interface);
+                
+	} else if (dbus_message_is_method_call (message,
+						"org.freedesktop.Hal.Device",
+						"AcquireInterfaceLock")) {
+		return device_acquire_interface_lock (connection, message, local_interface);
+	} else if (dbus_message_is_method_call (message,
+						"org.freedesktop.Hal.Device",
+						"ReleaseInterfaceLock")) {
+		return device_release_interface_lock (connection, message, local_interface);
+	} else if (dbus_message_is_method_call (message,
+						"org.freedesktop.Hal.Device",
+						"IsCallerLockedOut")) {
+		return device_is_caller_locked_out (connection, message, local_interface);
+
 	} else if (dbus_message_is_method_call (message,
 					      "org.freedesktop.Hal.Device",
 					      "GetAllProperties")) {
@@ -3904,14 +4176,6 @@ hald_dbus_filter_handle_methods (DBusCon
 		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,
@@ -4001,6 +4265,18 @@ hald_dbus_filter_handle_methods (DBusCon
                         }
                 }
 
+                /* check if the interface on the device is locked by someone else 
+                 *
+                 * Note that method implementations can and will do their own
+                 * checks too; e.g. operations on org.freedesktop.Hal.Device.Volume
+                 * will check if the interface org.freedesktop.Hal.Device.Storage
+                 * is locked on the enclosing drive.
+                 */
+                if (access_check_caller_locked_out (ci_tracker, d, caller, interface)) {
+                        raise_interface_locked (connection, message, interface);
+                        return DBUS_HANDLER_RESULT_HANDLED;
+                }
+
                 /* first see if an addon grabbed the interface */
                 for (i = helper_interface_handlers; i != NULL; i = g_slist_next (i)) {
                         HelperInterfaceHandler *hih = i->data;
diff --git a/hald/hald_dbus.h b/hald/hald_dbus.h
index 3fba19d..d7f9acc 100644
--- a/hald/hald_dbus.h
+++ b/hald/hald_dbus.h
@@ -64,10 +64,6 @@ DBusHandlerResult device_lock           
 						     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);
diff --git a/libhal/libhal.c b/libhal/libhal.c
index 141ad61..c9697ce 100644
--- a/libhal/libhal.c
+++ b/libhal/libhal.c
@@ -4128,3 +4128,246 @@ libhal_device_free_changeset (LibHalChan
 	free (changeset->udi);
 	free (changeset);
 }
+
+
+dbus_bool_t 
+libhal_device_acquire_interface_lock (LibHalContext *ctx,
+                                      const char *udi,
+                                      const char *interface,
+                                      DBusError *error)
+{
+	DBusMessage *message;
+	DBusMessageIter iter;
+	DBusMessage *reply;
+
+	LIBHAL_CHECK_LIBHALCONTEXT(ctx, FALSE);
+	LIBHAL_CHECK_PARAM_VALID(udi, "*udi", FALSE);
+	LIBHAL_CHECK_PARAM_VALID(interface, "*interface", FALSE);
+
+	message = dbus_message_new_method_call ("org.freedesktop.Hal",
+						udi,
+						"org.freedesktop.Hal.Device",
+						"AcquireInterfaceLock");
+
+	if (message == NULL) {
+		fprintf (stderr,
+			 "%s %d : Couldn't allocate D-BUS message\n",
+			 __FILE__, __LINE__);
+		return FALSE;
+	}
+
+	dbus_message_iter_init_append (message, &iter);
+	dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &interface);
+	
+	reply = dbus_connection_send_with_reply_and_block (ctx->connection,
+							   message, -1,
+							   error);
+
+	if (error != NULL && dbus_error_is_set (error)) {
+		dbus_message_unref (message);
+		return FALSE;
+	}
+
+	dbus_message_unref (message);
+
+	if (reply == NULL)
+		return FALSE;
+
+	dbus_message_unref (reply);
+	return TRUE;
+}
+
+dbus_bool_t libhal_device_release_interface_lock (LibHalContext *ctx,
+                                                  const char *udi,
+                                                  const char *interface,
+                                                  DBusError *error)
+{
+	DBusMessage *message;
+	DBusMessageIter iter;
+	DBusMessage *reply;
+
+	LIBHAL_CHECK_LIBHALCONTEXT(ctx, FALSE);
+	LIBHAL_CHECK_PARAM_VALID(udi, "*udi", FALSE);
+	LIBHAL_CHECK_PARAM_VALID(interface, "*interface", FALSE);
+
+	message = dbus_message_new_method_call ("org.freedesktop.Hal",
+						udi,
+						"org.freedesktop.Hal.Device",
+						"ReleaseInterfaceLock");
+
+	if (message == NULL) {
+		fprintf (stderr,
+			 "%s %d : Couldn't allocate D-BUS message\n",
+			 __FILE__, __LINE__);
+		return FALSE;
+	}
+
+	dbus_message_iter_init_append (message, &iter);
+	dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &interface);
+	
+	reply = dbus_connection_send_with_reply_and_block (ctx->connection,
+							   message, -1,
+							   error);
+
+	if (error != NULL && dbus_error_is_set (error)) {
+		dbus_message_unref (message);
+		return FALSE;
+	}
+
+	dbus_message_unref (message);
+
+	if (reply == NULL)
+		return FALSE;
+
+	dbus_message_unref (reply);
+	return TRUE;
+}
+
+dbus_bool_t libhal_acquire_global_interface_lock (LibHalContext *ctx,
+                                                  const char *interface,
+                                                  DBusError *error)
+{
+	DBusMessage *message;
+	DBusMessageIter iter;
+	DBusMessage *reply;
+
+	LIBHAL_CHECK_LIBHALCONTEXT(ctx, FALSE);
+	LIBHAL_CHECK_PARAM_VALID(interface, "*interface", FALSE);
+
+	message = dbus_message_new_method_call ("org.freedesktop.Hal",
+						"/org/freedesktop/Hal/Manager",
+						"org.freedesktop.Hal.Device",
+						"AcquireGlobalInterfaceLock");
+
+	if (message == NULL) {
+		fprintf (stderr,
+			 "%s %d : Couldn't allocate D-BUS message\n",
+			 __FILE__, __LINE__);
+		return FALSE;
+	}
+
+	dbus_message_iter_init_append (message, &iter);
+	dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &interface);
+	
+	reply = dbus_connection_send_with_reply_and_block (ctx->connection,
+							   message, -1,
+							   error);
+
+	if (error != NULL && dbus_error_is_set (error)) {
+		dbus_message_unref (message);
+		return FALSE;
+	}
+
+	dbus_message_unref (message);
+
+	if (reply == NULL)
+		return FALSE;
+
+	dbus_message_unref (reply);
+	return TRUE;
+}
+
+dbus_bool_t libhal_release_global_interface_lock (LibHalContext *ctx,
+                                                  const char *interface,
+                                                  DBusError *error)
+{
+	DBusMessage *message;
+	DBusMessageIter iter;
+	DBusMessage *reply;
+
+	LIBHAL_CHECK_LIBHALCONTEXT(ctx, FALSE);
+	LIBHAL_CHECK_PARAM_VALID(interface, "*interface", FALSE);
+
+	message = dbus_message_new_method_call ("org.freedesktop.Hal",
+						"/org/freedesktop/Hal/Manager",
+						"org.freedesktop.Hal.Device",
+						"ReleaseGlobalInterfaceLock");
+
+	if (message == NULL) {
+		fprintf (stderr,
+			 "%s %d : Couldn't allocate D-BUS message\n",
+			 __FILE__, __LINE__);
+		return FALSE;
+	}
+
+	dbus_message_iter_init_append (message, &iter);
+	dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &interface);
+	
+	reply = dbus_connection_send_with_reply_and_block (ctx->connection,
+							   message, -1,
+							   error);
+
+	if (error != NULL && dbus_error_is_set (error)) {
+		dbus_message_unref (message);
+		return FALSE;
+	}
+
+	dbus_message_unref (message);
+
+	if (reply == NULL)
+		return FALSE;
+
+	dbus_message_unref (reply);
+	return TRUE;
+}
+
+dbus_bool_t
+libhal_device_is_caller_locked_out (LibHalContext *ctx,
+                                    const char *udi,
+                                    const char *interface,
+                                    const char *caller,
+                                    DBusError *error)
+{
+	DBusMessage *message;
+	DBusMessageIter iter;
+	DBusMessage *reply;
+	DBusMessageIter reply_iter;
+        dbus_bool_t value;
+
+	LIBHAL_CHECK_LIBHALCONTEXT(ctx, FALSE);
+	LIBHAL_CHECK_PARAM_VALID(udi, "*udi", FALSE);
+	LIBHAL_CHECK_PARAM_VALID(interface, "*interface", FALSE);
+	LIBHAL_CHECK_PARAM_VALID(caller, "*caller", FALSE);
+
+	message = dbus_message_new_method_call ("org.freedesktop.Hal",
+						udi,
+						"org.freedesktop.Hal.Device",
+						"IsCallerLockedOut");
+
+	if (message == NULL) {
+		fprintf (stderr,
+			 "%s %d : Couldn't allocate D-BUS message\n",
+			 __FILE__, __LINE__);
+		return TRUE;
+	}
+
+	dbus_message_iter_init_append (message, &iter);
+	dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &interface);
+	dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &caller);
+	
+	reply = dbus_connection_send_with_reply_and_block (ctx->connection,
+							   message, -1,
+							   error);
+
+	if (error != NULL && dbus_error_is_set (error)) {
+		dbus_message_unref (message);
+		return TRUE;
+	}
+
+	dbus_message_unref (message);
+
+	if (reply == NULL)
+		return TRUE;
+
+	/* now analyze reply */
+	dbus_message_iter_init (reply, &reply_iter);
+	if (dbus_message_iter_get_arg_type (&reply_iter) != DBUS_TYPE_BOOLEAN) {
+		dbus_message_unref (message);
+		dbus_message_unref (reply);
+		return TRUE;
+	}
+	dbus_message_iter_get_basic (&reply_iter, &value);
+	dbus_message_unref (reply);
+	return value;
+}
+
diff --git a/libhal/libhal.h b/libhal/libhal.h
index acddc11..5021f87 100644
--- a/libhal/libhal.h
+++ b/libhal/libhal.h
@@ -603,6 +603,36 @@ dbus_bool_t libhal_device_claim_interfac
 dbus_bool_t libhal_device_addon_is_ready (LibHalContext *ctx, const char *udi, DBusError *error);
 
 
+/* Take a mandatory lock on an interface on a device. */
+dbus_bool_t libhal_device_acquire_interface_lock (LibHalContext *ctx,
+                                                  const char *udi,
+                                                  const char *interface,
+                                                  DBusError *error);
+
+/* Release a mandatory lock on an interface on a device. */
+dbus_bool_t libhal_device_release_interface_lock (LibHalContext *ctx,
+                                                  const char *udi,
+                                                  const char *interface,
+                                                  DBusError *error);
+
+/* Take a mandatory lock on an interface (the lock affects all devices the caller have access to). */
+dbus_bool_t libhal_acquire_global_interface_lock (LibHalContext *ctx,
+                                                  const char *interface,
+                                                  DBusError *error);
+
+/* Release a mandatory lock on an interface (affects all devices the caller have access to). */
+dbus_bool_t libhal_release_global_interface_lock (LibHalContext *ctx,
+                                                  const char *interface,
+                                                  DBusError *error);
+
+/* Determine if a given caller is locked out of a given interface on a given device */
+dbus_bool_t libhal_device_is_caller_locked_out (LibHalContext *ctx,
+                                                const char *udi,
+                                                const char *interface,
+                                                const char *caller,
+                                                DBusError *error);
+
+
 #if defined(__cplusplus)
 }
 #endif
diff --git a/tools/hal-storage-mount.c b/tools/hal-storage-mount.c
index 6514d48..ee5c381 100644
--- a/tools/hal-storage-mount.c
+++ b/tools/hal-storage-mount.c
@@ -509,6 +509,8 @@ handle_mount (LibHalContext *hal_ctx, 
 
 	bailout_if_in_fstab (hal_ctx, device, label, uuid);
 
+        bailout_if_drive_is_locked (hal_ctx, drive, invoked_by_syscon_name);
+
 	/* TODO: sanity check that what hal exports is correct (cf. Martin Pitt's email) */
 
 	/* read from stdin */
diff --git a/tools/hal-storage-shared.c b/tools/hal-storage-shared.c
index 5931cd2..6c8e9d2 100644
--- a/tools/hal-storage-shared.c
+++ b/tools/hal-storage-shared.c
@@ -204,6 +204,26 @@ fstab_close (gpointer handle)
 #endif
 
 void
+bailout_if_drive_is_locked (LibHalContext *hal_ctx, LibHalDrive *drive, const char *invoked_by_syscon_name)
+{
+        DBusError error;
+
+        if (drive != NULL) {
+                dbus_error_init (&error);
+                if (libhal_device_is_caller_locked_out (hal_ctx, 
+                                                        libhal_drive_get_udi (drive),
+                                                        "org.freedesktop.Hal.Device.Storage",
+                                                        invoked_by_syscon_name,
+                                                        &error)) {
+                        fprintf (stderr, "org.freedesktop.Hal.Device.InterfaceLocked\n");
+                        fprintf (stderr, "The enclosing drive for the volume is locked\n");
+                        exit (1);
+                }
+        }
+}
+
+
+void
 unknown_error (const char *detail)
 {
 	fprintf (stderr, "org.freedesktop.Hal.Device.Volume.UnknownFailure\n");
@@ -303,6 +323,7 @@ handle_unmount (LibHalContext *hal_ctx, 
 		}
 	}
 
+        bailout_if_drive_is_locked (hal_ctx, drive, invoked_by_syscon_name);
 
 	/* check hal's mtab file to verify the device to unmount is actually mounted by hal */
 	hal_mtab_orig = fopen ("/media/.hal-mtab", "r");
@@ -560,6 +581,8 @@ try_open_excl_again:
 	printf ("invoked by system bus connection = %s\n", invoked_by_syscon_name);
 #endif
 
+        bailout_if_drive_is_locked (hal_ctx, drive, invoked_by_syscon_name);
+
 	/* construct arguments to EJECT (e.g. /usr/bin/eject) */
 	na = 0;
 	args[na++] = EJECT;
diff --git a/tools/hal-storage-shared.h b/tools/hal-storage-shared.h
index 96cd760..529cf49 100644
--- a/tools/hal-storage-shared.h
+++ b/tools/hal-storage-shared.h
@@ -46,6 +46,8 @@ void unlock_hal_mtab (void);
 
 void unknown_error (const char *detail);
 
+void bailout_if_drive_is_locked (LibHalContext *hal_ctx, LibHalDrive *drive, const char *invoked_by_syscon_name);
+
 void handle_unmount (LibHalContext *hal_ctx, 
 #ifdef HAVE_POLKIT
 		     LibPolKitContext *pol_ctx, 


More information about the hal-commit mailing list