| --- 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); |
| } |
| } |
| |