linux-hotplug.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [adventure] replace /sbin/hotplug by udevd
@ 2004-11-18  0:27 Kay Sievers
  2004-11-18  1:25 ` Greg KH
                   ` (8 more replies)
  0 siblings, 9 replies; 10+ messages in thread
From: Kay Sievers @ 2004-11-18  0:27 UTC (permalink / raw)
  To: linux-hotplug

[-- Attachment #1: Type: text/plain, Size: 2166 bytes --]

!!! Please, don't try this if you don't know how to bring your      !!!
!!! system back if it does not boot anymore cause of a broken udev. !!!

This experimental patch changes udevd/udev to handle the _whole_ hotplug
event from the kernel up to the multiplexing of /etc/hotplug.d/. It
should solve most of the current shortcomings of the hotplug event handling:
  - sysfs if not fully populated at event time
  - at device disconnect/reconnect, the "remove" may beat the "add"
  - an event may already work on the same DEVPATH
  - the hotplug scripts don't know the device node name
  - the total count of event processes can't be controlled

The architecture is very similar to udevd today, only that we wait for
_all_ sysfs devices internally and then also calls /etc/hotplug/* from
the forked udev process.

/sbin/hotplug is replaced by /sbin/udevsend. The kernel just calls this
small binary to place the event into the udevd daemon. We may also use
the netlink uevent in the future, so no fork at all is done from the
kernel for a hotplug event.

The daemon listens, reorders events and forks an instance for every event,
which:
   -waits for sysfs (also for /sys/devices/*)
   -creates/removes the device node
   -exports interesting values to its environment
   -calls/multiplexes /etc/hotplug.d/*
   -calls/multiplexes /etc/dev.d/* (we don't really need that anymore)
   -exits

This way get all what todays hotplug events can dream of:
   -fully populated sysfs
   -events in the right order (SEQNUM)
   -delayed events for the _same_ DEVPATH, all others run in parallel
   -all interesting values to the environment
   -device node ready and its name available
   -possibly limit the total count of events processes (not implemented now)

One single hotplug event for the creation of a partition now looks like this:
  ACTION=add
  SUBSYSTEM=block
  DEVPATH=/block/sda/sda1
  SEQNUM=1027
  DEVNAME=/dev/sda1
  PHYSDEVPATH=/devices/pci0000:00/0000:00:1d.1/usb3/3-1/3-1:1.0/host0/target0:0:0/0:0:0:0
  PHYSDEVBUS=scsi
  PHYSDEVDRIVER=sd


This patch applies on top of udev v045. /proc/sys/kernel/hotplug should be
set to /sbin/udevsend.

Any thoughts,
Kay


[-- Attachment #2: udev-as-hotplugd-02.patch --]
[-- Type: text/plain, Size: 11617 bytes --]

===== dev_d.c 1.20 vs edited =====
--- 1.20/dev_d.c	2004-11-11 22:32:18 +01:00
+++ edited/dev_d.c	2004-11-16 00:47:59 +01:00
@@ -37,6 +37,11 @@ static int run_program(const char *filen
 	int fd;
 	struct udevice *udev = data;
 
+	if (strstr(filename, "udev.hotplug") != NULL) {
+		dbg("prevent loop, don't call link pointing to ourself");
+		return 0;
+	}
+
 	dbg("running %s", filename);
 
 	pid = fork();
@@ -96,13 +101,17 @@ void dev_d_execute(struct udevice *udev,
 		temp = strchr(temp, '/');
 	}
 
-	snprintf(dirname, PATH_MAX, "%s/%s", basedir, udev->name);
-	dirname[PATH_MAX-1] = '\0';
-	call_foreach_file(run_program, dirname, suffix, udev);
-
-	snprintf(dirname, PATH_MAX, "%s/%s", basedir, udev->subsystem);
-	dirname[PATH_MAX-1] = '\0';
-	call_foreach_file(run_program, dirname, suffix, udev);
+	if (udev->name[0] != '\0') {
+		snprintf(dirname, PATH_MAX, "%s/%s", basedir, udev->name);
+		dirname[PATH_MAX-1] = '\0';
+		call_foreach_file(run_program, dirname, suffix, udev);
+	}
+
+	if (udev->subsystem[0] != '\0') {
+		snprintf(dirname, PATH_MAX, "%s/%s", basedir, udev->subsystem);
+		dirname[PATH_MAX-1] = '\0';
+		call_foreach_file(run_program, dirname, suffix, udev);
+	}
 
 	snprintf(dirname, PATH_MAX, "%s/default", basedir);
 	dirname[PATH_MAX-1] = '\0';
===== udev.c 1.82 vs edited =====
--- 1.82/udev.c	2004-11-13 06:51:09 +01:00
+++ edited/udev.c	2004-11-16 15:00:15 +01:00
@@ -4,6 +4,7 @@
  * Userspace devfs
  *
  * Copyright (C) 2003,2004 Greg Kroah-Hartman <greg@kroah.com>
+ * Copyright (C) 2004 Kay Sievers <kay.sievers@vrfy.org>
  *
  *	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
@@ -68,66 +69,19 @@ int main(int argc, char *argv[], char *e
 {
 	struct sigaction act;
 	struct sysfs_class_device *class_dev;
+	struct sysfs_device *devices_dev;
 	struct udevice udev;
 	char path[SYSFS_PATH_MAX];
 	int retval = -EINVAL;
-	enum {
-		ADD,
-		REMOVE,
-		UDEVSTART,
-	} act_type;
+	const char *error;
+	const char *action = getenv("ACTION");
+	const char *devpath = getenv("DEVPATH");
+	const char *subsystem = argv[1];
 
 	dbg("version %s", UDEV_VERSION);
 	logging_init("udev");
 	udev_init_config();
 
-	if (strstr(argv[0], "udevstart") || (argv[1] != NULL && strstr(argv[1], "udevstart"))) {
-		act_type = UDEVSTART;
-	} else {
-		const char *action = getenv("ACTION");
-		const char *devpath = getenv("DEVPATH");
-		const char *subsystem = argv[1];
-
-		if (!action) {
-			dbg("no action?");
-			goto exit;
-		}
-		if (strcmp(action, "add") == 0) {
-			act_type = ADD;
-		} else if (strcmp(action, "remove") == 0) {
-			act_type = REMOVE;
-		} else {
-			dbg("no action '%s' for us", action);
-			goto exit;
-		}
-
-		if (!devpath) {
-			dbg("no devpath?");
-			goto exit;
-		}
-		dbg("looking at '%s'", devpath);
-
-		/* we only care about class devices and block stuff */
-		if (!strstr(devpath, "class") && !strstr(devpath, "block")) {
-			dbg("not a block or class device");
-			goto exit;
-		}
-
-		if (!subsystem) {
-			dbg("no subsystem");
-			goto exit;
-		}
-
-		udev_set_values(&udev, devpath, subsystem, action);
-
-		/* skip blacklisted subsystems */
-		if (udev.type != 'n' && subsystem_expect_no_dev(subsystem)) {
-			dbg("don't care about '%s' devices", subsystem);
-			goto exit;
-		};
-
-	}
-
 	/* set signal handlers */
 	act.sa_handler = (void (*) (int))sig_handler;
 	sigemptyset (&act.sa_mask);
@@ -137,51 +91,104 @@ int main(int argc, char *argv[], char *e
 	sigaction(SIGINT, &act, NULL);
 	sigaction(SIGTERM, &act, NULL);
 
-	/* trigger timout to interrupt blocking syscalls */
+	/* trigger timeout to interrupt blocking syscalls */
 	alarm(ALARM_TIMEOUT);
 
-	switch(act_type) {
-	case UDEVSTART:
-		dbg("udevstart");
+	udev_set_values(&udev, devpath, subsystem, action);
+
+	if (strstr(argv[0], "udevstart") || (argv[1] != NULL && strstr(argv[1], "udevstart"))) {
+		dbg("event: udevstart");
 		udev_log = 0;
 		namedev_init();
 		retval = udev_start();
-		break;
-	case ADD:
-		dbg("udev add");
-
-		/* open the device */
-		snprintf(path, SYSFS_PATH_MAX, "%s%s", sysfs_path, udev.devpath);
-		class_dev = sysfs_open_class_device_path(path);
-		if (class_dev == NULL) {
-			dbg ("sysfs_open_class_device_path failed");
-			break;
-		}
-		dbg("opened class_dev->name='%s'", class_dev->name);
+		goto exit;
+	}
 
-		/* init rules */
-		namedev_init();
+	if (!action) {
+		dbg("no action");
+		goto hotplug;
+	}
+
+	if (!subsystem) {
+		dbg("no subsystem");
+		goto hotplug;
+	}
+
+	if (!devpath) {
+		dbg("no devpath");
+		goto hotplug;
+	}
+
+	if ((strncmp(devpath, "/block/", 7) == 0) || (strncmp(devpath, "/class/", 7) == 0)) {
+		if (strcmp(action, "add") == 0) {
+			/* wait for sysfs and possibly add node */
+			dbg("event: udev add");
+
+			/* skip blacklisted subsystems */
+			if (udev.type != 'n' && subsystem_expect_no_dev(udev.subsystem)) {
+				dbg("don't care about '%s' devices", udev.subsystem);
+				goto hotplug;
+			};
+
+			snprintf(path, SYSFS_PATH_MAX, "%s%s", sysfs_path, udev.devpath);
+			class_dev = wait_class_device_open(path);
+			if (class_dev == NULL) {
+				dbg ("sysfs_open_class_device_path failed");
+				goto hotplug;
+			}
+			dbg("opened class_dev->name='%s'", class_dev->name);
+
+			wait_for_class_device(class_dev, &error);
+
+			/* init rules, permissions */
+			namedev_init();
+
+			/* name, create node, store in db */
+			retval = udev_add_device(&udev, class_dev);
+
+			/* run dev.d/ scripts if we created a node or changed a netif name */
+			if (udev.devname[0] != '\0') {
+				setenv("DEVNAME", udev.devname, 1);
+				dev_d_execute(&udev, DEVD_DIR, DEVD_SUFFIX);
+			}
 
-		/* name, create node, store in db */
-		retval = udev_add_device(&udev, class_dev);
+			sysfs_close_class_device(class_dev);
+		} else if (strcmp(action, "remove") == 0) {
+			/* possibly remove a node */
+			dbg("event: udev remove");
 
-		/* run dev.d/ scripts if we created a node or changed a netif name */
-		if (udev.devname[0] != '\0') {
-			setenv("DEVNAME", udev.devname, 1);
+			/* get node from db, delete it */
+			retval = udev_remove_device(&udev);
+
+			/* run scripts */
 			dev_d_execute(&udev, DEVD_DIR, DEVD_SUFFIX);
 		}
+	} else if ((strncmp(devpath, "/devices/", 9) == 0)) {
+		if (strcmp(action, "add") == 0) {
+			/* wait for sysfs */
+			dbg("event: devices add");
 
-		sysfs_close_class_device(class_dev);
-		break;
-	case REMOVE:
-		dbg("udev remove");
+			snprintf(path, SYSFS_PATH_MAX, "%s%s", sysfs_path, devpath);
+			devices_dev = wait_devices_device_open(path);
+			if (!devices_dev) {
+				dbg("error: devices device unavailable (probably remove has beaten us)");
+				goto hotplug;
+			}
+			dbg("devices device opened '%s'", path);
 
-		/* get node from db, delete it*/
-		retval = udev_remove_device(&udev);
+			wait_for_devices_device(devices_dev, &error);
 
-		/* run scripts */
-		dev_d_execute(&udev, DEVD_DIR, DEVD_SUFFIX);
+			sysfs_close_device(devices_dev);
+		} else if (strcmp(action, "remove") == 0) {
+			dbg("event: devices remove");
+		}
+	} else {
+		dbg("event: unhandled");
 	}
+
+hotplug:
+	/* call the hotplug scripts */
+	dev_d_execute(&udev, HOTPLUGD_DIR, HOTPLUG_SUFFIX);
 
 exit:
 	logging_close();
===== udev.h 1.72 vs edited =====
--- 1.72/udev.h	2004-11-13 06:43:23 +01:00
+++ edited/udev.h	2004-11-16 00:29:14 +01:00
@@ -44,6 +44,9 @@
 #define DEVD_DIR			"/etc/dev.d"
 #define DEVD_SUFFIX			".dev"
 
+#define HOTPLUGD_DIR			"/etc/hotplug.d"
+#define HOTPLUG_SUFFIX			".hotplug"
+
 struct udevice {
 	char devpath[DEVPATH_SIZE];
 	char subsystem[SUBSYSTEM_SIZE];
===== udev_lib.c 1.16 vs edited =====
--- 1.16/udev_lib.c	2004-11-13 04:36:46 +01:00
+++ edited/udev_lib.c	2004-11-16 01:54:09 +01:00
@@ -40,37 +40,32 @@
 #define CLASS_PATH		"/class/"
 #define NET_PATH		"/class/net/"
 
-char get_device_type(const char *path, const char *subsystem)
-{
-	if (strcmp(subsystem, "block") == 0)
-		return 'b';
-
-	if (strcmp(subsystem, "net") == 0)
-		return 'n';
-
-	if (strncmp(path, BLOCK_PATH, strlen(BLOCK_PATH)) == 0 &&
-	    strlen(path) > strlen(BLOCK_PATH))
-		return 'b';
-
-	if (strncmp(path, NET_PATH, strlen(NET_PATH)) == 0 &&
-	    strlen(path) > strlen(NET_PATH))
-		return 'n';
-
-	if (strncmp(path, CLASS_PATH, strlen(CLASS_PATH)) == 0 &&
-	    strlen(path) > strlen(CLASS_PATH))
-		return 'c';
-
-	return '\0';
-}
 
 void udev_set_values(struct udevice *udev, const char* devpath,
 		     const char *subsystem, const char* action)
 {
 	memset(udev, 0x00, sizeof(struct udevice));
-	strfieldcpy(udev->devpath, devpath);
-	strfieldcpy(udev->subsystem, subsystem);
-	strfieldcpy(udev->action, action);
-	udev->type = get_device_type(devpath, subsystem);
+	if (devpath)
+		strfieldcpy(udev->devpath, devpath);
+	if (subsystem)
+		strfieldcpy(udev->subsystem, subsystem);
+	if (action)
+		strfieldcpy(udev->action, action);
+
+	if (strcmp(udev->subsystem, "block") == 0)
+		udev->type = 'b';
+
+	if (strcmp(udev->subsystem, "net") == 0)
+		udev->type = 'n';
+
+	if (strncmp(udev->devpath, "/block/", 7) == 0)
+		udev->type = 'b';
+
+	if (strncmp(udev->devpath, "/class/net/", 11) == 0)
+		udev->type = 'n';
+
+	if (strncmp(udev->devpath, "/class/", 7) == 0)
+		udev->type = 'c';
 }
 
 int kernel_release_satisfactory(int version, int patchlevel, int sublevel)
===== udev_lib.h 1.16 vs edited =====
--- 1.16/udev_lib.h	2004-11-13 04:36:46 +01:00
+++ edited/udev_lib.h	2004-11-16 01:23:01 +01:00
@@ -76,7 +76,6 @@ do { \
 # define asmlinkage	/* nothing */
 #endif
 
-extern char get_device_type(const char *path, const char *subsystem);
 extern void udev_set_values(struct udevice *udev, const char* devpath,
 			    const char *subsystem, const char* action);
 extern int kernel_release_satisfactory(int version, int patchlevel, int sublevel);
===== udevd.c 1.47 vs edited =====
--- 1.47/udevd.c	2004-11-11 22:18:28 +01:00
+++ edited/udevd.c	2004-11-16 02:35:46 +01:00
@@ -153,9 +153,14 @@ static void udev_run(struct hotplug_msg 
 static struct hotplug_msg *running_with_devpath(struct hotplug_msg *msg)
 {
 	struct hotplug_msg *loop_msg;
-	list_for_each_entry(loop_msg, &running_list, list)
-		if (strncmp(loop_msg->devpath, msg->devpath, sizeof(loop_msg->devpath)) == 0)
+	list_for_each_entry(loop_msg, &running_list, list) {
+		if (loop_msg->devpath == NULL || msg->devpath == NULL)
+			continue;
+
+		if (strcmp(loop_msg->devpath, msg->devpath) == 0)
 			return loop_msg;
+	}
+
 	return NULL;
 }
 
===== udevinfo.c 1.32 vs edited =====
--- 1.32/udevinfo.c	2004-11-12 21:33:25 +01:00
+++ edited/udevinfo.c	2004-11-16 01:22:40 +01:00
@@ -125,10 +125,6 @@ static int print_device_chain(const char
 	struct sysfs_device *sysfs_dev;
 	struct sysfs_device *sysfs_dev_parent;
 	int retval = 0;
-	char type;
-
-	type = get_device_type(path, "");
-	dbg("device type is %c", type);
 
 	/*  get the class dev */
 	class_dev = sysfs_open_class_device_path(path);
@@ -144,16 +140,10 @@ static int print_device_chain(const char
 	       "to match the device for which the node will be created.\n"
 	       "\n");
 
-	if (type == 'b' || type =='c') {
-		/* read the 'dev' file for major/minor*/
-		attr = sysfs_get_classdev_attr(class_dev, "dev");
-		if (attr == NULL) {
-			printf("couldn't get the \"dev\" file\n");
-			retval = -1;
-			goto exit;
-		}
+	/* look for the 'dev' file */
+	attr = sysfs_get_classdev_attr(class_dev, "dev");
+	if (attr == NULL)
 		printf("device '%s' has major:minor %s", class_dev->path, attr->value);
-	}
 
 	/* open sysfs class device directory and print all attributes */
 	printf("  looking at class device '%s':\n", class_dev->path);

^ permalink raw reply	[flat|nested] 10+ messages in thread

end of thread, other threads:[~2004-11-19 16:03 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2004-11-18  0:27 [adventure] replace /sbin/hotplug by udevd Kay Sievers
2004-11-18  1:25 ` Greg KH
2004-11-18  4:04 ` Kevin P. Fleming
2004-11-18  6:40 ` Stefan Schweizer
2004-11-18 23:47 ` Kay Sievers
2004-11-19  0:03 ` Kay Sievers
2004-11-19  0:55 ` Greg KH
2004-11-19  1:11 ` Kay Sievers
2004-11-19  7:27 ` Greg KH
2004-11-19 16:03 ` Kay Sievers

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).