hal: Branch 'master' - 7 commits

David Zeuthen david at kemper.freedesktop.org
Sun Apr 1 00:03:34 PDT 2007


 HACKING                               |    8 
 configure.in                          |    2 
 doc/man/Makefile.am                   |    6 
 doc/man/hal-disable-polling.1.in      |  137 +++++++++++++++
 doc/man/hal-find-by-capability.1.in   |    2 
 doc/man/hal-find-by-property.1.in     |    2 
 doc/man/hal-get-property.1.in         |    2 
 doc/man/hal-is-caller-locked-out.1.in |    2 
 doc/man/hal-lock.1.in                 |   12 +
 doc/man/hal-set-property.1.in         |    2 
 doc/spec/hal-spec-interfaces.xml      |   70 +++++++
 doc/spec/hal-spec-locking.xml         |  197 +++++++++++++++++++--
 examples/interface-locking-test.py    |   21 +-
 hald/access-check.c                   |  211 +++++++++++++++++------
 hald/access-check.h                   |    5 
 hald/ci-tracker.c                     |   26 ++
 hald/ci-tracker.h                     |    1 
 hald/device.c                         |   77 +++++++-
 hald/device.h                         |   10 +
 hald/device_store.c                   |   63 +++++++
 hald/device_store.h                   |   10 +
 hald/hald.c                           |   35 +++
 hald/hald_dbus.c                      |  305 ++++++++++++++++++++++++++++++++--
 hald/hald_dbus.h                      |    6 
 hald/hald_marshal.list                |    2 
 hald/linux/addons/addon-storage.c     |  113 ++++++++++++
 libhal/libhal.c                       |  281 ++++++++++++++++++++++++++++++-
 libhal/libhal.h                       |   79 ++++++++
 tools/Makefile.am                     |    6 
 tools/hal-disable-polling.c           |  276 ++++++++++++++++++++++++++++++
 tools/hal-lock.c                      |  141 ++++++++++++++-
 tools/lshal.c                         |   75 ++++++++
 32 files changed, 2048 insertions(+), 137 deletions(-)

New commits:
diff-tree c003685ace0936d4c813bf1ba422d521c72d4d62 (from 6d5d221b3130a8b30b05313ba69e0d7ec77fbdd6)
Author: David Zeuthen <davidz at redhat.com>
Date:   Sun Apr 1 02:56:53 2007 -0400

    add hal-disable-polling(1) program along with a man page

diff --git a/configure.in b/configure.in
index 349102a..d3dd295 100644
--- a/configure.in
+++ b/configure.in
@@ -893,6 +893,7 @@ doc/man/hal-find-by-property.1
 doc/man/hal-find-by-capability.1
 doc/man/hal-is-caller-locked-out.1
 doc/man/hal-lock.1
+doc/man/hal-disable-polling.1
 po/Makefile.in
 ])
 
diff --git a/doc/man/Makefile.am b/doc/man/Makefile.am
index 4f22641..5f5416d 100644
--- a/doc/man/Makefile.am
+++ b/doc/man/Makefile.am
@@ -1,9 +1,9 @@
 
 if MAN_PAGES_ENABLED
 
-MAN_IN_FILES = hald.1.in lshal.1.in hal-get-property.1.in hal-set-property.1.in hal-find-by-property.1.in hal-find-by-capability.1.in hal-is-caller-locked-out.1.in hal-lock.1.in
+MAN_IN_FILES = hald.1.in lshal.1.in hal-get-property.1.in hal-set-property.1.in hal-find-by-property.1.in hal-find-by-capability.1.in hal-is-caller-locked-out.1.in hal-lock.1.in hal-disable-polling.1.in
 
-man_MANS = hald.1 lshal.1 hal-get-property.1 hal-set-property.1 hal-find-by-property.1 hal-find-by-capability.1 hal-is-caller-locked-out.1 hal-lock.1
+man_MANS = hald.1 lshal.1 hal-get-property.1 hal-set-property.1 hal-find-by-property.1 hal-find-by-capability.1 hal-is-caller-locked-out.1 hal-lock.1 hal-disable-polling.1
 
 endif # MAN_PAGES_ENABLED
 
diff --git a/doc/man/hal-disable-polling.1.in b/doc/man/hal-disable-polling.1.in
new file mode 100644
index 0000000..26f2769
--- /dev/null
+++ b/doc/man/hal-disable-polling.1.in
@@ -0,0 +1,137 @@
+.\" 
+.\" hal-disable-polling manual page.
+.\" Copyright (C) 2007 David Zeuthen <david at fubar.dk>
+.\"
+.TH HAL-DISABLE-POLLING 1
+.SH NAME
+hal-disable-polling \- disable polling on drives with removable media
+.SH SYNOPSIS
+.PP
+.B hal-disable-polling
+[options]
+
+.SH DESCRIPTION
+
+\fIhal-disable-polling\fP can be used to to disable and enable media
+detection on drives with removable storage. For more information about
+both the big picture and specific
+.B HAL
+properties, refer to the \fIHAL spec\fP which can be found in
+.I "/usr/share/doc/hal- at VERSION@/spec/hal-spec.html"
+depending on the distribution.
+
+.SH OPTIONS
+The following options are supported:
+.TP
+.I "--udi"
+The UDI (\fIUnique Device Identifier\fP) of the device object. 
+.TP
+.I "--device"
+The device file of the drive.
+.TP
+.I "--enable-polling"
+Enable polling instead of disabling it.
+.TP
+.I "--help"
+Print out usage.
+.TP
+.I "--version"
+Print the version.
+
+.SH NOTES
+.PP
+This program requires super user privileges.
+
+.SH RETURN VALUE
+.PP
+If the requested operation was successful, this program will exit with
+exit code 0.
+
+.SH HISTORY
+.PP
+Polling a storage drive is a necessary evil to detect when the user
+inserts or removes media. Human computer interaction studies have
+shown that a broad class of users expect their system to react within
+a few seconds of this. Thus, the
+.I hald
+daemon polls through the 
+.I hald-addon-storage
+addon (one instance for each drive with removable media).
+
+The purpose of the 
+.I hald-addon-storage
+addon is simply to open the special device file at a regular interval
+(either every 2 or every 16 seconds) to check for new media. This
+program tries to open the device file using the
+.B O_EXCL
+option which means that programs like \&\fIcdrecord\fR\|(1) that uses
+.B O_EXCL
+automatically prevents the 
+.I hald-addon-storage
+for interferring by continously opening the device file. In addition,
+if the drive is locked using HAL (see \&\fIhal-lock\fR\|(1)) the addon
+also stops polling.
+
+Unfortunately, polling a storage drive can have adverse side effects
+if the hardware and/or device driver for the hardware is
+malfunctioning. Additionally, the operating system kernel itself may
+offer multiple interfaces for the same device (e.g. \&\fI/dev/sg0\fR\|
+and \&\fI/dev/scd0\fR\|) so even
+.B O_EXCL
+won't work. Also, polling a drive may decrease throughput in certain
+(odd and/or broken) configurations; for example, if two IDE drives
+shares the same host (master/slave), bus traffic and contention caused
+by polling e.g. the optical drive (slave) can reduce throughput to the
+hard disk (master) and/or interfere with CD burning on another optical
+drive (master). Finally, polling a drive incurs an overhead both in
+the host system (processes get woken up often, preventing the CPU to
+stay in a deep power saving states) and it may prevent the actual
+drive from reaching deep power states as well. As a result, more power
+is consumed and this affects battery life for laptops.
+
+Despite the existence of support for asynchronous media change
+notification in recent MMC (Multi-Media Commands) specifications,
+virtually no optical drives are compliant with the
+specification. Fortunately newer SATA ATAPI hardware seems to support
+Asynchronous Notification (AN) and at this time of writing (March
+2007) work is underway to make both the
+.I Linux 
+operating system kernel and
+.I HAL
+take advantage of this.
+
+It is the position of the
+.I HAL
+team that polling should be avoided at all costs as long as it doesn't
+heavily impact the user experience in a negative way. This tool is
+provided as a stop-gap measure to use if a system is rendered useless
+due to bugs in drivers and/or hardware that is provoked by HAL polling
+the drive. If such a bug is encountered it should be reported (see the
+.B BUGS
+section below) so it can be fixed - historically
+.B hald
+have triggered a number of bugs in 
+.I Linux
+storage drivers and related subsystems (such as USB) that have later
+been fixed.
+
+.SH BUGS
+.PP
+Please send bug reports to either the distribution or the HAL
+mailing list, see 
+.I "http://lists.freedesktop.org/mailman/listinfo/hal"
+on how to subscribe.
+
+.SH SEE ALSO
+.PP
+\&\fIhald\fR\|(1),
+\&\fIlshal\fR\|(1),
+\&\fIhal-lock\fR\|(1),
+\&\fIopen\fR\|(2),
+\&\fIhttp://www.t10.org/scsi-3.htm\fR\|,
+\&\fIhttps://bugzilla.redhat.com/bugzilla/show_bug.cgi?id=halpolling\fR\|
+
+.SH AUTHOR
+Written by David Zeuthen <david at fubar.dk> with a lot of help from many
+others.
+
diff --git a/tools/Makefile.am b/tools/Makefile.am
index 0f73f61..1a8347b 100644
--- a/tools/Makefile.am
+++ b/tools/Makefile.am
@@ -31,7 +31,8 @@ bin_PROGRAMS =                    \
 	hal-find-by-property      \
 	hal-device		  \
 	hal-is-caller-locked-out  \
-	hal-lock
+	hal-lock		  \
+	hal-disable-polling
 
 lshal_SOURCES = lshal.c
 lshal_LDADD = @GLIB_LIBS@ $(top_builddir)/libhal/libhal.la
@@ -57,6 +58,9 @@ hal_is_caller_locked_out_LDADD = @DBUS_L
 hal_lock_SOURCES = hal-lock.c
 hal_lock_LDADD = @GLIB_LIBS@ @DBUS_LIBS@ $(top_builddir)/libhal/libhal.la
 
+hal_disable_polling_SOURCES = hal-disable-polling.c
+hal_disable_polling_LDADD = @GLIB_LIBS@ @DBUS_LIBS@ $(top_builddir)/libhal/libhal.la
+
 libexec_PROGRAMS =                          \
 	hal-storage-mount	            \
 	hal-storage-unmount 	            \
diff --git a/tools/hal-disable-polling.c b/tools/hal-disable-polling.c
new file mode 100644
index 0000000..44d886c
--- /dev/null
+++ b/tools/hal-disable-polling.c
@@ -0,0 +1,276 @@
+/***************************************************************************
+ * CVSID: $Id$
+ *
+ * hal-disable-polling.c : Disable polling on a drive
+ *
+ * Copyright (C) 2007 David Zeuthen, <david at 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 <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <glib.h>
+#include <libhal.h>
+
+/** 
+ *  usage:
+ *  @argc:                Number of arguments given to program
+ *  @argv:                Arguments given to program
+ *
+ *  Print out program usage. 
+ */
+static void
+usage (int argc, char *argv[])
+{
+	fprintf (stderr,
+                 "\n"
+                 "usage : hal-disable-polling [--udi <udi> | --device <device-file>]\n"
+                 "                            [--enable-polling]\n"
+                 "                            [--help] [--version]\n");
+	fprintf (stderr,
+                 "\n"
+                 "        --udi            Unique Device Id\n"
+                 "        --device         Device file\n"
+                 "        --enable-polling Enable polling instead of disabling it\n"
+                 "        --version        Show version and exit\n"
+                 "        --help           Show this information and exit\n"
+                 "\n"
+                 "This program is provided to make HAL stop polling a drive. Please read.\n"
+                 "the entire manual page before using this program.\n"
+                 "\n");
+}
+
+/** 
+ *  main:
+ *  @argc:                Number of arguments given to program
+ *  @argv:                Arguments given to program
+ *
+ *  Returns:              Return code
+ *
+ *  Main entry point 
+ */
+int
+main (int argc, char *argv[])
+{
+	char *udi = NULL;
+	char *device = NULL;
+        dbus_bool_t is_version = FALSE;
+        dbus_bool_t enable_polling = FALSE;
+	DBusError error;
+        LibHalContext *hal_ctx;
+        FILE *f;
+        char *filename;
+
+	if (argc <= 1) {
+		usage (argc, argv);
+		return 1;
+	}
+
+	while (1) {
+		int c;
+		int option_index = 0;
+		const char *opt;
+		static struct option long_options[] = {
+			{"udi", 1, NULL, 0},
+			{"device", 1, NULL, 0},
+                        {"enable-polling", 0, NULL, 0},
+			{"version", 0, NULL, 0},
+			{"help", 0, NULL, 0},
+			{NULL, 0, NULL, 0}
+		};
+
+		c = getopt_long (argc, argv, "",
+				 long_options, &option_index);
+		if (c == -1)
+			break;
+
+		switch (c) {
+		case 0:
+			opt = long_options[option_index].name;
+
+			if (strcmp (opt, "help") == 0) {
+				usage (argc, argv);
+				return 0;
+			} else if (strcmp (opt, "version") == 0) {
+				is_version = TRUE;
+			} else if (strcmp (opt, "udi") == 0) {
+				udi = strdup (optarg);
+			} else if (strcmp (opt, "device") == 0) {
+				device = strdup (optarg);
+			} else if (strcmp (opt, "enable-polling") == 0) {
+				enable_polling = TRUE;
+			}
+			break;
+
+		default:
+			usage (argc, argv);
+			return 1;
+			break;
+		}
+	}
+
+	if (is_version) {
+		printf ("hal-disable-polling " PACKAGE_VERSION "\n");
+		return 0;
+	}
+
+	if (udi == NULL && device == NULL) {
+		usage (argc, argv);
+		return 1;
+	}
+
+	if (udi != NULL && device != NULL) {
+		usage (argc, argv);
+		return 1;
+	}
+
+	dbus_error_init (&error);	
+	if ((hal_ctx = libhal_ctx_new ()) == NULL) {
+		fprintf (stderr, "error: libhal_ctx_new\n");
+		return 1;
+	}
+	if (!libhal_ctx_set_dbus_connection (hal_ctx, dbus_bus_get (DBUS_BUS_SYSTEM, &error))) {
+		fprintf (stderr, "error: libhal_ctx_set_dbus_connection: %s: %s\n", error.name, error.message);
+		LIBHAL_FREE_DBUS_ERROR (&error);
+		return 1;
+	}
+	if (!libhal_ctx_init (hal_ctx, &error)) {
+		if (dbus_error_is_set(&error)) {
+			fprintf (stderr, "error: libhal_ctx_init: %s: %s\n", error.name, error.message);
+			dbus_error_free (&error);
+		}
+		fprintf (stderr, "Could not initialise connection to hald.\n"
+				 "Normally this means the HAL daemon (hald) is not running or not ready.\n");
+		return 1;
+	}
+
+        if (getuid () != 0) {
+		fprintf (stderr, "This program requires super user (root) privileges.\n");
+                return 1;
+        }
+
+        if (device != NULL) {
+                char **devices;
+                int num_devices;
+                int n;
+
+                devices = libhal_manager_find_device_string_match (hal_ctx, "block.device", device, &num_devices, NULL);
+                if (devices == NULL) {
+                        fprintf (stderr, "Cannot find device %s.\n", device);
+                        return 1;
+                }
+
+                for (n = 0; devices[n] != NULL; n++) {
+                        if (libhal_device_query_capability (hal_ctx, devices[n], "storage", NULL)) {
+                                udi = devices[n];
+                                break;
+                        }
+                }
+
+                if (udi == NULL) {
+                        fprintf (stderr, "Cannot find storage device %s.\n", device);
+                        return 1;
+                }
+
+                /* mmmkay, we don't care about leaking the variable devices... mmkay? mmkay! */
+        } else {
+                if (!libhal_device_exists (hal_ctx, udi, &error)) {
+                        fprintf (stderr, "Cannot find device with udi %s.\n", udi);
+                        return 1;
+                }
+                if (!libhal_device_query_capability (hal_ctx, udi, "storage", NULL)) {
+                        fprintf (stderr, "Device with udi %s is not a storage device.\n", udi);
+                        return 1;
+                }
+                device = libhal_device_get_property_string (hal_ctx, udi, "block.device", NULL);
+                if (device == NULL) {
+                        fprintf (stderr, "Device with udi %s does not have block.device set.\n", udi);
+                        return 1;
+                }
+        }
+
+        if (!libhal_device_get_property_bool (hal_ctx, udi, "storage.removable", NULL)) {
+                fprintf (stderr, "The given drive don't use removable media so it's not polled anyway.\n");
+                return 1;
+        }
+
+        filename = g_strdup_printf (PACKAGE_SYSCONF_DIR "/hal/fdi/information/media-check-disable-%s.fdi",
+                                    g_basename (udi));
+
+        if (enable_polling) {
+                if (libhal_device_get_property_bool (hal_ctx, udi, "storage.media_check_enabled", NULL)) {
+                        fprintf (stderr, "Polling is already enabled on the given drive.\n");
+                        return 1;
+                }
+
+                if (!g_file_test (filename, G_FILE_TEST_EXISTS)) {
+                        fprintf (stderr, "Cannot find fdi file %s. Perhaps polling wasn't disabled using this tool?\n", filename);
+                        return 1;
+                }
+                if (unlink (filename) != 0) {
+                        fprintf (stderr, "Cannot delete fdi file %s.\n", filename);
+                        return 1;
+                }
+        } else {
+                if (!libhal_device_get_property_bool (hal_ctx, udi, "storage.media_check_enabled", NULL)) {
+                        fprintf (stderr, "Polling is already disabled on the given drive.\n");
+                        return 1;
+                }
+
+                if (g_file_test (filename, G_FILE_TEST_EXISTS)) {
+                        fprintf (stderr, "The fdi file %s already exist. Cowardly refusing to overwrite it.\n", filename);
+                        return 1;
+                }
+
+                f = fopen (filename, "w");
+                if (f == NULL) {
+                        fprintf (stderr, "Cannot open %s for writing.\n", filename);
+                        return 1;
+                }
+                fprintf (f, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+                         "\n"
+                         "<deviceinfo version=\"0.2\">\n"
+                         "  <device>\n"
+                         "    <match key=\"info.udi\" string=\"%s\">\n"
+                         "      <merge key=\"storage.media_check_enabled\" type=\"bool\">false</merge>\n"
+                         "    </match>\n"
+                         "  </device>\n"
+                         "</deviceinfo>\n"
+                         "\n", udi);
+                fclose (f);
+        }
+
+        libhal_device_reprobe (hal_ctx, udi, &error);
+
+        if (enable_polling)
+                printf ("Polling for drive %s have been enabled. The fdi file deleted was\n"
+                        "  %s\n", device, filename);
+        else
+                printf ("Polling for drive %s have been disabled. The fdi file written was\n"
+                        "  %s\n", device, filename);
+
+        return 0;
+}
diff-tree 6d5d221b3130a8b30b05313ba69e0d7ec77fbdd6 (from dfef8c679fd3f46c64b257cce7edb1872e467de8)
Author: David Zeuthen <davidz at redhat.com>
Date:   Sun Apr 1 00:43:38 2007 -0400

    update hal-lock(1)'s man page and fix usage()

diff --git a/doc/man/hal-lock.1.in b/doc/man/hal-lock.1.in
index a5c5492..bbff769 100644
--- a/doc/man/hal-lock.1.in
+++ b/doc/man/hal-lock.1.in
@@ -41,6 +41,11 @@ Kill the program if the acquired lock is
 .I UDI
 due to the semantics of HAL locks.
 .TP
+.I "--exit-with-dev"
+Kill the program if the device is removed. This only makes sense if you pass a specific
+.I UDI
+due to the semantics of HAL locks.
+.TP
 .I "--help"
 Print out usage.
 .TP
diff --git a/tools/hal-lock.c b/tools/hal-lock.c
index 4d02c05..09fec20 100644
--- a/tools/hal-lock.c
+++ b/tools/hal-lock.c
@@ -58,7 +58,7 @@ usage (int argc, char *argv[])
                  "                 [--udi <udi>]\n"
                  "                 [--exclusive]\n"
                  "                 [--exit-with-lock]\n"
-                 "                 [--exit-with-device]\n"
+                 "                 [--exit-with-dev]\n"
                  "                 [--help] [--version]\n");
 	fprintf (stderr,
                  "\n"
diff-tree dfef8c679fd3f46c64b257cce7edb1872e467de8 (from a3b09f0ed8364e74405538dfecf1cebbaa8773b8)
Author: David Zeuthen <davidz at redhat.com>
Date:   Sun Apr 1 00:41:50 2007 -0400

    make hal-lock(1) provide an --exit-with-dev option

diff --git a/tools/hal-lock.c b/tools/hal-lock.c
index f58431e..4d02c05 100644
--- a/tools/hal-lock.c
+++ b/tools/hal-lock.c
@@ -57,7 +57,8 @@ usage (int argc, char *argv[])
                  "                 --run <program-and-args>\n"
                  "                 [--udi <udi>]\n"
                  "                 [--exclusive]\n"
-                 "                 [--exit-with-lock]"
+                 "                 [--exit-with-lock]\n"
+                 "                 [--exit-with-device]\n"
                  "                 [--help] [--version]\n");
 	fprintf (stderr,
                  "\n"
@@ -67,6 +68,7 @@ usage (int argc, char *argv[])
                  "                         ommitted the global lock will be tried\n"
                  "        --exclusive      Whether the lock can be held by others\n"
                  "        --exit-with-lock Kill the program if the acquired lock is lost\n"
+                 "        --exit-with-dev  Kill the program if the locked device is removed\n"
                  "        --version        Show version and exit\n"
                  "        --help           Show this information and exit\n"
                  "\n"
@@ -93,6 +95,15 @@ guardian (GPid pid, int status, gpointer
 }
 
 static void
+device_removed (LibHalContext *ctx, const char *_udi)
+{
+        if (strcmp (udi, _udi) == 0) {
+                fprintf (stderr, "Lost the device; killing child...\n");
+                kill (child_pid, SIGTERM);
+        }
+}
+
+static void
 interface_lock_released (LibHalContext *ctx,
                          const char *_udi,
                          const char *_interface,
@@ -126,6 +137,7 @@ main (int argc, char *argv[])
         dbus_bool_t exclusive = FALSE;
         dbus_bool_t got_lock = FALSE;
         dbus_bool_t exit_with_lock = FALSE;
+        dbus_bool_t exit_with_dev = FALSE;
         DBusConnection *con;
 	DBusError error;
         LibHalContext *hal_ctx;
@@ -150,6 +162,7 @@ main (int argc, char *argv[])
 			{"run", 1, NULL, 0},
 			{"exclusive", 0, NULL, 0},
 			{"exit-with-lock", 0, NULL, 0},
+			{"exit-with-dev", 0, NULL, 0},
 			{"version", 0, NULL, 0},
 			{"help", 0, NULL, 0},
 			{NULL, 0, NULL, 0}
@@ -179,6 +192,8 @@ main (int argc, char *argv[])
 				interface = strdup (optarg);
 			} else if (strcmp (opt, "exit-with-lock") == 0) {
 				exit_with_lock = TRUE;
+			} else if (strcmp (opt, "exit-with-dev") == 0) {
+				exit_with_dev = TRUE;
 			}
 			break;
 
@@ -206,7 +221,13 @@ main (int argc, char *argv[])
                 goto out;
         }
 
-	if (exit_with_lock)
+        if (exit_with_dev && udi == NULL) {
+                fprintf (stderr, "--exit-with-lock requires UDI to be given.\n");
+                usage (argc, argv);
+                goto out;
+        }
+
+	if (exit_with_lock || exit_with_dev)
 		loop = g_main_loop_new (NULL, FALSE);
 	else
 		loop = NULL;
@@ -228,13 +249,17 @@ main (int argc, char *argv[])
                 goto out;
         }
 
-        if (exit_with_lock) {
+        if (exit_with_lock || exit_with_dev) {
                 unique_name = dbus_bus_get_unique_name (con);
-                fprintf (stderr, "unique name is '%s'\n", unique_name);
-                libhal_ctx_set_interface_lock_released (hal_ctx, interface_lock_released);
 		dbus_connection_setup_with_g_main (con, NULL);
         }
 
+        if (exit_with_lock)
+                libhal_ctx_set_interface_lock_released (hal_ctx, interface_lock_released);
+
+        if (exit_with_dev)
+                libhal_ctx_set_device_removed (hal_ctx, device_removed);
+
         if (!libhal_ctx_init (hal_ctx, &error)) {
                 if (dbus_error_is_set(&error)) {
                         fprintf (stderr, "error: libhal_ctx_init: %s: %s\n", error.name, error.message);
@@ -276,7 +301,7 @@ main (int argc, char *argv[])
                 goto out;
         }
 
-        if (exit_with_lock) {
+        if (exit_with_lock || exit_with_dev) {
                 int _argc;
                 char **_argv;
 
diff-tree a3b09f0ed8364e74405538dfecf1cebbaa8773b8 (from 9548dacbb323101ca0a9dcbb45678eb58acc3a5d)
Author: David Zeuthen <davidz at redhat.com>
Date:   Sun Apr 1 00:41:23 2007 -0400

    avoid polling when a drive is locked
    
    Also update the proc title if the device file is locked using O_EXCL

diff --git a/hald/linux/addons/addon-storage.c b/hald/linux/addons/addon-storage.c
index 469fc2a..a58b8aa 100644
--- a/hald/linux/addons/addon-storage.c
+++ b/hald/linux/addons/addon-storage.c
@@ -59,6 +59,7 @@ static DBusConnection *con = NULL;
 static guint poll_timer = -1;
 static GMainLoop *loop;
 static gboolean system_is_idle = FALSE;
+static gboolean check_lock_state = TRUE;
 
 static void 
 force_unmount (LibHalContext *ctx, const char *udi)
@@ -277,11 +278,30 @@ enum {
 
 static gboolean poll_for_media (gpointer user_data);
 
+static int interval_in_seconds = 2;
+
+static gboolean is_locked_by_hal = FALSE;
+static gboolean is_locked_via_o_excl = FALSE;
+
 static void
-update_polling_interval (void)
+update_proc_title (void)
 {
-	int interval_in_seconds;
+        if (is_locked_by_hal) {
+                if (is_locked_via_o_excl) {
+                        hal_set_proc_title ("hald-addon-storage: no polling because %s is locked via HAL and O_EXCL", device_file);
+                } else {
+                        hal_set_proc_title ("hald-addon-storage: no polling because %s is locked via HAL", device_file);
+                }
+        } else if (is_locked_via_o_excl) {
+                hal_set_proc_title ("hald-addon-storage: no polling because %s is locked via O_EXCL", device_file);
+        } else {
+                hal_set_proc_title ("hald-addon-storage: polling %s (every %d sec)", device_file, interval_in_seconds);
+        }
+}
 
+static void
+update_polling_interval (void)
+{
 	/* TODO: ideally we want all things that do polling to do it
 	 * at the same time.. such as to minimize battery
 	 * usage. Suppose we want to wake up to do poll_for_media()
@@ -308,7 +328,8 @@ update_polling_interval (void)
 	if (poll_timer > 0)
 		g_source_remove (poll_timer);
 	poll_timer = g_timeout_add (interval_in_seconds * 1000, poll_for_media, NULL);
-	hal_set_proc_title ("hald-addon-storage: polling %s (every %d sec)", device_file, interval_in_seconds);
+
+        update_proc_title ();
 }
 
 static gboolean
@@ -316,6 +337,29 @@ poll_for_media (gpointer user_data)
 {
 	int fd;
 	int got_media;
+
+        if (check_lock_state) {
+                DBusError error;
+
+                check_lock_state = FALSE;
+
+                HAL_INFO (("Checking whether device %s is locked on HAL", device_file));
+                dbus_error_init (&error);
+                if (libhal_device_is_locked_by_others (ctx, udi, "org.freedesktop.Hal.Device.Storage", &error)) {
+                        HAL_INFO (("... device %s is locked on HAL", device_file));
+                        is_locked_by_hal = TRUE;
+                        update_proc_title ();
+                        goto skip_check;
+                } else {
+                        HAL_INFO (("... device %s is not locked on HAL", device_file));
+                        is_locked_by_hal = FALSE;
+                        update_proc_title ();
+                }
+        }
+
+        /* TODO: we could remove the timeout completely... */
+        if (is_locked_by_hal)
+                goto skip_check;
 	
 	got_media = FALSE;
 	
@@ -333,8 +377,15 @@ poll_for_media (gpointer user_data)
 			 * actually is mounted. If it is we retry to open
 			 * without O_EXCL
 			 */
-			if (!is_mounted (device_file))
+			if (!is_mounted (device_file)) {
+                                if (!is_locked_via_o_excl) {
+                                        is_locked_via_o_excl = TRUE;
+                                        update_proc_title ();
+                                } else {
+                                        is_locked_via_o_excl = TRUE;
+                                }
 				goto skip_check;
+                        }
 			
 			fd = open (device_file, O_RDONLY | O_NONBLOCK);
 		}
@@ -343,6 +394,11 @@ poll_for_media (gpointer user_data)
 			HAL_ERROR (("open failed for %s: %s", device_file, strerror (errno))); 
 			goto skip_check;
 		}
+
+                if (is_locked_via_o_excl) {
+                        is_locked_via_o_excl = FALSE;
+                        update_proc_title ();
+                }
 		
 		
 		/* Check if a disc is in the drive
@@ -517,6 +573,7 @@ error:
 }
 #endif /* HAVE_CONKIT */
 
+
 static DBusHandlerResult
 dbus_filter_function (DBusConnection *connection, DBusMessage *message, void *user_data)
 {
@@ -538,9 +595,24 @@ dbus_filter_function (DBusConnection *co
 			update_polling_interval ();
 		}
 	}
+
 out:
 #endif /* HAVE_CONKIT */
 
+        /* Check, just before the next poll, whether lock state have changed; 
+         * 
+         * Note that we get called on at least these signals
+         *
+         * 1. CK.Manager  - SystemIdleHintChanged
+         * 2. CK.Seat     - ActiveSessionChanged
+         * 2. HAL.Manager - GlobalLockAcquired, GlobalLockReleased
+         * 3. HAL.Device  - LockAcquired, LockReleased
+         *
+         * meaning that every time the locking situation changes, we
+         * will get updated.
+         */
+        check_lock_state = TRUE;
+
 	return DBUS_HANDLER_RESULT_HANDLED;
 }
 
@@ -551,6 +623,7 @@ main (int argc, char *argv[])
 	char *bus;
 	char *drive_type;
 	char *support_media_changed_str;
+        char *str;
 
 	hal_set_proc_title_init (argc, argv);
 
@@ -616,9 +689,39 @@ main (int argc, char *argv[])
 			    "type='signal'"
 			    ",interface='org.freedesktop.ConsoleKit.Manager'"
 			    ",sender='org.freedesktop.ConsoleKit'"
-			    ",member='SystemIdleHintChanged'",
+                            ",member='SystemIdleHintChanged'",
+			    NULL);
+	dbus_bus_add_match (con,
+			    "type='signal'"
+			    ",interface='org.freedesktop.ConsoleKit.Seat'"
+			    ",sender='org.freedesktop.ConsoleKit'"
+                            ",member='ActiveSessionChanged'",
 			    NULL);
 #endif
+
+        /* this is a bit weird; but we want to listen to signals about
+         * locking from hald.. and signals are not pushed over direct
+         * connections (for a good reason).
+         */
+	dbus_bus_add_match (con,
+			    "type='signal'"
+			    ",interface='org.freedesktop.Hal.Manager'"
+			    ",sender='org.freedesktop.Hal'",
+			    NULL);
+	dbus_bus_add_match (con,
+			    "type='signal'"
+			    ",interface='org.freedesktop.Hal.Manager'"
+			    ",sender='org.freedesktop.Hal'",
+			    NULL);
+        str = g_strdup_printf ("type='signal'"
+                               ",interface='org.freedesktop.Hal.Device'"
+                               ",sender='org.freedesktop.Hal'"
+                               ",path='%s'",
+                               udi);
+	dbus_bus_add_match (con,
+                            str,
+			    NULL);
+        g_free (str);
 	dbus_connection_add_filter (con, dbus_filter_function, NULL, NULL);
 
 	update_polling_interval ();
diff-tree 9548dacbb323101ca0a9dcbb45678eb58acc3a5d (from 4a36ab4a5c0b0b9b9c7e593e8d227af99cd3a5dc)
Author: David Zeuthen <davidz at redhat.com>
Date:   Sun Apr 1 00:40:13 2007 -0400

    avoid crash if an addon is using IsLockedByOthers()

diff --git a/hald/access-check.c b/hald/access-check.c
index ab68ce4..a17d34f 100644
--- a/hald/access-check.c
+++ b/hald/access-check.c
@@ -362,7 +362,8 @@ access_check_locked_by_others (CITracker
 
         if (holders != NULL) {
                 for (n = 0; holders[n] != NULL; n++) {
-                        if (strcmp (holders[n], caller_unique_sysbus_name) != 0) {
+                        if (caller_unique_sysbus_name == NULL || /* <-- callers using direct connection to hald */
+                            strcmp (holders[n], caller_unique_sysbus_name) != 0) {
                                 /* someone else is holding the lock */
                                 goto out;
                         }
@@ -371,7 +372,8 @@ access_check_locked_by_others (CITracker
 
         if (global_holders != NULL) {
                 for (n = 0; global_holders[n] != NULL; n++) {
-                        if (strcmp (global_holders[n], caller_unique_sysbus_name) != 0) {
+                        if (caller_unique_sysbus_name == NULL || /* <-- callers using direct connection to hald */
+                            strcmp (global_holders[n], caller_unique_sysbus_name) != 0) {
                                 /* someone else is holding the global lock... */
                                 if (access_check_caller_have_access_to_device (cit, device, NULL, global_holders[n])) {
                                         /* ... and they can can access the device */
diff-tree 4a36ab4a5c0b0b9b9c7e593e8d227af99cd3a5dc (from 975a4defadb47ec8d3d6156f3eb20f6a67ec3009)
Author: David Zeuthen <davidz at redhat.com>
Date:   Sat Mar 31 22:37:48 2007 -0400

    change locking semantics slightly and add missing locking bits

diff --git a/HACKING b/HACKING
index 7e9bb5b..2a77fdc 100644
--- a/HACKING
+++ b/HACKING
@@ -131,3 +131,11 @@ Coding Style
    heuristically parse a file and accept not-well-formed
    data). Avoiding heuristics is also important for security reasons;
    if it looks funny, ignore it (or exit, or disconnect).
+
+Configuring the sources
+===
+
+To configure the HAL sources for a generic Linux desktop distribution
+using Linux 2.6.20, one should use
+
+--enable-console-kit --enable-acl-management --enable-docbook-docs --enable-acpi-ibm --enable-acpi-toshiba
diff --git a/configure.in b/configure.in
index 8e14728..349102a 100644
--- a/configure.in
+++ b/configure.in
@@ -140,6 +140,7 @@ AC_ARG_ENABLE(verbose-mode,     [  --ena
 AC_ARG_ENABLE(docbook-docs,     [  --enable-docbook-docs   build documentation (requires xmlto)],enable_docbook_docs=$enableval,enable_docbook_docs=no)
 AC_ARG_ENABLE(man-pages,        [  --enable-man-pages      build manual pages],enable_man_pages=$enableval,enable_man_pages=yes)
 AM_CONDITIONAL(MAN_PAGES_ENABLED, test x$enable_man_pages = xyes)
+AC_SUBST(MAN_PAGES_ENABLED)
 
 ## eject 
 AC_ARG_WITH(eject,              [  --with-eject=<path>     Specify eject program. (default /usr/bin/eject)])
diff --git a/doc/man/Makefile.am b/doc/man/Makefile.am
index 295c694..4f22641 100644
--- a/doc/man/Makefile.am
+++ b/doc/man/Makefile.am
@@ -10,4 +10,4 @@ endif # MAN_PAGES_ENABLED
 EXTRA_DIST=$(man_MANS) $(MAN_IN_FILES)
 
 clean-local:
-	rm -f *~
+	rm -f *~ *.1
diff --git a/doc/man/hal-find-by-capability.1.in b/doc/man/hal-find-by-capability.1.in
index 5d7e155..5c593ca 100644
--- a/doc/man/hal-find-by-capability.1.in
+++ b/doc/man/hal-find-by-capability.1.in
@@ -19,7 +19,7 @@ device database by looking at device cap
 more information about both the big picture and specific
 .B HAL
 properties, refer to the \fIHAL spec\fP which can be found in
-.I "/usr/share/doc/hal-0.5.9/spec/hal-spec.html"
+.I "/usr/share/doc/hal- at VERSION@/spec/hal-spec.html"
 depending on the distribution.
 
 .SH OPTIONS
diff --git a/doc/man/hal-find-by-property.1.in b/doc/man/hal-find-by-property.1.in
index 0dd29c3..73bc99b 100644
--- a/doc/man/hal-find-by-property.1.in
+++ b/doc/man/hal-find-by-property.1.in
@@ -19,7 +19,7 @@ device database by looking at device pro
 more information about both the big picture and specific
 .B HAL
 properties, refer to the \fIHAL spec\fP which can be found in
-.I "/usr/share/doc/hal-0.5.9/spec/hal-spec.html"
+.I "/usr/share/doc/hal- at VERSION@/spec/hal-spec.html"
 depending on the distribution.
 
 .SH OPTIONS
diff --git a/doc/man/hal-get-property.1.in b/doc/man/hal-get-property.1.in
index d61e459..87ab9d9 100644
--- a/doc/man/hal-get-property.1.in
+++ b/doc/man/hal-get-property.1.in
@@ -19,7 +19,7 @@ device database. For
 more information about both the big picture and specific
 .B HAL
 properties, refer to the \fIHAL spec\fP which can be found in
-.I "/usr/share/doc/hal-0.5.9/spec/hal-spec.html"
+.I "/usr/share/doc/hal- at VERSION@/spec/hal-spec.html"
 depending on the distribution.
 
 .SH OPTIONS
diff --git a/doc/man/hal-is-caller-locked-out.1.in b/doc/man/hal-is-caller-locked-out.1.in
index 3df1c1d..d5de39a 100644
--- a/doc/man/hal-is-caller-locked-out.1.in
+++ b/doc/man/hal-is-caller-locked-out.1.in
@@ -17,7 +17,7 @@ locked out of a specific D-Bus interface
 more information about both the big picture and specific
 .B HAL
 properties, refer to the \fIHAL spec\fP which can be found in
-.I "/usr/share/doc/hal-0.5.9/spec/hal-spec.html"
+.I "/usr/share/doc/hal- at VERSION@/spec/hal-spec.html"
 depending on the distribution.
 
 .SH OPTIONS
diff --git a/doc/man/hal-lock.1.in b/doc/man/hal-lock.1.in
index abd6af3..a5c5492 100644
--- a/doc/man/hal-lock.1.in
+++ b/doc/man/hal-lock.1.in
@@ -17,7 +17,7 @@ either on a given device or globally. Fo
 the big picture and the semantics of 
 .B HAL
 locks, refer to the \fIHAL spec\fP which can be found in
-.I "/usr/share/doc/hal-0.5.9/spec/hal-spec.html"
+.I "/usr/share/doc/hal- at VERSION@/spec/hal-spec.html"
 depending on the distribution.
 
 .SH OPTIONS
@@ -36,6 +36,11 @@ is ommitted, the global lock will be tri
 .I "--exclusive"
 Whether the lock can be held by others.
 .TP
+.I "--exit-with-lock"
+Kill the program if the acquired lock is lost. This only makes sense if you pass a specific
+.I UDI
+due to the semantics of HAL locks.
+.TP
 .I "--help"
 Print out usage.
 .TP
diff --git a/doc/man/hal-set-property.1.in b/doc/man/hal-set-property.1.in
index 2c4294c..d62bfcd 100644
--- a/doc/man/hal-set-property.1.in
+++ b/doc/man/hal-set-property.1.in
@@ -19,7 +19,7 @@ device database. For
 more information about both the big picture and specific
 .B HAL
 properties, refer to the \fIHAL spec\fP which can be found in
-.I "/usr/share/doc/hal-0.5.9/spec/hal-spec.html"
+.I "/usr/share/doc/hal- at VERSION@/spec/hal-spec.html"
 depending on the distribution.
 
 .SH OPTIONS
diff --git a/doc/spec/hal-spec-interfaces.xml b/doc/spec/hal-spec-interfaces.xml
index 9858c71..1ad525b 100644
--- a/doc/spec/hal-spec-interfaces.xml
+++ b/doc/spec/hal-spec-interfaces.xml
@@ -112,6 +112,28 @@ $ dbus-send --system --print-reply --des
               the global device list (GDL). Caller must be uid 0.
             </entry>
           </row>
+
+          <row>
+            <entry>AcquireGlobalInterfaceLock</entry>
+            <entry></entry>
+            <entry>String interface_name, Bool exclusive</entry>
+            <entry>Device.InterfaceAlreadyLocked</entry>
+            <entry>
+              Acquires a global lock on an interface. See
+              <xref linkend="locking"/> for details.
+            </entry>
+          </row>
+          <row>
+            <entry>ReleaseGlobalInterfaceLock</entry>
+            <entry></entry>
+            <entry>String interface_name</entry>
+            <entry>Device.InterfaceNotLocked</entry>
+            <entry>
+              Releases a global lock on an interface. See
+              <xref linkend="locking"/> for details.
+            </entry>
+          </row>
+
         </tbody>
       </tgroup>
     </informaltable>
@@ -151,6 +173,20 @@ $ dbus-send --system --print-reply --des
               A device gained a new capability.
             </entry>
           </row>
+          <row>
+            <entry>GlobalInterfaceLockAcquired</entry>
+            <entry>String lock_name, String lock_owner, Int num_holders</entry>
+            <entry>
+              Sent when a process acquires a global interface lock.
+            </entry>
+          </row>
+          <row>
+            <entry>GlobalInterfaceLockReleased</entry>
+            <entry>String lock_name, String lock_owner, Int num_holders</entry>
+            <entry>
+              Sent when a process releases a global interface lock.
+            </entry>
+          </row>
         </tbody>
       </tgroup>
     </informaltable>
@@ -377,7 +413,6 @@ $ dbus-send --system --print-reply --des
               Releases an advisory lock on the device. Returns TRUE if the lock was released.
             </entry>
           </row>
-
           <row>
             <entry>AcquireInterfaceLock</entry>
             <entry></entry>
@@ -388,7 +423,6 @@ $ dbus-send --system --print-reply --des
               device. See <xref linkend="locking"/> for details.
             </entry>
           </row>
-
           <row>
             <entry>ReleaseInterfaceLock</entry>
             <entry></entry>
@@ -399,7 +433,6 @@ $ dbus-send --system --print-reply --des
               <xref linkend="locking"/> for details.
             </entry>
           </row>
-
           <row>
             <entry>IsCallerLockedOut</entry>
             <entry>Bool</entry>
@@ -412,6 +445,17 @@ $ dbus-send --system --print-reply --des
               method. See <xref linkend="locking"/> for details.
             </entry>
           </row>
+          <row>
+            <entry>IsLockedByOthers</entry>
+            <entry>Bool</entry>
+            <entry>String interface_name</entry>
+            <entry></entry>
+            <entry>
+              Determines whether a determines other processes than the
+              caller holds a lock on the given device. See
+              <xref linkend="locking"/> for details.
+            </entry>
+          </row>
 
           <row>
             <entry>StringListAppend</entry>
@@ -539,6 +583,20 @@ $ dbus-send --system --print-reply --des
               dedicated interface.).
             </entry>
           </row>
+          <row>
+            <entry>InterfaceLockAcquired</entry>
+            <entry>String lock_name, String lock_owner, Int num_holders</entry>
+            <entry>
+              Sent when a process acquires an interface lock on the device.
+            </entry>
+          </row>
+          <row>
+            <entry>InterfaceLockReleased</entry>
+            <entry>String lock_name, String lock_owner, Int num_holders</entry>
+            <entry>
+              Sent when a process releases an interface lock on the device.
+            </entry>
+          </row>
         </tbody>
       </tgroup>
     </informaltable>
diff --git a/doc/spec/hal-spec-locking.xml b/doc/spec/hal-spec-locking.xml
index df62f0a..7306dbb 100644
--- a/doc/spec/hal-spec-locking.xml
+++ b/doc/spec/hal-spec-locking.xml
@@ -52,11 +52,28 @@
       interface on the given device. The locker can specify whether
       the lock is <emphasis>exclusive</emphasis> meaning if multiple
       clients clients can hold the lock or if only one client can hold
-      the lock at one time. If a client don't have access to a device,
-      attempts to lock will fail with
+      the lock at one time. If a client don't have access to the
+      interface of the device, attempts to lock will fail with
       a <literal>org.freedesktop.Hal.PermissionDenied</literal>
-      exception. If another client already holds the lock exclusively,
-      attempts from other clients to acquire the lock will fail with
+      exception. If a client loses access to a device (say, if his
+      session is switched away from using fast user switching) while
+      holding a lock, he will lose the lock; this can be tracked by
+      listening to the <literal>InterfaceLockReleased</literal>
+      signal.
+    </para>
+
+    <para>
+      All local clients, whether they are active or not, can always
+      lock interfaces on the root computer device object (this doesn't
+      mean that they are privileged to use the interfaces though) -
+      the rationale is that this device object represents shared
+      infrastructure, e.g. power management, and even inactive
+      sessions needs to participate in managing this.
+    </para>
+
+    <para>
+      If another client already holds a lock exclusively, attempts
+      from other clients to acquire the lock will fail with
       the <literal>org.freedesktop.Hal.Device.InterfaceAlreadyLocked</literal>
       exception even if they have access to the device.
     </para>
@@ -218,7 +235,7 @@
 
           <listitem>
             <para>
-              ... MUST check that no other process is holding the lock
+              ... MUST check that no other process is holding the lock (using the <literal>IsLockedByOthers</literal> method on the standard <literal>org.freedesktop.Hal.Device</literal> interface)
               before calling into
               the <literal>org.freedesktop.Hal.Device.SystemPowerManagement</literal>
               interface. If another process is holding the lock, it
diff --git a/examples/interface-locking-test.py b/examples/interface-locking-test.py
index ca60a91..51552b4 100755
--- a/examples/interface-locking-test.py
+++ b/examples/interface-locking-test.py
@@ -22,11 +22,15 @@ device_volume = dbus.Interface(bus.get_o
                                "org.freedesktop.Hal.Device.Volume")
 
 #manager.AcquireGlobalInterfaceLock("org.freedesktop.Hal.Device.Storage", True)
-#time.sleep(2)
+#time.sleep(10)
 #manager.ReleaseGlobalInterfaceLock("org.freedesktop.Hal.Device.Storage")
 
 device.AcquireInterfaceLock("org.freedesktop.Hal.Device.Volume", False)
-device_volume.Mount("", "", [])
-time.sleep(2)
+#device_volume.Mount("", "", [])
+if device.IsLockedByOthers("org.freedesktop.Hal.Device.Volume"):
+    print "device is locked by another process too!"
+else:
+    print "we are the only process locking the device"    
+time.sleep(10)
 device.ReleaseInterfaceLock("org.freedesktop.Hal.Device.Volume")
 
diff --git a/hald/access-check.c b/hald/access-check.c
index 6f0542b..ab68ce4 100644
--- a/hald/access-check.c
+++ b/hald/access-check.c
@@ -110,17 +110,30 @@ 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
+ * @privilege: the type of access; right now this can be #NULL or
+ * "lock"; will be replaced by PolicyKit privileges in the future
+ * @caller_unique_sysbus_name: The unique system bus connection
+ * name (e.g. ":1.43") of the caller
  *
  * Determine if a given caller should have access to a device. This
  * depends on how the security is set up and may change according to
  * how the system is configured.
  *
- * If ConsoleKit is used this function currently will return TRUE if,
- * and only if, the caller is in an active session. TODO: once
- * multi-seat is properly supported it will also depend on what seat
- * the device belongs to and what seat the caller's session belongs
- * to.
+ * If the privilege parameter is #NULL, it means "check if the caller
+ * can access D-Bus interfaces". If it is "lock" it means "check if
+ * the caller can lock an interface on the device".
+ *
+ * If ConsoleKit is used this function currently will return TRUE when
+ * privilege is #NULL if, and only if, the caller is in an active
+ * local session. If privilege is "lock" the only difference is that
+ * the it will always return TRUE for the root computer device
+ * object. This is used to ensure that any caller can always lock the
+ * SystemPowerManagement interface cf. the "Locking Guidelines"
+ * section of the HAL spec.
+ *
+ * (TODO: once PolicyKit and multi-seat is properly supported, this
+ * result from this function will also depend on what seat the device
+ * belongs to and what seat the caller's session belongs to.)
  *
  * If ConsoleKit is not used, this function will just return TRUE; the
  * OS vendor is supposed to have locked down access to HAL through OS
@@ -132,7 +145,7 @@ out:
  * Returns: TRUE iff the caller have access to the device.
  */
 gboolean
-access_check_caller_have_access_to_device (CITracker *cit, HalDevice *device, const char *caller_unique_sysbus_name)
+access_check_caller_have_access_to_device (CITracker *cit, HalDevice *device, const char *privilege, const char *caller_unique_sysbus_name)
 #ifdef HAVE_CONKIT
 {
         gboolean ret;
@@ -152,19 +165,30 @@ access_check_caller_have_access_to_devic
                 goto out;
         }
 
+        /* must be tracked by ConsoleKit */
         if (ci_tracker_caller_get_ck_session_path (ci) == NULL) {
                 goto out;
         }
 
+        /* require caller to be local */
+        if (!ci_tracker_caller_is_local (ci))
+                goto out;
 
-        if (!ci_tracker_caller_in_active_session (ci)) {
+        /* allow inactive sessions to lock interfaces on root computer device object */
+        if (privilege != NULL && 
+            strcmp (privilege, "lock") == 0 && 
+            strcmp (hal_device_get_udi (device), "/org/freedesktop/Hal/devices/computer") == 0) {
+                ret = TRUE;
                 goto out;
         }
-
+        
+        /* require caller to be in active session */
+        if (!ci_tracker_caller_in_active_session (ci))
+                goto out;
+                
         ret = TRUE;
 out:
         return ret;
-
 }
 #else /* HAVE_CONKIT */
 {
@@ -254,7 +278,7 @@ access_check_caller_locked_out (CITracke
                 for (n = 0; global_holders[n] != NULL; n++) {
                         if (strcmp (global_holders[n], caller_unique_sysbus_name) == 0) {
                                 /* we are holding the global lock... */
-                                if (access_check_caller_have_access_to_device (cit, device, global_holders[n])) {
+                                if (access_check_caller_have_access_to_device (cit, device, NULL, global_holders[n])) {
                                         /* only applies if the caller can access the device... */
                                         is_locked_by_self = TRUE;
                                         /* this is good enough; we are holding the lock ourselves */
@@ -265,7 +289,7 @@ access_check_caller_locked_out (CITracke
                                 /* 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])) {
+                                if (access_check_caller_have_access_to_device (cit, device, NULL, global_holders[n])) {
                                         /* They certainly do. Mark as locked. */
                                         is_locked = TRUE;
                                 }
@@ -293,3 +317,77 @@ out:
         return ret;
 }
 
+
+
+/**
+ * access_check_locked_by_others:
+ * @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 other processes than the caller holds a lock
+ * on the given device.
+ * 
+ * Returns: TRUE iff other processes is holding a lock on the device
+ */
+gboolean
+access_check_locked_by_others (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);
+
+        if (holders != NULL) {
+                for (n = 0; holders[n] != NULL; n++) {
+                        if (strcmp (holders[n], caller_unique_sysbus_name) != 0) {
+                                /* someone else is holding the lock */
+                                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... */
+                                if (access_check_caller_have_access_to_device (cit, device, NULL, global_holders[n])) {
+                                        /* ... and they can can access the device */
+                                        goto out;
+                                }
+                        }
+                }
+        }
+
+        /* done all the checks so noone else is locking... */
+        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 ab05f07..3da49ed 100644
--- a/hald/access-check.h
+++ b/hald/access-check.h
@@ -35,10 +35,15 @@ gboolean access_check_message_caller_is_
                                                      DBusMessage *message);
 gboolean access_check_caller_have_access_to_device  (CITracker   *cit,
                                                      HalDevice   *device,
+                                                     const char  *privilege,
                                                      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);
+gboolean access_check_locked_by_others              (CITracker   *cit,
+                                                     HalDevice   *device,
+                                                     const char  *caller_unique_sysbus_name,
+                                                     const char  *interface_name);
 
 #endif /* ACCESS_CHECK_H */
diff --git a/hald/ci-tracker.c b/hald/ci-tracker.c
index 6a8bd81..af83654 100644
--- a/hald/ci-tracker.c
+++ b/hald/ci-tracker.c
@@ -52,6 +52,7 @@ struct CICallerInfo_s {
 #ifdef HAVE_CONKIT
 	pid_t  pid;                   /* process ID of caller */
 	gboolean in_active_session;   /* caller is in an active session */
+        gboolean is_local;            /* session is on a local seat */
 	char *session_objpath;        /* obj path of ConsoleKit session */
 #endif
 	char *system_bus_unique_name; /* unique name of caller on the system bus */
@@ -261,6 +262,25 @@ ci_tracker_get_info (CITracker *cit, con
 	dbus_message_iter_get_basic (&iter, &ci->in_active_session);
 	dbus_message_unref (message);
 	dbus_message_unref (reply);
+
+
+	message = dbus_message_new_method_call ("org.freedesktop.ConsoleKit", 
+						ci->session_objpath,
+						"org.freedesktop.ConsoleKit.Session",
+						"IsLocal");
+	dbus_error_init (&error);
+	reply = dbus_connection_send_with_reply_and_block (cit->dbus_connection, message, -1, &error);
+	if (reply == NULL || dbus_error_is_set (&error)) {
+		HAL_WARNING (("Error doing IsLocal on ConsoleKit: %s: %s", error.name, error.message));
+		dbus_message_unref (message);
+		if (reply != NULL)
+			dbus_message_unref (reply);
+		goto error;
+	}
+	dbus_message_iter_init (reply, &iter);
+	dbus_message_iter_get_basic (&iter, &ci->is_local);
+	dbus_message_unref (message);
+	dbus_message_unref (reply);
 	
 store_caller_info:
 #endif /* HAVE_CONKIT */
@@ -311,6 +331,12 @@ ci_tracker_caller_get_pid (CICallerInfo 
 }
 
 gboolean
+ci_tracker_caller_is_local (CICallerInfo *ci)
+{
+        return ci->is_local;
+}
+
+gboolean
 ci_tracker_caller_in_active_session (CICallerInfo *ci)
 {
         return ci->in_active_session;
diff --git a/hald/ci-tracker.h b/hald/ci-tracker.h
index 7d762e9..5b0794a 100644
--- a/hald/ci-tracker.h
+++ b/hald/ci-tracker.h
@@ -63,6 +63,7 @@ uid_t         ci_tracker_caller_get_uid 
 const char   *ci_tracker_caller_get_sysbus_unique_name (CICallerInfo *ci);
 #ifdef HAVE_CONKIT
 pid_t         ci_tracker_caller_get_pid                (CICallerInfo *ci);
+gboolean      ci_tracker_caller_is_local               (CICallerInfo *ci);
 gboolean      ci_tracker_caller_in_active_session      (CICallerInfo *ci);
 const char   *ci_tracker_caller_get_ck_session_path    (CICallerInfo *ci);
 #endif
diff --git a/hald/device.c b/hald/device.c
index f1f5847..e95cc19 100644
--- a/hald/device.c
+++ b/hald/device.c
@@ -376,6 +376,8 @@ struct _HalDevicePrivate
 enum {
 	PROPERTY_CHANGED,
 	CAPABILITY_ADDED,
+        LOCK_ACQUIRED,
+        LOCK_RELEASED,
 	LAST_SIGNAL
 };
 
@@ -441,6 +443,30 @@ hal_device_class_init (HalDeviceClass *k
 			      hald_marshal_VOID__STRING,
 			      G_TYPE_NONE, 1,
 			      G_TYPE_STRING);
+
+	signals[LOCK_ACQUIRED] =
+		g_signal_new ("lock_acquired",
+			      G_TYPE_FROM_CLASS (klass),
+			      G_SIGNAL_RUN_LAST,
+			      G_STRUCT_OFFSET (HalDeviceClass,
+					       lock_acquired),
+			      NULL, NULL,
+			      hald_marshal_VOID__STRING_STRING,
+			      G_TYPE_NONE, 2,
+			      G_TYPE_STRING,
+			      G_TYPE_STRING);
+
+	signals[LOCK_RELEASED] =
+		g_signal_new ("lock_released",
+			      G_TYPE_FROM_CLASS (klass),
+			      G_SIGNAL_RUN_LAST,
+			      G_STRUCT_OFFSET (HalDeviceClass,
+					       lock_released),
+			      NULL, NULL,
+			      hald_marshal_VOID__STRING_STRING,
+			      G_TYPE_NONE, 2,
+			      G_TYPE_STRING,
+			      G_TYPE_STRING);
 }
 
 static void
@@ -1654,6 +1680,8 @@ hal_device_acquire_lock (HalDevice *devi
 
         add_to_locked_set (device);
 
+        g_signal_emit (device, signals[LOCK_ACQUIRED], 0, lock_name, sender);
+
         ret = TRUE;
 out:
         return ret;
@@ -1711,6 +1739,8 @@ hal_device_release_lock (HalDevice *devi
 		hal_device_property_strlist_remove (device, buf, sender);
 	}
 
+        g_signal_emit (device, signals[LOCK_RELEASED], 0, lock_name, sender);
+
         ret = TRUE;
 
 out:
@@ -1740,6 +1770,32 @@ hal_device_get_lock_holders (HalDevice *
 }
 
 /**
+ * hal_device_get_lock_holders:
+ * @device: the device to check for
+ * @lock_name: the lock name
+ *
+ * Get the number of lock holders on a device.
+ *
+ * Returns: Number of callers holding the given lock.
+ */
+int 
+hal_device_get_num_lock_holders (HalDevice *device, const char *lock_name)
+{
+        int num;
+        char **holders;
+
+        num = 0;
+        holders = hal_device_get_lock_holders (device, lock_name);
+        if (holders == NULL)
+                goto out;
+
+        num = g_strv_length (holders);
+        g_strfreev (holders);
+out:
+        return num;
+}
+
+/**
  * hal_device_client_disconnected:
  * @sender: the client that disconnected from the bus
  *
@@ -1756,20 +1812,19 @@ hal_device_client_disconnected (const ch
 
         for (i = locked_devices; i != NULL; i = g_slist_next (i)) {
                 HalDevice *device = i->data;
-                GSList *locks;
-                GSList *j;
-                GSList *k;
+                char **locks;
+                int n;
 
                 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);
+                locks = hal_device_property_dup_strlist_as_strv (device, "info.named_locks");
+                if (locks != NULL) {
+                        for (n = 0; locks[n] != NULL; n++) {
+                                char *lock_name = locks[n];
+                                HAL_INFO (("Lock '%s'", lock_name));
+                                hal_device_release_lock (device, lock_name, sender);
+                        }
+                        g_strfreev (locks);
                 }
         }
 }
diff --git a/hald/device.h b/hald/device.h
index 5dd11f5..1942e2f 100644
--- a/hald/device.h
+++ b/hald/device.h
@@ -53,6 +53,14 @@ struct _HalDeviceClass {
 
 	void (*capability_added) (HalDevice *device,
 				  const char *capability);
+
+	void (*lock_acquired) (HalDevice *device,
+                               const char *lock_name,
+                               const char *lock_owner);
+
+	void (*lock_released) (HalDevice *device,
+                               const char *lock_name,
+                               const char *lock_owner);
 };
 
 #define HAL_TYPE_DEVICE             (hal_device_get_type ())
@@ -212,6 +220,8 @@ gboolean      hal_device_release_lock (H
 
 char        **hal_device_get_lock_holders (HalDevice *device, const char *lock_name);
 
+int           hal_device_get_num_lock_holders (HalDevice *device, const char *lock_name);
+
 /* static method */
 void          hal_device_client_disconnected (const char *sender);
 
diff --git a/hald/device_store.c b/hald/device_store.c
index 7c06aab..23c054d 100644
--- a/hald/device_store.c
+++ b/hald/device_store.c
@@ -41,6 +41,8 @@ enum {
 	STORE_CHANGED,
 	DEVICE_PROPERTY_CHANGED,
 	DEVICE_CAPABILITY_ADDED,
+        DEVICE_LOCK_ACQUIRED,
+        DEVICE_LOCK_RELEASED,
 	LAST_SIGNAL
 };
 
@@ -103,6 +105,33 @@ hal_device_store_class_init (HalDeviceSt
 			      G_TYPE_NONE, 2,
 			      G_TYPE_OBJECT,
 			      G_TYPE_STRING);
+
+	signals[DEVICE_LOCK_ACQUIRED] =
+		g_signal_new ("device_lock_acquired",
+			      G_TYPE_FROM_CLASS (klass),
+			      G_SIGNAL_RUN_LAST,
+			      G_STRUCT_OFFSET (HalDeviceStoreClass,
+					       device_lock_acquired),
+			      NULL, NULL,
+			      hald_marshal_VOID__OBJECT_STRING_STRING,
+			      G_TYPE_NONE, 3,
+			      G_TYPE_OBJECT,
+			      G_TYPE_STRING,
+			      G_TYPE_STRING);
+
+	signals[DEVICE_LOCK_RELEASED] =
+		g_signal_new ("device_lock_released",
+			      G_TYPE_FROM_CLASS (klass),
+			      G_SIGNAL_RUN_LAST,
+			      G_STRUCT_OFFSET (HalDeviceStoreClass,
+					       device_lock_released),
+			      NULL, NULL,
+			      hald_marshal_VOID__OBJECT_STRING_STRING,
+			      G_TYPE_NONE, 3,
+			      G_TYPE_OBJECT,
+			      G_TYPE_STRING,
+			      G_TYPE_STRING);
+
 }
 
 static void
@@ -169,6 +198,30 @@ emit_device_capability_added (HalDevice 
 		       device, capability);
 }
 
+static void
+emit_device_lock_acquired (HalDevice *device,
+                           const char *lock_name,
+                           const char *lock_owner,
+                           gpointer data)
+{
+	HalDeviceStore *store = HAL_DEVICE_STORE (data);
+
+	g_signal_emit (store, signals[DEVICE_LOCK_ACQUIRED], 0,
+		       device, lock_name, lock_owner);
+}
+
+static void
+emit_device_lock_released (HalDevice *device,
+                           const char *lock_name,
+                           const char *lock_owner,
+                           gpointer data)
+{
+	HalDeviceStore *store = HAL_DEVICE_STORE (data);
+
+	g_signal_emit (store, signals[DEVICE_LOCK_RELEASED], 0,
+		       device, lock_name, lock_owner);
+}
+
 void
 hal_device_store_add (HalDeviceStore *store, HalDevice *device)
 {
@@ -187,6 +240,10 @@ hal_device_store_add (HalDeviceStore *st
 			  G_CALLBACK (emit_device_property_changed), store);
 	g_signal_connect (device, "capability_added",
 			  G_CALLBACK (emit_device_capability_added), store);
+	g_signal_connect (device, "lock_acquired",
+			  G_CALLBACK (emit_device_lock_acquired), store);
+	g_signal_connect (device, "lock_released",
+			  G_CALLBACK (emit_device_lock_released), store);
 
 	g_signal_emit (store, signals[STORE_CHANGED], 0, device, TRUE);
 
@@ -208,6 +265,12 @@ hal_device_store_remove (HalDeviceStore 
 	g_signal_handlers_disconnect_by_func (device,
 					      (gpointer)emit_device_capability_added,
 					      store);
+	g_signal_handlers_disconnect_by_func (device,
+					      (gpointer)emit_device_lock_acquired,
+					      store);
+	g_signal_handlers_disconnect_by_func (device,
+					      (gpointer)emit_device_lock_released,
+					      store);
 
 	g_signal_emit (store, signals[STORE_CHANGED], 0, device, FALSE);
 
diff --git a/hald/device_store.h b/hald/device_store.h
index 0e1578a..300f4c7 100644
--- a/hald/device_store.h
+++ b/hald/device_store.h
@@ -58,6 +58,16 @@ struct _HalDeviceStoreClass {
 					 HalDevice *device,
 					 const char *capability);
 
+
+	void (*device_lock_acquired) (HalDeviceStore *store,
+                                      HalDevice *device,
+                                      const char *lock_name,
+                                      const char *lock_owner);
+
+	void (*device_lock_released) (HalDeviceStore *store,
+                                      HalDevice *device,
+                                      const char *lock_name,
+                                      const char *lock_owner);
 };
 
 #define HAL_TYPE_DEVICE_STORE              (hal_device_store_get_type ())
diff --git a/hald/hald.c b/hald/hald.c
index c130ca8..1a13a31 100644
--- a/hald/hald.c
+++ b/hald/hald.c
@@ -161,6 +161,32 @@ gdl_capability_added (HalDeviceStore *st
 	/*hal_callout_capability (device, capability, TRUE)*/;
 }
 
+static void
+gdl_lock_acquired (HalDeviceStore *store, HalDevice *device, const char *lock_name, const char *lock_owner)
+{
+	if (hal_device_are_all_addons_ready (device)) {
+                if (strncmp (lock_name, "Global.", 7) == 0 && 
+                    strcmp (hal_device_get_udi (device), "/org/freedesktop/Hal/devices/computer") == 0) {
+                        manager_send_signal_interface_lock_acquired (lock_name + 7, lock_owner);
+                } else {
+                        device_send_signal_interface_lock_acquired (device, lock_name, lock_owner);
+                }
+	}
+}
+
+static void
+gdl_lock_released (HalDeviceStore *store, HalDevice *device, const char *lock_name, const char *lock_owner)
+{
+	if (hal_device_are_all_addons_ready (device)) {
+                if (strncmp (lock_name, "Global.", 7) == 0 && 
+                    strcmp (hal_device_get_udi (device), "/org/freedesktop/Hal/devices/computer") == 0) {
+                        manager_send_signal_interface_lock_released (lock_name + 7, lock_owner);
+                } else {
+                        device_send_signal_interface_lock_released (device, lock_name, lock_owner);
+                }
+	}
+}
+
 HalDeviceStore *
 hald_get_gdl (void)
 {
@@ -176,6 +202,15 @@ hald_get_gdl (void)
 		g_signal_connect (global_device_list,
 				  "device_capability_added",
 				  G_CALLBACK (gdl_capability_added), NULL);
+		g_signal_connect (global_device_list,
+				  "device_property_changed",
+				  G_CALLBACK (gdl_property_changed), NULL);
+		g_signal_connect (global_device_list,
+				  "device_lock_acquired",
+				  G_CALLBACK (gdl_lock_acquired), NULL);
+		g_signal_connect (global_device_list,
+				  "device_lock_released",
+				  G_CALLBACK (gdl_lock_released), NULL);
 	}
 
 	return global_device_list;
diff --git a/hald/hald_dbus.c b/hald/hald_dbus.c
index 2816947..90dc9f7 100644
--- a/hald/hald_dbus.c
+++ b/hald/hald_dbus.c
@@ -761,6 +761,146 @@ out:
 	;
 }
 
+void
+manager_send_signal_interface_lock_acquired (const char *interface_name, const char *sender)
+{
+        const char *udi;
+        char *lock_name;
+        int num_locks;
+        HalDevice *d;
+	DBusMessage *message;
+	DBusMessageIter iter;
+
+	if (dbus_connection == NULL || hald_is_initialising)
+		goto out;
+
+	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)
+                goto out;
+
+        lock_name = g_strdup_printf ("Global.%s", interface_name);
+        num_locks = hal_device_get_num_lock_holders (d, lock_name);
+        g_free (lock_name);
+
+	message = dbus_message_new_signal ("/org/freedesktop/Hal/Manager",
+					   "org.freedesktop.Hal.Manager",
+					   "GlobalInterfaceLockAcquired");
+
+	dbus_message_iter_init_append (message, &iter);
+	dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &interface_name);
+	dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &sender);
+	dbus_message_iter_append_basic (&iter, DBUS_TYPE_INT32, &num_locks);
+
+	if (!dbus_connection_send (dbus_connection, message, NULL))
+		DIE (("error broadcasting message"));
+
+	dbus_message_unref (message);
+out:
+	;
+}
+
+void
+manager_send_signal_interface_lock_released (const char *interface_name, const char *sender)
+{
+        const char *udi;
+        char *lock_name;
+        int num_locks;
+        HalDevice *d;
+	DBusMessage *message;
+	DBusMessageIter iter;
+
+	if (dbus_connection == NULL || hald_is_initialising)
+		goto out;
+
+	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)
+                goto out;
+
+        lock_name = g_strdup_printf ("Global.%s", interface_name);
+        num_locks = hal_device_get_num_lock_holders (d, lock_name);
+        g_free (lock_name);
+
+	message = dbus_message_new_signal ("/org/freedesktop/Hal/Manager",
+					   "org.freedesktop.Hal.Manager",
+					   "GlobalInterfaceLockReleased");
+
+	dbus_message_iter_init_append (message, &iter);
+	dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &interface_name);
+	dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &sender);
+	dbus_message_iter_append_basic (&iter, DBUS_TYPE_INT32, &num_locks);
+
+	if (!dbus_connection_send (dbus_connection, message, NULL))
+		DIE (("error broadcasting message"));
+
+	dbus_message_unref (message);
+out:
+	;
+}
+
+void
+device_send_signal_interface_lock_acquired (HalDevice *device, const char *interface_name, const char *sender)
+{
+        int num_locks;
+	DBusMessage *message;
+	DBusMessageIter iter;
+
+	if (dbus_connection == NULL || hald_is_initialising)
+		goto out;
+
+        num_locks = hal_device_get_num_lock_holders (device, interface_name);
+
+	message = dbus_message_new_signal (hal_device_get_udi (device),
+					   "org.freedesktop.Hal.Device",
+					   "InterfaceLockAcquired");
+
+	dbus_message_iter_init_append (message, &iter);
+	dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &interface_name);
+	dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &sender);
+	dbus_message_iter_append_basic (&iter, DBUS_TYPE_INT32, &num_locks);
+
+	if (!dbus_connection_send (dbus_connection, message, NULL))
+		DIE (("error broadcasting message"));
+
+	dbus_message_unref (message);
+out:
+	;
+}
+
+void
+device_send_signal_interface_lock_released (HalDevice *device, const char *interface_name, const char *sender)
+{
+        int num_locks;
+	DBusMessage *message;
+	DBusMessageIter iter;
+
+	if (dbus_connection == NULL || hald_is_initialising)
+		goto out;
+
+        num_locks = hal_device_get_num_lock_holders (device, interface_name);
+
+	message = dbus_message_new_signal (hal_device_get_udi (device),
+					   "org.freedesktop.Hal.Device",
+					   "InterfaceLockReleased");
+
+	dbus_message_iter_init_append (message, &iter);
+	dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &interface_name);
+	dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &sender);
+	dbus_message_iter_append_basic (&iter, DBUS_TYPE_INT32, &num_locks);
+
+	if (!dbus_connection_send (dbus_connection, message, NULL))
+		DIE (("error broadcasting message"));
+
+	dbus_message_unref (message);
+out:
+	;
+}
+
 static void
 foreach_property_append (HalDevice *device, 
 			 const char *key,
@@ -1903,7 +2043,7 @@ device_acquire_interface_lock (DBusConne
 	sender = dbus_message_get_sender (message);
 
         if (!local_interface) {
-                if (!access_check_caller_have_access_to_device (ci_tracker, d, sender)) {
+                if (!access_check_caller_have_access_to_device (ci_tracker, d, "lock", sender)) {
                         raise_permission_denied (connection, message, "AcquireInterfaceLock: no access to device");
                         return DBUS_HANDLER_RESULT_HANDLED;
                 }
@@ -1960,7 +2100,7 @@ device_release_interface_lock (DBusConne
 	sender = dbus_message_get_sender (message);
 
         if (!local_interface) {
-                if (!access_check_caller_have_access_to_device (ci_tracker, d, sender)) {
+                if (!access_check_caller_have_access_to_device (ci_tracker, d, "lock", sender)) {
                         raise_permission_denied (connection, message, "ReleaseInterfaceLock: no access to device");
                         return DBUS_HANDLER_RESULT_HANDLED;
                 }
@@ -1990,7 +2130,6 @@ device_release_interface_lock (DBusConne
 	return DBUS_HANDLER_RESULT_HANDLED;
 }
 
-
 static DBusHandlerResult
 device_is_caller_locked_out (DBusConnection *connection, DBusMessage *message, dbus_bool_t local_interface)
 {
@@ -2049,6 +2188,62 @@ device_is_caller_locked_out (DBusConnect
 	return DBUS_HANDLER_RESULT_HANDLED;
 }
 
+
+/*------------------------------------------------------------------------*/
+
+static DBusHandlerResult
+device_is_locked_by_others (DBusConnection *connection, DBusMessage *message, dbus_bool_t local_interface)
+{
+	const char *udi;
+	HalDevice *d;
+	DBusMessage *reply;
+	DBusError error;
+	const char *sender;
+	char *interface_name;
+        dbus_bool_t result;
+        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;
+	}
+
+	sender = dbus_message_get_sender (message);
+
+        /* anyone can ask this question */
+
+	dbus_error_init (&error);
+	if (!dbus_message_get_args (message, &error,
+				    DBUS_TYPE_STRING, &interface_name,
+				    DBUS_TYPE_INVALID)) {
+		raise_syntax (connection, message, "IsLockedByOthers");
+		return DBUS_HANDLER_RESULT_HANDLED;
+	}
+
+        result = access_check_locked_by_others (ci_tracker, d, sender, 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
@@ -3707,6 +3902,16 @@ do_introspect (DBusConnection  *connecti
 				       "      <arg name=\"udi\" type=\"s\"/>\n"
 				       "      <arg name=\"cap_name\" type=\"s\"/>\n"
 				       "    </signal>\n"
+				       "    <signal name=\"GlobalInterfaceLockAcquired\">\n"
+				       "      <arg name=\"interface_name\" type=\"s\"/>\n"
+				       "      <arg name=\"lock_holder\" type=\"s\"/>\n"
+				       "      <arg name=\"num_locks\" type=\"i\"/>\n"
+				       "    </signal>\n"
+				       "    <signal name=\"GlobalInterfaceLockReleased\">\n"
+				       "      <arg name=\"interface_name\" type=\"s\"/>\n"
+				       "      <arg name=\"lock_holder\" type=\"s\"/>\n"
+				       "      <arg name=\"num_locks\" type=\"i\"/>\n"
+				       "    </signal>\n"
 
 				       "  </interface>\n");
 	} else {
@@ -3816,6 +4021,10 @@ do_introspect (DBusConnection  *connecti
 				       "      <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=\"IsLockedByOthers\">\n"
+				       "      <arg name=\"interface_name\" direction=\"in\" type=\"s\"/>\n"
+				       "      <arg name=\"whether_it_is_locked_by_others\" direction=\"out\" type=\"b\"/>\n"
+				       "    </method>\n"
 
 				       "    <method name=\"StringListAppend\">\n"
 				       "      <arg name=\"key\" direction=\"in\" type=\"s\"/>\n"
@@ -3860,6 +4069,17 @@ do_introspect (DBusConnection  *connecti
 				       "      <arg name=\"cond_details\" type=\"s\"/>\n"
 				       "    </signal>\n"
 
+				       "    <signal name=\"InterfaceLockAcquired\">\n"
+				       "      <arg name=\"interface_name\" type=\"s\"/>\n"
+				       "      <arg name=\"lock_holder\" type=\"s\"/>\n"
+				       "      <arg name=\"num_locks\" type=\"i\"/>\n"
+				       "    </signal>\n"
+				       "    <signal name=\"InterfaceLockReleased\">\n"
+				       "      <arg name=\"interface_name\" type=\"s\"/>\n"
+				       "      <arg name=\"lock_holder\" type=\"s\"/>\n"
+				       "      <arg name=\"num_locks\" type=\"i\"/>\n"
+				       "    </signal>\n"
+
 				       "  </interface>\n");
 			HalDeviceStrListIter if_iter;
 
@@ -4085,7 +4305,10 @@ hald_dbus_filter_handle_methods (DBusCon
 						"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",
+						"IsLockedByOthers")) {
+		return device_is_locked_by_others (connection, message, local_interface);
 	} else if (dbus_message_is_method_call (message,
 					      "org.freedesktop.Hal.Device",
 					      "GetAllProperties")) {
@@ -4249,7 +4472,7 @@ hald_dbus_filter_handle_methods (DBusCon
 
                 /* bypass security checks on direct connections */
                 if (!local_interface) {
-                        if (!access_check_caller_have_access_to_device (ci_tracker, d, caller)) {
+                        if (!access_check_caller_have_access_to_device (ci_tracker, d, NULL, caller)) {
                                 HAL_INFO (("Caller '%s' does not have access to device '%s'", caller, udi));
                                 /* TODO: need to fix up reason */
                                 raise_permission_denied (connection, message, "Not in active session");
@@ -4366,13 +4589,6 @@ DBusHandlerResult
 hald_dbus_filter_function (DBusConnection * connection,
 			   DBusMessage * message, void *user_data)
 {
-#ifdef HAVE_CONKIT
-	/* TODO: only push the appropriate messages to the tracker; see ck-tracker.h */
-	if (ck_tracker != NULL) {
-		ck_tracker_process_system_bus_message (ck_tracker, message);
-	}
-#endif
-
 	if (dbus_message_is_signal (message, DBUS_INTERFACE_LOCAL, "Disconnected") &&
 	    strcmp (dbus_message_get_path (message), DBUS_PATH_LOCAL) == 0) {
 
@@ -4439,10 +4655,26 @@ hald_dbus_filter_function (DBusConnectio
 		HAL_INFO (("active=%d for session %s", is_active, session_objpath));
 		ci_tracker_active_changed (ci_tracker, session_objpath, is_active);
 #endif /* HAVE_CONKIT */
-	} else 
+	} else {
+
+#ifdef HAVE_CONKIT
+                /* TODO: only push the appropriate messages to the tracker; see ck-tracker.h */
+                if (ck_tracker != NULL) {
+                        ck_tracker_process_system_bus_message (ck_tracker, message);
+                }
+#endif
 		return hald_dbus_filter_handle_methods (connection, message, user_data, FALSE);
+        }
 
 out:
+
+#ifdef HAVE_CONKIT
+	/* TODO: only push the appropriate messages to the tracker; see ck-tracker.h */
+	if (ck_tracker != NULL) {
+		ck_tracker_process_system_bus_message (ck_tracker, message);
+	}
+#endif
+
 	return DBUS_HANDLER_RESULT_HANDLED;
 }
 
@@ -4698,6 +4930,50 @@ hald_dbus_seat_removed (CKTracker *track
 	/* TODO: we could run callouts here... but they wouldn't do anything useful right now */
 }
 
+static gboolean
+validate_lock_for_device (HalDeviceStore *store,
+                          HalDevice      *device,
+                          gpointer        user_data)
+{
+        int n, m;
+        char **holders;
+        char **locked_interfaces;
+
+        locked_interfaces = hal_device_property_dup_strlist_as_strv (device, "info.named_locks");
+        if (locked_interfaces == NULL)
+                goto out;
+
+        for (n = 0; locked_interfaces[n] != NULL; n++) {
+                holders = hal_device_get_lock_holders (device, locked_interfaces[n]);
+                if (holders == NULL)
+                        continue;
+                for (m = 0; holders[m] != NULL; m++) {
+                        HAL_INFO (("Validating lock holder '%s' on interface '%s' on udi '%s'",
+                                   holders[m], locked_interfaces[n], hal_device_get_udi (device)));
+
+                        if (!access_check_caller_have_access_to_device (ci_tracker, device, "lock", holders[m])) {
+                                HAL_INFO (("Kicking out lock holder '%s' on interface '%s' on udi '%s' "
+                                           "as he no longer has access to the device",
+                                           holders[m], locked_interfaces[n], hal_device_get_udi (device)));
+                                hal_device_release_lock (device, locked_interfaces[n], holders[m]);
+                        }
+
+                }
+                g_strfreev (holders);
+        }
+
+        g_strfreev (locked_interfaces);
+out:
+        return TRUE;
+}
+
+static void
+validate_locks (void)
+{
+        hal_device_store_foreach (hald_get_tdl (), validate_lock_for_device, NULL);
+        hal_device_store_foreach (hald_get_gdl (), validate_lock_for_device, NULL);
+}
+
 static void 
 hald_dbus_session_active_changed (CKTracker *tracker, CKSession *session, void *user_data)
 {
@@ -4713,6 +4989,9 @@ hald_dbus_session_active_changed (CKTrac
 		   ck_session_get_id (session),
 		   ck_session_is_active (session) ? "ACTIVE" : "INACTIVE"));
 
+        /* revalidate all locks (to remove locks from callers in that session who no longer has access to devices */
+        validate_locks ();
+
 	d = hal_device_store_find (hald_get_gdl (), "/org/freedesktop/Hal/devices/computer");
 	if (d == NULL) {
 		d = hal_device_store_find (hald_get_tdl (), "/org/freedesktop/Hal/devices/computer");
diff --git a/hald/hald_dbus.h b/hald/hald_dbus.h
index d7f9acc..4a5deb1 100644
--- a/hald/hald_dbus.h
+++ b/hald/hald_dbus.h
@@ -83,6 +83,12 @@ void manager_send_signal_device_removed 
 void manager_send_signal_new_capability (HalDevice *device,
 					 const char *capability);
 
+void manager_send_signal_interface_lock_acquired (const char *interface_name, const char *sender);
+void manager_send_signal_interface_lock_released (const char *interface_name, const char *sender);
+
+void device_send_signal_interface_lock_acquired (HalDevice *device, const char *interface_name, const char *sender);
+void device_send_signal_interface_lock_released (HalDevice *device, const char *interface_name, const char *sender);
+
 void device_send_signal_property_modified (HalDevice *device,
 					   const char *key,
 					   dbus_bool_t removed,
diff --git a/hald/hald_marshal.list b/hald/hald_marshal.list
index 8e3863a..14dd6f8 100644
--- a/hald/hald_marshal.list
+++ b/hald/hald_marshal.list
@@ -1,3 +1,5 @@
+VOID:OBJECT,STRING,STRING
+VOID:STRING,STRING
 VOID:STRING,BOOL,BOOL
 VOID:STRING
 VOID:OBJECT,BOOL
diff --git a/libhal/libhal.c b/libhal/libhal.c
index b5f3c1e..8e76032 100644
--- a/libhal/libhal.c
+++ b/libhal/libhal.c
@@ -245,6 +245,18 @@ struct LibHalContext_s {
 
 	/** A non-continous event on the device occured  */
 	LibHalDeviceCondition device_condition;
+        
+        /** A global interface lock is acquired  */
+        LibHalGlobalInterfaceLockAcquired global_interface_lock_acquired;
+
+        /** A global interface lock is released  */
+        LibHalGlobalInterfaceLockReleased global_interface_lock_released;
+
+        /** An interface lock is acquired  */
+        LibHalInterfaceLockAcquired interface_lock_acquired;
+
+        /** An interface lock is released  */
+        LibHalInterfaceLockReleased interface_lock_released;
 
 	void *user_data;                      /**< User data */
 };
@@ -512,10 +524,11 @@ oom:
 	return NULL;
 }
 
-/* libhal_property_set_sort:
+/**
+ * libhal_property_set_sort:
  * @set: property-set to sort
  *
- * sort all properties according to property name 
+ * Sort all properties according to property name.
  */
 void 
 libhal_property_set_sort (LibHalPropertySet *set)
@@ -824,6 +837,38 @@ filter_func (DBusConnection * connection
 			LIBHAL_FREE_DBUS_ERROR(&error);
 		}
 		return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+	} else if (dbus_message_is_signal (message, "org.freedesktop.Hal.Manager","GlobalInterfaceLockAcquired")) {
+		char *lock_name;
+		char *lock_owner;
+                int num_locks;
+		if (dbus_message_get_args (message, &error,
+					   DBUS_TYPE_STRING, &lock_name,
+					   DBUS_TYPE_STRING, &lock_owner,
+					   DBUS_TYPE_INT32, &num_locks,
+					   DBUS_TYPE_INVALID)) {
+			if (ctx->global_interface_lock_acquired != NULL) {
+				ctx->global_interface_lock_acquired (ctx, lock_name, lock_owner, num_locks);
+			}
+		} else {
+			LIBHAL_FREE_DBUS_ERROR(&error);
+		}
+		return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+	} else if (dbus_message_is_signal (message, "org.freedesktop.Hal.Manager","GlobalInterfaceLockReleased")) {
+		char *lock_name;
+		char *lock_owner;
+                int num_locks;
+		if (dbus_message_get_args (message, &error,
+					   DBUS_TYPE_STRING, &lock_name,
+					   DBUS_TYPE_STRING, &lock_owner,
+					   DBUS_TYPE_INT32, &num_locks,
+					   DBUS_TYPE_INVALID)) {
+			if (ctx->global_interface_lock_released != NULL) {
+				ctx->global_interface_lock_released (ctx, lock_name, lock_owner, num_locks);
+			}
+		} else {
+			LIBHAL_FREE_DBUS_ERROR(&error);
+		}
+		return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
 	} else if (dbus_message_is_signal (message, "org.freedesktop.Hal.Device", "Condition")) {
 		char *condition_name;
 		char *condition_detail;
@@ -838,6 +883,38 @@ filter_func (DBusConnection * connection
 			LIBHAL_FREE_DBUS_ERROR(&error);
 		}
 		return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+	} else if (dbus_message_is_signal (message, "org.freedesktop.Hal.Device","InterfaceLockAcquired")) {
+		char *lock_name;
+		char *lock_owner;
+                int num_locks;
+		if (dbus_message_get_args (message, &error,
+					   DBUS_TYPE_STRING, &lock_name,
+					   DBUS_TYPE_STRING, &lock_owner,
+					   DBUS_TYPE_INT32, &num_locks,
+					   DBUS_TYPE_INVALID)) {
+			if (ctx->interface_lock_acquired != NULL) {
+				ctx->interface_lock_acquired (ctx, object_path, lock_name, lock_owner, num_locks);
+			}
+		} else {
+			LIBHAL_FREE_DBUS_ERROR(&error);
+		}
+		return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+	} else if (dbus_message_is_signal (message, "org.freedesktop.Hal.Device","InterfaceLockReleased")) {
+		char *lock_name;
+		char *lock_owner;
+                int num_locks;
+		if (dbus_message_get_args (message, &error,
+					   DBUS_TYPE_STRING, &lock_name,
+					   DBUS_TYPE_STRING, &lock_owner,
+					   DBUS_TYPE_INT32, &num_locks,
+					   DBUS_TYPE_INVALID)) {
+			if (ctx->interface_lock_released != NULL) {
+				ctx->interface_lock_released (ctx, object_path, lock_name, lock_owner, num_locks);
+			}
+		} else {
+			LIBHAL_FREE_DBUS_ERROR(&error);
+		}
+		return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
 	} else if (dbus_message_is_signal (message, "org.freedesktop.Hal.Device", "PropertyModified")) {
 		if (ctx->device_property_modified != NULL) {
 			int i;
@@ -3901,7 +3978,7 @@ out:
 }
 
 /**
- * libhal_device_set_property_strlist:
+ * libhal_changeset_set_property_strlist:
  * @changeset: the changeset
  * @key: key of property 
  * @value: the value to set - NULL terminated array of strings
@@ -4130,6 +4207,18 @@ libhal_device_free_changeset (LibHalChan
 }
 
 
+/**
+ * libhal_device_acquire_interface_lock:
+ * @ctx: the context for the connection to hald
+ * @udi: the Unique id of device
+ * @interface: the intername name to lock
+ * @exclusive: whether the lock should be exclusive
+ * @error: pointer to an initialized dbus error object for returning errors
+ * 
+ * Releases a lock on an interface for a specific device. 
+ * 
+ * Returns: TRUE iff the lock was acquired
+ **/
 dbus_bool_t 
 libhal_device_acquire_interface_lock (LibHalContext *ctx,
                                       const char *udi,
@@ -4179,6 +4268,17 @@ libhal_device_acquire_interface_lock (Li
 	return TRUE;
 }
 
+/**
+ * libhal_device_release_interface_lock:
+ * @ctx: the context for the connection to hald
+ * @udi: the Unique id of device
+ * @interface: the intername name to unlock
+ * @error: pointer to an initialized dbus error object for returning errors
+ * 
+ * Acquires a lock on an interface for a specific device.
+ * 
+ * Returns: TRUE iff the lock was released.
+ **/
 dbus_bool_t libhal_device_release_interface_lock (LibHalContext *ctx,
                                                   const char *udi,
                                                   const char *interface,
@@ -4225,6 +4325,17 @@ dbus_bool_t libhal_device_release_interf
 	return TRUE;
 }
 
+/**
+ * libhal_acquire_global_interface_lock:
+ * @ctx: the context for the connection to hald
+ * @interface: the intername name to lock
+ * @exclusive: whether the lock should be exclusive
+ * @error: pointer to an initialized dbus error object for returning errors
+ * 
+ * Acquires a global lock on an interface.
+ * 
+ * Returns: TRUE iff the lock was acquired
+ **/
 dbus_bool_t libhal_acquire_global_interface_lock (LibHalContext *ctx,
                                                   const char *interface,
                                                   dbus_bool_t exclusive,
@@ -4271,6 +4382,16 @@ dbus_bool_t libhal_acquire_global_interf
 	return TRUE;
 }
 
+/**
+ * libhal_release_global_interface_lock:
+ * @ctx: the context for the connection to hald
+ * @interface: the intername name to unlock
+ * @error: pointer to an initialized dbus error object for returning errors
+ * 
+ * Releases a global lock on an interface.
+ * 
+ * Returns: TRUE iff the lock was released
+ **/
 dbus_bool_t libhal_release_global_interface_lock (LibHalContext *ctx,
                                                   const char *interface,
                                                   DBusError *error)
@@ -4315,6 +4436,20 @@ dbus_bool_t libhal_release_global_interf
 	return TRUE;
 }
 
+/**
+ * libhal_device_is_caller_locked_out:
+ * @ctx: the context for the connection to hald
+ * @udi: the Unique id of device
+ * @interface: the intername name to check
+ * @caller: the caller to check for
+ * @error: pointer to an initialized dbus error object for returning errors
+ * 
+ * Determines whether a given process on the system message bus is
+ * locked out from an interface on a specific device. Only HAL helpers
+ * are privileged to use this method.
+ * 
+ * Returns: Whether the given caller is locked out
+ **/
 dbus_bool_t
 libhal_device_is_caller_locked_out (LibHalContext *ctx,
                                     const char *udi,
@@ -4375,3 +4510,143 @@ libhal_device_is_caller_locked_out (LibH
 	return value;
 }
 
+
+/** 
+ * libhal_ctx_set_global_interface_lock_acquired:
+ * @ctx: the context for the connection to hald
+ * @callback: the callback
+ *
+ * Set the callback for when a global interface lock is acquired.
+ *
+ * Returns: TRUE if callback was successfully set, FALSE otherwise
+ */
+dbus_bool_t
+libhal_ctx_set_global_interface_lock_acquired (LibHalContext *ctx, LibHalGlobalInterfaceLockAcquired callback)
+{
+	LIBHAL_CHECK_LIBHALCONTEXT (ctx, FALSE);
+	ctx->global_interface_lock_acquired = callback;
+	return TRUE;
+}
+
+/** 
+ * libhal_ctx_set_global_interface_lock_released:
+ * @ctx: the context for the connection to hald
+ * @callback: the callback
+ *
+ * Set the callback for when a global interface lock is released.
+ *
+ * Returns: TRUE if callback was successfully set, FALSE otherwise
+ */
+dbus_bool_t
+libhal_ctx_set_global_interface_lock_released (LibHalContext *ctx, LibHalGlobalInterfaceLockReleased callback)
+{
+	LIBHAL_CHECK_LIBHALCONTEXT (ctx, FALSE);
+	ctx->global_interface_lock_released = callback;
+	return TRUE;
+}
+
+
+/** 
+ * libhal_ctx_set_interface_lock_acquired:
+ * @ctx: the context for the connection to hald
+ * @callback: the callback
+ *
+ * Set the callback for when an interface lock is acquired.
+ *
+ * Returns: TRUE if callback was successfully set, FALSE otherwise
+ */
+dbus_bool_t
+libhal_ctx_set_interface_lock_acquired (LibHalContext *ctx, LibHalInterfaceLockAcquired callback)
+{
+	LIBHAL_CHECK_LIBHALCONTEXT (ctx, FALSE);
+	ctx->interface_lock_acquired = callback;
+	return TRUE;
+}
+
+/** 
+ * libhal_ctx_set_interface_lock_released:
+ * @ctx: the context for the connection to hald
+ * @callback: the callback
+ *
+ * Set the callback for when an interface lock is released.
+ *
+ * Returns: TRUE if callback was successfully set, FALSE otherwise
+ */
+dbus_bool_t
+libhal_ctx_set_interface_lock_released (LibHalContext *ctx, LibHalInterfaceLockReleased callback)
+{
+	LIBHAL_CHECK_LIBHALCONTEXT (ctx, FALSE);
+	ctx->interface_lock_released = callback;
+	return TRUE;
+}
+
+
+
+/**
+ * libhal_device_is_locked_by_others:
+ * @ctx: the context for the connection to hald
+ * @udi: the Unique id of device
+ * @interface: the intername name to check
+ * @error: pointer to an initialized dbus error object for returning errors or NULL
+ * 
+ * Determines whether a determines other processes than the caller holds a lock on the given device.
+ * 
+ * Returns: If another process is holding a lock on the device
+ **/
+dbus_bool_t 
+libhal_device_is_locked_by_others (LibHalContext *ctx,
+                                   const char *udi,
+                                   const char *interface,
+                                   DBusError *error)
+{
+	DBusMessage *message;
+	DBusMessageIter iter;
+	DBusMessage *reply;
+	DBusMessageIter reply_iter;
+        dbus_bool_t value;
+
+	LIBHAL_CHECK_LIBHALCONTEXT(ctx, TRUE);
+	LIBHAL_CHECK_PARAM_VALID(udi, "*udi", TRUE);
+	LIBHAL_CHECK_PARAM_VALID(interface, "*interface", TRUE);
+
+	message = dbus_message_new_method_call ("org.freedesktop.Hal",
+						udi,
+						"org.freedesktop.Hal.Device",
+						"IsLockedByOthers");
+
+	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);
+	
+	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 897787a..015406c 100644
--- a/libhal/libhal.h
+++ b/libhal/libhal.h
@@ -191,6 +191,67 @@ typedef void (*LibHalDeviceCondition) (L
 				       const char *condition_name,
 				       const char *condition_detail);
 
+/** 
+ * LibHalGlobalInterfaceLockAcquired:
+ * @ctx: context for connection to hald
+ * @interface_name: the name of the interface
+ * @lock_owner: what service acquired the lock
+ * @num_locks: number of locks on the interface
+ *
+ * Type for callback when someone acquires a global lock.
+ */
+typedef void (*LibHalGlobalInterfaceLockAcquired) (LibHalContext *ctx,
+                                                   const char *interface_name,
+                                                   const char *lock_owner,
+                                                   int         num_locks);
+
+/** 
+ * LibHalGlobalInterfaceLockReleased:
+ * @ctx: context for connection to hald
+ * @interface_name: the name of the interface
+ * @lock_owner: what service released the lock
+ * @num_locks: number of locks on the interface
+ *
+ * Type for callback when someone releases a global lock.
+ */
+typedef void (*LibHalGlobalInterfaceLockReleased) (LibHalContext *ctx,
+                                                   const char *interface_name,
+                                                   const char *lock_owner,
+                                                   int         num_locks);
+
+/** 
+ * LibHalInterfaceLockAcquired:
+ * @ctx: context for connection to hald
+ * @udi: the Unique Device Id
+ * @interface_name: the name of the interface
+ * @lock_owner: what service acquired the lock
+ * @num_locks: number of locks on the interface
+ *
+ * Type for callback when someone acquires a lock on a device.
+ */
+typedef void (*LibHalInterfaceLockAcquired) (LibHalContext *ctx,
+                                             const char *udi,
+                                             const char *interface_name,
+                                             const char *lock_owner,
+                                             int         num_locks);
+
+/** 
+ * LibHalInterfaceLockReleased:
+ * @ctx: context for connection to hald
+ * @udi: the Unique Device Id
+ * @interface_name: the name of the interface
+ * @lock_owner: what service released the lock
+ * @num_locks: number of locks on the interface
+ *
+ * Type for callback when someone acquires a lock on a device.
+ */
+typedef void (*LibHalInterfaceLockReleased) (LibHalContext *ctx,
+                                             const char *udi,
+                                             const char *interface_name,
+                                             const char *lock_owner,
+                                             int         num_locks);
+
+
 
 /* Create a new context for a connection with hald */
 LibHalContext *libhal_ctx_new                          (void);
@@ -228,6 +289,18 @@ dbus_bool_t    libhal_ctx_set_device_pro
 /* Set the callback for when a device emits a condition */
 dbus_bool_t    libhal_ctx_set_device_condition         (LibHalContext *ctx, LibHalDeviceCondition callback);
 
+/* Set the callback for when a global interface lock is acquired  */
+dbus_bool_t    libhal_ctx_set_global_interface_lock_acquired (LibHalContext *ctx, LibHalGlobalInterfaceLockAcquired callback);
+
+/* Set the callback for when a global interface lock is released  */
+dbus_bool_t    libhal_ctx_set_global_interface_lock_released (LibHalContext *ctx, LibHalGlobalInterfaceLockReleased callback);
+
+/* Set the callback for when an interface lock is acquired  */
+dbus_bool_t    libhal_ctx_set_interface_lock_acquired (LibHalContext *ctx, LibHalInterfaceLockAcquired callback);
+
+/* Set the callback for when an interface lock is released  */
+dbus_bool_t    libhal_ctx_set_interface_lock_released (LibHalContext *ctx, LibHalInterfaceLockReleased callback);
+
 /* Initialize the connection to hald */
 dbus_bool_t    libhal_ctx_init                         (LibHalContext *ctx, DBusError *error);
 
@@ -634,6 +707,12 @@ dbus_bool_t libhal_device_is_caller_lock
                                                 const char *caller,
                                                 DBusError *error);
 
+/* Determines whether a determines other processes than the caller holds a lock on the given device.  */
+dbus_bool_t libhal_device_is_locked_by_others (LibHalContext *ctx,
+                                               const char *udi,
+                                               const char *interface,
+                                               DBusError *error);
+
 
 #if defined(__cplusplus)
 }
diff --git a/tools/hal-lock.c b/tools/hal-lock.c
index cd54f26..f58431e 100644
--- a/tools/hal-lock.c
+++ b/tools/hal-lock.c
@@ -37,6 +37,7 @@
 #include <sys/wait.h>
 #include <signal.h>
 #include <glib.h>
+#include <dbus/dbus-glib-lowlevel.h>
 
 #include "libhal.h"
 
@@ -56,6 +57,7 @@ usage (int argc, char *argv[])
                  "                 --run <program-and-args>\n"
                  "                 [--udi <udi>]\n"
                  "                 [--exclusive]\n"
+                 "                 [--exit-with-lock]"
                  "                 [--help] [--version]\n");
 	fprintf (stderr,
                  "\n"
@@ -64,6 +66,7 @@ usage (int argc, char *argv[])
                  "        --udi            Unique Device Id of device to lock. If\n"
                  "                         ommitted the global lock will be tried\n"
                  "        --exclusive      Whether the lock can be held by others\n"
+                 "        --exit-with-lock Kill the program if the acquired lock is lost\n"
                  "        --version        Show version and exit\n"
                  "        --help           Show this information and exit\n"
                  "\n"
@@ -77,6 +80,35 @@ usage (int argc, char *argv[])
                  "\n");
 }
 
+static char *udi = NULL;
+static char *interface = NULL;
+static const char *unique_name;
+static GPid child_pid;
+
+static void
+guardian (GPid pid, int status, gpointer data)
+{
+        /* exit along with the child */
+        exit (0);
+}
+
+static void
+interface_lock_released (LibHalContext *ctx,
+                         const char *_udi,
+                         const char *_interface,
+                         const char *lock_owner,
+                         int num_locks)
+{
+
+
+        if (strcmp (udi, _udi) == 0 &&
+            strcmp (interface, _interface) == 0 &&
+            strcmp (lock_owner, unique_name) == 0) {
+                fprintf (stderr, "Lost the lock; killing child...\n");
+                kill (child_pid, SIGTERM);
+        }
+}
+
 /** 
  *  main:
  *  @argc:                Number of arguments given to program
@@ -89,16 +121,17 @@ usage (int argc, char *argv[])
 int
 main (int argc, char *argv[])
 {
-	char *udi = NULL;
-	char *interface = NULL;
         char *run = NULL;
         dbus_bool_t is_version = FALSE;
         dbus_bool_t exclusive = FALSE;
         dbus_bool_t got_lock = FALSE;
+        dbus_bool_t exit_with_lock = FALSE;
+        DBusConnection *con;
 	DBusError error;
         LibHalContext *hal_ctx;
         int ret;
         GError *g_error = NULL;
+	GMainLoop *loop;
 
         ret = 1;
 
@@ -116,6 +149,7 @@ main (int argc, char *argv[])
 			{"interface", 1, NULL, 0},
 			{"run", 1, NULL, 0},
 			{"exclusive", 0, NULL, 0},
+			{"exit-with-lock", 0, NULL, 0},
 			{"version", 0, NULL, 0},
 			{"help", 0, NULL, 0},
 			{NULL, 0, NULL, 0}
@@ -143,6 +177,8 @@ main (int argc, char *argv[])
                                 exclusive = TRUE;
 			} else if (strcmp (opt, "interface") == 0) {
 				interface = strdup (optarg);
+			} else if (strcmp (opt, "exit-with-lock") == 0) {
+				exit_with_lock = TRUE;
 			}
 			break;
 
@@ -164,17 +200,41 @@ main (int argc, char *argv[])
                 goto out;
 	}
 
+        if (exit_with_lock && udi == NULL) {
+                fprintf (stderr, "--exit-with-lock requires UDI to be given.\n");
+                usage (argc, argv);
+                goto out;
+        }
+
+	if (exit_with_lock)
+		loop = g_main_loop_new (NULL, FALSE);
+	else
+		loop = NULL;
+
 
         dbus_error_init (&error);	
         if ((hal_ctx = libhal_ctx_new ()) == NULL) {
                 fprintf (stderr, "error: libhal_ctx_new\n");
                 goto out;
         }
-        if (!libhal_ctx_set_dbus_connection (hal_ctx, dbus_bus_get (DBUS_BUS_SYSTEM, &error))) {
-                fprintf (stderr, "error: libhal_ctx_set_dbus_connection: %s: %s\n", error.name, error.message);
+        con = dbus_bus_get (DBUS_BUS_SYSTEM, &error);
+        if (con == NULL) {
+                fprintf (stderr, "error: dbus_bus_get: %s: %s\n", error.name, error.message);
                 LIBHAL_FREE_DBUS_ERROR (&error);
                 goto out;
         }
+        if (!libhal_ctx_set_dbus_connection (hal_ctx, con)) {
+                fprintf (stderr, "error: libhal_ctx_set_dbus_connection\n");
+                goto out;
+        }
+
+        if (exit_with_lock) {
+                unique_name = dbus_bus_get_unique_name (con);
+                fprintf (stderr, "unique name is '%s'\n", unique_name);
+                libhal_ctx_set_interface_lock_released (hal_ctx, interface_lock_released);
+		dbus_connection_setup_with_g_main (con, NULL);
+        }
+
         if (!libhal_ctx_init (hal_ctx, &error)) {
                 if (dbus_error_is_set(&error)) {
                         fprintf (stderr, "error: libhal_ctx_init: %s: %s\n", error.name, error.message);
@@ -184,6 +244,9 @@ main (int argc, char *argv[])
                          "Normally this means the HAL daemon (hald) is not running or not ready.\n");
                 goto out;
         }
+
+        if (exit_with_lock)
+                libhal_device_add_property_watch (hal_ctx, udi, &error);
         
         if (udi != NULL) {
                 got_lock = libhal_device_acquire_interface_lock (hal_ctx,
@@ -213,16 +276,43 @@ main (int argc, char *argv[])
                 goto out;
         }
 
-        /* now run the program while holding the lock */
-        if (!g_spawn_command_line_sync (run,
-                                        NULL,
-                                        NULL,
-                                        NULL,
-                                        &g_error)) {
+        if (exit_with_lock) {
+                int _argc;
+                char **_argv;
+
+                if (!g_shell_parse_argv (run, &_argc, &_argv, &g_error)) {
+                        fprintf (stderr, "error: g_shell_parse_argv: %s\n", g_error->message);
+                        g_error_free (g_error);
+                        goto out;
+                }
                 
-                fprintf (stderr, "error: g_spawn_command_line_sync: %s\n", g_error->message);
-                g_error_free (g_error);
-                goto out;
+                if (!g_spawn_async (NULL,
+                                    _argv,
+                                    NULL,
+                                    G_SPAWN_SEARCH_PATH | G_SPAWN_DO_NOT_REAP_CHILD,
+                                    NULL,
+                                    NULL,
+                                    &child_pid,
+                                    &g_error)) {
+                        fprintf (stderr, "error: g_spawn_command_line_async: %s\n", g_error->message);
+                        g_error_free (g_error);
+                        goto out;
+                }
+                g_child_watch_add (child_pid, guardian, NULL);
+		g_main_loop_run (loop);
+
+        } else {
+                /* now run the program while holding the lock */
+                if (!g_spawn_command_line_sync (run,
+                                                NULL,
+                                                NULL,
+                                                NULL,
+                                                &g_error)) {
+                        
+                        fprintf (stderr, "error: g_spawn_command_line_sync: %s\n", g_error->message);
+                        g_error_free (g_error);
+                        goto out;
+                }
         }
 
         ret = 0;
diff --git a/tools/lshal.c b/tools/lshal.c
index 0951657..7e2bf82 100644
--- a/tools/lshal.c
+++ b/tools/lshal.c
@@ -581,6 +581,77 @@ device_condition (LibHalContext *ctx,
 	}
 }
 
+static void
+do_interface_lock (LibHalContext *ctx,
+                   dbus_bool_t acquired,
+                   dbus_bool_t global,
+                   const char *udi,
+                   const char *interface_name,
+                   const char *lock_owner,
+                   int num_locks)
+{
+	if (show_device && strcmp(show_device, udi))
+		return;
+
+	if (long_list) {
+                if (global)
+                        printf ("*** %s: lshal: global_interface_lock_%s\n", get_time (), acquired ? "acquired" : "released");
+                else
+                        printf ("*** %s: lshal: interface_lock_%s, udi=%s\n", get_time (), acquired ? "acquired" : "released", udi);
+		printf ("           interface_name=%s\n", interface_name);
+		printf ("           lock_owner=%s\n", lock_owner);
+		printf ("           num_locks=%d\n", num_locks);
+		printf ("\n");
+	} else {
+                if (global)
+                        printf ("%s: global_interface_lock_%s %s by %s (%d lockers)\n", get_time (),
+                                acquired ? "acquired" : "released",
+                                interface_name, lock_owner, num_locks);
+                else
+                        printf ("%s: %s interface_lock_%s %s by %s (%d lockers)\n", get_time (), short_name (udi),
+                                acquired ? "acquired" : "released",
+                                interface_name, lock_owner, num_locks);
+	}
+}
+
+static void
+global_interface_lock_acquired (LibHalContext *ctx,
+                                const char *interface_name,
+                                const char *lock_owner,
+                                int num_locks)
+{
+        do_interface_lock (ctx, TRUE, TRUE, NULL, interface_name, lock_owner, num_locks);
+}
+
+static void
+global_interface_lock_released (LibHalContext *ctx,
+                                const char *interface_name,
+                                const char *lock_owner,
+                                int num_locks)
+{
+        do_interface_lock (ctx, FALSE, TRUE, NULL, interface_name, lock_owner, num_locks);
+}
+
+static void
+interface_lock_acquired (LibHalContext *ctx,
+                         const char *udi,
+                         const char *interface_name,
+                         const char *lock_owner,
+                         int num_locks)
+{
+        do_interface_lock (ctx, TRUE, FALSE, udi, interface_name, lock_owner, num_locks);
+}
+
+static void
+interface_lock_released (LibHalContext *ctx,
+                         const char *udi,
+                         const char *interface_name,
+                         const char *lock_owner,
+                         int num_locks)
+{
+        do_interface_lock (ctx, FALSE, FALSE, udi, interface_name, lock_owner, num_locks);
+}
+
 
 /** 
  *  usage:
@@ -750,6 +821,10 @@ main (int argc, char *argv[])
 	libhal_ctx_set_device_lost_capability (hal_ctx, device_lost_capability);
 	libhal_ctx_set_device_property_modified (hal_ctx, property_modified);
 	libhal_ctx_set_device_condition (hal_ctx, device_condition);
+	libhal_ctx_set_global_interface_lock_acquired (hal_ctx, global_interface_lock_acquired);
+	libhal_ctx_set_global_interface_lock_released (hal_ctx, global_interface_lock_released);
+	libhal_ctx_set_interface_lock_acquired (hal_ctx, interface_lock_acquired);
+	libhal_ctx_set_interface_lock_released (hal_ctx, interface_lock_released);
 
 	if (show_device)
 		dump_device (show_device);
diff-tree 975a4defadb47ec8d3d6156f3eb20f6a67ec3009 (from 154c2bac8ec500eba9a4f15a9e5ea244be1c9e5d)
Author: David Zeuthen <davidz at redhat.com>
Date:   Sat Mar 31 18:07:59 2007 -0400

    change locking semantics a bit and add guidelines for SystemPowerManagement
    
    The change in locking semantics is that a lock-holder can now access a
    device even if other processes have locks on the device.

diff --git a/doc/spec/hal-spec-interfaces.xml b/doc/spec/hal-spec-interfaces.xml
index fe0c112..9858c71 100644
--- a/doc/spec/hal-spec-interfaces.xml
+++ b/doc/spec/hal-spec-interfaces.xml
@@ -653,6 +653,12 @@ $ dbus-send --system --print-reply --des
     <para>
       This interface does not emit any signals.
     </para>
+
+    <para>
+      Implementors of power management daemons should make sure that
+      their software respects the locking guidelines described in
+      <xref linkend="interfaces"/>.
+    </para>
   </sect1>
 
   <sect1 id="interface-cpufreq">
diff --git a/doc/spec/hal-spec-locking.xml b/doc/spec/hal-spec-locking.xml
index 443ac48..df62f0a 100644
--- a/doc/spec/hal-spec-locking.xml
+++ b/doc/spec/hal-spec-locking.xml
@@ -5,8 +5,8 @@
   <title>Locking</title>
 
   <para>
-    As HAL enables programs in a desktop session to automatically
-    enforce the policy of the users choice, unexpected things can
+    As HAL is a mechanism that enables programs in a desktop session
+    to enforce the policy of the users choice, unexpected things can
     happen. For example, if the user is in the middle of partitioning
     a disk drive, it is desirable to keep the desktop from mounting
     partitions that have not yet been prepared with a suitable file
@@ -72,37 +72,42 @@
       also be obtained exclusively if the caller so desires. Unlike
       per-device interface locking, it is not checked at locking time
       whether the locker have access to a given device; instead
-      checking is done when callers attempt to access the interface.
+      checking is done when callers attempt to access the
+      interface.
     </para>
 
     <para>
       The algorithm used for determining if a caller is locked out is
-      shown below. A caller is locked out if:
+      shown below. A caller A is locked out of an interface IFACE on a
+      device object DEVICE if, and only if,
     </para>
 
     <orderedlist>
       <listitem> 
 	<para>
-          another caller is holding a lock on the interface on the
-          device (non-withstanding that the caller to check for holds
-          the lock himself).
+          Another caller B is holding a lock on the interface IFACE on
+          DEVICE and A don't have either a global lock on IFACE or a
+          lock on IFACE on DEVICE; or
         </para>
       </listitem>
       <listitem>
         <para>
-          Another caller is holding the global lock for the interface
-          and that other caller has access to the device in question.
+          Another caller B is holding the global lock on the
+          interface IFACE and B has access to DEVICE and and A don't
+          have either a global lock on IFACE or a lock on IFACE on
+          DEVICE.
         </para>
       </listitem>
     </orderedlist>
 
     <para>
-      In other words, a client Foo can grab a global lock, 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 an 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.
+      In other words, a caller A can grab a global lock, but that
+      doesn't mean A can lock other clients out of devices that A
+      doesn't have access to. Specifically a caller is never locked
+      out if he has locked an interface either globally or on the
+      device in question. However, if two clients have a lock on a
+      device, then both can access it. To ensure that everyone is
+      locked out, a caller needs to use an exclusive lock.
     </para>
 
     <para>
@@ -135,21 +140,148 @@
           block devices directly (and pokes the kernel to reload the
           partitioning table) should lock out automounters by either
           a) obtaining
-          the <literal>org.freedesktop.Hal.Device.Storage</literal> on
-          each drive being processed; or b) obtaintaing the global
+          the <literal>org.freedesktop.Hal.Device.Storage</literal>
+          lock on each drive being processed; or b) obtaintaing the
+          global
           <literal>org.freedesktop.Hal.Device.Storage</literal>
           lock. This includes programs like fdisk, gparted, parted and
           operating system installers. See also
-          <xref linkend="interface-device-volume"/>.
+          <xref linkend="interface-device-volume"/> and
+          the <literal>hal-lock</literal>(1) program and manual page.
         </para>
       </listitem>
 
       <listitem><para>
-          <emphasis>System-wide Power Management</emphasis>
+          <emphasis>Power Management</emphasis>
         </para>
         <para>
-          (this guideline is not finalized!)
+          Typically, a desktop session includes a session-wide power
+          management daemon that enforces the policy of the users
+          choice, e.g. whether the system should suspend to ram on lid
+          close, whether to hibernate the system after the user being
+          idle for 30 minutes and so on. In a multi-user setup (both
+          fast user switching and multi-seat), this can break in
+          various interesting ways unless the power management daemons
+          cooperate. Also, there may be software running at the system
+          level who will want to inhibit a desktop session power
+          management daemon from suspending / shutting down.
         </para>
+
+        <itemizedlist>
+          <listitem>
+            <para>
+              System-level software that do not wish to be interrupted
+              by the effect of someone calling into the
+              <literal>org.freedesktop.Hal.Device.SystemPowerManagement</literal>
+              interface MUST hold the
+              <literal>org.freedesktop.Hal.Device.SystemPowerManagement</literal>
+              lock non-exclusively on the root computer device
+              object. For example, the YUM software updater should
+              hold the lock when doing an RPM transaction.
+            </para>
+          </listitem>
+        </itemizedlist>
+
+        <para>
+          In addition, any power management session daemon instance
+        </para>
+
+        <itemizedlist>
+          <listitem>
+            <para>
+              ... MUST hold the <literal>org.freedesktop.Hal.Device.SystemPowerManagement</literal> lock
+              non-exclusively on the root computer device object
+              unless it is prepared to call into this interface
+              itself. This typically means that the PM daemon instance
+              simply acquires the lock on start up and releases it
+              just before it calls into
+              the <literal>org.freedesktop.Hal.Device.SystemPowerManagement</literal>
+              interface. In other words, the PM daemon instance needs
+              to hold the lock exactly when it doesn't want other PM
+              daemon instances to call into
+              the <literal>org.freedesktop.Hal.Device.SystemPowerManagement</literal> interface.
+              This means that if the user have configured the PM
+              daemon instance to go to sleep after 30 minutes of
+              inactivity, the lock should be released then.
+            </para>
+          </listitem>
+
+          <listitem>
+            <para>
+              ... MUST not hold the lock when the session is inactive
+              (fast user switching) UNLESS an application in the
+              session have explicitly called Inhibit() on
+              the <literal>org.freedesktop.PowerManagement</literal>
+              D-Bus session bus interface of the PM daemon.
+            </para>
+          </listitem>
+
+          <listitem>
+            <para>
+              ... MUST check that no other process is holding the lock
+              before calling into
+              the <literal>org.freedesktop.Hal.Device.SystemPowerManagement</literal>
+              interface. If another process is holding the lock, it
+              means that either 1) another session is not prepared to
+              call into
+              the <literal>org.freedesktop.Hal.Device.SystemPowerManagement</literal>
+              interface; OR 2) some system-level software is holding
+              the lock. The PM daemon instance MUST respect this by
+              not calling into
+              the <literal>org.freedesktop.Hal.Device.SystemPowerManagement</literal>
+              interface itself.
+            </para>
+          </listitem>
+
+        </itemizedlist>
+        <para>
+          However, any Power management daemon instance
+        </para>
+        <itemizedlist>
+
+          <listitem>
+            <para>
+              ... MAY prompt the user, if applicable, to ask if she
+              still wants to perform the requested action (e.g. call
+              into
+              the <literal>org.freedesktop.Hal.Device.SystemPowerManagement</literal>
+              interface) despite the fact that another process
+              (possibly from another user) is indicating that it does
+              not want the system to e.g. suspend. Only if the user
+              agrees, the power management instance should call into
+              the <literal>org.freedesktop.Hal.Device.SystemPowerManagement</literal>
+              interface. Typically, it's only useful to prompt the
+              user with such questions if the request to call into
+              the <literal>org.freedesktop.Hal.Device.SystemPowerManagement</literal>
+              interface originates from user input, e.g. either a
+              hotkey, the user clicking a suspend button in the UI or
+              an application invoking the <literal>Suspend()</literal> method on the 
+              <literal>org.freedesktop.PowerManagement</literal> D-Bus
+              session interface of the PM daemon.
+            </para>
+          </listitem>
+
+          <listitem>
+            <para>
+              ... MAY ignore that other processes are holding the lock
+              and call into
+              the <literal>org.freedesktop.Hal.Device.SystemPowerManagement</literal>
+              interface anyway, but ONLY if if the request to call
+              into
+              the <literal>org.freedesktop.Hal.Device.SystemPowerManagement</literal>
+              interface originated from e.g. lid close, critically low
+              battery or other similar conditions.
+            </para>
+          </listitem>
+
+          <listitem>
+            <para>
+              ... MAY still call <literal>SetPowerSave()</literal> on
+              the <literal>org.freedesktop.Hal.Device.SystemPowerManagement</literal>
+              interface even if other processes are holding the lock.
+            </para>
+          </listitem>
+        </itemizedlist>
       </listitem>
       
     </itemizedlist>
diff --git a/examples/interface-locking-test.py b/examples/interface-locking-test.py
index 3b98cd1..ca60a91 100755
--- a/examples/interface-locking-test.py
+++ b/examples/interface-locking-test.py
@@ -17,9 +17,16 @@ device = dbus.Interface(bus.get_object("
                                          "/org/freedesktop/Hal/devices/volume_uuid_2232_1F11"),
                           "org.freedesktop.Hal.Device")
 
-manager.AcquireGlobalInterfaceLock("org.freedesktop.Hal.Device.Storage", True)
-#device.AcquireInterfaceLock("org.freedesktop.Hal.Device.Volume", True)
+device_volume = dbus.Interface(bus.get_object("org.freedesktop.Hal",
+                                              "/org/freedesktop/Hal/devices/volume_uuid_2232_1F11"),
+                               "org.freedesktop.Hal.Device.Volume")
+
+#manager.AcquireGlobalInterfaceLock("org.freedesktop.Hal.Device.Storage", True)
+#time.sleep(2)
+#manager.ReleaseGlobalInterfaceLock("org.freedesktop.Hal.Device.Storage")
+
+device.AcquireInterfaceLock("org.freedesktop.Hal.Device.Volume", False)
+device_volume.Mount("", "", [])
 time.sleep(2)
-manager.ReleaseGlobalInterfaceLock("org.freedesktop.Hal.Device.Storage")
-#device.ReleaseInterfaceLock("org.freedesktop.Hal.Device.Volume")
+device.ReleaseInterfaceLock("org.freedesktop.Hal.Device.Volume")
 
diff --git a/hald/access-check.c b/hald/access-check.c
index d96ddaa..6f0542b 100644
--- a/hald/access-check.c
+++ b/hald/access-check.c
@@ -180,24 +180,24 @@ out:
  * @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:
+ * interface on a given device. A caller A is locked out of an
+ * interface IFACE on a device object DEVICE if, and only if 
  *
- * 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.
+ * 1. Another caller B is holding a lock on the interface IFACE on
+ *    DEVICE and A don't have either a global lock on IFACE or a lock
+ *    on IFACE on DEVICE; or 
  *
- * 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.
+ * 2. Another caller B is holding the global lock on the interface
+ *    IFACE and B has access to DEVICE and and A don't have either a
+ *    global lock on IFACE or a lock on IFACE on DEVICE.
  *
- *    (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.
+ * In other words, a caller A can grab a global lock, but that doesn't
+ * mean A can lock other clients out of devices that A doesn't have
+ * access to. Specifically a caller is never locked out if he has
+ * locked an interface either globally or on the device in
+ * question. However, if two clients have a lock on a device, then
+ * both can access it. To ensure that everyone else is locked out, a
+ * caller needs to use an exclusive lock.
  * 
  * Returns: TRUE iff the caller is locked out
  */
@@ -213,6 +213,8 @@ access_check_caller_locked_out (CITracke
         char **holders;
         char **global_holders;
         HalDevice *computer;
+        gboolean is_locked;
+        gboolean is_locked_by_self;
 
         global_lock_name = NULL;
         holders = NULL;
@@ -234,16 +236,15 @@ access_check_caller_locked_out (CITracke
          * assumed to have access to the device since they got to hold
          * the lock in the first place. 
          */
+        is_locked = FALSE;
+        is_locked_by_self = FALSE;
         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]));
+                        is_locked = TRUE;
+                        if (strcmp (holders[n], caller_unique_sysbus_name) == 0) {
+                                is_locked_by_self = TRUE;
+                                /* this is good enough; we are holding the lock ourselves */
+                                ret = FALSE;
                                 goto out;
                         }
                 }
@@ -251,27 +252,37 @@ access_check_caller_locked_out (CITracke
 
         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 (strcmp (global_holders[n], caller_unique_sysbus_name) == 0) {
+                                /* we are holding the global lock... */
                                 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]));
+                                        /* only applies if the caller can access the device... */
+                                        is_locked_by_self = TRUE;
+                                        /* this is good enough; we are holding the lock ourselves */
+                                        ret = FALSE;
                                         goto out;
                                 }
+                        } else {
+                                /* 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. Mark as locked. */
+                                        is_locked = TRUE;
+                                }
                         }
                 }
         }
 
+        if (is_locked && !is_locked_by_self) {
+                /* Yup, there's someone else... can't do it Sally */
+                HAL_INFO (("Caller '%s' is locked out of interface '%s' on device '%s' "
+                           "because someone else got a lock on the interface on the device",
+                           caller_unique_sysbus_name,
+                           interface_name,
+                           hal_device_get_udi (device)));
+                goto out;
+        }
+
         /* done all the checks so we're not locked out */
         ret = FALSE;
 


More information about the hal-commit mailing list