apply BusyBox 1.21.0 hotfixes
[busybox-power] / debian / patches / hotfixes / busybox-1.21.0-mdev.patch
1 --- busybox-1.21.0/util-linux/mdev.c
2 +++ busybox-1.21.0-mdev/util-linux/mdev.c
3 @@ -80,7 +80,7 @@
4  //usage:       IF_FEATURE_MDEV_CONF(
5  //usage:       "\n"
6  //usage:       "It uses /etc/mdev.conf with lines\n"
7 -//usage:       "       [-]DEVNAME UID:GID PERM"
8 +//usage:       "       [-][ENV=regex;]...DEVNAME UID:GID PERM"
9  //usage:                       IF_FEATURE_MDEV_RENAME(" [>|=PATH]|[!]")
10  //usage:                       IF_FEATURE_MDEV_EXEC(" [@|$|*PROG]")
11  //usage:       "\n"
12 @@ -230,9 +230,34 @@
13   * SUBSYSTEM=block
14   */
15  
16 -static const char keywords[] ALIGN1 = "add\0remove\0change\0";
17 +#define DEBUG_LVL 2
18 +
19 +#if DEBUG_LVL >= 1
20 +# define dbg1(...) do { if (G.verbose) bb_error_msg(__VA_ARGS__); } while(0)
21 +#else
22 +# define dbg1(...) ((void)0)
23 +#endif
24 +#if DEBUG_LVL >= 2
25 +# define dbg2(...) do { if (G.verbose >= 2) bb_error_msg(__VA_ARGS__); } while(0)
26 +#else
27 +# define dbg2(...) ((void)0)
28 +#endif
29 +#if DEBUG_LVL >= 3
30 +# define dbg3(...) do { if (G.verbose >= 3) bb_error_msg(__VA_ARGS__); } while(0)
31 +#else
32 +# define dbg3(...) ((void)0)
33 +#endif
34 +
35 +
36 +static const char keywords[] ALIGN1 = "add\0remove\0"; // "change\0"
37  enum { OP_add, OP_remove };
38  
39 +struct envmatch {
40 +       struct envmatch *next;
41 +       char *envname;
42 +       regex_t match;
43 +};
44 +
45  struct rule {
46         bool keep_matching;
47         bool regex_compiled;
48 @@ -243,12 +268,14 @@ struct rule {
49         char *ren_mov;
50         IF_FEATURE_MDEV_EXEC(char *r_cmd;)
51         regex_t match;
52 +       struct envmatch *envmatch;
53  };
54  
55  struct globals {
56         int root_major, root_minor;
57         smallint verbose;
58         char *subsystem;
59 +       char *subsys_env; /* for putenv("SUBSYSTEM=subsystem") */
60  #if ENABLE_FEATURE_MDEV_CONF
61         const char *filename;
62         parser_t *parser;
63 @@ -256,6 +283,7 @@ struct globals {
64         unsigned rule_idx;
65  #endif
66         struct rule cur_rule;
67 +       char timestr[sizeof("60.123456")];
68  } FIX_ALIASING;
69  #define G (*(struct globals*)&bb_common_bufsiz1)
70  #define INIT_G() do { \
71 @@ -270,13 +298,6 @@ struct globals {
72  /* We use additional 64+ bytes in make_device() */
73  #define SCRATCH_SIZE 80
74  
75 -#if 0
76 -# define dbg(...) bb_error_msg(__VA_ARGS__)
77 -#else
78 -# define dbg(...) ((void)0)
79 -#endif
80 -
81 -
82  #if ENABLE_FEATURE_MDEV_CONF
83  
84  static void make_default_cur_rule(void)
85 @@ -288,14 +309,65 @@ static void make_default_cur_rule(void)
86  
87  static void clean_up_cur_rule(void)
88  {
89 +       struct envmatch *e;
90 +
91         free(G.cur_rule.envvar);
92 +       free(G.cur_rule.ren_mov);
93         if (G.cur_rule.regex_compiled)
94                 regfree(&G.cur_rule.match);
95 -       free(G.cur_rule.ren_mov);
96         IF_FEATURE_MDEV_EXEC(free(G.cur_rule.r_cmd);)
97 +       e = G.cur_rule.envmatch;
98 +       while (e) {
99 +               free(e->envname);
100 +               regfree(&e->match);
101 +               e = e->next;
102 +       }
103         make_default_cur_rule();
104  }
105  
106 +/* In later versions, endofname is in libbb */
107 +#define endofname mdev_endofname
108 +static
109 +const char* FAST_FUNC
110 +endofname(const char *name)
111 +{
112 +#define is_name(c)      ((c) == '_' || isalpha((unsigned char)(c)))
113 +#define is_in_name(c)   ((c) == '_' || isalnum((unsigned char)(c)))
114 +       if (!is_name(*name))
115 +               return name;
116 +       while (*++name) {
117 +               if (!is_in_name(*name))
118 +                       break;
119 +       }
120 +       return name;
121 +}
122 +
123 +static char *parse_envmatch_pfx(char *val)
124 +{
125 +       struct envmatch **nextp = &G.cur_rule.envmatch;
126 +
127 +       for (;;) {
128 +               struct envmatch *e;
129 +               char *semicolon;
130 +               char *eq = strchr(val, '=');
131 +               if (!eq /* || eq == val? */)
132 +                       return val;
133 +               if (endofname(val) != eq)
134 +                       return val;
135 +               semicolon = strchr(eq, ';');
136 +               if (!semicolon)
137 +                       return val;
138 +               /* ENVVAR=regex;... */
139 +               *nextp = e = xzalloc(sizeof(*e));
140 +               nextp = &e->next;
141 +               e->envname = xstrndup(val, eq - val);
142 +               *semicolon = '\0';
143 +               xregcomp(&e->match, eq + 1, REG_EXTENDED);
144 +               *semicolon = ';';
145 +               val = semicolon + 1;
146 +       }
147 +}
148 +
149  static void parse_next_rule(void)
150  {
151         /* Note: on entry, G.cur_rule is set to default */
152 @@ -308,12 +380,13 @@ static void parse_next_rule(void)
153                         break;
154  
155                 /* Fields: [-]regex uid:gid mode [alias] [cmd] */
156 -               dbg("token1:'%s'", tokens[1]);
157 +               dbg3("token1:'%s'", tokens[1]);
158  
159                 /* 1st field */
160                 val = tokens[0];
161                 G.cur_rule.keep_matching = ('-' == val[0]);
162                 val += G.cur_rule.keep_matching; /* swallow leading dash */
163 +               val = parse_envmatch_pfx(val);
164                 if (val[0] == '@') {
165                         /* @major,minor[-minor2] */
166                         /* (useful when name is ambiguous:
167 @@ -328,8 +401,10 @@ static void parse_next_rule(void)
168                         if (sc == 2)
169                                 G.cur_rule.min1 = G.cur_rule.min0;
170                 } else {
171 +                       char *eq = strchr(val, '=');
172                         if (val[0] == '$') {
173 -                               char *eq = strchr(++val, '=');
174 +                               /* $ENVVAR=regex ... */
175 +                               val++;
176                                 if (!eq) {
177                                         bb_error_msg("bad $envvar=regex on line %d", G.parser->lineno);
178                                         goto next_rule;
179 @@ -373,7 +448,7 @@ static void parse_next_rule(void)
180                 clean_up_cur_rule();
181         } /* while (config_read) */
182  
183 -       dbg("config_close(G.parser)");
184 +       dbg3("config_close(G.parser)");
185         config_close(G.parser);
186         G.parser = NULL;
187  
188 @@ -390,7 +465,7 @@ static const struct rule *next_rule(void
189  
190         /* Open conf file if we didn't do it yet */
191         if (!G.parser && G.filename) {
192 -               dbg("config_open('%s')", G.filename);
193 +               dbg3("config_open('%s')", G.filename);
194                 G.parser = config_open2(G.filename, fopen_for_read);
195                 G.filename = NULL;
196         }
197 @@ -399,7 +474,7 @@ static const struct rule *next_rule(void
198                 /* mdev -s */
199                 /* Do we have rule parsed already? */
200                 if (G.rule_vec[G.rule_idx]) {
201 -                       dbg("< G.rule_vec[G.rule_idx:%d]=%p", G.rule_idx, G.rule_vec[G.rule_idx]);
202 +                       dbg3("< G.rule_vec[G.rule_idx:%d]=%p", G.rule_idx, G.rule_vec[G.rule_idx]);
203                         return G.rule_vec[G.rule_idx++];
204                 }
205                 make_default_cur_rule();
206 @@ -416,13 +491,28 @@ static const struct rule *next_rule(void
207                         rule = memcpy(xmalloc(sizeof(G.cur_rule)), &G.cur_rule, sizeof(G.cur_rule));
208                         G.rule_vec = xrealloc_vector(G.rule_vec, 4, G.rule_idx);
209                         G.rule_vec[G.rule_idx++] = rule;
210 -                       dbg("> G.rule_vec[G.rule_idx:%d]=%p", G.rule_idx, G.rule_vec[G.rule_idx]);
211 +                       dbg3("> G.rule_vec[G.rule_idx:%d]=%p", G.rule_idx, G.rule_vec[G.rule_idx]);
212                 }
213         }
214  
215         return rule;
216  }
217  
218 +static int env_matches(struct envmatch *e)
219 +{
220 +       while (e) {
221 +               int r;
222 +               char *val = getenv(e->envname);
223 +               if (!val)
224 +                       return 0;
225 +               r = regexec(&e->match, val, /*size*/ 0, /*range[]*/ NULL, /*eflags*/ 0);
226 +               if (r != 0) /* no match */
227 +                       return 0;
228 +               e = e->next;
229 +       }
230 +       return 1;
231 +}
232 +
233  #else
234  
235  # define next_rule() (&G.cur_rule)
236 @@ -479,9 +569,6 @@ static void make_device(char *device_nam
237  {
238         int major, minor, type, len;
239  
240 -       if (G.verbose)
241 -               bb_error_msg("device: %s, %s", device_name, path);
242 -
243         /* Try to read major/minor string.  Note that the kernel puts \n after
244          * the data, so we don't need to worry about null terminating the string
245          * because sscanf() will stop at the first nondigit, which \n is.
246 @@ -500,8 +587,7 @@ static void make_device(char *device_nam
247                         /* no "dev" file, but we can still run scripts
248                          * based on device name */
249                 } else if (sscanf(++dev_maj_min, "%u:%u", &major, &minor) == 2) {
250 -                       if (G.verbose)
251 -                               bb_error_msg("maj,min: %u,%u", major, minor);
252 +                       dbg1("dev %u,%u", major, minor);
253                 } else {
254                         major = -1;
255                 }
256 @@ -511,7 +597,8 @@ static void make_device(char *device_nam
257         /* Determine device name, type, major and minor */
258         if (!device_name)
259                 device_name = (char*) bb_basename(path);
260 -       /* http://kernel.org/doc/pending/hotplug.txt says that only
261 +       /*
262 +        * http://kernel.org/doc/pending/hotplug.txt says that only
263          * "/sys/block/..." is for block devices. "/sys/bus" etc is not.
264          * But since 2.6.25 block devices are also in /sys/class/block.
265          * We use strstr("/block/") to forestall future surprises.
266 @@ -537,6 +624,8 @@ static void make_device(char *device_nam
267                 rule = next_rule();
268  
269  #if ENABLE_FEATURE_MDEV_CONF
270 +               if (!env_matches(rule->envmatch))
271 +                       continue;
272                 if (rule->maj >= 0) {  /* @maj,min rule */
273                         if (major != rule->maj)
274                                 continue;
275 @@ -547,7 +636,7 @@ static void make_device(char *device_nam
276                 }
277                 if (rule->envvar) { /* $envvar=regex rule */
278                         str_to_match = getenv(rule->envvar);
279 -                       dbg("getenv('%s'):'%s'", rule->envvar, str_to_match);
280 +                       dbg3("getenv('%s'):'%s'", rule->envvar, str_to_match);
281                         if (!str_to_match)
282                                 continue;
283                 }
284 @@ -555,7 +644,7 @@ static void make_device(char *device_nam
285  
286                 if (rule->regex_compiled) {
287                         int regex_match = regexec(&rule->match, str_to_match, ARRAY_SIZE(off), off, 0);
288 -                       dbg("regex_match for '%s':%d", str_to_match, regex_match);
289 +                       dbg3("regex_match for '%s':%d", str_to_match, regex_match);
290                         //bb_error_msg("matches:");
291                         //for (int i = 0; i < ARRAY_SIZE(off); i++) {
292                         //      if (off[i].rm_so < 0) continue;
293 @@ -574,9 +663,8 @@ static void make_device(char *device_nam
294                 }
295                 /* else: it's final implicit "match-all" rule */
296   rule_matches:
297 +               dbg2("rule matched, line %d", G.parser ? G.parser->lineno : -1);
298  #endif
299 -               dbg("rule matched");
300 -
301                 /* Build alias name */
302                 alias = NULL;
303                 if (ENABLE_FEATURE_MDEV_RENAME && rule->ren_mov) {
304 @@ -619,34 +707,30 @@ static void make_device(char *device_nam
305                                 }
306                         }
307                 }
308 -               dbg("alias:'%s'", alias);
309 +               dbg3("alias:'%s'", alias);
310  
311                 command = NULL;
312                 IF_FEATURE_MDEV_EXEC(command = rule->r_cmd;)
313                 if (command) {
314 -                       const char *s = "$@*";
315 -                       const char *s2 = strchr(s, command[0]);
316 -
317                         /* Are we running this command now?
318 -                        * Run $cmd on delete, @cmd on create, *cmd on both
319 +                        * Run @cmd on create, $cmd on delete, *cmd on any
320                          */
321 -                       if (s2 - s != (operation == OP_remove) || *s2 == '*') {
322 -                               /* We are here if: '*',
323 -                                * or: '@' and delete = 0,
324 -                                * or: '$' and delete = 1
325 -                                */
326 +                       if ((command[0] == '@' && operation == OP_add)
327 +                        || (command[0] == '$' && operation == OP_remove)
328 +                        || (command[0] == '*')
329 +                       ) {
330                                 command++;
331                         } else {
332                                 command = NULL;
333                         }
334                 }
335 -               dbg("command:'%s'", command);
336 +               dbg3("command:'%s'", command);
337  
338                 /* "Execute" the line we found */
339                 node_name = device_name;
340                 if (ENABLE_FEATURE_MDEV_RENAME && alias) {
341                         node_name = alias = build_alias(alias, device_name);
342 -                       dbg("alias2:'%s'", alias);
343 +                       dbg3("alias2:'%s'", alias);
344                 }
345  
346                 if (operation == OP_add && major >= 0) {
347 @@ -656,8 +740,17 @@ static void make_device(char *device_nam
348                                 mkdir_recursive(node_name);
349                                 *slash = '/';
350                         }
351 -                       if (G.verbose)
352 -                               bb_error_msg("mknod: %s (%d,%d) %o", node_name, major, minor, rule->mode | type);
353 +                       if (ENABLE_FEATURE_MDEV_CONF) {
354 +                               dbg1("mknod %s (%d,%d) %o"
355 +                                       " %u:%u",
356 +                                       node_name, major, minor, rule->mode | type,
357 +                                       rule->ugid.uid, rule->ugid.gid
358 +                               );
359 +                       } else {
360 +                               dbg1("mknod %s (%d,%d) %o",
361 +                                       node_name, major, minor, rule->mode | type
362 +                               );
363 +                       }
364                         if (mknod(node_name, rule->mode | type, makedev(major, minor)) && errno != EEXIST)
365                                 bb_perror_msg("can't create '%s'", node_name);
366                         if (ENABLE_FEATURE_MDEV_CONF) {
367 @@ -671,8 +764,7 @@ static void make_device(char *device_nam
368  //TODO: on devtmpfs, device_name already exists and symlink() fails.
369  //End result is that instead of symlink, we have two nodes.
370  //What should be done?
371 -                                       if (G.verbose)
372 -                                               bb_error_msg("symlink: %s", device_name);
373 +                                       dbg1("symlink: %s", device_name);
374                                         symlink(node_name, device_name);
375                                 }
376                         }
377 @@ -681,27 +773,21 @@ static void make_device(char *device_nam
378                 if (ENABLE_FEATURE_MDEV_EXEC && command) {
379                         /* setenv will leak memory, use putenv/unsetenv/free */
380                         char *s = xasprintf("%s=%s", "MDEV", node_name);
381 -                       char *s1 = xasprintf("%s=%s", "SUBSYSTEM", G.subsystem);
382                         putenv(s);
383 -                       putenv(s1);
384 -                       if (G.verbose)
385 -                               bb_error_msg("running: %s", command);
386 +                       dbg1("running: %s", command);
387                         if (system(command) == -1)
388                                 bb_perror_msg("can't run '%s'", command);
389 -                       bb_unsetenv_and_free(s1);
390                         bb_unsetenv_and_free(s);
391                 }
392  
393                 if (operation == OP_remove && major >= -1) {
394                         if (ENABLE_FEATURE_MDEV_RENAME && alias) {
395                                 if (aliaslink == '>') {
396 -                                       if (G.verbose)
397 -                                               bb_error_msg("unlink: %s", device_name);
398 +                                       dbg1("unlink: %s", device_name);
399                                         unlink(device_name);
400                                 }
401                         }
402 -                       if (G.verbose)
403 -                               bb_error_msg("unlink: %s", node_name);
404 +                       dbg1("unlink: %s", node_name);
405                         unlink(node_name);
406                 }
407  
408 @@ -746,9 +832,16 @@ static int FAST_FUNC dirAction(const cha
409          * under /sys/class/ */
410         if (1 == depth) {
411                 free(G.subsystem);
412 +               if (G.subsys_env) {
413 +                       bb_unsetenv_and_free(G.subsys_env);
414 +                       G.subsys_env = NULL;
415 +               }
416                 G.subsystem = strrchr(fileName, '/');
417 -               if (G.subsystem)
418 +               if (G.subsystem) {
419                         G.subsystem = xstrdup(G.subsystem + 1);
420 +                       G.subsys_env = xasprintf("%s=%s", "SUBSYSTEM", G.subsystem);
421 +                       putenv(G.subsys_env);
422 +               }
423         }
424  
425         return (depth >= MAX_SYSFS_DEPTH ? SKIP : TRUE);
426 @@ -813,12 +906,107 @@ static void load_firmware(const char *fi
427                 full_write(loading_fd, "-1", 2);
428  
429   out:
430 +       xchdir("/dev");
431         if (ENABLE_FEATURE_CLEAN_UP) {
432                 close(firmware_fd);
433                 close(loading_fd);
434         }
435  }
436  
437 +static char *curtime(void)
438 +{
439 +       struct timeval tv;
440 +       gettimeofday(&tv, NULL);
441 +       sprintf(G.timestr, "%u.%06u", (unsigned)tv.tv_sec % 60, (unsigned)tv.tv_usec);
442 +       return G.timestr;
443 +}
444 +
445 +static void open_mdev_log(const char *seq, unsigned my_pid)
446 +{
447 +       int logfd = open("mdev.log", O_WRONLY | O_APPEND);
448 +       if (logfd >= 0) {
449 +               xmove_fd(logfd, STDERR_FILENO);
450 +               G.verbose = 2;
451 +               applet_name = xasprintf("%s[%s]", applet_name, seq ? seq : utoa(my_pid));
452 +       }
453 +}
454 +
455 +/* If it exists, does /dev/mdev.seq match $SEQNUM?
456 + * If it does not match, earlier mdev is running
457 + * in parallel, and we need to wait.
458 + * Active mdev pokes us with SIGCHLD to check the new file.
459 + */
460 +static int
461 +wait_for_seqfile(const char *seq)
462 +{
463 +       /* We time out after 2 sec */
464 +       static const struct timespec ts = { 0, 32*1000*1000 };
465 +       int timeout = 2000 / 32;
466 +       int seq_fd = -1;
467 +       int do_once = 1;
468 +       sigset_t set_CHLD;
469 +
470 +       sigemptyset(&set_CHLD);
471 +       sigaddset(&set_CHLD, SIGCHLD);
472 +       sigprocmask(SIG_BLOCK, &set_CHLD, NULL);
473 +
474 +       for (;;) {
475 +               int seqlen;
476 +               char seqbuf[sizeof(int)*3 + 2];
477 +
478 +               if (seq_fd < 0) {
479 +                       seq_fd = open("mdev.seq", O_RDWR);
480 +                       if (seq_fd < 0)
481 +                               break;
482 +               }
483 +               seqlen = pread(seq_fd, seqbuf, sizeof(seqbuf) - 1, 0);
484 +               if (seqlen < 0) {
485 +                       close(seq_fd);
486 +                       seq_fd = -1;
487 +                       break;
488 +               }
489 +               seqbuf[seqlen] = '\0';
490 +               if (seqbuf[0] == '\n') {
491 +                       /* seed file: write out seq ASAP */
492 +                       xwrite_str(seq_fd, seq);
493 +                       xlseek(seq_fd, 0, SEEK_SET);
494 +                       dbg2("first seq written");
495 +                       break;
496 +               }
497 +               if (strcmp(seq, seqbuf) == 0) {
498 +                       /* correct idx */
499 +                       break;
500 +               }
501 +               if (do_once) {
502 +                       dbg2("%s waiting for '%s'", curtime(), seqbuf);
503 +                       do_once = 0;
504 +               }
505 +               if (sigtimedwait(&set_CHLD, NULL, &ts) >= 0) {
506 +                       dbg3("woken up");
507 +                       continue; /* don't decrement timeout! */
508 +               }
509 +               if (--timeout == 0) {
510 +                       dbg1("%s waiting for '%s'", "timed out", seqbuf);
511 +                       break;
512 +               }
513 +       }
514 +       sigprocmask(SIG_UNBLOCK, &set_CHLD, NULL);
515 +       return seq_fd;
516 +}
517 +
518 +static void signal_mdevs(unsigned my_pid)
519 +{
520 +       procps_status_t* p = NULL;
521 +       while ((p = procps_scan(p, PSSCAN_ARGV0)) != NULL) {
522 +               if (p->pid != my_pid
523 +                && p->argv0
524 +                && strcmp(bb_basename(p->argv0), "mdev") == 0
525 +               ) {
526 +                       kill(p->pid, SIGCHLD);
527 +               }
528 +       }
529 +}
530 +
531  int mdev_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
532  int mdev_main(int argc UNUSED_PARAM, char **argv)
533  {
534 @@ -840,8 +1028,8 @@ int mdev_main(int argc UNUSED_PARAM, cha
535         xchdir("/dev");
536  
537         if (argv[1] && strcmp(argv[1], "-s") == 0) {
538 -               /* Scan:
539 -                * mdev -s
540 +               /*
541 +                * Scan: mdev -s
542                  */
543                 struct stat st;
544  
545 @@ -853,6 +1041,8 @@ int mdev_main(int argc UNUSED_PARAM, cha
546                 G.root_major = major(st.st_dev);
547                 G.root_minor = minor(st.st_dev);
548  
549 +               putenv((char*)"ACTION=add");
550 +
551                 /* ACTION_FOLLOWLINKS is needed since in newer kernels
552                  * /sys/block/loop* (for example) are symlinks to dirs,
553                  * not real directories.
554 @@ -878,11 +1068,13 @@ int mdev_main(int argc UNUSED_PARAM, cha
555                 char *action;
556                 char *env_devname;
557                 char *env_devpath;
558 +               unsigned my_pid;
559 +               int seq_fd;
560                 smalluint op;
561  
562                 /* Hotplug:
563                  * env ACTION=... DEVPATH=... SUBSYSTEM=... [SEQNUM=...] mdev
564 -                * ACTION can be "add" or "remove"
565 +                * ACTION can be "add", "remove", "change"
566                  * DEVPATH is like "/block/sda" or "/class/input/mice"
567                  */
568                 action = getenv("ACTION");
569 @@ -893,39 +1085,20 @@ int mdev_main(int argc UNUSED_PARAM, cha
570                 if (!action || !env_devpath /*|| !G.subsystem*/)
571                         bb_show_usage();
572                 fw = getenv("FIRMWARE");
573 -               /* If it exists, does /dev/mdev.seq match $SEQNUM?
574 -                * If it does not match, earlier mdev is running
575 -                * in parallel, and we need to wait */
576                 seq = getenv("SEQNUM");
577 -               if (seq) {
578 -                       int timeout = 2000 / 32; /* 2000 msec */
579 -                       do {
580 -                               int seqlen;
581 -                               char seqbuf[sizeof(int)*3 + 2];
582 -
583 -                               seqlen = open_read_close("mdev.seq", seqbuf, sizeof(seqbuf) - 1);
584 -                               if (seqlen < 0) {
585 -                                       seq = NULL;
586 -                                       break;
587 -                               }
588 -                               seqbuf[seqlen] = '\0';
589 -                               if (seqbuf[0] == '\n' /* seed file? */
590 -                                || strcmp(seq, seqbuf) == 0 /* correct idx? */
591 -                               ) {
592 -                                       break;
593 -                               }
594 -                               usleep(32*1000);
595 -                       } while (--timeout);
596 -               }
597  
598 -               {
599 -                       int logfd = open("/dev/mdev.log", O_WRONLY | O_APPEND);
600 -                       if (logfd >= 0) {
601 -                               xmove_fd(logfd, STDERR_FILENO);
602 -                               G.verbose = 1;
603 -                               bb_error_msg("seq: %s action: %s", seq, action);
604 -                       }
605 -               }
606 +               my_pid = getpid();
607 +               open_mdev_log(seq, my_pid);
608 +
609 +               seq_fd = seq ? wait_for_seqfile(seq) : -1;
610 +
611 +               dbg1("%s "
612 +                       "ACTION:%s SUBSYSTEM:%s DEVNAME:%s DEVPATH:%s"
613 +                       "%s%s",
614 +                       curtime(),
615 +                       action, G.subsystem, env_devname, env_devpath,
616 +                       fw ? " FW:" : "", fw ? fw : ""
617 +               );
618  
619                 snprintf(temp, PATH_MAX, "/sys%s", env_devpath);
620                 if (op == OP_remove) {
621 @@ -935,16 +1108,18 @@ int mdev_main(int argc UNUSED_PARAM, cha
622                         if (!fw)
623                                 make_device(env_devname, temp, op);
624                 }
625 -               else if (op == OP_add) {
626 +               else {
627                         make_device(env_devname, temp, op);
628                         if (ENABLE_FEATURE_MDEV_LOAD_FIRMWARE) {
629 -                               if (fw)
630 +                               if (op == OP_add && fw)
631                                         load_firmware(fw, temp);
632                         }
633                 }
634  
635 -               if (seq) {
636 -                       xopen_xwrite_close("mdev.seq", utoa(xatou(seq) + 1));
637 +               dbg1("%s exiting", curtime());
638 +               if (seq_fd >= 0) {
639 +                       xwrite_str(seq_fd, utoa(xatou(seq) + 1));
640 +                       signal_mdevs(my_pid);
641                 }
642         }
643