diff options
Diffstat (limited to 'package')
| -rw-r--r-- | package/busybox/1.21.0/busybox-1.21.0-mdev.patch | 640 | 
1 files changed, 624 insertions, 16 deletions
| 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 index cb873fafc..8f6c8d80e 100644 --- a/package/busybox/1.21.0/busybox-1.21.0-mdev.patch +++ b/package/busybox/1.21.0/busybox-1.21.0-mdev.patch @@ -1,15 +1,429 @@  --- 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 +@@ -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) { -+				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); +@@ -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); + 				}   			} -@@ -813,6 +815,7 @@ static void load_firmware(const char *fi +-			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: @@ -17,19 +431,213 @@   	if (ENABLE_FEATURE_CLEAN_UP) {   		close(firmware_fd);   		close(loading_fd); -@@ -919,11 +922,13 @@ int mdev_main(int argc UNUSED_PARAM, cha - 		} + 	} + } +  ++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); -+			int logfd = open("mdev.log", O_WRONLY | O_APPEND); - 			if (logfd >= 0) { - 				xmove_fd(logfd, STDERR_FILENO); - 				G.verbose = 1; +-			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); +-			} +-		} ++		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); + 		} + 	} +  | 
