commit cf0ac2f526b5e82d94a1cd02428ebd8bfb5cb00b
parent 18552e102e3d739d8c03379716b8acbc9ddbc8f7
Author: sin <sin@2f30.org>
Date:   Thu, 22 Aug 2013 10:45:24 +0100
Introduce struct Event and add primitive hotplugging support
This unifies the way we handle hotplug events versus static
population of the /dev directory.  We also remove the assumption
for createdev() to only work correctly if the current directory
is set to the corresponding sysfs entry.
Diffstat:
| M | config.h |  |  | 10 | +++++----- | 
| M | smdev.c |  |  | 144 | ++++++++++++++++++++++++++++++++++++++++++++++++++----------------------------- | 
2 files changed, 97 insertions(+), 57 deletions(-)
diff --git a/config.h b/config.h
@@ -1,11 +1,11 @@
 /* See LICENSE file for copyright and license details. */
 struct Rule {
-        const char *devregex;
-        const char *user;
-        const char *group;
+        char *devregex;
+        char *user;
+        char *group;
         int mode;
-        const char *path;
-        const char *cmd;
+        char *path;
+        char *cmd;
 } Rules[] = {
         { "null",       "root", "root",  0666, NULL, "@chmod 666 $SMDEV"    },
         { "zero",       "root", "root",  0666, NULL, NULL                   },
diff --git a/smdev.c b/smdev.c
@@ -15,9 +15,18 @@
 #include "mkpath.h"
 #include "util.h"
 
-static int matchrule(const struct Rule *Rule, const char *devname);
-static int create_dev(const char *path);
-static void sysrecurse(const char *path);
+struct Event {
+	int min;
+	int maj;
+	char *action;
+	char *devpath;
+	char *devname;
+};
+
+static int dohotplug(void);
+static int matchrule(struct Rule *Rule, char *devname);
+static int createdev(struct Event *ev);
+static void populatedev(const char *path);
 
 static void
 usage(void)
@@ -38,17 +47,43 @@ main(int argc, char *argv[])
 		usage();
 	} ARGEND;
 
-	if (!sflag)
-		usage();
-
 	umask(0);
-	recurse("/sys/devices", sysrecurse);
+	if (sflag)
+		recurse("/sys/devices", populatedev);
+	else
+		if (dohotplug() < 0)
+			return 1;
+	return 0;
+}
+
+static int
+dohotplug(void)
+{
+	char *min, *maj;
+	struct Event ev;
+
+	min = getenv("MINOR");
+	maj = getenv("MAJOR");
+	ev.action = getenv("ACTION");
+	ev.devpath = getenv("DEVPATH");
+	ev.devname = getenv("DEVNAME");
+	if (!min || !maj || !ev.action || !ev.devpath ||
+	    !ev.devname)
+		return -1;
+
+	ev.min = estrtol(min, 10);
+	ev.maj = estrtol(maj, 10);
+
+	if (!strcmp(ev.action, "add"))
+		return createdev(&ev);
+	else
+		eprintf("Unsupported action '%s'\n", ev.action);
 
 	return 0;
 }
 
 static int
-matchrule(const struct Rule *Rule, const char *devname)
+matchrule(struct Rule *Rule, char *devname)
 {
 	regex_t match;
 	regmatch_t off;
@@ -67,37 +102,23 @@ matchrule(const struct Rule *Rule, const char *devname)
 }
 
 static int
-create_dev(const char *path)
+createdev(struct Event *ev)
 {
 	struct Rule *Rule;
 	struct passwd *pw;
 	struct group *gr;
-	char buf[BUFSIZ], *p;
-	const char *devname;
-	char origdevname[PATH_MAX];
-	int maj, min, type;
-	int i, ret;
-
-	p = strrchr(path, '/');
-	if (!p)
-		return -1;
-	p++;
-	devname = p;
-	snprintf(origdevname, sizeof(origdevname), "/dev/%s", devname);
+	char devpath[PATH_MAX], *devname;
+	char buf[BUFSIZ];
+	int type;
+	int i;
 
-	snprintf(buf, sizeof(buf), "%s/dev", path);
-	ret = devtomajmin(buf, &maj, &min);
-	if (ret < 0)
-		return -1;
-
-	snprintf(buf, sizeof(buf), "%d:%d", maj, min);
+	snprintf(buf, sizeof(buf), "%d:%d", ev->maj, ev->min);
 	type = devtype(buf);
 	if (type < 0)
 		return -1;
 
-	if (chdir("/dev") < 0)
-		eprintf("chdir /dev:");
-
+	devname = ev->devname;
+	snprintf(devpath, sizeof(devpath), "/dev/%s", devname);
 	for (i = 0; i < LEN(Rules); i++) {
 		Rule = &Rules[i];
 
@@ -108,40 +129,53 @@ create_dev(const char *path)
 			if (Rule->path[0] != '=' && Rule->path[0] != '>')
 				eprintf("Invalid path '%s'\n", Rule->path);
 			if (Rule->path[strlen(Rule->path) - 1] == '/') {
+				snprintf(buf, sizeof(buf), "/dev/%s", &Rule->path[1]);
 				umask(022);
-				if (mkpath(&Rule->path[1], 0755) < 0)
-					eprintf("mkdir %s:", &Rule->path[1]);
+				if (mkpath(buf, 0755) < 0)
+					eprintf("mkdir %s:", buf);
 				umask(0);
-				if (chdir(&Rule->path[1]) < 0)
-					eprintf("chdir %s:", &Rule->path[1]);
+				snprintf(devpath, sizeof(devpath), "/dev/%s%s",
+					 &Rule->path[1], devname);
 			} else {
 				devname = &Rule->path[1];
+				snprintf(devpath, sizeof(devpath), "/dev/%s", devname);
 			}
 		}
 
 		/* Create the actual dev nodes */
-		ret = mknod(devname, Rules[i].mode | type, makedev(maj, min));
-		if (ret < 0 && errno != EEXIST)
-			eprintf("mknod %s:", devname);
+		if (mknod(devpath, Rules[i].mode | type,
+			  makedev(ev->maj, ev->min)) < 0 &&
+		    errno != EEXIST)
+			eprintf("mknod %s:", devpath);
+
+		errno = 0;
 		pw = getpwnam(Rules[i].user);
-		if (!pw)
+		if (errno)
 			eprintf("getpwnam %s:", Rules[i].user);
+		else if (!pw)
+			enprintf(1, "getpwnam %s: no such user\n",
+				 Rules[i].user);
+
+		errno = 0;
 		gr = getgrnam(Rules[i].group);
-		if (!gr)
+		if (errno)
 			eprintf("getgrnam %s:", Rules[i].group);
-		ret = chown(devname, pw->pw_uid, gr->gr_gid);
-		if (ret < 0)
-			eprintf("chown %s:", devname);
+		else if (!gr)
+			enprintf(1, "getgrnam %s: no such group\n",
+				 Rules[i].group);
+
+		if (chown(devpath, pw->pw_uid, gr->gr_gid) < 0)
+			eprintf("chown %s:", devpath);
 
 		/* Create symlinks */
 		if (Rule->path && Rule->path[0] == '>') {
-			snprintf(buf, sizeof(buf), "%s%s", &Rule->path[1], devname);
-			if (symlink(buf, origdevname))
+			snprintf(buf, sizeof(buf), "/dev/%s", ev->devname);
+			if (symlink(devpath, buf))
 				eprintf("symlink %s -> %s:",
-					origdevname, buf);
+					ev->devname, devpath);
 		}
 
-		snprintf(buf, sizeof(buf), "SMDEV=%s", devname);
+		snprintf(buf, sizeof(buf), "SMDEV=%s", devpath);
 		if (putenv(buf) < 0)
 			eprintf("putenv:");
 
@@ -162,21 +196,27 @@ create_dev(const char *path)
 		break;
 	}
 
-	if (chdir(path) < 0)
-		eprintf("chdir %s:", path);
-
 	return 0;
 }
 
 static void
-sysrecurse(const char *path)
+populatedev(const char *path)
 {
+	char tmppath[PATH_MAX];
 	char *cwd;
+	struct Event ev;
 
-	recurse(path, sysrecurse);
+	recurse(path, populatedev);
 	if (!strcmp(path, "dev")) {
 		cwd = agetcwd();
-		create_dev(cwd);
+		ev.action = "add";
+		ev.devpath = cwd + strlen("/sys");
+		ev.devname = basename(cwd);
+		snprintf(tmppath, sizeof(tmppath), "/sys%s/dev",
+			 ev.devpath);
+		if (devtomajmin(tmppath, &ev.maj, &ev.min) < 0)
+			return;
+		createdev(&ev);
 		free(cwd);
 	}
 }