hal: Branch 'origin' - 4 commits

David Zeuthen david at kemper.freedesktop.org
Mon Feb 19 00:29:29 PST 2007


 fdi/policy/10osvendor/20-acl-management.fdi |   82 +-
 hald/ck-tracker.c                           |   85 +-
 hald/ck-tracker.h                           |    4 
 hald/create_cache.c                         |    2 
 hald/device_info.c                          |   54 +
 hald/hald_dbus.c                            |   91 ++
 hald/hald_runner.c                          |   12 
 hald/rule.h                                 |    1 
 tools/Makefile.am                           |   11 
 tools/hal-acl-add.c                         |   97 --
 tools/hal-acl-remove.c                      |   16 
 tools/hal-acl-tool.c                        | 1051 ++++++++++++++++++++++++++++
 tools/hal-storage-shared.c                  |    1 
 13 files changed, 1351 insertions(+), 156 deletions(-)

New commits:
diff-tree e94e5d6bbe489a1269d0e2a5152a1a1f167c7674 (from 04ff0ba613b2dc3d9c3ffabac2399d992a8d7363)
Author: David Zeuthen <davidz at redhat.com>
Date:   Sun Feb 18 17:44:23 2007 -0500

    add ACL management for remaining devices recognized by HAL
    
    Ugh, had to introduce a new matching directive to make this work
    
     <!-- usb cameras -->
     <match key="info.capabilities" contains="usbraw">
      <match key="info.capabilities" sibling_contains="camera">
       <append key="info.capabilities" type="strlist">access_control</append>
       <merge key="access_control.file" type="copy_property">usbraw.device</merge>
      </match>
     </match>
    
    So right now this works because of these two assumptions
    
     - for coldplug we get the usbraw interface *after* the usb devices and
       usb interfaces - this is probably because we sort the sysfs paths -
       see hald/linux/coldplug.c:coldplug_synthesize_events()
    
     - for hotplug we seem to be getting the usbraw interfaces as the last
       one too. This may just be pure luck.
    
    The whole way that USB device nodes are handed out by the Linux kernel
    is, sorta-kinda, on crack and makes it hard for user space.
    
    Really, the usbraw class device needs to go and we need to get the
    device file for the entire USB device along with the first usb_device
    uevent. Interfaces too could have separate device files and they too
    could give you a device file.
    
    The latter would be nice for multi-function USB devices, such as some
    multi-function printer/scanner/thumbdrive combos from HP (these exist,
    my mom got one where it's one USB device and sevaral USB interfaces) -
    this would enable us to give access to the scanning USB interface to
    unprivileged users without, at the same time, give them raw access to
    the USB storage device interface.
    
    I'll poke Kay and Greg KH about this. For now, it seems to work
    nicely. End of rant.

diff --git a/fdi/policy/10osvendor/20-acl-management.fdi b/fdi/policy/10osvendor/20-acl-management.fdi
index 8033521..41b9ebd 100644
--- a/fdi/policy/10osvendor/20-acl-management.fdi
+++ b/fdi/policy/10osvendor/20-acl-management.fdi
@@ -3,21 +3,61 @@
 <deviceinfo version="0.2">
   <device>
 
-    <!-- allow local sessions to access ALSA device files -->
+
+    <!-- classification of devices where access can be controlled goes here -->
+
+    <!-- sound card (ALSA) -->
     <match key="info.capabilities" contains="alsa">
       <append key="info.capabilities" type="strlist">access_control</append>
-      <merge key="access_control.grant_local_session" type="bool">true</merge>
       <merge key="access_control.file" type="copy_property">alsa.device_file</merge>
     </match>
 
-    <!-- allow local sessions to access OSS device files -->
+    <!-- sound card (OSS) -->
     <match key="info.capabilities" contains="oss">
       <append key="info.capabilities" type="strlist">access_control</append>
-      <merge key="access_control.grant_local_session" type="bool">true</merge>
       <merge key="access_control.file" type="copy_property">oss.device_file</merge>
     </match>
 
-    <!-- TODO: add more rules for adding/removing ACL's (e.g. webcam, cd drive etc.) -->
+    <!-- video4linux devices -->
+    <match key="info.capabilities" contains="video4linux">
+      <append key="info.capabilities" type="strlist">access_control</append>
+      <merge key="access_control.file" type="copy_property">video4linux.device</merge>
+    </match>
+
+    <!-- optical drives -->
+    <match key="info.capabilities" contains="storage.cdrom">
+      <append key="info.capabilities" type="strlist">access_control</append>
+      <merge key="access_control.file" type="copy_property">block.device</merge>
+    </match>
+
+    <!-- DVB cards -->
+    <match key="info.capabilities" contains="dvb">
+      <append key="info.capabilities" type="strlist">access_control</append>
+      <merge key="access_control.file" type="copy_property">dvb.device</merge>
+    </match>
+
+    <!-- usb cameras -->
+    <match key="info.capabilities" contains="usbraw">
+      <match key="info.capabilities" sibling_contains="camera">
+	<append key="info.capabilities" type="strlist">access_control</append>
+	<merge key="access_control.file" type="copy_property">usbraw.device</merge>
+      </match>
+    </match>
+
+
+    <!-- policy goes here - this can be amended by 3rd party packages,
+         e.g.  the flumotion package may provide a fdi-file that
+         appends the 'flumotion' user to access_control.grant_user for
+         e.g. webcam's or audio devices - see RH bug #140853 for
+         details. -->
+
+    <!-- grant access to local session whether it's active or not -->
+    <match key="info.capabilities" contains="access_control">
+      <merge key="access_control.grant_local_session" type="bool">true</merge>
+    </match>
+
+
+    <!-- enforcement of policy goes here -->
 
     <!-- add / remove ACL's when devices are added and removed -->
     <match key="info.capabilities" contains="access_control">
@@ -26,6 +66,7 @@
     </match>
 
     <match key="info.udi" string="/org/freedesktop/Hal/devices/computer">
+
       <!-- remove all previously added ACL's on start-up -->
       <append key="info.callouts.add" type="strlist">hal-acl-tool --remove-all</append>
 
diff --git a/hald/create_cache.c b/hald/create_cache.c
index 4d016d1..2b40d3d 100644
--- a/hald/create_cache.c
+++ b/hald/create_cache.c
@@ -107,6 +107,8 @@ match_type get_match_type(const char *st
 		return MATCH_ISASCII;
 	if (strcmp (str, "is_absolute_path") == 0)
 		return MATCH_IS_ABS_PATH;
+	if (strcmp (str, "sibling_contains") == 0)
+		return MATCH_SIBLING_CONTAINS;
 	if (strcmp (str, "contains") == 0)
 		return MATCH_CONTAINS;
 	if (strcmp (str, "contains_ncase") == 0)
diff --git a/hald/device_info.c b/hald/device_info.c
index c4891cf..5d68f17 100644
--- a/hald/device_info.c
+++ b/hald/device_info.c
@@ -78,6 +78,8 @@ get_match_type_str (enum match_type type
 		return "is_ascii";
 	case MATCH_IS_ABS_PATH:
 		return "is_absolute_path";
+	case MATCH_SIBLING_CONTAINS:
+		return "sibling_contains";
 	case MATCH_CONTAINS:
 		return "contains";
 	case MATCH_CONTAINS_NCASE:
@@ -465,6 +467,58 @@ handle_match (struct rule *rule, HalDevi
 		return contains;
 	}
 
+	case MATCH_SIBLING_CONTAINS:
+	{
+		dbus_bool_t contains = FALSE;
+		const char *parent_udi;
+
+		parent_udi = hal_device_property_get_string (d, "info.parent");
+		if (parent_udi != NULL) {
+			GSList *i;
+			GSList *siblings;
+
+			siblings = hal_device_store_match_multiple_key_value_string (hald_get_gdl (),
+										     "info.parent",
+										     parent_udi);
+			for (i = siblings; i != NULL; i = g_slist_next (i)) {
+				HalDevice *sib = HAL_DEVICE (i->data);
+
+				if (sib == d)
+					continue;
+
+				HAL_INFO (("Checking sibling '%s' of '%s' whether '%s' contains '%s'",
+					   hal_device_get_udi (sib), hal_device_get_udi (d), prop_to_check, value));
+
+				if (hal_device_property_get_type (sib, prop_to_check) == HAL_PROPERTY_TYPE_STRING) {
+					if (hal_device_has_property (sib, prop_to_check)) {
+						const char *haystack;
+						
+						haystack = hal_device_property_get_string (sib, prop_to_check);
+						if (value != NULL && haystack != NULL && strstr (haystack, value))
+							contains = TRUE;
+					}
+				} else if (hal_device_property_get_type (sib, prop_to_check) == HAL_PROPERTY_TYPE_STRLIST && value != NULL) {
+					HalDeviceStrListIter iter;
+					for (hal_device_property_strlist_iter_init (sib, prop_to_check, &iter);
+					     hal_device_property_strlist_iter_is_valid (&iter);
+					     hal_device_property_strlist_iter_next (&iter)) {
+						const char *str = hal_device_property_strlist_iter_get_value (&iter);
+						if (strcmp (str, value) == 0) {
+							contains = TRUE;
+							break;
+						}
+					}
+				}
+
+				if (contains)
+					break;
+
+			} /* for all siblings */
+		}
+
+		return contains;
+	}
+
 	case MATCH_CONTAINS_NCASE:
 	{
 		dbus_bool_t contains_ncase = FALSE;
diff --git a/hald/rule.h b/hald/rule.h
index cb82e62..b386fc9 100644
--- a/hald/rule.h
+++ b/hald/rule.h
@@ -78,6 +78,7 @@ match_type {
 	MATCH_COMPARE_LE,
 	MATCH_COMPARE_GT,
 	MATCH_COMPARE_GE,
+	MATCH_SIBLING_CONTAINS
 };
 
 /* a "rule" structure that is a generic node of the fdi file */
diff-tree 04ff0ba613b2dc3d9c3ffabac2399d992a8d7363 (from b23a5a78919e4d3ea52e152c82ce8734edddbe2a)
Author: David Zeuthen <davidz at redhat.com>
Date:   Sat Feb 17 21:22:07 2007 -0500

    use $(localstatedir) instead of /var and make hald DTRT when CK goes away
    
    Doing the "right thing" when ConsoleKit goes away / reappears
    basically sums up to calling "hal-acl-tool --reconfigure" such that
    ACL's granted to users based upon seat/sessions are removed.
    
    See
    
     http://lists.freedesktop.org/archives/hal/2007-February/007180.html
    
    and surrounding messages for discussion.
    
    Also fix up some code to be conditional on whether we have ConsoleKit or not.

diff --git a/hald/ck-tracker.c b/hald/ck-tracker.c
index 7276ad6..02d7bf2 100644
--- a/hald/ck-tracker.c
+++ b/hald/ck-tracker.c
@@ -63,6 +63,8 @@ struct CKTracker_s {
 	CKSessionAddedCB session_added_cb;
 	CKSessionRemovedCB session_removed_cb;
 	CKSessionActiveChangedCB session_active_changed_cb;
+	CKServiceDisappearedCB service_disappeared_cb;
+	CKServiceAppearedCB service_appeared_cb;
 	GSList *seats;
 	GSList *sessions;
 };
@@ -371,24 +373,33 @@ ck_tracker_ref (CKTracker *tracker)
 	tracker->refcount++;
 }
 
+static void
+ck_tracker_remove_all_seats_and_sessions (CKTracker *tracker)
+{
+	GSList *i;
+	
+	for (i = tracker->sessions; i != NULL; i = g_slist_next (i)) {
+		CKSession *session = (CKSession *) i->data;
+		ck_session_unref (session);
+	}
+	g_slist_free (tracker->sessions);
+	tracker->sessions = NULL;
+	
+	for (i = tracker->seats; i != NULL; i = g_slist_next (i)) {
+		CKSeat *seat = (CKSeat *) i->data;
+		ck_seat_unref (seat);
+	}
+	g_slist_free (tracker->seats);
+	tracker->seats = NULL;
+}
+
 void
 ck_tracker_unref (CKTracker *tracker)
 {
 	tracker->refcount--;
 
 	if (tracker->refcount == 0) {
-		GSList *i;
-
-		for (i = tracker->sessions; i != NULL; i = g_slist_next (i)) {
-			CKSession *session = (CKSession *) i->data;
-			ck_session_unref (session);
-		}
-		
-		for (i = tracker->seats; i != NULL; i = g_slist_next (i)) {
-			CKSeat *seat = (CKSeat *) i->data;
-			ck_seat_unref (seat);
-		}
-		
+		ck_tracker_remove_all_seats_and_sessions (tracker);
 		tracker->dbus_connection = NULL;
 	}
 }
@@ -423,6 +434,17 @@ ck_tracker_set_session_active_changed_cb
 	tracker->session_active_changed_cb = cb;
 }
 
+void
+ck_tracker_set_service_disappeared_cb  (CKTracker *tracker, CKServiceDisappearedCB cb)
+{
+	tracker->service_disappeared_cb = cb;
+}
+
+void
+ck_tracker_set_service_appeared_cb  (CKTracker *tracker, CKServiceAppearedCB cb)
+{
+	tracker->service_appeared_cb = cb;
+}
 
 void
 ck_tracker_process_system_bus_message (CKTracker *tracker, DBusMessage *message)
@@ -546,8 +568,39 @@ ck_tracker_process_system_bus_message (C
 		if (i == NULL) {
 			HAL_ERROR (("No such session '%s'", session_objpath));
 		}
+	} else if (dbus_message_is_signal (message, DBUS_INTERFACE_DBUS, "NameOwnerChanged")) {
+		char *name;
+		char *old_service_name;
+		char *new_service_name;
+
+		if (!dbus_message_get_args (message, NULL,
+					    DBUS_TYPE_STRING, &name,
+					    DBUS_TYPE_STRING, &old_service_name,
+					    DBUS_TYPE_STRING, &new_service_name,
+					    DBUS_TYPE_INVALID)) {
+			HAL_ERROR (("Invalid NameOwnerChanged signal from bus!"));
+			goto out;
+		}
+
+		if (strlen (new_service_name) == 0 && strcmp (name, "org.freedesktop.ConsoleKit") == 0) {
+			HAL_INFO (("uh, oh, ConsoleKit went away!"));
+			ck_tracker_remove_all_seats_and_sessions (tracker);
+			if (tracker->service_disappeared_cb != NULL) {
+				tracker->service_disappeared_cb (tracker, tracker->user_data);
+			}
+		}
+
+		if (strlen (old_service_name) == 0 && strcmp (name, "org.freedesktop.ConsoleKit") == 0) {
+			HAL_INFO (("ConsoleKit reappeared!"));
+			ck_tracker_init (tracker);
+			if (tracker->service_appeared_cb != NULL) {
+				tracker->service_appeared_cb (tracker, tracker->user_data);
+			}
+		}
+
 
 	}
+
 out:
 	;
 }
diff --git a/hald/ck-tracker.h b/hald/ck-tracker.h
index 6c3147b..6f8b5dd 100644
--- a/hald/ck-tracker.h
+++ b/hald/ck-tracker.h
@@ -41,6 +41,8 @@ typedef struct CKSession_s CKSession;
 typedef void (*CKSessionAddedCB) (CKTracker *tracker, CKSession *session, void *user_data);
 typedef void (*CKSessionRemovedCB) (CKTracker *tracker, CKSession *session, void *user_data);
 typedef void (*CKSessionActiveChangedCB) (CKTracker *tracker, CKSession *session, void *user_data);
+typedef void (*CKServiceDisappearedCB) (CKTracker *tracker, void *user_data);
+typedef void (*CKServiceAppearedCB) (CKTracker *tracker, void *user_data);
 
 CKTracker  *ck_tracker_new                        (void);
 void        ck_tracker_set_system_bus_connection     (CKTracker *tracker, DBusConnection *system_bus_connection);
@@ -48,6 +50,8 @@ void        ck_tracker_set_user_data    
 void        ck_tracker_set_session_added_cb          (CKTracker *tracker, CKSessionAddedCB cb);
 void        ck_tracker_set_session_removed_cb        (CKTracker *tracker, CKSessionRemovedCB cb);
 void        ck_tracker_set_session_active_changed_cb (CKTracker *tracker, CKSessionActiveChangedCB cb);
+void        ck_tracker_set_service_disappeared_cb (CKTracker *tracker, CKServiceDisappearedCB cb);
+void        ck_tracker_set_service_appeared_cb    (CKTracker *tracker, CKServiceAppearedCB cb);
 /* TODO: also handle seat_added, seat_removed */
 gboolean    ck_tracker_init                          (CKTracker *tracker);
 
diff --git a/hald/hald_dbus.c b/hald/hald_dbus.c
index 9cc75b9..ae70232 100644
--- a/hald/hald_dbus.c
+++ b/hald/hald_dbus.c
@@ -4404,6 +4404,8 @@ hald_dbus_local_server_shutdown (void)
 	}
 }
 
+#ifdef HAVE_CONKIT
+
 static void 
 hald_dbus_session_added (CKTracker *tracker, CKSession *session, void *user_data)
 {
@@ -4542,6 +4544,48 @@ out:
 	;
 }
 
+static void
+hald_dbus_ck_availability_changed (gboolean ck_available)
+{
+	HalDevice *d;
+	char *extra_env[1] = {NULL};
+
+	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");
+	}
+	if (d == NULL) {
+		HAL_ERROR (("No computer object?!?"));
+		goto out;
+	}
+
+	hald_runner_run (d,
+			 "hal-acl-tool --reconfigure", 
+			 extra_env,
+			 HAL_HELPER_TIMEOUT, 
+			 NULL, /* run_terminated_cb */
+			 NULL  /* userdata1 */, 
+			 NULL  /* userdata2 */ );
+out:
+	;
+}
+
+static void
+hald_dbus_ck_disappeared (CKTracker *tracker, void *user_data)
+{
+	HAL_INFO (("In hald_dbus_ck_disappeared"));
+	hald_dbus_ck_availability_changed (FALSE);
+}
+
+static void
+hald_dbus_ck_appeared (CKTracker *tracker, void *user_data)
+{
+	HAL_INFO (("In hald_dbus_ck_appeared"));
+	hald_dbus_ck_availability_changed (TRUE);
+}
+
+#endif /* HAVE_CONKIT */
+
 gboolean
 hald_dbus_init_preprobe (void)
 {
@@ -4597,9 +4641,10 @@ hald_dbus_init_preprobe (void)
 	ck_tracker_set_session_added_cb (ck_tracker, hald_dbus_session_added);
 	ck_tracker_set_session_removed_cb (ck_tracker, hald_dbus_session_removed);
 	ck_tracker_set_session_active_changed_cb (ck_tracker, hald_dbus_session_active_changed);
+	ck_tracker_set_service_disappeared_cb (ck_tracker, hald_dbus_ck_disappeared);
+	ck_tracker_set_service_appeared_cb (ck_tracker, hald_dbus_ck_appeared);
 	if (!ck_tracker_init (ck_tracker)) {
-		ck_tracker_unref (ck_tracker);
-		DIE (("Could not initialize seats and sessions from ConsoleKit"));
+		HAL_WARNING (("Could not initialize seats and sessions from ConsoleKit"));
 	}
 
 #endif /* HAVE_CONKIT */
diff --git a/hald/hald_runner.c b/hald/hald_runner.c
index 7eb7b14..008c2cf 100644
--- a/hald/hald_runner.c
+++ b/hald/hald_runner.c
@@ -644,8 +644,10 @@ call_notify (DBusPendingCall * pending, 
 		g_array_append_vals (error, &value, 1);
 	}
 
-	hb->cb (hb->d, exitt, return_code,
-		(gchar **) error->data, hb->data1, hb->data2);
+	if (hb->cb != NULL) {
+		hb->cb (hb->d, exitt, return_code,
+			(gchar **) error->data, hb->data1, hb->data2);
+	}
 
 	if (hb->d != NULL)
 		g_object_unref (hb->d);
@@ -662,8 +664,10 @@ call_notify (DBusPendingCall * pending, 
       malformed:
 	/* Send a Fail callback on malformed messages */
 	HAL_ERROR (("Malformed or unexpected reply message"));
-	hb->cb (hb->d, HALD_RUN_FAILED, return_code, NULL, hb->data1,
-		hb->data2);
+	if (hb->cb != NULL) {
+		hb->cb (hb->d, HALD_RUN_FAILED, return_code, NULL, hb->data1,
+			hb->data2);
+	}
 
 	if (hb->d != NULL)
 		g_object_unref (hb->d);
diff --git a/tools/Makefile.am b/tools/Makefile.am
index b5332e3..e594bde 100644
--- a/tools/Makefile.am
+++ b/tools/Makefile.am
@@ -125,5 +125,10 @@ check:
             fi; \
         done;
 
+install-data-local:
+	-$(mkdir_p) $(DESTDIR)$(localstatedir)/lib/hal
+	-chown $(HAL_USER):$(HAL_GROUP) $(DESTDIR)$(localstatedir)/lib/hal
+	-chmod 0700 $(DESTDIR)$(localstatedir)/lib/hal
+
 clean-local :
 	rm -f *~
diff --git a/tools/hal-acl-tool.c b/tools/hal-acl-tool.c
index d49d097..2d8b6ea 100644
--- a/tools/hal-acl-tool.c
+++ b/tools/hal-acl-tool.c
@@ -24,6 +24,7 @@
 #include <stdlib.h>
 #include <sys/file.h>
 #include <sys/types.h>
+#include <sys/stat.h>
 #include <unistd.h>
 #include <errno.h>
 #include <string.h>
@@ -218,7 +219,7 @@ acl_apply_changes (GSList *new_acl_list,
 	}
 
 	/* success; now atomically set the new list */
-	g_file_set_contents ("/var/lib/hal/acl-list", 
+	g_file_set_contents (PACKAGE_LOCALSTATEDIR "/lib/hal/acl-list", 
 			     new_acl_file_contents, 
 			     strlen (new_acl_file_contents),
 			     NULL);
@@ -247,9 +248,9 @@ get_current_acl_list (GSList **l)
 	f = NULL;
 	ret = FALSE;
 
-	f = fopen ("/var/lib/hal/acl-list", "r");
+	f = fopen (PACKAGE_LOCALSTATEDIR "/lib/hal/acl-list", "r");
 	if (f == NULL) {
-		printf ("%d: cannot open /var/lib/hal/acl-list\n", getpid ());
+		printf ("%d: cannot open " PACKAGE_LOCALSTATEDIR "/lib/hal/acl-list\n", getpid ());
 		goto out;
 	}
 
@@ -982,15 +983,14 @@ acl_lock (void)
 	if (lock_acl_fd >= 0)
 		return TRUE;
 
-	printf ("%d: attempting to get lock on /var/lib/hal/acl-list\n", getpid ());
-
-	lock_acl_fd = open ("/var/lib/hal/acl-list", O_CREAT | O_RDWR);
-	/* TODO: set correct mode, owner etc. */
+	printf ("%d: attempting to get lock on " PACKAGE_LOCALSTATEDIR "/lib/hal/acl-list\n", getpid ());
 
+	lock_acl_fd = open (PACKAGE_LOCALSTATEDIR "/lib/hal/acl-list", O_CREAT | O_RDWR);
 	if (lock_acl_fd < 0) {
-		printf ("%d: error opening/creating /var/lib/hal/acl-list\n", getpid ());
+		printf ("%d: error opening/creating " PACKAGE_LOCALSTATEDIR "/lib/hal/acl-list\n", getpid ());
 		return FALSE;
 	}
+	
 
 tryagain:
 #if sun
@@ -1004,7 +1004,7 @@ tryagain:
 		return FALSE;
 	}
 	
-	printf ("%d: got lock on /var/lib/hal/acl-list\n", getpid ());
+	printf ("%d: got lock on " PACKAGE_LOCALSTATEDIR "/lib/hal/acl-list\n", getpid ());
 	return TRUE;
 }
 
@@ -1018,7 +1018,7 @@ acl_unlock (void)
 #endif
 	close (lock_acl_fd);
 	lock_acl_fd = -1;
-	printf ("%d: released lock on /var/lib/hal/acl-list\n", getpid ());
+	printf ("%d: released lock on " PACKAGE_LOCALSTATEDIR "/lib/hal/acl-list\n", getpid ());
 }
 
 
diff-tree b23a5a78919e4d3ea52e152c82ce8734edddbe2a (from d7d9e1100f9f26da1004597ee52814fd41610c08)
Author: David Zeuthen <davidz at redhat.com>
Date:   Sat Feb 17 17:26:32 2007 -0500

    fix typo for HALD_ACTION for session changes

diff --git a/hald/hald_dbus.c b/hald/hald_dbus.c
index 24d0d53..9cc75b9 100644
--- a/hald/hald_dbus.c
+++ b/hald/hald_dbus.c
@@ -4499,7 +4499,7 @@ hald_dbus_session_active_changed (CKTrac
 {
 	HalDevice *d;
 	char **programs;
-	char *extra_env[5] = {"HALD_ACTION=session_remove", 
+	char *extra_env[5] = {"HALD_ACTION=session_active_changed", 
 			      NULL /* "HALD_SESSION_ACTIVE_CHANGED_SESSION_ID=" */,
 			      NULL /* "HALD_SESSION_ACTIVE_CHANGED_SESSION_UID=" */,
 			      NULL /* "HALD_SESSION_ACTIVE_CHANGED_SESSION_IS_ACTIVE=" */,
diff-tree d7d9e1100f9f26da1004597ee52814fd41610c08 (from 573c40932cb08837d75f1981904e493734765338)
Author: David Zeuthen <davidz at redhat.com>
Date:   Sat Feb 17 17:24:54 2007 -0500

    more ACL management work; now it's actually usuable

diff --git a/fdi/policy/10osvendor/20-acl-management.fdi b/fdi/policy/10osvendor/20-acl-management.fdi
index ca1f21a..8033521 100644
--- a/fdi/policy/10osvendor/20-acl-management.fdi
+++ b/fdi/policy/10osvendor/20-acl-management.fdi
@@ -5,33 +5,40 @@
 
     <!-- allow local sessions to access ALSA device files -->
     <match key="info.capabilities" contains="alsa">
-      <merge key="acl.is_managed" type="bool">true</merge>
-      <merge key="acl.grant_local_session" type="bool">true</merge>
-      <merge key="acl.file" type="copy_property">alsa.device_file</merge>
+      <append key="info.capabilities" type="strlist">access_control</append>
+      <merge key="access_control.grant_local_session" type="bool">true</merge>
+      <merge key="access_control.file" type="copy_property">alsa.device_file</merge>
     </match>
 
     <!-- allow local sessions to access OSS device files -->
     <match key="info.capabilities" contains="oss">
-      <merge key="acl.is_managed" type="bool">true</merge>
-      <merge key="acl.grant_local_session" type="bool">true</merge>
-      <merge key="acl.file" type="copy_property">oss.device_file</merge>
+      <append key="info.capabilities" type="strlist">access_control</append>
+      <merge key="access_control.grant_local_session" type="bool">true</merge>
+      <merge key="access_control.file" type="copy_property">oss.device_file</merge>
     </match>
 
     <!-- TODO: add more rules for adding/removing ACL's (e.g. webcam, cd drive etc.) -->
 
-    <!-- add and remove ACL's when devices are added and removed -->
-    <match key="acl.is_managed" bool="true">
-      <append key="info.callouts.add" type="strlist">hal-acl-add --device</append>
-      <append key="info.callouts.remove" type="strlist">hal-acl-remove --device</append>
+    <!-- add / remove ACL's when devices are added and removed -->
+    <match key="info.capabilities" contains="access_control">
+      <append key="info.callouts.add" type="strlist">hal-acl-tool --add-device</append>
+      <append key="info.callouts.remove" type="strlist">hal-acl-tool --remove-device</append>
     </match>
 
     <match key="info.udi" string="/org/freedesktop/Hal/devices/computer">
       <!-- remove all previously added ACL's on start-up -->
-      <append key="info.callouts.add" type="strlist">hal-acl-remove --remove-all</append>
+      <append key="info.callouts.add" type="strlist">hal-acl-tool --remove-all</append>
+
+      <!-- reconfigure all ACL's sessions are added and removed -->
+      <append key="info.callouts.session_add" type="strlist">hal-acl-tool --reconfigure</append>
+      <append key="info.callouts.session_remove" type="strlist">hal-acl-tool --reconfigure</append>
+
+      <!-- reconfigure all ACL's when a session becomes active -->
+      <append key="info.callouts.session_active" type="strlist">hal-acl-tool --reconfigure</append>
+
+      <!-- reconfigure all ACL's when a session becomes inactive -->
+      <append key="info.callouts.session_inactive" type="strlist">hal-acl-tool --reconfigure</append>
 
-      <!-- add and remove ACL's when sessions are added and removed -->
-      <append key="info.callouts.session_add" type="strlist">hal-acl-add --session</append>
-      <append key="info.callouts.session_remove" type="strlist">hal-acl-remove --session</append>
     </match>
 
   </device>
diff --git a/hald/ck-tracker.c b/hald/ck-tracker.c
index b943729..7276ad6 100644
--- a/hald/ck-tracker.c
+++ b/hald/ck-tracker.c
@@ -496,10 +496,6 @@ ck_tracker_process_system_bus_message (C
 			session = (CKSession *) i->data;
 			if (strcmp (session->session_objpath, session_objpath) == 0) {
 
-				if (tracker->session_removed_cb != NULL) {
-					tracker->session_removed_cb (tracker, session, tracker->user_data);
-				}
-
 				if (session->seat == NULL) {
 					HAL_ERROR (("Session '%s' to be removed is not attached to a seat", 
 						    session_objpath));
@@ -509,6 +505,10 @@ ck_tracker_process_system_bus_message (C
 				tracker->sessions = g_slist_remove (tracker->sessions, session);
 				ck_session_unref (session);
 
+				if (tracker->session_removed_cb != NULL) {
+					tracker->session_removed_cb (tracker, session, tracker->user_data);
+				}
+
 				break;
 			}
 		}
diff --git a/hald/hald_dbus.c b/hald/hald_dbus.c
index 51089a5..24d0d53 100644
--- a/hald/hald_dbus.c
+++ b/hald/hald_dbus.c
@@ -4215,7 +4215,7 @@ hald_dbus_filter_function (DBusConnectio
 			goto out;
 		}
 
-		HAL_INFO (("NameOwnerChanged name=%s old=%s new=%s", name, old_service_name, new_service_name));
+		/*HAL_INFO (("NameOwnerChanged name=%s old=%s new=%s", name, old_service_name, new_service_name));*/
 
 		ci_tracker_name_owner_changed (name, old_service_name, new_service_name);
 
@@ -4497,9 +4497,49 @@ out:
 static void 
 hald_dbus_session_active_changed (CKTracker *tracker, CKSession *session, void *user_data)
 {
+	HalDevice *d;
+	char **programs;
+	char *extra_env[5] = {"HALD_ACTION=session_remove", 
+			      NULL /* "HALD_SESSION_ACTIVE_CHANGED_SESSION_ID=" */,
+			      NULL /* "HALD_SESSION_ACTIVE_CHANGED_SESSION_UID=" */,
+			      NULL /* "HALD_SESSION_ACTIVE_CHANGED_SESSION_IS_ACTIVE=" */,
+			      NULL};
+
 	HAL_INFO (("In hald_dbus_session_active_changed for session '%s': %s", 
 		   ck_session_get_id (session),
 		   ck_session_is_active (session) ? "ACTIVE" : "INACTIVE"));
+
+	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");
+	}
+	if (d == NULL) {
+		goto out;
+	}
+
+	programs = hal_device_property_dup_strlist_as_strv (d, 
+							    ck_session_is_active (session) ?
+							    "info.callouts.session_active" :
+							    "info.callouts.session_inactive");
+	if (programs == NULL) {
+		goto out;
+	}
+
+	extra_env[1] = g_strdup_printf ("HALD_SESSION_ACTIVE_CHANGED_SESSION_ID=%s", ck_session_get_id (session));
+	extra_env[2] = g_strdup_printf ("HALD_SESSION_ACTIVE_CHANGED_SESSION_UID=%d", ck_session_get_user (session));
+	extra_env[3] = g_strdup_printf ("HALD_SESSION_ACTIVE_CHANGED_SESSION_IS_ACTIVE=%s", 
+					ck_session_is_active (session) ? "true" : "false");
+	hal_callout_device (d, 
+			    NULL /* callback */,
+			    NULL /* userdata1 */,
+			    NULL /* userdata2 */, 
+			    programs, 
+			    extra_env);
+	g_free (extra_env[1]);
+	g_free (extra_env[2]);
+	g_free (extra_env[3]);
+out:
+	;
 }
 
 gboolean
diff --git a/tools/Makefile.am b/tools/Makefile.am
index 96844ce..b5332e3 100644
--- a/tools/Makefile.am
+++ b/tools/Makefile.am
@@ -63,10 +63,10 @@ libexec_PROGRAMS +=			    \
 endif
 
 if HAVE_ACLMGMT
-libexec_PROGRAMS += hal-acl-add hal-acl-remove
+libexec_PROGRAMS += hal-acl-tool
 
-hal_acl_add_SOURCES = hal-acl-add.c
-hal_acl_remove_SOURCES = hal-acl-remove.c
+hal_acl_tool_SOURCES = hal-acl-tool.c
+hal_acl_tool_LDADD = @GLIB_LIBS@ $(top_builddir)/libhal/libhal.la
 endif
 
 
diff --git a/tools/hal-acl-add.c b/tools/hal-acl-add.c
deleted file mode 100644
index 4d358d5..0000000
--- a/tools/hal-acl-add.c
+++ /dev/null
@@ -1,97 +0,0 @@
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <sys/file.h>
-#include <sys/types.h>
-#include <unistd.h>
-#include <errno.h>
-
-#include <glib.h>
-
-#if 0
-static int lock_acl_fd = -1;
-
-static gboolean
-lock_hal_acl (void)
-{
-	if (lock_acl_fd >= 0)
-		return TRUE;
-
-	printf ("%d: attempting to get lock on /var/lib/hal/acl-list\n", getpid ());
-
-	lock_acl_fd = open ("/var/lib/hal/acl-list", O_CREAT | O_RDWR);
-
-	if (lock_acl_fd < 0)
-		return FALSE;
-
-tryagain:
-#if sun
-	if (lockf (lock_acl_fd, F_LOCK, 0) != 0) {
-#else
-	if (flock (lock_acl_fd, LOCK_EX) != 0) {
-#endif
-		if (errno == EINTR)
-			goto tryagain;
-		return FALSE;
-	}
-	
-	printf ("%d: got lock on /var/lib/hal/acl-list\n", getpid ());
-	
-	
-	return TRUE;
-}
-	
-static void
-unlock_hal_acl (void)
-{
-#if sun
-	lockf (lock_acl_fd, F_ULOCK, 0);
-#else
-	flock (lock_acl_fd, LOCK_UN);
-#endif
-	close (lock_acl_fd);
-	lock_acl_fd = -1;
-	printf ("%d: released lock on /var/lib/hal/acl-list\n", getpid ());
-}
-
-/* Each entry here represents a line in the acl-list file 
- *
- *   <device-file>\t<hal-udi>\t<uid-as-number>\t<gid-as-number>\t<session-id>
- *
- * example:
- *
- *   /dev/snd/controlC0\t/org/freedesktop/Hal/devices/pci_8086_27d8_alsa_control__1\t500\t\t/org/freedesktop/ConsoleKit/Session0
- *
- * This means that the 
- */
-typedef struct HalACL_s {
-	const char *device;
-	const char *uid;
-	uid_t uid;           /* 0 if unset */
-	gid_t gid;           /* 0 if unset */
-	const char *session; /* NULL if unset */
-} HalACL;
-
-/* hal-acl-grant can run in two modes of operation;
- *
- * 1) as a hal callout via info.callouts.add - this will set ACL's on the device file pointed to by acl.file
- *    using the information provided by the other acl.* properties to determine what ACL's to add to the given
- *    device file
- *
- * 2) 
- *
- */
-#endif
-
-int 
-main (int argc, char *argv[])
-{
-	int i;
-
-	fprintf (stderr, "hal-acl-add %d\n", argc);
-	for (i = 0; i < argc; i++) {
-		fprintf (stderr, " arg %2d: %s\n", i, argv[i]);		
-	}
-	system ("env |sort");
-	return 0;
-}
diff --git a/tools/hal-acl-remove.c b/tools/hal-acl-remove.c
deleted file mode 100644
index 83f1c7c..0000000
--- a/tools/hal-acl-remove.c
+++ /dev/null
@@ -1,16 +0,0 @@
-
-#include <stdio.h>
-#include <stdlib.h>
-
-int 
-main (int argc, char *argv[])
-{
-	int i;
-
-	fprintf (stderr, "hal-acl-remove %d\n", argc);
-	for (i = 0; i < argc; i++) {
-		fprintf (stderr, " arg %2d: %s\n", i, argv[i]);		
-	}
-	system ("env |sort");
-	return 0;
-}
diff --git a/tools/hal-acl-tool.c b/tools/hal-acl-tool.c
new file mode 100644
index 0000000..d49d097
--- /dev/null
+++ b/tools/hal-acl-tool.c
@@ -0,0 +1,1051 @@
+/***************************************************************************
+ *
+ * hal-acl-tool.c : Manage ACL's on device nodes
+ *
+ * Copyright (C) 2007 David Zeuthen, <david at fubar.dk>
+ *
+ * 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
+ *
+ **************************************************************************/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/file.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+#include <pwd.h>
+#include <grp.h>
+
+#include <glib.h>
+#include <libhal.h>
+
+/* How this works (or "An introduction to this code")
+ *
+ * - all ACL's granted by this tool is kept in /var/lib/hal/acl-list
+ *
+ * - every time tool is launched we read this file and keep each line
+ *   as an ACLCurrent instance. These are kept in a list.
+ *
+ * - we do locking so only one instance of this tool is doing work
+ *   at one time. This is essential as we maintain ACL's in a file.
+ *
+ * - there's an overarching --reconfigure method that basically
+ *   - finds all devices of capability 'access_control'
+ *   - computes what ACL's these devices should have
+ *   - modifies the in-memory acl-current-list
+ *     - ACL's to be removed are tagged with setting remove -> TRUE
+ *     - ACL's to be added are appended to the list and add -> TRUE
+ *   - we then compute the argument vector to setfacl(1) for adding /
+ *     removing ACL's
+ *   - if setfacl(1) succeeds (rc == 0) then we write the new acl-current-list
+ *
+ * Notably, the HAL daemon will invoke us with --reconfigure on every
+ *  - session add
+ *  - session remove
+ *  - session inactive
+ *  - session active
+ *
+ * event. Also, when devices are added we're invoked with --add-device
+ * respectively --remove-device. When the HAL daemon starts we're invoked
+ * with --remove-all.
+ *
+ * Optimizations
+ *
+ * - the HAL daemon exports the ConsoleKit seat + session
+ *   configuration in CK_* environment variables. So we don't
+ *   need to do IPC to the ConsoleKit daemon to learn about
+ *   about seats and sessions.
+ *
+ * - special casing for --add-device and --remove-device; here we
+ *   don't need roundtrips to the HAL daemon. Only --reconfigure
+ *   requires that. As such no IPC is required for these cases
+ *   which is fortunate as --add-device is invoked quite a lot
+ *   on startup
+ *
+ */
+
+/* Each entry here represents a line in the /var/lib/hal/acl-list file
+ * of ACL's that have been set by HAL and as such are currently
+ * applied
+ *
+ *   <device-file>    <type>    <uid-or-gid>
+ *
+ * where <type>='u'|'g' for respectively uid and gid (the spacing represent tabs).
+ *
+ * Example:
+ *
+ *   /dev/snd/controlC0    u    500
+ *   /dev/snd/controlC0    u    501
+ *   /dev/snd/controlC0    g    1001
+ */
+typedef struct ACLCurrent_s {
+	char *device;
+	int type;
+	union {
+		uid_t uid;
+		gid_t gid;
+	} v;
+
+	gboolean remove;
+	gboolean add;
+} ACLCurrent;
+
+/* used for type member in ACLCurrent */
+enum {
+	HAL_ACL_UID,
+	HAL_ACL_GID
+};
+
+static void
+hal_acl_free (ACLCurrent *ha)
+{
+	g_free (ha->device);
+	g_free (ha);
+}
+
+static int
+ha_sort (ACLCurrent *a, ACLCurrent *b)
+{
+	return strcmp (a->device, b->device);
+}
+
+static gboolean
+acl_apply_changes (GSList *new_acl_list, gboolean only_update_acllist, gboolean missing_files_ok)
+{
+	GString *str;
+	GString *setfacl_cmdline;
+	char *new_acl_file_contents = NULL;
+	char *setfacl_cmdline_str = NULL;
+	GSList *i;
+	gboolean ret;
+	GError *error = NULL;
+	int exit_status;
+
+	ret = FALSE;
+
+	new_acl_list = g_slist_sort (new_acl_list, (GCompareFunc) ha_sort);
+
+	/* first compute the contents of the new acl-file 
+	 * and build up the command line for setfacl(1)
+	 */
+	str = g_string_new ("");
+	setfacl_cmdline = g_string_new ("");
+	for (i = new_acl_list; i != NULL; i = g_slist_next (i)) {
+		ACLCurrent *ha = (ACLCurrent *) i->data;
+
+		if (ha->remove) {
+			if (setfacl_cmdline->len > 0) {
+				g_string_append_c (setfacl_cmdline, ' ');
+			}
+			g_string_append_printf (setfacl_cmdline, "-x %c:%d %s",
+						(ha->type == HAL_ACL_UID) ? 'u' : 'g',
+						(ha->type == HAL_ACL_UID) ? ha->v.uid : ha->v.gid,
+						ha->device);
+			continue;
+		}
+
+		if (ha->add) {
+			if (setfacl_cmdline->len > 0) {
+				g_string_append_c (setfacl_cmdline, ' ');
+			}
+			g_string_append_printf (setfacl_cmdline, "-m %c:%d:rw %s",
+						(ha->type == HAL_ACL_UID) ? 'u' : 'g',
+						(ha->type == HAL_ACL_UID) ? ha->v.uid : ha->v.gid,
+						ha->device);
+		}
+
+		g_string_append_printf (str, 
+					"%s\t%c\t%d\n",
+					ha->device,
+					(ha->type == HAL_ACL_UID) ? 'u' : 'g',
+					(ha->type == HAL_ACL_UID) ? ha->v.uid : ha->v.gid);
+	}
+	new_acl_file_contents = g_string_free (str, FALSE);
+
+	/* TODO FIXME NOTE XXX WARNING: 
+	 *
+	 * The variable 'only_update_acllist' is set to TRUE only on
+	 * device_remove events. It effectively means "only update the
+	 * /var/lib/hal/acl-list, do not apply ACL's on disk". So this
+	 * is done for systems where /dev is dynamic and we know for
+	 * sure that the device file is gone. 
+	 *
+	 * So if you're running a static /dev you need to comment the
+	 * only_update_acllist conditional out below so the ACL's are
+	 * removed accordingly. As this is uncommon and against all
+	 * recommendations for a system using HAL (we depend on recent
+	 * udev versions!) we don't bother the syscall overhead of
+	 * stat(2)'ing every file.
+	 */
+
+	if (setfacl_cmdline->len == 0 || only_update_acllist) {
+		g_string_free (setfacl_cmdline, TRUE);
+		setfacl_cmdline_str = NULL;
+	} else {
+		g_string_prepend (setfacl_cmdline, "setfacl ");
+		setfacl_cmdline_str = g_string_free (setfacl_cmdline, FALSE);
+
+		printf ("%d: invoking '%s'\n", getpid (), setfacl_cmdline_str);
+		if (!g_spawn_command_line_sync (setfacl_cmdline_str, NULL, NULL, &exit_status, &error)) {
+			printf ("%d: Error - couldn't invoke setfacl(1): %s\n", getpid (), error->message);
+			g_error_free (error);
+			goto out;
+		}
+
+		if (exit_status != 0) {
+			if (!missing_files_ok) {
+				printf ("%d: setfacl(1) exit code != 0 but OK as some missing files are expected\n",
+					getpid ());
+			} else {
+				printf ("%d: Error - setfacl(1) failed\n", getpid ());
+				goto out;
+			}
+		}
+	}
+
+	/* success; now atomically set the new list */
+	g_file_set_contents ("/var/lib/hal/acl-list", 
+			     new_acl_file_contents, 
+			     strlen (new_acl_file_contents),
+			     NULL);
+
+	ret = TRUE;
+
+out:
+	if (new_acl_file_contents != NULL)
+		g_free (new_acl_file_contents);
+
+	if (setfacl_cmdline_str != NULL)
+		g_free (setfacl_cmdline_str);
+
+	return ret;
+}
+
+
+static gboolean
+get_current_acl_list (GSList **l)
+{
+	FILE *f;
+	gboolean ret;
+	char buf[1024];
+
+	*l = NULL;
+	f = NULL;
+	ret = FALSE;
+
+	f = fopen ("/var/lib/hal/acl-list", "r");
+	if (f == NULL) {
+		printf ("%d: cannot open /var/lib/hal/acl-list\n", getpid ());
+		goto out;
+	}
+
+	while (fgets (buf, sizeof(buf), f) != NULL) {
+		ACLCurrent *ha;
+		char **val;
+		char *endptr;
+
+		ha = g_new0 (ACLCurrent, 1);
+		val = g_strsplit(buf, "\t", 0);
+		if (g_strv_length (val) != 3) {
+			printf ("Line is malformed: '%s'\n", buf);
+			g_strfreev (val);
+			goto out;
+		}
+
+		ha->device = g_strdup (val[0]);
+		if (strcmp (val[1], "u") == 0) {
+			ha->type = HAL_ACL_UID;
+			ha->v.uid = strtol (val[2], &endptr, 10);
+			if (*endptr != '\0' && *endptr != '\n') {
+				printf ("Line is malformed: '%s'\n", buf);
+				g_strfreev (val);
+				goto out;
+			}
+		} else if (strcmp (val[1], "g") == 0) {
+			ha->type = HAL_ACL_GID;
+			ha->v.gid = strtol (val[2], &endptr, 10);
+			if (*endptr != '\0' && *endptr != '\n') {
+				printf ("Line is malformed: '%s'\n", buf);
+				g_strfreev (val);
+				goto out;
+			}
+		} else {
+			printf ("Line is malformed: '%s'\n", buf);
+			g_strfreev (val);
+			goto out;
+		}
+		g_strfreev (val);
+
+		/*printf ("  ACL '%s' %d %d\n", ha->device, ha->type, ha->v.uid);*/
+
+		*l = g_slist_prepend (*l, ha);
+	}
+
+	ret = TRUE;
+
+out:
+	if (f != NULL)
+		fclose (f);
+	return ret;
+}
+
+typedef void (*SeatSessionVisitor) (const char *seat_id, 
+				    gboolean seat_is_local,
+				    int num_sessions_on_seat,
+				    const char *session_id,     /* may be NULL */
+				    uid_t session_uid,
+				    gboolean session_is_active, 
+				    gpointer user_data);
+
+/* Visits all seats and sessions.
+ *
+ * NOTE: when a seat is visited session_id will be NULL and session_uid, session_is_active are undefined.
+ */
+static gboolean
+visit_seats_and_sessions (SeatSessionVisitor visitor_cb, gpointer user_data)
+{
+	int i;
+	int j;
+	char *s;
+	char *p;
+	char **seats;
+	gboolean ret;
+
+	ret = FALSE;
+
+	if ((s = getenv ("CK_SEATS")) == NULL) {
+		printf ("%d: CK_SEATS is not set!\n", getpid());
+		goto out;
+	}
+	seats = g_strsplit (s, "\t", 0);
+	/* for all seats */
+	for (i = 0; seats[i] != NULL; i++) {
+		char *seat = seats[i];
+		char **sessions;
+		int num_sessions_on_seat;
+		gboolean seat_is_local;
+
+		p = g_strdup_printf ("CK_SEAT_IS_LOCAL_%s", seat);
+		if ((s = getenv (p)) == NULL) {
+			printf ("%d: CK_SEAT_IS_LOCAL_%s is not set!\n", getpid(), seat);
+			g_free (p);
+			goto out;
+		}
+		g_free (p);
+		seat_is_local = (strcmp (s, "true") == 0);
+
+		p = g_strdup_printf ("CK_SEAT_%s", seat);
+		if ((s = getenv (p)) == NULL) {
+			printf ("%d: CK_SEAT_%s is not set!\n", getpid(), seat);
+			g_free (p);
+			goto out;
+		}
+		g_free (p);
+		sessions = g_strsplit (s, "\t", 0);
+		num_sessions_on_seat = g_strv_length (sessions);
+
+		visitor_cb (seat, seat_is_local, num_sessions_on_seat, NULL, 0, FALSE, user_data);
+
+		/* for all sessions on seat */
+		for (j = 0; sessions[j] != NULL; j++) {
+			char *session = sessions[j];
+			gboolean session_is_active;
+			uid_t session_uid;
+			char *endptr;
+
+			p = g_strdup_printf ("CK_SESSION_IS_ACTIVE_%s", session);
+			if ((s = getenv (p)) == NULL) {
+				printf ("%d: CK_SESSION_IS_ACTIVE_%s is not set!\n", getpid(), session);
+				g_free (p);
+				goto out;
+			}
+			g_free (p);
+			session_is_active = (strcmp (s, "true") == 0);
+
+			p = g_strdup_printf ("CK_SESSION_UID_%s", session);
+			if ((s = getenv (p)) == NULL) {
+				printf ("%d: CK_SESSION_UID_%s is not set!\n", getpid(), session);
+				g_free (p);
+				goto out;
+			}
+			g_free (p);
+			session_uid = strtol (s, &endptr, 10);
+			if (*endptr != '\0') {
+				printf ("%d: CK_SESSION_UID_%s set to '%s' is malformed!\n", getpid(), session, s);
+				goto out;
+			}
+
+			visitor_cb (seat, seat_is_local, num_sessions_on_seat, 
+				    session, session_uid, session_is_active, user_data);
+
+		}
+		g_strfreev (sessions);
+	}
+	g_strfreev (seats);
+
+	ret = TRUE;
+
+out:
+	return ret;
+}
+
+/* this data structure is for collecting what ACL's a device should have */
+typedef struct {
+	/* identifying the device */
+	char *udi;      /* HAL UDI of device */
+	char *device;   /* device file */
+
+	/* policy for how to apply ACL's (must be set by the caller prior to visiting the device) */
+
+	/* access is granted to any session on a local seat */
+	gboolean grant_to_local_seat;
+
+	/* access is granted only to active sessions on local seats */
+	gboolean grant_to_local_seat_active_only;
+
+	/* will be set by the visitor */
+	GSList *uid;    /* list of uid's (int) that should have access to this device */
+	GSList *gid;    /* list of gid's (int) that should have access to this device */
+} ACLForDevice;
+
+static void
+afd_grant_to_uid (ACLForDevice *afd, uid_t uid)
+{
+	GSList *i;
+
+	for (i = afd->uid; i != NULL; i = g_slist_next (i)) {
+		uid_t iuid = GPOINTER_TO_INT (i->data);
+		if (uid == iuid)
+			goto out;
+	}
+	afd->uid = g_slist_prepend (afd->uid, GINT_TO_POINTER (uid));
+out:
+	;
+}
+
+static void
+afd_grant_to_gid (ACLForDevice *afd, gid_t gid)
+{
+	GSList *i;
+
+	for (i = afd->gid; i != NULL; i = g_slist_next (i)) {
+		gid_t igid = GPOINTER_TO_INT (i->data);
+		if (gid == igid)
+			goto out;
+	}
+	afd->gid = g_slist_prepend (afd->gid, GINT_TO_POINTER (gid));
+out:
+	;
+}
+
+static uid_t
+util_name_to_uid (const char *username, gid_t *default_gid)
+{
+        int rc;
+        uid_t res;
+        char *buf = NULL;
+        unsigned int bufsize;
+        struct passwd pwd;
+        struct passwd *pwdp;
+
+        res = (uid_t) -1;
+
+        bufsize = sysconf (_SC_GETPW_R_SIZE_MAX);
+        buf = g_new0 (char, bufsize);
+                
+        rc = getpwnam_r (username, &pwd, buf, bufsize, &pwdp);
+        if (rc != 0 || pwdp == NULL) {
+                /*g_warning ("getpwnam_r() returned %d", rc);*/
+                goto out;
+        }
+
+        res = pwdp->pw_uid;
+        if (default_gid != NULL)
+                *default_gid = pwdp->pw_gid;
+
+out:
+        g_free (buf);
+        return res;
+}
+
+static gid_t 
+util_name_to_gid (const char *groupname)
+{
+        int rc;
+        gid_t res;
+        char *buf = NULL;
+        unsigned int bufsize;
+        struct group gbuf;
+        struct group *gbufp;
+
+        res = (gid_t) -1;
+
+        bufsize = sysconf (_SC_GETGR_R_SIZE_MAX);
+        buf = g_new0 (char, bufsize);
+                
+        rc = getgrnam_r (groupname, &gbuf, buf, bufsize, &gbufp);
+        if (rc != 0 || gbufp == NULL) {
+                /*g_warning ("getgrnam_r() returned %d", rc);*/
+                goto out;
+        }
+
+        res = gbufp->gr_gid;
+
+out:
+        g_free (buf);
+        return res;
+}
+
+static void
+afd_grant_to_uid_from_userlist (ACLForDevice *afd, char **sv)
+{
+	int i;
+	uid_t uid;
+	char *endptr;
+
+	for (i = 0; sv[i] != NULL; i++) {
+		uid = strtol (sv[i], &endptr, 10);
+		if (*endptr != '\0') {
+			uid = util_name_to_uid (sv[i], NULL);
+			if ((int) uid == -1) {
+				printf ("%d: warning; username '%s' is unknown\n", getpid (), sv[i]);
+				continue;
+			}
+		}
+		afd_grant_to_uid (afd, uid);
+	}
+}
+
+static void
+afd_grant_to_gid_from_grouplist (ACLForDevice *afd, char **sv)
+{
+	int i;
+	gid_t gid;
+	char *endptr;
+
+	for (i = 0; sv[i] != NULL; i++) {
+		gid = strtol (sv[i], &endptr, 10);
+		if (*endptr != '\0') {
+			gid = util_name_to_gid (sv[i]);
+			if ((int) gid == -1) {
+				printf ("%d: warning; group '%s' is unknown\n", getpid (), sv[i]);
+				continue;
+			}
+		}
+		afd_grant_to_gid (afd, gid);
+	}
+}
+
+static ACLForDevice *
+acl_for_device_new (const char *udi)
+{
+	ACLForDevice *afd;
+
+	afd = g_new0 (ACLForDevice, 1);
+	afd->udi = g_strdup (udi);
+
+	return afd;
+}
+
+static void
+acl_for_device_set_device (ACLForDevice *afd, const char *device)
+{
+	afd->device = g_strdup (device);
+}
+
+static void
+acl_for_device_free (ACLForDevice* afd)
+{
+	g_free (afd->udi);
+	g_free (afd->device);
+	g_slist_free (afd->uid);
+	g_slist_free (afd->gid);
+	g_free (afd);
+}
+
+static void 
+acl_device_added_visitor (const char *seat_id, 
+			  gboolean seat_is_local,
+			  int num_sessions_on_seat,
+			  const char *session_id, 
+			  uid_t session_uid,
+			  gboolean session_is_active, 
+			  gpointer user_data)
+{
+	GSList *i;
+	GSList *afd_list = (GSList *) user_data;
+
+#if 0
+	if (session_id == NULL) {
+		/* means we're just visiting the seat; each session on the seat will be visited accordingly */
+		printf ("Visiting seat '%s' (is_local=%d) with %d sessions\n", 
+			seat_id, seat_is_local, num_sessions_on_seat);
+	} else {
+		printf ("  %s: Visiting session '%s' with uid %d (is_active=%d)\n",
+			seat_id, session_id, session_uid, session_is_active);
+	}
+#endif
+
+	/* for each entry ACLForDevice in afd_list, add to the uid and
+	 * gid lists for users and groups that should have access to
+	 * the device in question
+	 */
+	for (i = afd_list; i != NULL; i = g_slist_next (i)) {
+		ACLForDevice *afd = (ACLForDevice *) i->data;
+
+		if (session_id == NULL) {
+			/* we only grant access to sessions - someone suggested that if a device is tied
+			 * to a seat.. the owner might want to give access to user 'dilbert' if, and only
+			 * if, no sessions is occuring at that seat. Cuz then the user 'dilbert' could
+			 * have a cron job running that takes a webcam shot every 5 minutes or so..
+			 *
+			 * this can be achieved by testing num_sessions_on_seat==0
+			 */
+			continue;
+		}
+
+
+		/* apply the policy defined by grant_to_local_seat and grant_to_local_seat_active_only */
+
+		/* we only grant access to local seats... */
+		if (!seat_is_local)
+			continue;
+
+		if (afd->grant_to_local_seat)
+			afd_grant_to_uid (afd, session_uid);
+		else {
+			if (afd->grant_to_local_seat_active_only) {
+				if (session_is_active) {
+					afd_grant_to_uid (afd, session_uid);
+				}
+			}
+		}
+	}
+
+}
+
+static void
+acl_compute_changes (GSList *afd_list, gboolean only_update_acllist)
+{
+	GSList *current_acl_list = NULL;
+	GSList *i;
+	GSList *j;
+	GSList *k;
+
+	/* get the list of ACL's currently applied */
+	if (!get_current_acl_list (&current_acl_list)) {
+		printf ("Error getting ACL's currently applied\n");
+		goto out;
+	}
+
+	/* for each entry in ACLForDevice, we need to modify current_acl_list
+	 * such that it matches the entry. This is achieved by
+	 *
+	 *  - setting the 'remove' boolean to TRUE for existing
+	 *    entries that should be removed
+	 *
+	 *  - adding a new entry with the 'add' boolean set to TRUE,
+         *    in the front, for entries we need to have added
+	 */
+	for (i = afd_list; i != NULL; i = g_slist_next (i)) {
+		ACLForDevice *afd = (ACLForDevice *) i->data;
+
+		/* OK, so we have the ACL's we want for a device - this is expressed in afd
+		 *
+		 * go through all the ACL's that we've *already*
+		 * added.. 
+		 *
+		 *   if an ACL we want is already there
+		 *   we simply remove it from afd (the ACL's we want).
+		 *
+		 *   If we've already got an ACL that we don't want
+		 *   tag the current list with this for removal.
+		 *
+		 * At the end the afd will express the ACL's that we
+		 * need to add.. so make entries in the current_list
+		 * with 'add' set to TRUE.
+		 */
+		for (j = current_acl_list; j != NULL; j = g_slist_next (j)) {
+			ACLCurrent *ha = (ACLCurrent *) j->data;
+
+			if (strcmp (afd->device, ha->device) == 0) {
+				switch (ha->type) {
+				case HAL_ACL_UID:
+					/* see if this is already in the ACLForDevice entry */
+					for (k = afd->uid; k != NULL; k = g_slist_next (k)) {
+						uid_t uid = GPOINTER_TO_INT (k->data);
+						if (uid == ha->v.uid) {
+							/* yup, so we're all good - remove it from the afd_list
+							 * since we don't need it to be added later...
+							 */
+							afd->uid = g_slist_delete_link (afd->uid, k);
+							break;
+						}
+					}
+					if (k == NULL) {
+						/* nope, element wasn't there so this ACLCurrent should be removed */
+						ha->remove = TRUE;
+					}
+					break;
+				case HAL_ACL_GID:
+					/* see if this is already in the ACLForDevice entry */
+					for (k = afd->gid; k != NULL; k = g_slist_next (k)) {
+						gid_t gid = GPOINTER_TO_INT (k->data);
+						if (gid == ha->v.gid) {
+							/* yup, so we're all good - remove it from the afd_list
+							 * since we don't need it to be added later...
+							 */
+							afd->gid = g_slist_delete_link (afd->gid, k);
+							break;
+						}
+					}
+					if (k == NULL) {
+						/* nope, element wasn't there so this ACLCurrent should be removed */
+						ha->remove = TRUE;
+					}
+					break;
+				}
+			}
+
+		}
+
+		/* now go through remaining entries in afd->uid and afd->gid 
+		 * and create ACLCurrent entries
+		 */
+		for (j = afd->uid; j != NULL; j = g_slist_next (j)) {
+			ACLCurrent *ha;
+			ha = g_new0 (ACLCurrent, 1);
+			ha->add = TRUE;
+			ha->device = g_strdup (afd->device);
+			ha->type = HAL_ACL_UID;
+			ha->v.uid = GPOINTER_TO_INT (j->data);
+			current_acl_list = g_slist_prepend (current_acl_list, ha);
+		}
+		for (j = afd->gid; j != NULL; j = g_slist_next (j)) {
+			ACLCurrent *ha;
+			ha = g_new0 (ACLCurrent, 1);
+			ha->add = TRUE;
+			ha->device = g_strdup (afd->device);
+			ha->type = HAL_ACL_GID;
+			ha->v.gid = GPOINTER_TO_INT (j->data);
+			current_acl_list = g_slist_prepend (current_acl_list, ha);
+		}
+	}
+
+#if 0
+	printf ("====================================\n");
+	for (i = current_acl_list; i != NULL; i = g_slist_next (i)) {
+		ACLCurrent *ha = (ACLCurrent *) i->data;
+
+		printf ("  ACL '%s' %d %d rem=%d add=%d\n", ha->device, ha->type, ha->v.uid, ha->remove, ha->add);
+		
+	}
+	printf ("====================================\n");
+#endif
+
+	acl_apply_changes (current_acl_list, only_update_acllist, FALSE);
+
+out:
+	if (current_acl_list != NULL) {
+		g_slist_foreach (current_acl_list, (GFunc) hal_acl_free, NULL);
+		g_slist_free (current_acl_list);
+	}
+}
+
+
+static void
+acl_device_added (void)
+{
+	char *s;
+	char *udi;
+	char *device;
+	GSList *afd_list = NULL;
+	ACLForDevice *afd = NULL;
+
+	/* we can avoid round-trips to the HAL daemon by using what's in the environment */
+
+	if ((udi = getenv ("UDI")) == NULL)
+		goto out;
+
+	if ((device = getenv ("HAL_PROP_ACCESS_CONTROL_FILE")) == NULL)
+		goto out;
+
+	afd = acl_for_device_new (udi);
+	acl_for_device_set_device (afd, device);
+	afd_list = g_slist_prepend (NULL, afd);
+
+	/* get ACL granting policy from HAL properties */
+	if ((s = getenv ("HAL_PROP_ACCESS_CONTROL_GRANT_LOCAL_SESSION")) != NULL) {
+		afd->grant_to_local_seat = (strcmp (s, "true") == 0);
+	}
+	if ((s = getenv ("HAL_PROP_ACCESS_CONTROL_GRANT_LOCAL_ACTIVE_SESSION")) != NULL) {
+		afd->grant_to_local_seat_active_only = (strcmp (s, "true") == 0);
+	}
+	if ((s = getenv ("HAL_PROP_ACCESS_CONTROL_GRANT_USER")) != NULL) {
+		char **sv;
+		sv = g_strsplit (s, "\t", 0);
+		afd_grant_to_uid_from_userlist (afd, sv);
+		g_strfreev (sv);
+	}
+	if ((s = getenv ("HAL_PROP_ACCESS_CONTROL_GRANT_GROUP")) != NULL) {
+		char **sv;
+		sv = g_strsplit (s, "\t", 0);
+		afd_grant_to_gid_from_grouplist (afd, sv);
+		g_strfreev (sv);
+	}
+
+	/* determine what ACL's we want to put on the given device
+	 * files; e.g. apply the seat / session policy
+	 *
+	 * (entries in afd_list will be modified, see ACLForDevice
+	 * data structure)
+	 */
+	if (!visit_seats_and_sessions (acl_device_added_visitor, (gpointer) afd_list)) {
+		printf ("Error visiting seats and sessions\n");
+		goto out;
+	}
+	
+	printf ("%d: adding ACL's for %s\n", getpid (), device);
+
+	acl_compute_changes (afd_list, FALSE);
+
+out:
+	if (afd != NULL)
+		acl_for_device_free (afd);
+	if (afd_list != NULL)
+		g_slist_free (afd_list);
+}
+
+static void
+acl_device_removed (void)
+{
+	char *udi;
+	char *device;
+	GSList *afd_list = NULL;
+	ACLForDevice *afd = NULL;
+
+	/* we can avoid round-trips to the HAL daemon by using what's in the environment */
+
+	if ((udi = getenv ("UDI")) == NULL)
+		goto out;
+
+	if ((device = getenv ("HAL_PROP_ACCESS_CONTROL_FILE")) == NULL)
+		goto out;
+
+	afd = acl_for_device_new (udi);
+	acl_for_device_set_device (afd, device);
+	afd_list = g_slist_prepend (NULL, afd);
+
+	/* since this device is to be removed don't set policy - this means "grant it to no-one"; and since
+	 * there is no-one to grant it to.. we don't need to visit any seats
+	 */
+	printf ("%d: removing ACL's for %s\n", getpid (), device);
+
+	/* only update the ACL list, don't invoke setfacl(1) on the
+	 * files (see note in acl_apply_changes()) 
+	 */
+	acl_compute_changes (afd_list, TRUE);
+
+out:
+	if (afd != NULL)
+		acl_for_device_free (afd);
+	if (afd_list != NULL)
+		g_slist_free (afd_list);
+}
+
+static void
+acl_reconfigure_all (void)
+{
+	int i;
+	int num_devices;
+	char **udis;
+	DBusError error;
+	LibHalContext *hal_ctx;
+	GSList *afd_list = NULL;
+
+	printf ("%d: reconfiguring all ACL's\n", getpid ());
+
+	dbus_error_init (&error);
+	if ((hal_ctx = libhal_ctx_init_direct (&error)) == NULL) {
+		printf ("%d: Cannot connect to hald: %s: %s\n", getpid (), error.name, error.message);
+		LIBHAL_FREE_DBUS_ERROR (&error);
+		goto out;
+	}
+
+	if ((udis = libhal_find_device_by_capability (hal_ctx, "access_control", &num_devices, &error)) == NULL) {
+		printf ("%d: Cannot get list of devices of capability 'acl'\n", getpid ());
+		goto out;
+	}
+
+	for (i = 0; udis[i] != NULL; i++) {
+		LibHalPropertySet *props;
+		LibHalPropertySetIterator psi;
+		char *device = NULL;
+		ACLForDevice *afd;
+		char **sv;
+
+		if ((props = libhal_device_get_all_properties (hal_ctx, udis[i], &error)) == NULL) {
+			printf ("%d: Cannot get list properties for '%s'\n", getpid (), udis[1]);
+			goto out;
+		}
+
+		afd = acl_for_device_new (udis[i]);
+
+		libhal_psi_init (&psi, props);
+		while (libhal_psi_has_more (&psi)) {
+			char *key;
+			key = libhal_psi_get_key (&psi);
+			if (strcmp (key, "access_control.file") == 0) {
+				device = libhal_psi_get_string (&psi);
+			} else if (strcmp (key, "access_control.grant_local_session") == 0) {
+				afd->grant_to_local_seat = libhal_psi_get_bool (&psi);
+			} else if (strcmp (key, "access_control.grant_local_active_session") == 0) {
+				afd->grant_to_local_seat_active_only = libhal_psi_get_bool (&psi);
+			} else if (strcmp (key, "access_control.grant_user") == 0) {
+				sv = libhal_psi_get_strlist (&psi);
+				afd_grant_to_uid_from_userlist (afd, sv);
+			} else if (strcmp (key, "access_control.grant_group") == 0) {
+				sv = libhal_psi_get_strlist (&psi);
+				afd_grant_to_gid_from_grouplist (afd, sv);
+			}
+			libhal_psi_next (&psi);
+		}
+
+		if (device == NULL) {
+			printf ("%d: access_control.file not set for '%s'\n", getpid (), udis[i]);
+			goto out;
+		} else {
+			acl_for_device_set_device (afd, device);
+			afd_list = g_slist_prepend (afd_list, afd);
+		}
+
+		libhal_free_property_set (props);
+	}
+	libhal_free_string_array (udis);
+
+	if (g_slist_length (afd_list) > 0) {
+		if (!visit_seats_and_sessions (acl_device_added_visitor, (gpointer) afd_list)) {
+			printf ("Error visiting seats and sessions\n");
+			goto out;
+		}
+		acl_compute_changes (afd_list, FALSE);
+	}
+
+out:
+	;
+}
+
+static void
+acl_remove_all (void)
+{
+	GSList *current_acl_list = NULL;
+	GSList *i;
+
+	if (!get_current_acl_list (&current_acl_list)) {
+		printf ("Error getting ACL's currently applied\n");
+		goto out;
+	}
+
+	for (i = current_acl_list; i != NULL; i = g_slist_next (i)) {
+		ACLCurrent *ha = (ACLCurrent *) i->data;
+		ha->remove = TRUE;
+	}
+
+	acl_apply_changes (current_acl_list, FALSE, FALSE);
+
+out:
+	if (current_acl_list != NULL) {
+		g_slist_foreach (current_acl_list, (GFunc) hal_acl_free, NULL);
+		g_slist_free (current_acl_list);
+	}
+}
+
+static int lock_acl_fd = -1;
+
+static gboolean
+acl_lock (void)
+{
+	if (lock_acl_fd >= 0)
+		return TRUE;
+
+	printf ("%d: attempting to get lock on /var/lib/hal/acl-list\n", getpid ());
+
+	lock_acl_fd = open ("/var/lib/hal/acl-list", O_CREAT | O_RDWR);
+	/* TODO: set correct mode, owner etc. */
+
+	if (lock_acl_fd < 0) {
+		printf ("%d: error opening/creating /var/lib/hal/acl-list\n", getpid ());
+		return FALSE;
+	}
+
+tryagain:
+#if sun
+	if (lockf (lock_acl_fd, F_LOCK, 0) != 0)
+#else
+	if (flock (lock_acl_fd, LOCK_EX) != 0)
+#endif
+	{
+		if (errno == EINTR)
+			goto tryagain;
+		return FALSE;
+	}
+	
+	printf ("%d: got lock on /var/lib/hal/acl-list\n", getpid ());
+	return TRUE;
+}
+
+static void
+acl_unlock (void)
+{
+#if sun
+	lockf (lock_acl_fd, F_ULOCK, 0);
+#else
+	flock (lock_acl_fd, LOCK_UN);
+#endif
+	close (lock_acl_fd);
+	lock_acl_fd = -1;
+	printf ("%d: released lock on /var/lib/hal/acl-list\n", getpid ());
+}
+
+
+int
+main (int argc, char *argv[])
+{
+	if (argc != 2) {
+		printf ("hal-acl-tool should only be invoked by hald\n");
+		goto out;
+	}
+	
+	if (!acl_lock ()) {
+		goto out;
+	}
+
+	if (strcmp (argv[1], "--add-device") == 0) {
+		acl_device_added ();
+	} else if (strcmp (argv[1], "--remove-device") == 0) {
+		acl_device_removed ();
+	} else if (strcmp (argv[1], "--reconfigure") == 0) {
+		acl_reconfigure_all ();
+	} else if (strcmp (argv[1], "--remove-all") == 0) {
+		acl_remove_all ();
+	}
+
+	acl_unlock ();
+
+out:
+	return 0;
+}
diff --git a/tools/hal-storage-shared.c b/tools/hal-storage-shared.c
index 19e1819..11ec3d1 100644
--- a/tools/hal-storage-shared.c
+++ b/tools/hal-storage-shared.c
@@ -573,6 +573,7 @@ lock_hal_mtab (void)
 	printf ("%d: XYA attempting to get lock on /media/.hal-mtab-lock\n", getpid ());
 
 	lock_mtab_fd = open ("/media/.hal-mtab-lock", O_CREAT | O_RDWR);
+	/* TODO: set correct mode, owner etc. */
 
 	if (lock_mtab_fd < 0)
 		return FALSE;


More information about the hal-commit mailing list