hal: Branch 'origin'

David Zeuthen david at kemper.freedesktop.org
Sat Sep 23 14:15:03 PDT 2006


 hald/linux/blockdev.c     |    2 
 hald/linux/coldplug.c     |  645 ++++++++++++++++------------------------------
 hald/linux/hotplug.c      |  106 +------
 hald/linux/osspec.c       |   99 ++++---
 hald/linux/osspec_linux.h |    2 
 5 files changed, 329 insertions(+), 525 deletions(-)

New commits:
diff-tree cd1823f11eea4de5e7a236bc19b29397658946f8 (from 6670789d3ca8cbc7c5ea9634f0621664c85e7dbb)
Author: Kay Sievers <kay.sievers at suse.de>
Date:   Sat Sep 23 00:13:56 2006 +0200

    rework Linux coldplug to work with future kernel changes
    
    Adapt the current HAL coldplug/hotplug logic to work with future
    kernels, where sysfs will have all device in a unified tree at
    /sys/devices.

diff --git a/hald/linux/blockdev.c b/hald/linux/blockdev.c
index d6b10c4..7f48dbe 100644
--- a/hald/linux/blockdev.c
+++ b/hald/linux/blockdev.c
@@ -326,7 +326,7 @@ generate_fakevolume_hotplug_event_add_fo
 
 	hotplug_event = g_new0 (HotplugEvent, 1);
 	hotplug_event->action = HOTPLUG_ACTION_ADD;
-	hotplug_event->type = HOTPLUG_EVENT_SYSFS;
+	hotplug_event->type = HOTPLUG_EVENT_SYSFS_BLOCK;
 	g_strlcpy (hotplug_event->sysfs.subsystem, "block", sizeof (hotplug_event->sysfs.subsystem));
 	g_strlcpy (hotplug_event->sysfs.sysfs_path, fake_sysfs_path, sizeof (hotplug_event->sysfs.sysfs_path));
 	if (device_file != NULL)
diff --git a/hald/linux/coldplug.c b/hald/linux/coldplug.c
index db9e922..2ce29f6 100644
--- a/hald/linux/coldplug.c
+++ b/hald/linux/coldplug.c
@@ -1,11 +1,9 @@
-/***************************************************************************
- * CVSID: $Id$
- *
- * coldplug.c : Synthesize hotplug events when starting up
+
+/*
+ * Synthesize device events when starting up
  *
  * Copyright (C) 2004 David Zeuthen, <david at fubar.dk>
- *
- * Licensed under the Academic Free License version 2.1
+ * Copyright (C) 2005-2006 Kay Sievers <kay.sievers at novell.com>
  *
  * 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
@@ -21,7 +19,7 @@
  * 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>
@@ -30,9 +28,9 @@
 #include <stdint.h>
 #include <stdio.h>
 #include <string.h>
+#include <dirent.h>
 #include <sys/stat.h>
 #include <unistd.h>
-
 #include <dbus/dbus.h>
 #include <dbus/dbus-glib.h>
 
@@ -41,49 +39,22 @@
 #include "../logger.h"
 #include "../osspec.h"
 #include "../util.h"
-
 #include "osspec_linux.h"
 #include "hotplug.h"
-
 #include "coldplug.h"
 
 #define DMPREFIX "dm-"
 
-/* For debugging */
-#define HAL_COLDPLUG_VERBOSE
+struct sysfs_device {
+	char *path;
+	char *subsystem;
+	HotplugEventType type;
+};
 
 static GHashTable *sysfs_to_udev_map;
+static GSList *device_list;
 static char dev_root[HAL_PATH_MAX];
 
-/* Returns the path of the udevinfo program 
- *
- * @return                      Path or NULL if udevinfo program is not found
- */
-static const gchar *
-hal_util_get_udevinfo_path (void)
-{
-	guint i;
-	struct stat s;
-	static gchar *path = NULL;
-	gchar *possible_paths[] = {
-		"/usr/bin/udevinfo",
-		"/bin/udevinfo",
-		"/usr/sbin/udevinfo",
-		"/sbin/udevinfo",
-	};
-
-	if (path != NULL)
-		 return path;
-
-	for (i = 0; i < sizeof (possible_paths) / sizeof (char *); i++) {
-		if (stat (possible_paths[i], &s) == 0 && S_ISREG (s.st_mode)) {
-			path = possible_paths[i];
-			break;
-		}
-	}
-	return path;
-}
-
 static gboolean
 hal_util_init_sysfs_to_udev_map (void)
 {
@@ -95,8 +66,6 @@ hal_util_init_sysfs_to_udev_map (void)
 	char *p;
 
 	sysfs_to_udev_map = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
-	udevdb_export_argv[0] = (char *) hal_util_get_udevinfo_path ();
-	udevroot_argv[0] = (char *) hal_util_get_udevinfo_path ();
 
 	/* get udevroot */
 	if (g_spawn_sync ("/", udevroot_argv, NULL, 0, NULL, NULL,
@@ -153,9 +122,8 @@ hal_util_init_sysfs_to_udev_map (void)
 		if (line[0] == '\0') {
 			if (hotplug_event != NULL) {
 				g_hash_table_insert (sysfs_to_udev_map, g_strdup (hotplug_event->sysfs.sysfs_path), hotplug_event);
-#ifdef HAL_COLDPLUG_VERBOSE
-				printf ("Got '%s' -> '%s'\n", hotplug_event->sysfs.sysfs_path, hotplug_event->sysfs.device_file);
-#endif
+				HAL_INFO (("found (udevdb export) '%s' -> '%s'",
+					   hotplug_event->sysfs.sysfs_path, hotplug_event->sysfs.device_file));
 				hotplug_event = NULL;
 			}
 			continue;
@@ -234,7 +202,7 @@ error:
 }
 
 static HotplugEvent
-*coldplug_get_hotplug_event(const gchar *sysfs_path, const gchar *subsystem)
+*coldplug_get_hotplug_event(const gchar *sysfs_path, const gchar *subsystem, HotplugEventType type)
 {
 	HotplugEvent *hotplug_event, *hotplug_event_udev;
 	const char *pos;
@@ -249,7 +217,7 @@ static HotplugEvent
 	hotplug_event_udev = (HotplugEvent *) g_hash_table_lookup (sysfs_to_udev_map, sysfs_path);
 	if (hotplug_event_udev != NULL) {
 		memcpy(hotplug_event, hotplug_event_udev, sizeof(HotplugEvent));
-		HAL_INFO (("found in udevdb '%s' '%s'", hotplug_event->sysfs.sysfs_path, hotplug_event->sysfs.device_file));
+		HAL_INFO (("new event (dev node from udev) '%s' '%s'", hotplug_event->sysfs.sysfs_path, hotplug_event->sysfs.device_file));
 	} else {
 		/* device is not in udev database */
 		g_strlcpy(hotplug_event->sysfs.sysfs_path, sysfs_path, sizeof(hotplug_event->sysfs.sysfs_path));
@@ -271,421 +239,272 @@ static HotplugEvent
 		if (!S_ISBLK (statbuf.st_mode) && !S_ISCHR (statbuf.st_mode))
 			goto no_node;
 
-		HAL_INFO (("found device_file %s for sysfs_path %s", path, sysfs_path));
+		HAL_INFO (("new event (dev node from kernel name) '%s' '%s'", sysfs_path, path));
 		g_strlcpy(hotplug_event->sysfs.device_file, path, sizeof(hotplug_event->sysfs.device_file));
 	}
 
 no_node:
+	if (hotplug_event->sysfs.device_file[0] == '\0')
+		HAL_INFO (("new event (no dev node) '%s'", sysfs_path));
 	g_strlcpy (hotplug_event->sysfs.subsystem, subsystem, sizeof (hotplug_event->sysfs.subsystem));
 	hotplug_event->action = HOTPLUG_ACTION_ADD;
-	hotplug_event->type = HOTPLUG_EVENT_SYSFS;
+	hotplug_event->type = type;
 	hotplug_event->sysfs.net_ifindex = -1;
 	return hotplug_event;
 }
 
-static gboolean
-coldplug_synthesize_block_event(const gchar *f);
-
-static void
-coldplug_compute_visit_device (const gchar *path, 
-			       GHashTable *sysfs_to_bus_map, 
-			       GHashTable *sysfs_to_class_in_devices_map);
-
-static void
-free_hash_sys_to_class_in_dev (gpointer key, gpointer value, gpointer user_data)
-{
-	GSList *i;
-	GSList *list = (GSList *) value;
-
-	for (i = list; i != NULL; i = g_slist_next (i))
-		g_free (i->data);
-	g_slist_free (list);
-}
-
-/** This function serves one major purpose : build an ordered list of
- *  pairs (sysfs path, subsystem) to process when starting up:
- *  coldplugging. The ordering is arranged such that all bus-devices
- *  are visited in the same order as performing a traversal through
- *  the tree; e.g. bus-device A is not processed before bus-device B
- *  if B is a parent of A connection-wise.
- *
- *  After all bus-devices are added to the list, then all block devices are
- *  processed in the order they appear.
- *
- *  Finally, all class devices are added to the list.
- *
- *  @return                     Ordered list of sysfs paths or NULL 
- *                              if there was an error
- */
-gboolean
-coldplug_synthesize_events (void)
+static int device_list_insert(const char *path, const char *subsystem,
+			      HotplugEventType type)
 {
-	GDir *dir;
-	GError *err = NULL;
-	gchar path[HAL_PATH_MAX];
-	gchar path1[HAL_PATH_MAX];
-	gchar path2[HAL_PATH_MAX];
-	const gchar *f;
-	const gchar *f1;
-	const gchar *f2;
-	GSList *li;
-
-	/** Mapping from sysfs path to subsystem for bus devices. This is consulted
-	 *  when traversing /sys/devices
-	 *
-	 *  Example:
-	 *
-	 * /sys/devices/pci0000:00/0000:00:07.2/usb1/1-1/1-1:1.0/host7/7:0:0:0  -> scsi
-	 * /sys/devices/pci0000:00/0000:00:07.1/ide1/1.1                        -> ide
-	 * /sys/devices/pci0000:00/0000:00:07.1/ide1/1.0                        -> ide
-	 * /sys/devices/pci0000:00/0000:00:07.1/ide0/0.0                        -> ide
-	 * /sys/devices/pci0000:00/0000:00:07.2/usb1/1-1/1-1:1.0                -> usb
-	 * /sys/devices/pci0000:00/0000:00:07.2/usb1/1-1                        -> usb
-	 * /sys/devices/pci0000:00/0000:00:07.2/usb1/1-0:1.0                    -> usb
-	 * /sys/devices/pci0000:00/0000:00:07.2/usb1                            -> usb
-	 * /sys/devices/pci0000:00/0000:00:04.1/0000:06:00.0                    -> pci
-	 * /sys/devices/pci0000:00/0000:00:01.0/0000:01:00.0                    -> pci
-	 * /sys/devices/pci0000:00/0000:00:08.0                                 -> pci
-	 * /sys/devices/platform/vesafb0                                        -> platform
-	 */
-	GHashTable *sysfs_to_bus_map = NULL;
-
-        /** Mapping from sysfs path in /sys/devices to the pairs (sysfs class path, classname)
-	 *  for class devices; note that more than one class device might map to a physical device
-	 *
-	 * Example:
-	 *
-	 * /sys/devices/pci0000:00/0000:00:07.2/usb1/1-1/1-1:1.0/host7  -> (/sys/class/scsi_host/host7, scsi_host)
-	 * /sys/devices/platform/i8042/serio0/serio2 -> (/sys/class/input/event2, input, /sys/class/input/mouse1, input)
-	 */
-	GHashTable *sysfs_to_class_in_devices_map = NULL;
-
-	/* Class devices without device links; string list; example
-	 *
-	 * (/sys/class/input/mice, mouse, /sys/class/mem/null, mem, ...)
-	 */
-	GSList *sysfs_other_class_dev = NULL;
-
-	/* Device mapper devices that should be added after all other block devices
-	 *
-	 * Example:
-	 *
-	 * (/sys/block/dm-0)
-	 */
-	GSList *sysfs_dm_dev = NULL;
+	char filename[HAL_PATH_MAX];
+	struct stat statbuf;
+	struct sysfs_device *sysfs_dev = NULL;
 
-	if (hal_util_init_sysfs_to_udev_map () == FALSE) {
-		HAL_ERROR (("Unable to get sysfs to dev map"));
+	/* we only have a device, if we have an uevent file */
+	g_strlcpy(filename, path, sizeof(filename));
+	g_strlcat(filename, "/uevent", sizeof(filename));
+	if (stat(filename, &statbuf) < 0)
 		goto error;
-	}
-
-	/* build bus map */
-	sysfs_to_bus_map = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
-	g_snprintf (path, HAL_PATH_MAX, "%s/bus", get_hal_sysfs_path ());
-	if ((dir = g_dir_open (path, 0, &err)) == NULL) {
-		HAL_ERROR (("Unable to open %/bus: %s", get_hal_sysfs_path (), err->message));
-		g_error_free (err);
+	if (!(statbuf.st_mode & S_IWUSR))
 		goto error;
-	}
-	while ((f = g_dir_read_name (dir)) != NULL) {
-		GDir *dir1;
 
-		g_snprintf (path, HAL_PATH_MAX, "%s/bus/%s", get_hal_sysfs_path (), f);
-		if ((dir1 = g_dir_open (path, 0, &err)) == NULL) {
-			HAL_ERROR (("Unable to open %/bus/%s: %s", get_hal_sysfs_path (), f, err->message));
-			g_error_free (err);
-			goto error;
-		}
-		while ((f1 = g_dir_read_name (dir1)) != NULL) {
-
-			if (strcmp (f1, "devices") == 0) {
-				GDir *dir2;
+	sysfs_dev = g_new0 (struct sysfs_device, 1);
+	if (sysfs_dev == NULL)
+		goto error;
 
-				g_snprintf (path, HAL_PATH_MAX, "%s/bus/%s/%s", 
-					    get_hal_sysfs_path (), f, f1);
-				if ((dir2 = g_dir_open (path, 0, &err)) == NULL) {
-					HAL_ERROR (("Unable to open %s/bus/%s/%s: %s", 
-						    get_hal_sysfs_path (), f, f1, err->message));
-					g_error_free (err);
-					goto error;
-				}
-				while ((f2 = g_dir_read_name (dir2)) != NULL) {
-					gchar *target;
-					gchar *normalized_target;
-					g_snprintf (path, HAL_PATH_MAX, "%s/bus/%s/%s/%s", 
-						    get_hal_sysfs_path (), f, f1, f2);
-					if ((target = g_file_read_link (path, &err)) == NULL) {
-						HAL_ERROR (("%s/bus/%s/%s/%s is not a symlink: %s!", 
-							    get_hal_sysfs_path (), 
-							    f, f1, f2, err->message));
-						g_error_free (err);
-						goto error;
-					}
-
-					g_snprintf (path, HAL_PATH_MAX, "%s/bus/%s/%s", get_hal_sysfs_path (), f, f1);
-					normalized_target = hal_util_get_normalized_path (path, target);
-					g_free (target);
+	/* resolve possible link to real target */
+	if (lstat(path, &statbuf) < 0)
+		goto error;
+	if (S_ISLNK(statbuf.st_mode)) {
+		gchar *target;
 
-					g_hash_table_insert (sysfs_to_bus_map, normalized_target, g_strdup(f));
+		if ((target = g_file_read_link (path, NULL)) != NULL) {
+			gchar *normalized_target;
 
-				}
-				g_dir_close (dir2);
-			}
+			g_strlcpy(filename, path, sizeof(filename));
+			hal_util_path_ascend (filename);
+			normalized_target = hal_util_get_normalized_path (filename, target);
+			g_free (target);
+			sysfs_dev->path = normalized_target;
+			goto found;
 		}
-		g_dir_close (dir1);
-	}
-	g_dir_close (dir);
-
-	/* build class map and class device map (values are free in separate foreach()) */
-	sysfs_to_class_in_devices_map = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
-	g_snprintf (path, HAL_PATH_MAX, "%s/class" , get_hal_sysfs_path ());
-	if ((dir = g_dir_open (path, 0, &err)) == NULL) {
-		HAL_ERROR (("Unable to open %/class: %s", get_hal_sysfs_path (), err->message));
 		goto error;
 	}
-	while ((f = g_dir_read_name (dir)) != NULL) {
-		GDir *dir1;
 
-		g_snprintf (path, HAL_PATH_MAX, "%s/class/%s" , get_hal_sysfs_path (), f);
-		if ((dir1 = g_dir_open (path, 0, &err)) == NULL) {
-			HAL_ERROR (("Unable to open %s/class/%s: %s", get_hal_sysfs_path (), f, err->message));
-			g_error_free (err);
-			goto error;
-		}
-		while ((f1 = g_dir_read_name (dir1)) != NULL) {
-			gchar *target;
-			gchar *normalized_target;
+	sysfs_dev->path = g_strdup (path);
+found:
+	sysfs_dev->subsystem = g_strdup (subsystem);
+	device_list = g_slist_prepend (device_list, sysfs_dev);
+	return 0;
 
-			g_snprintf (path2, HAL_PATH_MAX, "%s/class/%s/%s", get_hal_sysfs_path (), f, f1);
+error:
+	g_free (sysfs_dev);
+	return -1;
+}
 
-			/* check if we find a symlink here pointing to a device _inside_ a class device,
-			 * like "input" in 2.6.15. This kernel sysfs layout will change again in the future,
-			 * for now resolve the link to the "real" device path, like real hotplug events
-			 * devpath would have
-			 */
-			if ((target = g_file_read_link (path2, NULL)) != NULL) {
-				char *pos = strrchr(path2, '/');
-
-				if (pos)
-					pos[0] = '\0';
-				normalized_target = hal_util_get_normalized_path (path2, target);
-				g_free (target);
-				g_strlcpy(path2, normalized_target, sizeof(path2));
-				g_free (normalized_target);
-			}
-
-			/* Accept net devices without device links too, they may be coldplugged PCMCIA devices */
-			g_snprintf (path1, HAL_PATH_MAX, "%s/device", path2);
-			if (((target = g_file_read_link (path1, NULL)) == NULL)) {
-				/* no device link */
-				sysfs_other_class_dev = g_slist_append (sysfs_other_class_dev, g_strdup (path2));
-				sysfs_other_class_dev = g_slist_append (sysfs_other_class_dev, g_strdup (f));
-			} else {
-				GSList *classdev_strings;
-
-				normalized_target = hal_util_get_normalized_path (path2, target);
-				g_free (target);
-
-				classdev_strings = g_hash_table_lookup (sysfs_to_class_in_devices_map,
-									normalized_target);
-
-				classdev_strings = g_slist_append (classdev_strings, g_strdup (path2));
-				classdev_strings = g_slist_append (classdev_strings, g_strdup (f));
-				g_hash_table_replace (sysfs_to_class_in_devices_map,
-						      normalized_target, classdev_strings);
-			}				
+static void scan_bus(void)
+{
+	char base[HAL_PATH_MAX];
+	DIR *dir;
+	struct dirent *dent;
+
+	g_strlcpy(base, get_hal_sysfs_path (), sizeof(base));
+	g_strlcat(base, "/bus", sizeof(base));
+
+	dir = opendir(base);
+	if (dir != NULL) {
+		for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) {
+			char dirname[HAL_PATH_MAX];
+			DIR *dir2;
+			struct dirent *dent2;
+
+			if (dent->d_name[0] == '.')
+				continue;
+
+			g_strlcpy(dirname, base, sizeof(dirname));
+			g_strlcat(dirname, "/", sizeof(dirname));
+			g_strlcat(dirname, dent->d_name, sizeof(dirname));
+			g_strlcat(dirname, "/devices", sizeof(dirname));
+
+			/* look for devices */
+			dir2 = opendir(dirname);
+			if (dir2 != NULL) {
+				for (dent2 = readdir(dir2); dent2 != NULL; dent2 = readdir(dir2)) {
+					char dirname2[HAL_PATH_MAX];
+
+					if (dent2->d_name[0] == '.')
+						continue;
+
+					g_strlcpy(dirname2, dirname, sizeof(dirname2));
+					g_strlcat(dirname2, "/", sizeof(dirname2));
+					g_strlcat(dirname2, dent2->d_name, sizeof(dirname2));
+					device_list_insert(dirname2, dent->d_name, HOTPLUG_EVENT_SYSFS_BUS);
+				}
+				closedir(dir2);
+			}
 		}
-		g_dir_close (dir1);
+		closedir(dir);
 	}
-	g_dir_close (dir);
+}
 
-	/* Now traverse /sys/devices and consult the map we've just
-	 * built; this includes adding a) bus devices; and b) class
-	 * devices that sit in /sys/devices */
-	g_snprintf (path, HAL_PATH_MAX, "%s/devices", get_hal_sysfs_path ());
-	if ((dir = g_dir_open (path, 0, &err)) == NULL) {
-		HAL_ERROR (("Unable to open %/devices: %s", get_hal_sysfs_path (), err->message));
-		g_error_free (err);
-		goto error;
-	}
-	while ((f = g_dir_read_name (dir)) != NULL) {
-		GDir *dir1;
+static void scan_block(void)
+{
+	char base[HAL_PATH_MAX];
+	DIR *dir;
+	struct dirent *dent;
+	struct stat statbuf;
 
-		g_snprintf (path, HAL_PATH_MAX, "%s/devices/%s", get_hal_sysfs_path (), f);
-		if ((dir1 = g_dir_open (path, 0, &err)) == NULL) {
-			HAL_ERROR (("Unable to open %/devices/%s: %s", get_hal_sysfs_path (), f, err->message));
-			g_error_free (err);
-			goto error;
+	/* skip if "block" is already a "class" */
+	g_strlcpy(base, get_hal_sysfs_path (), sizeof(base));
+	g_strlcat(base, "/class/block", sizeof(base));
+	if (stat(base, &statbuf) == 0)
+		return;
+
+	g_strlcpy(base, get_hal_sysfs_path (), sizeof(base));
+	g_strlcat(base, "/block", sizeof(base));
+
+	dir = opendir(base);
+	if (dir != NULL) {
+		for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) {
+			char dirname[HAL_PATH_MAX];
+			DIR *dir2;
+			struct dirent *dent2;
+
+			if (dent->d_name[0] == '.')
+				continue;
+
+			g_strlcpy(dirname, base, sizeof(dirname));
+			g_strlcat(dirname, "/", sizeof(dirname));
+			g_strlcat(dirname, dent->d_name, sizeof(dirname));
+			if (device_list_insert(dirname, "block", HOTPLUG_EVENT_SYSFS_BLOCK) != 0)
+				continue;
+
+			/* look for partitions */
+			dir2 = opendir(dirname);
+			if (dir2 != NULL) {
+				for (dent2 = readdir(dir2); dent2 != NULL; dent2 = readdir(dir2)) {
+					char dirname2[HAL_PATH_MAX];
+
+					if (dent2->d_name[0] == '.')
+						continue;
+
+					if (!strcmp(dent2->d_name,"device"))
+						continue;
+					g_strlcpy(dirname2, dirname, sizeof(dirname2));
+					g_strlcat(dirname2, "/", sizeof(dirname2));
+					g_strlcat(dirname2, dent2->d_name, sizeof(dirname2));
+					device_list_insert(dirname2, "block", HOTPLUG_EVENT_SYSFS_BLOCK);
+				}
+				closedir(dir2);
+			}
 		}
-		while ((f1 = g_dir_read_name (dir1)) != NULL) {
+		closedir(dir);
+	}
+}
 
-			g_snprintf (path, HAL_PATH_MAX, "%s/devices/%s/%s", get_hal_sysfs_path (), f, f1);
-			coldplug_compute_visit_device (path, sysfs_to_bus_map, sysfs_to_class_in_devices_map);
+static void scan_class(void)
+{
+	char base[HAL_PATH_MAX];
+	DIR *dir;
+	struct dirent *dent;
+
+	g_strlcpy(base, get_hal_sysfs_path (), sizeof(base));
+	g_strlcat(base, "/class", sizeof(base));
+
+	dir = opendir(base);
+	if (dir != NULL) {
+		for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) {
+			char dirname[HAL_PATH_MAX];
+			DIR *dir2;
+			struct dirent *dent2;
+
+			if (dent->d_name[0] == '.')
+				continue;
+
+			g_strlcpy(dirname, base, sizeof(dirname));
+			g_strlcat(dirname, "/", sizeof(dirname));
+			g_strlcat(dirname, dent->d_name, sizeof(dirname));
+			dir2 = opendir(dirname);
+			if (dir2 != NULL) {
+				for (dent2 = readdir(dir2); dent2 != NULL; dent2 = readdir(dir2)) {
+					char dirname2[HAL_PATH_MAX];
+
+					if (dent2->d_name[0] == '.')
+						continue;
+					if (!strcmp(dent2->d_name, "device"))
+						continue;
+
+					g_strlcpy(dirname2, dirname, sizeof(dirname2));
+					g_strlcat(dirname2, "/", sizeof(dirname2));
+					g_strlcat(dirname2, dent2->d_name, sizeof(dirname2));
+					device_list_insert(dirname2, dent->d_name, HOTPLUG_EVENT_SYSFS_CLASS);
+				}
+				closedir(dir2);
+			}
 		}
-		g_dir_close (dir1);
+		closedir(dir);
 	}
-	g_dir_close (dir);
+}
 
-	g_hash_table_destroy (sysfs_to_bus_map);
-	/* free keys and values in this complex hash */
-	g_hash_table_foreach (sysfs_to_class_in_devices_map, free_hash_sys_to_class_in_dev, NULL);
-	g_hash_table_destroy (sysfs_to_class_in_devices_map);
-
-	/* we are guaranteed, per construction, that the len of this list is even */
-	for (li = sysfs_other_class_dev; li != NULL; li = g_slist_next (g_slist_next (li))) {
-		gchar *sysfs_path;
-		gchar *subsystem;
-		HotplugEvent *hotplug_event;
+static void queue_events(void)
+{
+	GSList *dev;
 
-		sysfs_path = (gchar *) li->data;
-		subsystem = (gchar *) li->next->data;
+	/* queue events for the devices */
+	for (dev = device_list; dev != NULL; dev = g_slist_next (dev)) {
+		HotplugEvent *hotplug_event;
+		struct sysfs_device *sysfs_dev = dev->data;
 
-#ifdef HAL_COLDPLUG_VERBOSE
-		printf ("class: %s (%s) (no device link)\n", sysfs_path, subsystem);
-#endif
-		hotplug_event = coldplug_get_hotplug_event (sysfs_path, subsystem);
+		hotplug_event = coldplug_get_hotplug_event (sysfs_dev->path,
+							    sysfs_dev->subsystem,
+							    sysfs_dev->type);
 		hotplug_event_enqueue (hotplug_event);
 
-		g_free (li->data);
-		g_free (li->next->data);
+		g_free (sysfs_dev->path);
+		g_free (sysfs_dev->subsystem);
 	}
-	g_slist_free (sysfs_other_class_dev);
 
-	/* add block devices */
-	g_snprintf (path, HAL_PATH_MAX, "%s/block", get_hal_sysfs_path ());
-	if ((dir = g_dir_open (path, 0, &err)) == NULL) {
-		HAL_ERROR (("Unable to open %s: %s", path, err->message));
-		g_error_free (err);
-		goto error;
-	}
-	while ((f = g_dir_read_name (dir)) != NULL) {
-		if (g_str_has_prefix (f, DMPREFIX)) {
-			/* defer dm devices */
-			sysfs_dm_dev = g_slist_append(sysfs_dm_dev, g_strdup(f));
-			continue;
-		}
-		if (coldplug_synthesize_block_event(f) == FALSE)
-			goto error;
-	}
-	/* process all dm devices last so that their backing devices exist */
-	for (li = sysfs_dm_dev; li != NULL; li = g_slist_next (g_slist_next (li))) {
-		if (coldplug_synthesize_block_event (li->data) == FALSE)
-			goto error;
-		g_free (li->data);
-	}
-	g_slist_free (sysfs_dm_dev);
-	g_dir_close (dir);
-
-	g_hash_table_destroy (sysfs_to_udev_map);
-
-	return TRUE;
-error:
-	HAL_ERROR (("Error building the ordered list of sysfs paths"));
-	return FALSE;
+	g_slist_free (device_list);
+	device_list = NULL;
 }
 
-static gboolean
-coldplug_synthesize_block_event(const gchar *f)
+static int _device_order (const void *d1, const void *d2)
 {
-	GDir *dir1;
-	HotplugEvent *hotplug_event;
-	GError *err = NULL;
-	gchar path[HAL_PATH_MAX];
-	gchar path1[HAL_PATH_MAX];
-	const gchar *f1;
+	const struct sysfs_device *dev1 = d1;
+	const struct sysfs_device *dev2 = d2;
 
-	g_snprintf (path, HAL_PATH_MAX, "%s/block/%s", get_hal_sysfs_path (), f);
-#ifdef HAL_COLDPLUG_VERBOSE
-	printf ("block: %s (block)\n",  path);
-#endif
-	hotplug_event = coldplug_get_hotplug_event (path, "block");
-	hotplug_event_enqueue (hotplug_event);
+	/* device mapper needs to be the last events, to have the other block devs already around */
+	if (strstr(dev2->path, "/" DMPREFIX))
+		return -1;
+	if (strstr(dev1->path, "/" DMPREFIX))
+		return 1;
 
-	if ((dir1 = g_dir_open (path, 0, &err)) == NULL) {
-		HAL_ERROR (("Unable to open %s: %s", path, err->message));
-		g_error_free (err);
-		goto error;
-	}
-	while ((f1 = g_dir_read_name (dir1)) != NULL) {
-		if (strncmp (f, f1, strlen (f)) == 0) {
-			g_snprintf (path1, HAL_PATH_MAX, "%s/%s", path, f1);
-#ifdef HAL_COLDPLUG_VERBOSE
-			printf ("block: %s (block)\n", path1);
-#endif
-			hotplug_event = coldplug_get_hotplug_event (path1, "block");
-			hotplug_event_enqueue (hotplug_event);
-		}
-	}
-	g_dir_close (dir1);
-       
-	return TRUE;
-error:
-	return FALSE;
+	return strcmp(dev1->path, dev2->path);
 }
 
-static void
-coldplug_compute_visit_device (const gchar *path,
-			       GHashTable *sysfs_to_bus_map, 
-			       GHashTable *sysfs_to_class_in_devices_map)
+gboolean
+coldplug_synthesize_events (void)
 {
-	gchar *bus;
-	GError *err = NULL;
-	GDir *dir;
-	const gchar *f;
-	/*HStringPair *pair;*/
-	GSList *class_devs;
-	GSList *i;
-
-	bus = g_hash_table_lookup (sysfs_to_bus_map, path);
-	if (bus != NULL) {
-		HotplugEvent *hotplug_event;
-#ifdef HAL_COLDPLUG_VERBOSE
-		printf ("bus:   %s (%s)\n", path, bus);
-#endif
-		hotplug_event = coldplug_get_hotplug_event (path, bus);
-		hotplug_event_enqueue (hotplug_event);
+	if (hal_util_init_sysfs_to_udev_map () == FALSE) {
+		HAL_ERROR (("Unable to get sysfs to dev map"));
+		goto error;
 	}
 
-	/* we are guaranteed, per construction, that the len of this list is even */
-	class_devs = g_hash_table_lookup (sysfs_to_class_in_devices_map, path);
-	for (i = class_devs; i != NULL; i = g_slist_next (g_slist_next (i))) {
-		gchar *sysfs_path;
-		gchar *subsystem;
-		HotplugEvent *hotplug_event;
+	scan_bus ();
+	device_list = g_slist_sort (device_list, _device_order);
+	queue_events ();
+
+	scan_class ();
+	device_list = g_slist_sort (device_list, _device_order);
+	queue_events ();
+
+	scan_block ();
+	device_list = g_slist_sort (device_list, _device_order);
+	queue_events ();
 
-		sysfs_path = (gchar *) i->data;
-		subsystem = (gchar *) i->next->data;
-#ifdef HAL_COLDPLUG_VERBOSE
-		printf ("class: %s (%s) (%s)\n", path, subsystem, sysfs_path);
-#endif
-		hotplug_event = coldplug_get_hotplug_event (sysfs_path, subsystem);
-		hotplug_event_enqueue (hotplug_event);
-	}
-
-	/* visit children; dont follow symlinks though.. */
-	err = NULL;
-	if ((dir = g_dir_open (path, 0, &err)) == NULL) {
-		/*HAL_ERROR (("Unable to open directory: %s", path, err->message));*/
-		g_error_free (err);
-		goto error;
-	}
-	while ((f = g_dir_read_name (dir)) != NULL) {
-		gchar path_child[HAL_PATH_MAX];
-		struct stat statbuf;
-
-		g_snprintf (path_child, HAL_PATH_MAX, "%s/%s", path, f);
-		if (lstat (path_child, &statbuf) == 0) {
-			if (!S_ISLNK (statbuf.st_mode)) {
-				/* recursion fun */
-				coldplug_compute_visit_device (path_child, 
-							       sysfs_to_bus_map, 
-							       sysfs_to_class_in_devices_map);
-			}
-		}
-	}
-	g_dir_close (dir);
+	g_hash_table_destroy (sysfs_to_udev_map);
+	return TRUE;
 
 error:
-	return;
+	return FALSE;
 }
-
diff --git a/hald/linux/hotplug.c b/hald/linux/hotplug.c
index 4eac8b9..5187c97 100644
--- a/hald/linux/hotplug.c
+++ b/hald/linux/hotplug.c
@@ -138,8 +138,13 @@ hotplug_event_begin_sysfs (HotplugEvent 
 		return;
 	}
 
+	/* subsystem "block" are all block devices */
+	if (hotplug_event->type == HOTPLUG_EVENT_SYSFS)
+		if (strcmp(hotplug_event->sysfs.subsystem, "block") == 0)
+			hotplug_event->type = HOTPLUG_EVENT_SYSFS_BLOCK;
+
 	/* get device type from already known device object */
-	if (d != NULL) {
+	if (hotplug_event->type == HOTPLUG_EVENT_SYSFS && d != NULL) {
 		HotplugEventType type;
 
 		type = hal_device_property_get_int (d, "linux.hotplug_type");
@@ -164,15 +169,15 @@ hotplug_event_begin_sysfs (HotplugEvent 
 		g_snprintf (subsystem, HAL_PATH_MAX, "%s/subsystem", hotplug_event->sysfs.sysfs_path);
 		subsystem_target = g_file_read_link (subsystem, NULL);
 		if (subsystem_target != NULL) {
-			if (strstr(subsystem_target, "/bus/") != NULL) {
+			if (strstr(subsystem_target, "/block") != NULL) {
+				HAL_INFO (("%s is a block device (subsystem)", hotplug_event->sysfs.sysfs_path));
+				hotplug_event->type = HOTPLUG_EVENT_SYSFS_BLOCK;
+			} else if (strstr(subsystem_target, "/bus/") != NULL) {
 				HAL_INFO (("%s is a bus device (subsystem)", hotplug_event->sysfs.sysfs_path));
 				hotplug_event->type = HOTPLUG_EVENT_SYSFS_BUS;
 			} else if (strstr(subsystem_target, "/class/") != NULL) {
 				HAL_INFO (("%s is a class device (subsystem)", hotplug_event->sysfs.sysfs_path));
 				hotplug_event->type = HOTPLUG_EVENT_SYSFS_CLASS;
-			} else if (strstr(subsystem_target, "/block") != NULL) {
-				HAL_INFO (("%s is a block device (subsystem)", hotplug_event->sysfs.sysfs_path));
-				hotplug_event->type = HOTPLUG_EVENT_SYSFS_BLOCK;
 			}
 			g_free (subsystem_target);
 		}
@@ -206,7 +211,8 @@ hotplug_event_begin_sysfs (HotplugEvent 
 	if (hotplug_event->type == HOTPLUG_EVENT_SYSFS_BUS) {
 		if (hotplug_event->action == HOTPLUG_ACTION_ADD) {
 			HalDevice *parent;
-			parent = hal_util_find_closest_ancestor (hotplug_event->sysfs.sysfs_path);
+
+			hal_util_find_known_parent (hotplug_event->sysfs.sysfs_path, &parent, NULL);
 			hotplug_event_begin_add_physdev (hotplug_event->sysfs.subsystem, 
 							 hotplug_event->sysfs.sysfs_path, 
 							 parent,
@@ -218,53 +224,23 @@ hotplug_event_begin_sysfs (HotplugEvent 
 		}
 	} else if (hotplug_event->type == HOTPLUG_EVENT_SYSFS_CLASS) {
 		if (hotplug_event->action == HOTPLUG_ACTION_ADD) {
-			gchar *target;
-			HalDevice *physdev;
-			char physdevpath[HAL_PATH_MAX];
-			gchar *sysfs_path_in_devices;
-
-			sysfs_path_in_devices = NULL;
+			HalDevice *parent;
+			gchar *parent_path;
 
 			/* /sbin/ifrename may be called from a hotplug handler before we process this,
 			 * so if index doesn't match, go ahead and find a new sysfs path
 			 */
 			fixup_net_device_for_renaming (hotplug_event);
 
-			g_snprintf (physdevpath, HAL_PATH_MAX, "%s/device", hotplug_event->sysfs.sysfs_path);
-			if (((target = g_file_read_link (physdevpath, NULL)) != NULL)) {
-				gchar *normalized_target;
-
-				normalized_target = hal_util_get_normalized_path (hotplug_event->sysfs.sysfs_path, target);
-				g_free (target);
-
-				sysfs_path_in_devices = g_strdup (normalized_target);
-
-				/* there may be ''holes'' in /sys/devices so try hard to find the closest match */
-				do {
-					physdev = hal_device_store_match_key_value_string (hald_get_gdl (), 
-											   "linux.sysfs_path_device", 
-											   normalized_target);
-					if (physdev != NULL)
-						break;
-
-					/* go up one directory */
-					if (!hal_util_path_ascend (normalized_target))
-						break;
-				} while (physdev == NULL);
-				g_free (normalized_target);
-			} else {
-				physdev = NULL;
-			}
-
+			hal_util_find_known_parent (hotplug_event->sysfs.sysfs_path,
+							&parent, &parent_path);
 			hotplug_event_begin_add_classdev (hotplug_event->sysfs.subsystem,
 							  hotplug_event->sysfs.sysfs_path,
 							  hotplug_event->sysfs.device_file,
-							  physdev,
-							  sysfs_path_in_devices,
+							  parent,
+							  parent_path,
 							  (void *) hotplug_event);
-
-			g_free (sysfs_path_in_devices);
-
+			g_free (parent_path);
 		} else if (hotplug_event->action == HOTPLUG_ACTION_REMOVE) {
 			hotplug_event_begin_remove_classdev (hotplug_event->sysfs.subsystem,
 							     hotplug_event->sysfs.sysfs_path,
@@ -272,11 +248,10 @@ hotplug_event_begin_sysfs (HotplugEvent 
 		}
 	} else if (hotplug_event->type == HOTPLUG_EVENT_SYSFS_BLOCK) {
 		if (hotplug_event->action == HOTPLUG_ACTION_ADD) {
-			HalDevice *parent = NULL;
+			HalDevice *parent;
 			int range;
 			gboolean is_partition;
-			gboolean is_fakevolume;
-			
+
 			/* it's a partition if and only if it doesn't have the range file...
 			 *
 			 * notably the device mapper partitions do have a range file, but that's
@@ -285,42 +260,13 @@ hotplug_event_begin_sysfs (HotplugEvent 
 			 * also, if the sysfs ends with "fakevolume" the hotplug event is synthesized
 			 * from within HAL for partitions on the main block device
 			 */
-			is_fakevolume = FALSE;
-			if (strcmp (hal_util_get_last_element (hotplug_event->sysfs.sysfs_path), "fakevolume") == 0) {
-				is_fakevolume = TRUE;
-			}
-			is_partition = TRUE;
-			if (is_fakevolume ||
-			    hal_util_get_int_from_file (hotplug_event->sysfs.sysfs_path, "range", &range, 0)) {
+			if ((strstr (hotplug_event->sysfs.sysfs_path, "/fakevolume") != NULL) ||
+			    hal_util_get_int_from_file (hotplug_event->sysfs.sysfs_path, "range", &range, 0))
 				is_partition = FALSE;
-			}
-
-			if (is_partition || is_fakevolume) {
-				gchar *parent_path;
-
-				parent_path = hal_util_get_parent_path (hotplug_event->sysfs.sysfs_path);
-
-				parent = hal_device_store_match_key_value_string (hald_get_gdl (),
-										  "linux.sysfs_path_device",
-										  parent_path);
-				g_free (parent_path);
-			} else {
-				gchar *target;
-				char physdevpath[HAL_PATH_MAX];
-
-				g_snprintf (physdevpath, HAL_PATH_MAX, "%s/device", hotplug_event->sysfs.sysfs_path);
-				if (((target = g_file_read_link (physdevpath, NULL)) != NULL)) {
-					gchar *normalized_target;
-
-					normalized_target = hal_util_get_normalized_path (hotplug_event->sysfs.sysfs_path, target);
-					g_free (target);
-					parent = hal_device_store_match_key_value_string (hald_get_gdl (),
-											  "linux.sysfs_path_device",
-											  normalized_target);
-					g_free (normalized_target);
-				}
-			}
+			else
+				is_partition = TRUE;
 
+			hal_util_find_known_parent (hotplug_event->sysfs.sysfs_path, &parent, NULL);
 			hotplug_event_begin_add_blockdev (hotplug_event->sysfs.sysfs_path,
 							  hotplug_event->sysfs.device_file,
 							  is_partition,
diff --git a/hald/linux/osspec.c b/hald/linux/osspec.c
index ed762a8..eb5d6b0 100644
--- a/hald/linux/osspec.c
+++ b/hald/linux/osspec.c
@@ -606,40 +606,79 @@ hal_util_set_driver (HalDevice *d, const
 	return ret;
 }
 
-/** Find the closest ancestor by looking at sysfs paths
- *
- *  @param  sysfs_path           Path into sysfs, e.g. /sys/devices/pci0000:00/0000:00:1d.7/usb1/1-0:1.0
- *  @return                      Parent Hal Device Object or #NULL if there is none
- */
-HalDevice *
-hal_util_find_closest_ancestor (const gchar *sysfs_path)
-{	
-	gchar buf[512];
-	HalDevice *parent;
-
-	parent = NULL;
-
-	strncpy (buf, sysfs_path, sizeof (buf));
-	do {
-		char *p;
-
-		p = strrchr (buf, '/');
-		if (p == NULL)
-			break;
-		*p = '\0';
-
-		parent = hal_device_store_match_key_value_string (hald_get_gdl (), 
-								  "linux.sysfs_path_device", 
-								  buf);
-		if (parent != NULL)
+static gboolean get_parent_device(char *path)
+{
+	/* go up one directory */
+	if (!hal_util_path_ascend (path))
+		return FALSE;
+	if (g_str_has_suffix (path, "/class"))
+		return FALSE;
+	if (g_str_has_suffix (path, "/block"))
+		return FALSE;
+	if (g_str_has_suffix (path, "/devices"))
+		return FALSE;
+	return TRUE;
+}
+/* return the first already known parent device */
+gboolean
+hal_util_find_known_parent (const gchar *sysfs_path, HalDevice **parent, gchar **parent_path)
+{
+	gchar *target;
+	HalDevice *parent_dev = NULL;
+	gchar *parent_devpath;
+	char parentdevpath[HAL_PATH_MAX];
+	gboolean retval = FALSE;
+
+	parent_devpath = g_strdup (sysfs_path);
+	while (TRUE) {
+		if (!get_parent_device (parent_devpath))
 			break;
 
-	} while (TRUE);
-
-	return parent;
+		parent_dev = hal_device_store_match_key_value_string (hald_get_gdl (),
+								      "linux.sysfs_path_device",
+								      parent_devpath);
+		if (parent_dev != NULL)
+			goto out;
+	}
+	g_free (parent_devpath);
+	parent_devpath = NULL;
+
+	/* try if the parent chain is constructed by the device-link */
+	g_snprintf (parentdevpath, HAL_PATH_MAX, "%s/device", sysfs_path);
+	if (((target = g_file_read_link (parentdevpath, NULL)) != NULL)) {
+		parent_devpath = hal_util_get_normalized_path (sysfs_path, target);
+		g_free (target);
+
+		while (TRUE) {
+			parent_dev = hal_device_store_match_key_value_string (hald_get_gdl (),
+									      "linux.sysfs_path_device",
+									      parent_devpath);
+			if (parent_dev != NULL)
+				goto out;
+
+			/* go up one directory */
+			if (!get_parent_device (parent_devpath))
+				break;
+		}
+		g_free (parent_devpath);
+		parent_devpath = NULL;
+	}
+
+out:
+	if (parent_dev != NULL) {
+		HAL_INFO (("hal_util_find_known_parent: '%s'->'%s'", sysfs_path, parent_devpath));
+		retval = TRUE;
+	}
+	if (parent != NULL)
+		*parent = parent_dev;
+	if (parent_path != NULL)
+		*parent_path = parent_devpath;
+	else
+		g_free (parent_devpath);
+	return retval;
 }
 
-void 
+void
 osspec_refresh_mount_state_for_block_device (HalDevice *d)
 {
 	blockdev_refresh_mount_state (d);
diff --git a/hald/linux/osspec_linux.h b/hald/linux/osspec_linux.h
index 88d516c..8ecda30 100644
--- a/hald/linux/osspec_linux.h
+++ b/hald/linux/osspec_linux.h
@@ -37,7 +37,7 @@ gboolean hal_util_get_driver_name (const
 
 gboolean hal_util_set_driver (HalDevice *d, const char *property_name, const char *sysfs_path);
 
-HalDevice *hal_util_find_closest_ancestor (const gchar *sysfs_path);
+gboolean hal_util_find_known_parent (const gchar *sysfs_path, HalDevice **parent, gchar **parent_path);
 
 
 #endif /* OSSPEC_LINUX_H */


More information about the hal-commit mailing list