Subject: [MDEV] mdev and CONFIG_SYSFS_DEPRECATED

From: Souf <[email-masked]>
Date: Wed, 09 May 2007 23:46:02 +0200

Hi all,
I rewrote mdev so that it can work with or without CONFIG_SYSFS_DEPRECATED in the kernel,
this rewriting implies a minor modification of the syntax of mdev.config
New the mdev can:
1- create symlinks
        <symlink> -> <target>
        stderr -> /proc/self/fd/2
        XOR -> null

2- create (sub-)directories
        <(sub-)directory> <octal permissions>
        bus/usb/001 0755
        disk/by-id 0755

3- create a device in a (sub-)directory
        <device regex> <uid>:<gid> <octal permissions> [</>>/>
<(sub-)directory> <octal permissions>]
        seq 0:0 0666 >> snd 0755
        pcmC[0-9]D[0-9]p 0:0 0666 >> snd 0755
        event[0-9] 0:0 0600 >> input 0755

4- execute an command
        <device regex> <uid>:<gid> <octal permissions> [</>>/>
<(sub-)directory> <octal permissions>] [<@|$|*> <command>]
        controlC[0-9] 0:0 0666 >> snd 0755  @ echo "hello $MDEV"
        seq     0:0 0666 $echo "good bye $MDEV"
        timer 0:0 0666 >> snd 0755 * echo "Hi $MDEV"    

        @ Run after creating the device.
        $ Run before removing the device.
        * Run both after creating and before removing the device.

5- work with or without CONFIG_SYSFS_DEPRECATED

I awaits your reactions and your ideas

Regards

Malek

_______________________________________________
busybox mailing list
[email-masked]
http://busybox.net/cgi-bin/mailman/listinfo/busybox

---

From: Souf <[email-masked]>
Date: Fri, 18 May 2007 19:47:06 +0200

Le vendredi 18 mai 2007 à 14:38 +0200, Natanael Copa a écrit :
> On Wed, 2007-05-09 at 23:46 +0200, Souf wrote:
> > Hi all,
> Is this new? Isnt that how current mdev works?
> 

I do not believe that the current medev is able to create symlinks,
neither (sub-)directories, nor devices in (sub-)directories. I agree for
command execution but it is not the same code.
In addition to that, I used libbb to parse mdev.conf and create devs:
xmalloc_getline()
last_char_is()
recursive_action()
bb_make_directory()
remove_file()
bb_get_last_path_component()
xmalloc_readlink_or_warn()

> I just came across this one while upgrading to 2.6.20. This should be
> fixed even if the rest is rejected.

I rectify: 
5- work with AND without CONFIG_SYSFS_DEPRECATED

> Havent looked at the code too much yet, but I definitively like the idea
> of letting mdev create devs in subdirs.
> 
> I wonder if it might be worth having dir creation as a config option?
Done, I added 2 options of configuration:
FEATURE_MDEV_MKDIR
FEATURE_MDEV_SYMLINK

This mdev works with busybox >= svn18362

Thanks

Malek

--- busybox/util-linux/Config.in.orig	2007-05-18 09:20:18.000000000 +0200
+++ busybox/util-linux/Config.in	2007-05-18 16:47:31.000000000 +0200
@@ -294,6 +294,26 @@ config FEATURE_MDEV_EXEC
 
 	  For more information, please see docs/mdev.txt
 
+config FEATURE_MDEV_MKDIR
+	bool "Support sub-dir creation"
+	default n
+	depends on FEATURE_MDEV_CONF
+	help
+	  This option allows mdev to create sub-directories in /dev,
+	  for example:
+		/dev/bus/usb/001/
+		/dev/disk/by-label/
+
+config FEATURE_MDEV_SYMLINK
+	bool "Support symlinks creation"
+	default n
+	depends on FEATURE_MDEV_CONF
+	help
+	  This option allows mdev to create symlinks in /dev,
+	  for example:
+		ram -> ram1
+		stderr -> /proc/self/fd/2
+
 config MKSWAP
 	bool "mkswap"
 	default n
--- busybox/util-linux/mdev.c.orig	2007-05-18 09:20:18.000000000 +0200
+++ busybox/util-linux/mdev.c	2007-05-18 17:01:01.000000000 +0200
@@ -24,9 +24,9 @@ struct mdev_globals
 /* mknod in /dev based on a path like "/sys/block/hda/hda1" */
 static void make_device(char *path, int delete)
 {
-	char *device_name;
-	int major, minor, type, len;
-	int mode = 0660;
+	char *device_name, *device_path = NULL;
+	int major, minor, type, len, mk_dir=0;
+	mode_t mode = 0660, mode_path = 0660;
 	uid_t uid = 0;
 	gid_t gid = 0;
 	char *temp = path + strlen(path);
@@ -47,129 +47,167 @@ static void make_device(char *path, int 
 	/* Determine device name, type, major and minor */
 
 	device_name = strrchr(path, '/') + 1;
-	type = path[5]=='c' ? S_IFCHR : S_IFBLK;
+	type = (path[5]=='b') ? S_IFBLK : S_IFCHR;
 
 	/* If we have a config file, look up permissions for this device */
 
 	if (ENABLE_FEATURE_MDEV_CONF) {
-		char *conf, *pos, *end;
-		int line, fd;
+		char *line, *tok_tmp, *tok_line=NULL, **tok = NULL;
+		FILE *file;
+		int i;
 
-		/* mmap the config file */
-		fd = open("/etc/mdev.conf", O_RDONLY);
-		if (fd < 0)
-			goto end_parse;
-		len = xlseek(fd, 0, SEEK_END);
-		conf = mmap(NULL, len, PROT_READ, MAP_PRIVATE, fd, 0);
-		close(fd);
-		if (!conf)
+		file = fopen_or_warn("/etc/mdev.conf", "r");
+		if (file < 0)
 			goto end_parse;
 
-		line = 0;
-		/* Loop through lines in mmaped file*/
-		for (pos=conf; pos-conf<len;) {
-			int field;
-			char *end2;
+		while ((line = xmalloc_getline(file)) != NULL) {
+			char *regex = NULL;
+			regex_t match;
+			regmatch_t off;
+			int result, tok_len = 1;
+			char *tok_id[2], *tok_id_tmp;
+			char *s;
 
-			line++;
-			/* find end of this line */
-			for (end=pos; end-conf<len && *end!='\n'; end++)
-				;
+			tok_line = strdupa(line);
+			if (tok_line[0] == '#' || strlen(tok_line)==0) continue;
 
-			/* Three fields: regex, uid:gid, mode */
-			for (field=0; field < (3 + ENABLE_FEATURE_MDEV_EXEC);
-					field++)
-			{
-				/* Skip whitespace */
-				while (pos<end && isspace(*pos)) pos++;
-				if (pos==end || *pos=='#') break;
-				for (end2=pos;
-					end2<end && !isspace(*end2) && *end2!='#'; end2++)
-					;
+			for (i=0; i<strlen(tok_line); i++) {
+				if (isspace(tok_line[i]) && !isspace(tok_line[i+1]))
+					tok_len++;
+			}
+			tok = (char **) xrealloc(tok, tok_len * sizeof(char *));
 
-				if (field == 0) {
-					/* Regex to match this device */
+			for (i=0; (tok_tmp=strtok(tok_line, " \t")); i++) {
+				tok[i] = tok_tmp;
+				tok_line = NULL;
+			}
 
-					char *regex = strndupa(pos, end2-pos);
-					regex_t match;
-					regmatch_t off;
-					int result;
+			if (tok_len == 2 && ENABLE_FEATURE_MDEV_MKDIR) {
+				mode_path = (mode_t)strtoul(tok[1], &s, 8);
+				bb_make_directory(tok[0], mode_path, FILEUTILS_RECUR);
+				continue;
+			}
 
-					/* Is this it? */
-					xregcomp(&match,regex, REG_EXTENDED);
-					result = regexec(&match, device_name, 1, &off, 0);
-					regfree(&match);
+			if (!strcmp(tok[1], "->") && ENABLE_FEATURE_MDEV_SYMLINK) {
+				if (symlink(tok[2], tok[0]) && errno != EEXIST)
+					bb_perror_msg_and_die("symlink %s", tok[0]);
+				continue;
+			}
 
-					/* If not this device, skip rest of line */
-					if (result || off.rm_so
-							|| off.rm_eo != strlen(device_name))
-						break;
-				}
-				if (field == 1) {
-					/* uid:gid */
+			/* Regex to match this device */
+			regex = tok[0];
+			xregcomp(&match,regex, REG_EXTENDED);
+			result = regexec(&match, device_name, 1, &off, 0);
+			regfree(&match);
 
-					char *s, *s2;
+			/* If not this device, skip rest of line */
+			if (result || off.rm_so || off.rm_eo != strlen(device_name))
+				continue;
 
-					/* Find : */
-					for (s=pos; s<end2 && *s!=':'; s++)
-						;
-					if (s == end2) break;
 
-					/* Parse UID */
-					uid = strtoul(pos, &s2, 10);
-					if (s != s2) {
-						struct passwd *pass;
-						pass = getpwnam(strndupa(pos, s-pos));
-						if (!pass) break;
-						uid = pass->pw_uid;
-					}
-					s++;
-					/* parse GID */
-					gid = strtoul(s, &s2, 10);
-					if (end2 != s2) {
-						struct group *grp;
-						grp = getgrnam(strndupa(s, end2-s));
-						if (!grp) break;
-						gid = grp->gr_gid;
-					}
-				}
-				if (field == 2) {
-					/* mode */
+			for (i=0; (tok_id_tmp=strtok(tok[1], ":")); i++) {
+				if (tok_id_tmp) tok_id[i] = tok_id_tmp;
+				tok[1] = NULL;
+			}
 
-					mode = strtoul(pos, &pos, 8);
-					if (pos != end2) break;
+			/* uid:gid */
+			uid = strtoul(tok_id[0], &s, 10);
+			if (tok_id[0] == s) {
+				struct passwd *pass;
+				pass = getpwnam(tok_id[0]);
+				if (!pass) continue;
+				uid = pass->pw_uid;
+			}
+
+			gid = strtoul(tok_id[1], &s, 10);
+			if (tok_id[1] == s) {
+				struct group *grp;
+				grp = getgrnam(tok_id[1]);
+				if (!grp) continue;
+				gid = grp->gr_gid;
+			}
+
+			/* mode */
+			mode = (mode_t)strtoul(tok[2], &s, 8);
+
+			if (tok_len > 3) {
+#if ENABLE_FEATURE_MDEV_EXEC
+				const char *s2 = "@$*";
+				char *cmd_tmp;
+				unsigned int cmd = 0;
+#endif
+				/* mk_dir */
+				if (!strcmp(tok[3], ">>")) {
+					mk_dir = 1;
+					device_path = strdupa(tok[4]);
+					if (last_char_is(device_path, '/') == NULL)
+						strcat(device_path, "/");
+
+					mode_path = (mode_t)strtoul(tok[5], &s, 8);
 				}
-				if (ENABLE_FEATURE_MDEV_EXEC && field == 3) {
-					// Command to run
-					const char *s = "@$*";
-					const char *s2;
-					s2 = strchr(s, *pos++);
-					if (!s2) {
-						// Force error
-						field = 1;
-						break;
+#if ENABLE_FEATURE_MDEV_EXEC
+				else {
+					if ((cmd_tmp = strpbrk(tok[3], s2))!=NULL) {
+						int cmd_len = strlen(cmd_tmp) == 1 ? 1 : 0;
+
+						cmd = *cmd_tmp;
+
+						if (cmd_len == 1) {
+							command = strdupa(tok[4]);
+						} else {
+							command = strdupa(strrchr(tok[3], cmd_tmp[0])+1);
+						}
+
+						for (i=4+cmd_len; i<tok_len; i++) {
+							strcat(strcat(command, " "), tok[i]);
+						}
 					}
-					if ((s2-s+1) & (1<<delete))
-						command = xstrndup(pos, end-pos);
+					
 				}
 
-				pos = end2;
-			}
+				if (tok_len > 6) {
+					if ((cmd_tmp = strpbrk(tok[6], s2))!=NULL) {
+						int cmd_len = strlen(cmd_tmp) == 1 ? 1 : 0;
 
-			/* Did everything parse happily? */
+						cmd = *cmd_tmp;
 
-			if (field > 2) break;
-			if (field) bb_error_msg_and_die("bad line %d",line);
+						if (cmd_len == 1) {
+							command = xstrdup(tok[7]);
+						} else {
+							command = xstrdup(strrchr(tok[6], cmd_tmp[0])+1);
+						}
 
-			/* Next line */
-			pos = ++end;
+						for (i=7+cmd_len; i<tok_len; i++) {
+							strcat(strcat(command, " "), tok[i]);
+						}
+					}
+				}
+
+				switch (cmd) {
+				case '@':
+					if (delete) command = NULL;
+					break;
+				case '$':
+					if (!delete) command = NULL;
+					break;
+				case '*':
+				default :
+					break;
+				}
+#endif
+			}
+			free(line);
 		}
-		munmap(conf, len);
+		fclose(file);
  end_parse:	/* nothing */ ;
 	}
 
 	umask(0);
 	if (!delete) {
+		if (mk_dir) {
+			bb_make_directory(device_path, mode_path, FILEUTILS_RECUR);
+			device_name = strcat(device_path, device_name);
+		}
 		if (sscanf(temp, "%d:%d", &major, &minor) != 2) return;
 		if (mknod(device_name, mode | type, makedev(major, minor)) && errno != EEXIST)
 			bb_perror_msg_and_die("mknod %s", device_name);
@@ -179,6 +217,7 @@ static void make_device(char *path, int 
 
 		if (ENABLE_FEATURE_MDEV_CONF) chown(device_name, uid, gid);
 	}
+
 	if (command) {
 		int rc;
 		char *s;
@@ -189,45 +228,65 @@ static void make_device(char *path, int 
 		s[4] = 0;
 		putenv(s);
 		free(s);
-		free(command);
 		if (rc == -1) bb_perror_msg_and_die("cannot run %s", command);
 	}
-	if (delete) unlink(device_name);
+
+	if (delete) {
+		if (device_path) {
+			char *tmp_path;
+
+			tmp_path = strdupa(device_path);
+			device_name = strcat(tmp_path, device_name);
+		}
+
+		unlink(device_name);
+
+		if (device_path) {
+			remove_file(device_path, FILEUTILS_FORCE | FILEUTILS_RECUR);
+		}
+	}
+
 }
 
 /* Recursive search of /sys/block or /sys/class.  path must be a writeable
  * buffer of size PATH_MAX containing the directory string to start at. */
 
-static void find_dev(char *path)
+static int fileAction(const char *fileName, struct stat *statbuf, void *userData, int depth)
 {
-	DIR *dir;
-	size_t len = strlen(path);
-	struct dirent *entry;
+	char *last;
 
-	dir = opendir(path);
-	if (dir == NULL)
-		return;
+	last = bb_get_last_path_component((char *)fileName);
 
-	while ((entry = readdir(dir)) != NULL) {
-		struct stat st;
+	if(!strcmp(last, "subsystem")) return FALSE;
 
-		/* Skip "." and ".." (also skips hidden files, which is ok) */
+	if (S_ISLNK(statbuf->st_mode)) {
+		char *link_path, *new_path, *tmp_link;
+		RESERVE_CONFIG_BUFFER(device_path, PATH_MAX);
 
-		if (entry->d_name[0] == '.')
-			continue;
+		tmp_link = xmalloc_readlink_or_warn(fileName);
+		link_path = strdupa(tmp_link);
+		free(tmp_link);
+		new_path = link_path;
 
-		// uClibc doesn't fill out entry->d_type reliably. so we use lstat().
+		while (!strncmp(new_path, "../", 3)) {
+			new_path = new_path + 3;
+		}
 
-		snprintf(path+len, PATH_MAX-len, "/%s", entry->d_name);
-		if (!lstat(path, &st) && S_ISDIR(st.st_mode)) find_dev(path);
-		path[len] = 0;
+		sprintf(device_path, "/sys/%s", new_path);
+		make_device(device_path, 0);
+		free(device_path);
+		return TRUE;
+	}
 
-		/* If there's a dev entry, mknod it */
+	if (!strcmp(last, "dev")) {
+		char *tmp_name;
 
-		if (!strcmp(entry->d_name, "dev")) make_device(path, 0);
+		tmp_name = strndupa(fileName, strlen(fileName) - 4);
+		make_device(tmp_name, 0);
+		return TRUE;
 	}
 
-	closedir(dir);
+	return FALSE;
 }
 
 int mdev_main(int argc, char **argv);
@@ -248,9 +307,9 @@ int mdev_main(int argc, char **argv)
 		bbg.root_major = major(st.st_dev);
 		bbg.root_minor = minor(st.st_dev);
 		strcpy(temp,"/sys/block");
-		find_dev(temp);
+		recursive_action(temp, ACTION_RECURSE, fileAction, 0, NULL, 0);
 		strcpy(temp,"/sys/class");
-		find_dev(temp);
+		recursive_action(temp, ACTION_RECURSE, fileAction, 0, NULL, 0);
 
 	/* Hotplug */

---

From: Ned Ludd <[email-masked]>
Date: Fri, 18 May 2007 11:02:02 -0700

> > > this rewriting implies a minor modification of the syntax of mdev.config

It would be ideal not to alter the syntax of mdev.conf in anyway that 
people who already have established working configs will be forced to 
upgrade. Doing just that sort of the thing is why many of us dislike 
udev with it's ever changing code base.

> command execution but it is not the same code.

I've done most of what your doing simply in the mdev.conf when creating 
the "null" device, events and such. It's hackish but I think the end 
result is probably smaller than additional compiled binary code.

> 5- work with AND without CONFIG_SYSFS_DEPRECATED

Yeah we want that part which checks for the symlinks.

-- 
Ned Ludd <[email-masked]>
Gentoo Linux

---

From: Souf <[email-masked]>
Date: Fri, 18 May 2007 20:45:51 +0200

> udev with it's ever changing code base.

The syntax of mdev.con was not altered, what exists continuous perfectly
to work but it there optional additions, I attached my mdev.conf

> Yeah we want that part which checks for the symlinks.

you had it in the preceding message.
http://busybox.net/lists/busybox/attachments/20070518/845f1b6f/attachment-0001.bin

Thanks.

Malek