diff mbox

busybox: rename patches as per the new standard and update 1.21.0 mdev

Message ID 1366723305-14386-1-git-send-email-gustavo@zacarias.com.ar
State Superseded
Headers show

Commit Message

Gustavo Zacarias April 23, 2013, 1:21 p.m. UTC
Rename patches as per the patch naming policy and update the mdev fix
patch for 1.21.0 which reinstates subsystem matching functionality.

Signed-off-by: Gustavo Zacarias <gustavo@zacarias.com.ar>
---
 ...x-1.19.4-udhcp.patch => busybox-01-udhcp.patch} |   0
 ...ernel_ver.patch => busybox-01-kernel_ver.patch} |   0
 ...box-1.20.2-ntpd.patch => busybox-02-ntpd.patch} |   0
 ...x.patch => busybox-03-pkg-config-selinux.patch} |   0
 ...esource.patch => busybox-04-sys-resource.patch} |   0
 package/busybox/1.21.0/busybox-01-mdev.patch       | 643 +++++++++++++++++++++
 ....0-platform.patch => busybox-02-platform.patch} |   0
 ...busybox-1.21.0-xz.patch => busybox-03-xz.patch} |   0
 package/busybox/1.21.0/busybox-1.21.0-mdev.patch   |  35 --
 9 files changed, 643 insertions(+), 35 deletions(-)
 rename package/busybox/1.19.4/{busybox-1.19.4-udhcp.patch => busybox-01-udhcp.patch} (100%)
 rename package/busybox/1.20.2/{busybox-1.20.2-kernel_ver.patch => busybox-01-kernel_ver.patch} (100%)
 rename package/busybox/1.20.2/{busybox-1.20.2-ntpd.patch => busybox-02-ntpd.patch} (100%)
 rename package/busybox/1.20.2/{busybox-1.20.2-pkg-config-selinux.patch => busybox-03-pkg-config-selinux.patch} (100%)
 rename package/busybox/1.20.2/{busybox-1.20.2-sys-resource.patch => busybox-04-sys-resource.patch} (100%)
 create mode 100644 package/busybox/1.21.0/busybox-01-mdev.patch
 rename package/busybox/1.21.0/{busybox-1.21.0-platform.patch => busybox-02-platform.patch} (100%)
 rename package/busybox/1.21.0/{busybox-1.21.0-xz.patch => busybox-03-xz.patch} (100%)
 delete mode 100644 package/busybox/1.21.0/busybox-1.21.0-mdev.patch

Comments

Peter Korsgaard April 23, 2013, 2:04 p.m. UTC | #1
>>>>> "Gustavo" == Gustavo Zacarias <gustavo@zacarias.com.ar> writes:

 Gustavo> Rename patches as per the patch naming policy and update the
 Gustavo> mdev fix patch for 1.21.0 which reinstates subsystem matching
 Gustavo> functionality.

Sorry, I disagree with this rename. I know this is what we discussed at
the dev day, but it imho makes more sense to use the upstream naming
convention for busybox:

http://busybox.net/downloads/fixes-1.21.0/

Could you send a patch that only updates the mdev bugfix instead?
Gustavo Zacarias April 23, 2013, 2:15 p.m. UTC | #2
On 04/23/2013 11:04 AM, Peter Korsgaard wrote:

> Sorry, I disagree with this rename. I know this is what we discussed at
> the dev day, but it imho makes more sense to use the upstream naming
> convention for busybox:
> 
> http://busybox.net/downloads/fixes-1.21.0/
> 
> Could you send a patch that only updates the mdev bugfix instead?

Sure, i just wanted the official "go" or "no go" as discussed with
Arnout so i went with the renaming to get a definitive answer to the
dilemma.
Maybe an update to the docs is in order to reflect this?
For instance "if upstream has versioned patches then it should be
respected" or something like that?
Regards.
Arnout Vandecappelle April 23, 2013, 9:10 p.m. UTC | #3
On 23/04/13 16:04, Peter Korsgaard wrote:
>>>>>> "Gustavo" == Gustavo Zacarias <gustavo@zacarias.com.ar> writes:
>
>   Gustavo> Rename patches as per the patch naming policy and update the
>   Gustavo> mdev fix patch for 1.21.0 which reinstates subsystem matching
>   Gustavo> functionality.
>
> Sorry, I disagree with this rename. I know this is what we discussed at
> the dev day, but it imho makes more sense to use the upstream naming
> convention for busybox:
>
> http://busybox.net/downloads/fixes-1.21.0/

  Actually, if upstream has the patches available for download, they 
should be downloaded instead of of included in buildroot, no?


  Regards,
  Arnout
Peter Korsgaard April 23, 2013, 9:42 p.m. UTC | #4
>>>>> "Arnout" == Arnout Vandecappelle <arnout@mind.be> writes:

 Arnout> On 23/04/13 16:04, Peter Korsgaard wrote:
 >>>>>>> "Gustavo" == Gustavo Zacarias <gustavo@zacarias.com.ar> writes:
 >> 
 Gustavo> Rename patches as per the patch naming policy and update the
 Gustavo> mdev fix patch for 1.21.0 which reinstates subsystem matching
 Gustavo> functionality.
 >> 
 >> Sorry, I disagree with this rename. I know this is what we discussed at
 >> the dev day, but it imho makes more sense to use the upstream naming
 >> convention for busybox:
 >> 
 >> http://busybox.net/downloads/fixes-1.21.0/

 Arnout>  Actually, if upstream has the patches available for download,
 Arnout> they should be downloaded instead of of included in buildroot,
 Arnout> no?

True. For busybox our patch download infrastructure unfortunately
doesn't work as the patches aren't located in the same directory as the
tarball (and the fact that we support multiple versions also makes it a
bit more complicated).
Gustavo Zacarias April 24, 2013, 6:07 a.m. UTC | #5
On 04/23/2013 06:42 PM, Peter Korsgaard wrote:

>  Arnout>  Actually, if upstream has the patches available for download,
>  Arnout> they should be downloaded instead of of included in buildroot,
>  Arnout> no?
> 
> True. For busybox our patch download infrastructure unfortunately
> doesn't work as the patches aren't located in the same directory as the
> tarball (and the fact that we support multiple versions also makes it a
> bit more complicated).

The problem with downloading busybox patches directly is that upstream
kind of sucks regarding naming and updates.
For instance the mdev patch changed without being reflected in the
filename, and so did other patches in the past.
This could cause unexpected behaviour depending on which patch each user
got, specially when they're being early adopters of new versions and
using a common download directory.
Telling people to "remove your patches to force a new download and try
again" would just be adding unnecessary noise/bugs.
Regards.
Peter Korsgaard April 24, 2013, 6:11 a.m. UTC | #6
>>>>> "Gustavo" == Gustavo Zacarias <gustavo@zacarias.com.ar> writes:

Hi,

 Gustavo> The problem with downloading busybox patches directly is that
 Gustavo> upstream kind of sucks regarding naming and updates.  For
 Gustavo> instance the mdev patch changed without being reflected in the
 Gustavo> filename, and so did other patches in the past.  This could
 Gustavo> cause unexpected behaviour depending on which patch each user
 Gustavo> got, specially when they're being early adopters of new
 Gustavo> versions and using a common download directory.  Telling
 Gustavo> people to "remove your patches to force a new download and try
 Gustavo> again" would just be adding unnecessary noise/bugs.

Indeed. I think the only sane option is to bundle the busybox patches in
BR until upstream changes their ways.
diff mbox

Patch

diff --git a/package/busybox/1.19.4/busybox-1.19.4-udhcp.patch b/package/busybox/1.19.4/busybox-01-udhcp.patch
similarity index 100%
rename from package/busybox/1.19.4/busybox-1.19.4-udhcp.patch
rename to package/busybox/1.19.4/busybox-01-udhcp.patch
diff --git a/package/busybox/1.20.2/busybox-1.20.2-kernel_ver.patch b/package/busybox/1.20.2/busybox-01-kernel_ver.patch
similarity index 100%
rename from package/busybox/1.20.2/busybox-1.20.2-kernel_ver.patch
rename to package/busybox/1.20.2/busybox-01-kernel_ver.patch
diff --git a/package/busybox/1.20.2/busybox-1.20.2-ntpd.patch b/package/busybox/1.20.2/busybox-02-ntpd.patch
similarity index 100%
rename from package/busybox/1.20.2/busybox-1.20.2-ntpd.patch
rename to package/busybox/1.20.2/busybox-02-ntpd.patch
diff --git a/package/busybox/1.20.2/busybox-1.20.2-pkg-config-selinux.patch b/package/busybox/1.20.2/busybox-03-pkg-config-selinux.patch
similarity index 100%
rename from package/busybox/1.20.2/busybox-1.20.2-pkg-config-selinux.patch
rename to package/busybox/1.20.2/busybox-03-pkg-config-selinux.patch
diff --git a/package/busybox/1.20.2/busybox-1.20.2-sys-resource.patch b/package/busybox/1.20.2/busybox-04-sys-resource.patch
similarity index 100%
rename from package/busybox/1.20.2/busybox-1.20.2-sys-resource.patch
rename to package/busybox/1.20.2/busybox-04-sys-resource.patch
diff --git a/package/busybox/1.21.0/busybox-01-mdev.patch b/package/busybox/1.21.0/busybox-01-mdev.patch
new file mode 100644
index 0000000..8f6c8d8
--- /dev/null
+++ b/package/busybox/1.21.0/busybox-01-mdev.patch
@@ -0,0 +1,643 @@ 
+--- busybox-1.21.0/util-linux/mdev.c
++++ busybox-1.21.0-mdev/util-linux/mdev.c
+@@ -80,7 +80,7 @@
+ //usage:	IF_FEATURE_MDEV_CONF(
+ //usage:       "\n"
+ //usage:       "It uses /etc/mdev.conf with lines\n"
+-//usage:       "	[-]DEVNAME UID:GID PERM"
++//usage:       "	[-][ENV=regex;]...DEVNAME UID:GID PERM"
+ //usage:			IF_FEATURE_MDEV_RENAME(" [>|=PATH]|[!]")
+ //usage:			IF_FEATURE_MDEV_EXEC(" [@|$|*PROG]")
+ //usage:       "\n"
+@@ -230,9 +230,34 @@
+  * SUBSYSTEM=block
+  */
+ 
+-static const char keywords[] ALIGN1 = "add\0remove\0change\0";
++#define DEBUG_LVL 2
++
++#if DEBUG_LVL >= 1
++# define dbg1(...) do { if (G.verbose) bb_error_msg(__VA_ARGS__); } while(0)
++#else
++# define dbg1(...) ((void)0)
++#endif
++#if DEBUG_LVL >= 2
++# define dbg2(...) do { if (G.verbose >= 2) bb_error_msg(__VA_ARGS__); } while(0)
++#else
++# define dbg2(...) ((void)0)
++#endif
++#if DEBUG_LVL >= 3
++# define dbg3(...) do { if (G.verbose >= 3) bb_error_msg(__VA_ARGS__); } while(0)
++#else
++# define dbg3(...) ((void)0)
++#endif
++
++
++static const char keywords[] ALIGN1 = "add\0remove\0"; // "change\0"
+ enum { OP_add, OP_remove };
+ 
++struct envmatch {
++	struct envmatch *next;
++	char *envname;
++	regex_t match;
++};
++
+ struct rule {
+ 	bool keep_matching;
+ 	bool regex_compiled;
+@@ -243,12 +268,14 @@ struct rule {
+ 	char *ren_mov;
+ 	IF_FEATURE_MDEV_EXEC(char *r_cmd;)
+ 	regex_t match;
++	struct envmatch *envmatch;
+ };
+ 
+ struct globals {
+ 	int root_major, root_minor;
+ 	smallint verbose;
+ 	char *subsystem;
++	char *subsys_env; /* for putenv("SUBSYSTEM=subsystem") */
+ #if ENABLE_FEATURE_MDEV_CONF
+ 	const char *filename;
+ 	parser_t *parser;
+@@ -256,6 +283,7 @@ struct globals {
+ 	unsigned rule_idx;
+ #endif
+ 	struct rule cur_rule;
++	char timestr[sizeof("60.123456")];
+ } FIX_ALIASING;
+ #define G (*(struct globals*)&bb_common_bufsiz1)
+ #define INIT_G() do { \
+@@ -270,13 +298,6 @@ struct globals {
+ /* We use additional 64+ bytes in make_device() */
+ #define SCRATCH_SIZE 80
+ 
+-#if 0
+-# define dbg(...) bb_error_msg(__VA_ARGS__)
+-#else
+-# define dbg(...) ((void)0)
+-#endif
+-
+-
+ #if ENABLE_FEATURE_MDEV_CONF
+ 
+ static void make_default_cur_rule(void)
+@@ -288,14 +309,65 @@ static void make_default_cur_rule(void)
+ 
+ static void clean_up_cur_rule(void)
+ {
++	struct envmatch *e;
++
+ 	free(G.cur_rule.envvar);
++	free(G.cur_rule.ren_mov);
+ 	if (G.cur_rule.regex_compiled)
+ 		regfree(&G.cur_rule.match);
+-	free(G.cur_rule.ren_mov);
+ 	IF_FEATURE_MDEV_EXEC(free(G.cur_rule.r_cmd);)
++	e = G.cur_rule.envmatch;
++	while (e) {
++		free(e->envname);
++		regfree(&e->match);
++		e = e->next;
++	}
+ 	make_default_cur_rule();
+ }
+ 
++/* In later versions, endofname is in libbb */
++#define endofname mdev_endofname
++static
++const char* FAST_FUNC
++endofname(const char *name)
++{
++#define is_name(c)      ((c) == '_' || isalpha((unsigned char)(c)))
++#define is_in_name(c)   ((c) == '_' || isalnum((unsigned char)(c)))
++	if (!is_name(*name))
++		return name;
++	while (*++name) {
++		if (!is_in_name(*name))
++			break;
++	}
++	return name;
++}
++
++static char *parse_envmatch_pfx(char *val)
++{
++	struct envmatch **nextp = &G.cur_rule.envmatch;
++
++	for (;;) {
++		struct envmatch *e;
++		char *semicolon;
++		char *eq = strchr(val, '=');
++		if (!eq /* || eq == val? */)
++			return val;
++		if (endofname(val) != eq)
++			return val;
++		semicolon = strchr(eq, ';');
++		if (!semicolon)
++			return val;
++		/* ENVVAR=regex;... */
++		*nextp = e = xzalloc(sizeof(*e));
++		nextp = &e->next;
++		e->envname = xstrndup(val, eq - val);
++		*semicolon = '\0';
++		xregcomp(&e->match, eq + 1, REG_EXTENDED);
++		*semicolon = ';';
++		val = semicolon + 1;
++	}
++}
++
+ static void parse_next_rule(void)
+ {
+ 	/* Note: on entry, G.cur_rule is set to default */
+@@ -308,12 +380,13 @@ static void parse_next_rule(void)
+ 			break;
+ 
+ 		/* Fields: [-]regex uid:gid mode [alias] [cmd] */
+-		dbg("token1:'%s'", tokens[1]);
++		dbg3("token1:'%s'", tokens[1]);
+ 
+ 		/* 1st field */
+ 		val = tokens[0];
+ 		G.cur_rule.keep_matching = ('-' == val[0]);
+ 		val += G.cur_rule.keep_matching; /* swallow leading dash */
++		val = parse_envmatch_pfx(val);
+ 		if (val[0] == '@') {
+ 			/* @major,minor[-minor2] */
+ 			/* (useful when name is ambiguous:
+@@ -328,8 +401,10 @@ static void parse_next_rule(void)
+ 			if (sc == 2)
+ 				G.cur_rule.min1 = G.cur_rule.min0;
+ 		} else {
++			char *eq = strchr(val, '=');
+ 			if (val[0] == '$') {
+-				char *eq = strchr(++val, '=');
++				/* $ENVVAR=regex ... */
++				val++;
+ 				if (!eq) {
+ 					bb_error_msg("bad $envvar=regex on line %d", G.parser->lineno);
+ 					goto next_rule;
+@@ -373,7 +448,7 @@ static void parse_next_rule(void)
+ 		clean_up_cur_rule();
+ 	} /* while (config_read) */
+ 
+-	dbg("config_close(G.parser)");
++	dbg3("config_close(G.parser)");
+ 	config_close(G.parser);
+ 	G.parser = NULL;
+ 
+@@ -390,7 +465,7 @@ static const struct rule *next_rule(void
+ 
+ 	/* Open conf file if we didn't do it yet */
+ 	if (!G.parser && G.filename) {
+-		dbg("config_open('%s')", G.filename);
++		dbg3("config_open('%s')", G.filename);
+ 		G.parser = config_open2(G.filename, fopen_for_read);
+ 		G.filename = NULL;
+ 	}
+@@ -399,7 +474,7 @@ static const struct rule *next_rule(void
+ 		/* mdev -s */
+ 		/* Do we have rule parsed already? */
+ 		if (G.rule_vec[G.rule_idx]) {
+-			dbg("< G.rule_vec[G.rule_idx:%d]=%p", G.rule_idx, G.rule_vec[G.rule_idx]);
++			dbg3("< G.rule_vec[G.rule_idx:%d]=%p", G.rule_idx, G.rule_vec[G.rule_idx]);
+ 			return G.rule_vec[G.rule_idx++];
+ 		}
+ 		make_default_cur_rule();
+@@ -416,13 +491,28 @@ static const struct rule *next_rule(void
+ 			rule = memcpy(xmalloc(sizeof(G.cur_rule)), &G.cur_rule, sizeof(G.cur_rule));
+ 			G.rule_vec = xrealloc_vector(G.rule_vec, 4, G.rule_idx);
+ 			G.rule_vec[G.rule_idx++] = rule;
+-			dbg("> G.rule_vec[G.rule_idx:%d]=%p", G.rule_idx, G.rule_vec[G.rule_idx]);
++			dbg3("> G.rule_vec[G.rule_idx:%d]=%p", G.rule_idx, G.rule_vec[G.rule_idx]);
+ 		}
+ 	}
+ 
+ 	return rule;
+ }
+ 
++static int env_matches(struct envmatch *e)
++{
++	while (e) {
++		int r;
++		char *val = getenv(e->envname);
++		if (!val)
++			return 0;
++		r = regexec(&e->match, val, /*size*/ 0, /*range[]*/ NULL, /*eflags*/ 0);
++		if (r != 0) /* no match */
++			return 0;
++		e = e->next;
++	}
++	return 1;
++}
++
+ #else
+ 
+ # define next_rule() (&G.cur_rule)
+@@ -479,9 +569,6 @@ static void make_device(char *device_nam
+ {
+ 	int major, minor, type, len;
+ 
+-	if (G.verbose)
+-		bb_error_msg("device: %s, %s", device_name, path);
+-
+ 	/* Try to read major/minor string.  Note that the kernel puts \n after
+ 	 * the data, so we don't need to worry about null terminating the string
+ 	 * because sscanf() will stop at the first nondigit, which \n is.
+@@ -500,8 +587,7 @@ static void make_device(char *device_nam
+ 			/* no "dev" file, but we can still run scripts
+ 			 * based on device name */
+ 		} else if (sscanf(++dev_maj_min, "%u:%u", &major, &minor) == 2) {
+-			if (G.verbose)
+-				bb_error_msg("maj,min: %u,%u", major, minor);
++			dbg1("dev %u,%u", major, minor);
+ 		} else {
+ 			major = -1;
+ 		}
+@@ -511,7 +597,8 @@ static void make_device(char *device_nam
+ 	/* Determine device name, type, major and minor */
+ 	if (!device_name)
+ 		device_name = (char*) bb_basename(path);
+-	/* http://kernel.org/doc/pending/hotplug.txt says that only
++	/*
++	 * http://kernel.org/doc/pending/hotplug.txt says that only
+ 	 * "/sys/block/..." is for block devices. "/sys/bus" etc is not.
+ 	 * But since 2.6.25 block devices are also in /sys/class/block.
+ 	 * We use strstr("/block/") to forestall future surprises.
+@@ -537,6 +624,8 @@ static void make_device(char *device_nam
+ 		rule = next_rule();
+ 
+ #if ENABLE_FEATURE_MDEV_CONF
++		if (!env_matches(rule->envmatch))
++			continue;
+ 		if (rule->maj >= 0) {  /* @maj,min rule */
+ 			if (major != rule->maj)
+ 				continue;
+@@ -547,7 +636,7 @@ static void make_device(char *device_nam
+ 		}
+ 		if (rule->envvar) { /* $envvar=regex rule */
+ 			str_to_match = getenv(rule->envvar);
+-			dbg("getenv('%s'):'%s'", rule->envvar, str_to_match);
++			dbg3("getenv('%s'):'%s'", rule->envvar, str_to_match);
+ 			if (!str_to_match)
+ 				continue;
+ 		}
+@@ -555,7 +644,7 @@ static void make_device(char *device_nam
+ 
+ 		if (rule->regex_compiled) {
+ 			int regex_match = regexec(&rule->match, str_to_match, ARRAY_SIZE(off), off, 0);
+-			dbg("regex_match for '%s':%d", str_to_match, regex_match);
++			dbg3("regex_match for '%s':%d", str_to_match, regex_match);
+ 			//bb_error_msg("matches:");
+ 			//for (int i = 0; i < ARRAY_SIZE(off); i++) {
+ 			//	if (off[i].rm_so < 0) continue;
+@@ -574,9 +663,8 @@ static void make_device(char *device_nam
+ 		}
+ 		/* else: it's final implicit "match-all" rule */
+  rule_matches:
++		dbg2("rule matched, line %d", G.parser ? G.parser->lineno : -1);
+ #endif
+-		dbg("rule matched");
+-
+ 		/* Build alias name */
+ 		alias = NULL;
+ 		if (ENABLE_FEATURE_MDEV_RENAME && rule->ren_mov) {
+@@ -619,34 +707,30 @@ static void make_device(char *device_nam
+ 				}
+ 			}
+ 		}
+-		dbg("alias:'%s'", alias);
++		dbg3("alias:'%s'", alias);
+ 
+ 		command = NULL;
+ 		IF_FEATURE_MDEV_EXEC(command = rule->r_cmd;)
+ 		if (command) {
+-			const char *s = "$@*";
+-			const char *s2 = strchr(s, command[0]);
+-
+ 			/* Are we running this command now?
+-			 * Run $cmd on delete, @cmd on create, *cmd on both
++			 * Run @cmd on create, $cmd on delete, *cmd on any
+ 			 */
+-			if (s2 - s != (operation == OP_remove) || *s2 == '*') {
+-				/* We are here if: '*',
+-				 * or: '@' and delete = 0,
+-				 * or: '$' and delete = 1
+-				 */
++			if ((command[0] == '@' && operation == OP_add)
++			 || (command[0] == '$' && operation == OP_remove)
++			 || (command[0] == '*')
++			) {
+ 				command++;
+ 			} else {
+ 				command = NULL;
+ 			}
+ 		}
+-		dbg("command:'%s'", command);
++		dbg3("command:'%s'", command);
+ 
+ 		/* "Execute" the line we found */
+ 		node_name = device_name;
+ 		if (ENABLE_FEATURE_MDEV_RENAME && alias) {
+ 			node_name = alias = build_alias(alias, device_name);
+-			dbg("alias2:'%s'", alias);
++			dbg3("alias2:'%s'", alias);
+ 		}
+ 
+ 		if (operation == OP_add && major >= 0) {
+@@ -656,8 +740,17 @@ static void make_device(char *device_nam
+ 				mkdir_recursive(node_name);
+ 				*slash = '/';
+ 			}
+-			if (G.verbose)
+-				bb_error_msg("mknod: %s (%d,%d) %o", node_name, major, minor, rule->mode | type);
++			if (ENABLE_FEATURE_MDEV_CONF) {
++				dbg1("mknod %s (%d,%d) %o"
++					" %u:%u",
++					node_name, major, minor, rule->mode | type,
++					rule->ugid.uid, rule->ugid.gid
++				);
++			} else {
++				dbg1("mknod %s (%d,%d) %o",
++					node_name, major, minor, rule->mode | type
++				);
++			}
+ 			if (mknod(node_name, rule->mode | type, makedev(major, minor)) && errno != EEXIST)
+ 				bb_perror_msg("can't create '%s'", node_name);
+ 			if (ENABLE_FEATURE_MDEV_CONF) {
+@@ -671,8 +764,7 @@ static void make_device(char *device_nam
+ //TODO: on devtmpfs, device_name already exists and symlink() fails.
+ //End result is that instead of symlink, we have two nodes.
+ //What should be done?
+-					if (G.verbose)
+-						bb_error_msg("symlink: %s", device_name);
++					dbg1("symlink: %s", device_name);
+ 					symlink(node_name, device_name);
+ 				}
+ 			}
+@@ -681,27 +773,21 @@ static void make_device(char *device_nam
+ 		if (ENABLE_FEATURE_MDEV_EXEC && command) {
+ 			/* setenv will leak memory, use putenv/unsetenv/free */
+ 			char *s = xasprintf("%s=%s", "MDEV", node_name);
+-			char *s1 = xasprintf("%s=%s", "SUBSYSTEM", G.subsystem);
+ 			putenv(s);
+-			putenv(s1);
+-			if (G.verbose)
+-				bb_error_msg("running: %s", command);
++			dbg1("running: %s", command);
+ 			if (system(command) == -1)
+ 				bb_perror_msg("can't run '%s'", command);
+-			bb_unsetenv_and_free(s1);
+ 			bb_unsetenv_and_free(s);
+ 		}
+ 
+ 		if (operation == OP_remove && major >= -1) {
+ 			if (ENABLE_FEATURE_MDEV_RENAME && alias) {
+ 				if (aliaslink == '>') {
+-					if (G.verbose)
+-						bb_error_msg("unlink: %s", device_name);
++					dbg1("unlink: %s", device_name);
+ 					unlink(device_name);
+ 				}
+ 			}
+-			if (G.verbose)
+-				bb_error_msg("unlink: %s", node_name);
++			dbg1("unlink: %s", node_name);
+ 			unlink(node_name);
+ 		}
+ 
+@@ -746,9 +832,16 @@ static int FAST_FUNC dirAction(const cha
+ 	 * under /sys/class/ */
+ 	if (1 == depth) {
+ 		free(G.subsystem);
++		if (G.subsys_env) {
++			bb_unsetenv_and_free(G.subsys_env);
++			G.subsys_env = NULL;
++		}
+ 		G.subsystem = strrchr(fileName, '/');
+-		if (G.subsystem)
++		if (G.subsystem) {
+ 			G.subsystem = xstrdup(G.subsystem + 1);
++			G.subsys_env = xasprintf("%s=%s", "SUBSYSTEM", G.subsystem);
++			putenv(G.subsys_env);
++		}
+ 	}
+ 
+ 	return (depth >= MAX_SYSFS_DEPTH ? SKIP : TRUE);
+@@ -813,12 +906,107 @@ static void load_firmware(const char *fi
+ 		full_write(loading_fd, "-1", 2);
+ 
+  out:
++	xchdir("/dev");
+ 	if (ENABLE_FEATURE_CLEAN_UP) {
+ 		close(firmware_fd);
+ 		close(loading_fd);
+ 	}
+ }
+ 
++static char *curtime(void)
++{
++	struct timeval tv;
++	gettimeofday(&tv, NULL);
++	sprintf(G.timestr, "%u.%06u", (unsigned)tv.tv_sec % 60, (unsigned)tv.tv_usec);
++	return G.timestr;
++}
++
++static void open_mdev_log(const char *seq, unsigned my_pid)
++{
++	int logfd = open("mdev.log", O_WRONLY | O_APPEND);
++	if (logfd >= 0) {
++		xmove_fd(logfd, STDERR_FILENO);
++		G.verbose = 2;
++		applet_name = xasprintf("%s[%s]", applet_name, seq ? seq : utoa(my_pid));
++	}
++}
++
++/* If it exists, does /dev/mdev.seq match $SEQNUM?
++ * If it does not match, earlier mdev is running
++ * in parallel, and we need to wait.
++ * Active mdev pokes us with SIGCHLD to check the new file.
++ */
++static int
++wait_for_seqfile(const char *seq)
++{
++	/* We time out after 2 sec */
++	static const struct timespec ts = { 0, 32*1000*1000 };
++	int timeout = 2000 / 32;
++	int seq_fd = -1;
++	int do_once = 1;
++	sigset_t set_CHLD;
++
++	sigemptyset(&set_CHLD);
++	sigaddset(&set_CHLD, SIGCHLD);
++	sigprocmask(SIG_BLOCK, &set_CHLD, NULL);
++
++	for (;;) {
++		int seqlen;
++		char seqbuf[sizeof(int)*3 + 2];
++
++		if (seq_fd < 0) {
++			seq_fd = open("mdev.seq", O_RDWR);
++			if (seq_fd < 0)
++				break;
++		}
++		seqlen = pread(seq_fd, seqbuf, sizeof(seqbuf) - 1, 0);
++		if (seqlen < 0) {
++			close(seq_fd);
++			seq_fd = -1;
++			break;
++		}
++		seqbuf[seqlen] = '\0';
++		if (seqbuf[0] == '\n') {
++			/* seed file: write out seq ASAP */
++			xwrite_str(seq_fd, seq);
++			xlseek(seq_fd, 0, SEEK_SET);
++			dbg2("first seq written");
++			break;
++		}
++		if (strcmp(seq, seqbuf) == 0) {
++			/* correct idx */
++			break;
++		}
++		if (do_once) {
++			dbg2("%s waiting for '%s'", curtime(), seqbuf);
++			do_once = 0;
++		}
++		if (sigtimedwait(&set_CHLD, NULL, &ts) >= 0) {
++			dbg3("woken up");
++			continue; /* don't decrement timeout! */
++		}
++		if (--timeout == 0) {
++			dbg1("%s waiting for '%s'", "timed out", seqbuf);
++			break;
++		}
++	}
++	sigprocmask(SIG_UNBLOCK, &set_CHLD, NULL);
++	return seq_fd;
++}
++
++static void signal_mdevs(unsigned my_pid)
++{
++	procps_status_t* p = NULL;
++	while ((p = procps_scan(p, PSSCAN_ARGV0)) != NULL) {
++		if (p->pid != my_pid
++		 && p->argv0
++		 && strcmp(bb_basename(p->argv0), "mdev") == 0
++		) {
++			kill(p->pid, SIGCHLD);
++		}
++	}
++}
++
+ int mdev_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+ int mdev_main(int argc UNUSED_PARAM, char **argv)
+ {
+@@ -840,8 +1028,8 @@ int mdev_main(int argc UNUSED_PARAM, cha
+ 	xchdir("/dev");
+ 
+ 	if (argv[1] && strcmp(argv[1], "-s") == 0) {
+-		/* Scan:
+-		 * mdev -s
++		/*
++		 * Scan: mdev -s
+ 		 */
+ 		struct stat st;
+ 
+@@ -853,6 +1041,8 @@ int mdev_main(int argc UNUSED_PARAM, cha
+ 		G.root_major = major(st.st_dev);
+ 		G.root_minor = minor(st.st_dev);
+ 
++		putenv((char*)"ACTION=add");
++
+ 		/* ACTION_FOLLOWLINKS is needed since in newer kernels
+ 		 * /sys/block/loop* (for example) are symlinks to dirs,
+ 		 * not real directories.
+@@ -878,11 +1068,13 @@ int mdev_main(int argc UNUSED_PARAM, cha
+ 		char *action;
+ 		char *env_devname;
+ 		char *env_devpath;
++		unsigned my_pid;
++		int seq_fd;
+ 		smalluint op;
+ 
+ 		/* Hotplug:
+ 		 * env ACTION=... DEVPATH=... SUBSYSTEM=... [SEQNUM=...] mdev
+-		 * ACTION can be "add" or "remove"
++		 * ACTION can be "add", "remove", "change"
+ 		 * DEVPATH is like "/block/sda" or "/class/input/mice"
+ 		 */
+ 		action = getenv("ACTION");
+@@ -893,39 +1085,20 @@ int mdev_main(int argc UNUSED_PARAM, cha
+ 		if (!action || !env_devpath /*|| !G.subsystem*/)
+ 			bb_show_usage();
+ 		fw = getenv("FIRMWARE");
+-		/* If it exists, does /dev/mdev.seq match $SEQNUM?
+-		 * If it does not match, earlier mdev is running
+-		 * in parallel, and we need to wait */
+ 		seq = getenv("SEQNUM");
+-		if (seq) {
+-			int timeout = 2000 / 32; /* 2000 msec */
+-			do {
+-				int seqlen;
+-				char seqbuf[sizeof(int)*3 + 2];
+-
+-				seqlen = open_read_close("mdev.seq", seqbuf, sizeof(seqbuf) - 1);
+-				if (seqlen < 0) {
+-					seq = NULL;
+-					break;
+-				}
+-				seqbuf[seqlen] = '\0';
+-				if (seqbuf[0] == '\n' /* seed file? */
+-				 || strcmp(seq, seqbuf) == 0 /* correct idx? */
+-				) {
+-					break;
+-				}
+-				usleep(32*1000);
+-			} while (--timeout);
+-		}
+ 
+-		{
+-			int logfd = open("/dev/mdev.log", O_WRONLY | O_APPEND);
+-			if (logfd >= 0) {
+-				xmove_fd(logfd, STDERR_FILENO);
+-				G.verbose = 1;
+-				bb_error_msg("seq: %s action: %s", seq, action);
+-			}
+-		}
++		my_pid = getpid();
++		open_mdev_log(seq, my_pid);
++
++		seq_fd = seq ? wait_for_seqfile(seq) : -1;
++
++		dbg1("%s "
++			"ACTION:%s SUBSYSTEM:%s DEVNAME:%s DEVPATH:%s"
++			"%s%s",
++			curtime(),
++			action, G.subsystem, env_devname, env_devpath,
++			fw ? " FW:" : "", fw ? fw : ""
++		);
+ 
+ 		snprintf(temp, PATH_MAX, "/sys%s", env_devpath);
+ 		if (op == OP_remove) {
+@@ -935,16 +1108,18 @@ int mdev_main(int argc UNUSED_PARAM, cha
+ 			if (!fw)
+ 				make_device(env_devname, temp, op);
+ 		}
+-		else if (op == OP_add) {
++		else {
+ 			make_device(env_devname, temp, op);
+ 			if (ENABLE_FEATURE_MDEV_LOAD_FIRMWARE) {
+-				if (fw)
++				if (op == OP_add && fw)
+ 					load_firmware(fw, temp);
+ 			}
+ 		}
+ 
+-		if (seq) {
+-			xopen_xwrite_close("mdev.seq", utoa(xatou(seq) + 1));
++		dbg1("%s exiting", curtime());
++		if (seq_fd >= 0) {
++			xwrite_str(seq_fd, utoa(xatou(seq) + 1));
++			signal_mdevs(my_pid);
+ 		}
+ 	}
+ 
diff --git a/package/busybox/1.21.0/busybox-1.21.0-platform.patch b/package/busybox/1.21.0/busybox-02-platform.patch
similarity index 100%
rename from package/busybox/1.21.0/busybox-1.21.0-platform.patch
rename to package/busybox/1.21.0/busybox-02-platform.patch
diff --git a/package/busybox/1.21.0/busybox-1.21.0-xz.patch b/package/busybox/1.21.0/busybox-03-xz.patch
similarity index 100%
rename from package/busybox/1.21.0/busybox-1.21.0-xz.patch
rename to package/busybox/1.21.0/busybox-03-xz.patch
diff --git a/package/busybox/1.21.0/busybox-1.21.0-mdev.patch b/package/busybox/1.21.0/busybox-1.21.0-mdev.patch
deleted file mode 100644
index cb873fa..0000000
--- a/package/busybox/1.21.0/busybox-1.21.0-mdev.patch
+++ /dev/null
@@ -1,35 +0,0 @@ 
---- busybox-1.21.0/util-linux/mdev.c
-+++ busybox-1.21.0-mdev/util-linux/mdev.c
-@@ -661,6 +661,8 @@ static void make_device(char *device_nam
- 			if (mknod(node_name, rule->mode | type, makedev(major, minor)) && errno != EEXIST)
- 				bb_perror_msg("can't create '%s'", node_name);
- 			if (ENABLE_FEATURE_MDEV_CONF) {
-+				if (G.verbose)
-+					bb_error_msg("chmod: %o chown: %u:%u", rule->mode, rule->ugid.uid, rule->ugid.gid);
- 				chmod(node_name, rule->mode);
- 				chown(node_name, rule->ugid.uid, rule->ugid.gid);
- 			}
-@@ -813,6 +815,7 @@ static void load_firmware(const char *fi
- 		full_write(loading_fd, "-1", 2);
- 
-  out:
-+	xchdir("/dev");
- 	if (ENABLE_FEATURE_CLEAN_UP) {
- 		close(firmware_fd);
- 		close(loading_fd);
-@@ -919,11 +922,13 @@ int mdev_main(int argc UNUSED_PARAM, cha
- 		}
- 
- 		{
--			int logfd = open("/dev/mdev.log", O_WRONLY | O_APPEND);
-+			int logfd = open("mdev.log", O_WRONLY | O_APPEND);
- 			if (logfd >= 0) {
- 				xmove_fd(logfd, STDERR_FILENO);
- 				G.verbose = 1;
--				bb_error_msg("seq: %s action: %s", seq, action);
-+				if (seq)
-+					applet_name = xasprintf("%s[%s]", applet_name, seq);
-+				bb_error_msg("action: %s", action);
- 			}
- 		}
-