aboutsummaryrefslogtreecommitdiff
path: root/util-linux/mdev.c
diff options
context:
space:
mode:
Diffstat (limited to 'util-linux/mdev.c')
-rw-r--r--util-linux/mdev.c341
1 files changed, 247 insertions, 94 deletions
diff --git a/util-linux/mdev.c b/util-linux/mdev.c
index c592ef687..5fe6bbbde 100644
--- a/util-linux/mdev.c
+++ b/util-linux/mdev.c
@@ -80,7 +80,7 @@
80//usage: IF_FEATURE_MDEV_CONF( 80//usage: IF_FEATURE_MDEV_CONF(
81//usage: "\n" 81//usage: "\n"
82//usage: "It uses /etc/mdev.conf with lines\n" 82//usage: "It uses /etc/mdev.conf with lines\n"
83//usage: " [-]DEVNAME UID:GID PERM" 83//usage: " [-][ENV=regex;]...DEVNAME UID:GID PERM"
84//usage: IF_FEATURE_MDEV_RENAME(" [>|=PATH]|[!]") 84//usage: IF_FEATURE_MDEV_RENAME(" [>|=PATH]|[!]")
85//usage: IF_FEATURE_MDEV_EXEC(" [@|$|*PROG]") 85//usage: IF_FEATURE_MDEV_EXEC(" [@|$|*PROG]")
86//usage: "\n" 86//usage: "\n"
@@ -230,9 +230,34 @@
230 * SUBSYSTEM=block 230 * SUBSYSTEM=block
231 */ 231 */
232 232
233static const char keywords[] ALIGN1 = "add\0remove\0change\0"; 233#define DEBUG_LVL 2
234
235#if DEBUG_LVL >= 1
236# define dbg1(...) do { if (G.verbose) bb_error_msg(__VA_ARGS__); } while(0)
237#else
238# define dbg1(...) ((void)0)
239#endif
240#if DEBUG_LVL >= 2
241# define dbg2(...) do { if (G.verbose >= 2) bb_error_msg(__VA_ARGS__); } while(0)
242#else
243# define dbg2(...) ((void)0)
244#endif
245#if DEBUG_LVL >= 3
246# define dbg3(...) do { if (G.verbose >= 3) bb_error_msg(__VA_ARGS__); } while(0)
247#else
248# define dbg3(...) ((void)0)
249#endif
250
251
252static const char keywords[] ALIGN1 = "add\0remove\0"; // "change\0"
234enum { OP_add, OP_remove }; 253enum { OP_add, OP_remove };
235 254
255struct envmatch {
256 struct envmatch *next;
257 char *envname;
258 regex_t match;
259};
260
236struct rule { 261struct rule {
237 bool keep_matching; 262 bool keep_matching;
238 bool regex_compiled; 263 bool regex_compiled;
@@ -243,12 +268,14 @@ struct rule {
243 char *ren_mov; 268 char *ren_mov;
244 IF_FEATURE_MDEV_EXEC(char *r_cmd;) 269 IF_FEATURE_MDEV_EXEC(char *r_cmd;)
245 regex_t match; 270 regex_t match;
271 struct envmatch *envmatch;
246}; 272};
247 273
248struct globals { 274struct globals {
249 int root_major, root_minor; 275 int root_major, root_minor;
250 smallint verbose; 276 smallint verbose;
251 char *subsystem; 277 char *subsystem;
278 char *subsys_env; /* for putenv("SUBSYSTEM=subsystem") */
252#if ENABLE_FEATURE_MDEV_CONF 279#if ENABLE_FEATURE_MDEV_CONF
253 const char *filename; 280 const char *filename;
254 parser_t *parser; 281 parser_t *parser;
@@ -256,6 +283,7 @@ struct globals {
256 unsigned rule_idx; 283 unsigned rule_idx;
257#endif 284#endif
258 struct rule cur_rule; 285 struct rule cur_rule;
286 char timestr[sizeof("60.123456")];
259} FIX_ALIASING; 287} FIX_ALIASING;
260#define G (*(struct globals*)&bb_common_bufsiz1) 288#define G (*(struct globals*)&bb_common_bufsiz1)
261#define INIT_G() do { \ 289#define INIT_G() do { \
@@ -270,13 +298,6 @@ struct globals {
270/* We use additional 64+ bytes in make_device() */ 298/* We use additional 64+ bytes in make_device() */
271#define SCRATCH_SIZE 80 299#define SCRATCH_SIZE 80
272 300
273#if 0
274# define dbg(...) bb_error_msg(__VA_ARGS__)
275#else
276# define dbg(...) ((void)0)
277#endif
278
279
280#if ENABLE_FEATURE_MDEV_CONF 301#if ENABLE_FEATURE_MDEV_CONF
281 302
282static void make_default_cur_rule(void) 303static void make_default_cur_rule(void)
@@ -288,14 +309,48 @@ static void make_default_cur_rule(void)
288 309
289static void clean_up_cur_rule(void) 310static void clean_up_cur_rule(void)
290{ 311{
312 struct envmatch *e;
313
291 free(G.cur_rule.envvar); 314 free(G.cur_rule.envvar);
315 free(G.cur_rule.ren_mov);
292 if (G.cur_rule.regex_compiled) 316 if (G.cur_rule.regex_compiled)
293 regfree(&G.cur_rule.match); 317 regfree(&G.cur_rule.match);
294 free(G.cur_rule.ren_mov);
295 IF_FEATURE_MDEV_EXEC(free(G.cur_rule.r_cmd);) 318 IF_FEATURE_MDEV_EXEC(free(G.cur_rule.r_cmd);)
319 e = G.cur_rule.envmatch;
320 while (e) {
321 free(e->envname);
322 regfree(&e->match);
323 e = e->next;
324 }
296 make_default_cur_rule(); 325 make_default_cur_rule();
297} 326}
298 327
328static char *parse_envmatch_pfx(char *val)
329{
330 struct envmatch **nextp = &G.cur_rule.envmatch;
331
332 for (;;) {
333 struct envmatch *e;
334 char *semicolon;
335 char *eq = strchr(val, '=');
336 if (!eq /* || eq == val? */)
337 return val;
338 if (endofname(val) != eq)
339 return val;
340 semicolon = strchr(eq, ';');
341 if (!semicolon)
342 return val;
343 /* ENVVAR=regex;... */
344 *nextp = e = xzalloc(sizeof(*e));
345 nextp = &e->next;
346 e->envname = xstrndup(val, eq - val);
347 *semicolon = '\0';
348 xregcomp(&e->match, eq + 1, REG_EXTENDED);
349 *semicolon = ';';
350 val = semicolon + 1;
351 }
352}
353
299static void parse_next_rule(void) 354static void parse_next_rule(void)
300{ 355{
301 /* Note: on entry, G.cur_rule is set to default */ 356 /* Note: on entry, G.cur_rule is set to default */
@@ -308,12 +363,13 @@ static void parse_next_rule(void)
308 break; 363 break;
309 364
310 /* Fields: [-]regex uid:gid mode [alias] [cmd] */ 365 /* Fields: [-]regex uid:gid mode [alias] [cmd] */
311 dbg("token1:'%s'", tokens[1]); 366 dbg3("token1:'%s'", tokens[1]);
312 367
313 /* 1st field */ 368 /* 1st field */
314 val = tokens[0]; 369 val = tokens[0];
315 G.cur_rule.keep_matching = ('-' == val[0]); 370 G.cur_rule.keep_matching = ('-' == val[0]);
316 val += G.cur_rule.keep_matching; /* swallow leading dash */ 371 val += G.cur_rule.keep_matching; /* swallow leading dash */
372 val = parse_envmatch_pfx(val);
317 if (val[0] == '@') { 373 if (val[0] == '@') {
318 /* @major,minor[-minor2] */ 374 /* @major,minor[-minor2] */
319 /* (useful when name is ambiguous: 375 /* (useful when name is ambiguous:
@@ -328,8 +384,10 @@ static void parse_next_rule(void)
328 if (sc == 2) 384 if (sc == 2)
329 G.cur_rule.min1 = G.cur_rule.min0; 385 G.cur_rule.min1 = G.cur_rule.min0;
330 } else { 386 } else {
387 char *eq = strchr(val, '=');
331 if (val[0] == '$') { 388 if (val[0] == '$') {
332 char *eq = strchr(++val, '='); 389 /* $ENVVAR=regex ... */
390 val++;
333 if (!eq) { 391 if (!eq) {
334 bb_error_msg("bad $envvar=regex on line %d", G.parser->lineno); 392 bb_error_msg("bad $envvar=regex on line %d", G.parser->lineno);
335 goto next_rule; 393 goto next_rule;
@@ -373,7 +431,7 @@ static void parse_next_rule(void)
373 clean_up_cur_rule(); 431 clean_up_cur_rule();
374 } /* while (config_read) */ 432 } /* while (config_read) */
375 433
376 dbg("config_close(G.parser)"); 434 dbg3("config_close(G.parser)");
377 config_close(G.parser); 435 config_close(G.parser);
378 G.parser = NULL; 436 G.parser = NULL;
379 437
@@ -390,7 +448,7 @@ static const struct rule *next_rule(void)
390 448
391 /* Open conf file if we didn't do it yet */ 449 /* Open conf file if we didn't do it yet */
392 if (!G.parser && G.filename) { 450 if (!G.parser && G.filename) {
393 dbg("config_open('%s')", G.filename); 451 dbg3("config_open('%s')", G.filename);
394 G.parser = config_open2(G.filename, fopen_for_read); 452 G.parser = config_open2(G.filename, fopen_for_read);
395 G.filename = NULL; 453 G.filename = NULL;
396 } 454 }
@@ -399,7 +457,7 @@ static const struct rule *next_rule(void)
399 /* mdev -s */ 457 /* mdev -s */
400 /* Do we have rule parsed already? */ 458 /* Do we have rule parsed already? */
401 if (G.rule_vec[G.rule_idx]) { 459 if (G.rule_vec[G.rule_idx]) {
402 dbg("< G.rule_vec[G.rule_idx:%d]=%p", G.rule_idx, G.rule_vec[G.rule_idx]); 460 dbg3("< G.rule_vec[G.rule_idx:%d]=%p", G.rule_idx, G.rule_vec[G.rule_idx]);
403 return G.rule_vec[G.rule_idx++]; 461 return G.rule_vec[G.rule_idx++];
404 } 462 }
405 make_default_cur_rule(); 463 make_default_cur_rule();
@@ -416,13 +474,28 @@ static const struct rule *next_rule(void)
416 rule = memcpy(xmalloc(sizeof(G.cur_rule)), &G.cur_rule, sizeof(G.cur_rule)); 474 rule = memcpy(xmalloc(sizeof(G.cur_rule)), &G.cur_rule, sizeof(G.cur_rule));
417 G.rule_vec = xrealloc_vector(G.rule_vec, 4, G.rule_idx); 475 G.rule_vec = xrealloc_vector(G.rule_vec, 4, G.rule_idx);
418 G.rule_vec[G.rule_idx++] = rule; 476 G.rule_vec[G.rule_idx++] = rule;
419 dbg("> G.rule_vec[G.rule_idx:%d]=%p", G.rule_idx, G.rule_vec[G.rule_idx]); 477 dbg3("> G.rule_vec[G.rule_idx:%d]=%p", G.rule_idx, G.rule_vec[G.rule_idx]);
420 } 478 }
421 } 479 }
422 480
423 return rule; 481 return rule;
424} 482}
425 483
484static int env_matches(struct envmatch *e)
485{
486 while (e) {
487 int r;
488 char *val = getenv(e->envname);
489 if (!val)
490 return 0;
491 r = regexec(&e->match, val, /*size*/ 0, /*range[]*/ NULL, /*eflags*/ 0);
492 if (r != 0) /* no match */
493 return 0;
494 e = e->next;
495 }
496 return 1;
497}
498
426#else 499#else
427 500
428# define next_rule() (&G.cur_rule) 501# define next_rule() (&G.cur_rule)
@@ -479,9 +552,6 @@ static void make_device(char *device_name, char *path, int operation)
479{ 552{
480 int major, minor, type, len; 553 int major, minor, type, len;
481 554
482 if (G.verbose)
483 bb_error_msg("device: %s, %s", device_name, path);
484
485 /* Try to read major/minor string. Note that the kernel puts \n after 555 /* Try to read major/minor string. Note that the kernel puts \n after
486 * the data, so we don't need to worry about null terminating the string 556 * the data, so we don't need to worry about null terminating the string
487 * because sscanf() will stop at the first nondigit, which \n is. 557 * because sscanf() will stop at the first nondigit, which \n is.
@@ -500,8 +570,7 @@ static void make_device(char *device_name, char *path, int operation)
500 /* no "dev" file, but we can still run scripts 570 /* no "dev" file, but we can still run scripts
501 * based on device name */ 571 * based on device name */
502 } else if (sscanf(++dev_maj_min, "%u:%u", &major, &minor) == 2) { 572 } else if (sscanf(++dev_maj_min, "%u:%u", &major, &minor) == 2) {
503 if (G.verbose) 573 dbg1("dev %u,%u", major, minor);
504 bb_error_msg("maj,min: %u,%u", major, minor);
505 } else { 574 } else {
506 major = -1; 575 major = -1;
507 } 576 }
@@ -511,7 +580,8 @@ static void make_device(char *device_name, char *path, int operation)
511 /* Determine device name, type, major and minor */ 580 /* Determine device name, type, major and minor */
512 if (!device_name) 581 if (!device_name)
513 device_name = (char*) bb_basename(path); 582 device_name = (char*) bb_basename(path);
514 /* http://kernel.org/doc/pending/hotplug.txt says that only 583 /*
584 * http://kernel.org/doc/pending/hotplug.txt says that only
515 * "/sys/block/..." is for block devices. "/sys/bus" etc is not. 585 * "/sys/block/..." is for block devices. "/sys/bus" etc is not.
516 * But since 2.6.25 block devices are also in /sys/class/block. 586 * But since 2.6.25 block devices are also in /sys/class/block.
517 * We use strstr("/block/") to forestall future surprises. 587 * We use strstr("/block/") to forestall future surprises.
@@ -537,6 +607,8 @@ static void make_device(char *device_name, char *path, int operation)
537 rule = next_rule(); 607 rule = next_rule();
538 608
539#if ENABLE_FEATURE_MDEV_CONF 609#if ENABLE_FEATURE_MDEV_CONF
610 if (!env_matches(rule->envmatch))
611 continue;
540 if (rule->maj >= 0) { /* @maj,min rule */ 612 if (rule->maj >= 0) { /* @maj,min rule */
541 if (major != rule->maj) 613 if (major != rule->maj)
542 continue; 614 continue;
@@ -547,7 +619,7 @@ static void make_device(char *device_name, char *path, int operation)
547 } 619 }
548 if (rule->envvar) { /* $envvar=regex rule */ 620 if (rule->envvar) { /* $envvar=regex rule */
549 str_to_match = getenv(rule->envvar); 621 str_to_match = getenv(rule->envvar);
550 dbg("getenv('%s'):'%s'", rule->envvar, str_to_match); 622 dbg3("getenv('%s'):'%s'", rule->envvar, str_to_match);
551 if (!str_to_match) 623 if (!str_to_match)
552 continue; 624 continue;
553 } 625 }
@@ -555,7 +627,7 @@ static void make_device(char *device_name, char *path, int operation)
555 627
556 if (rule->regex_compiled) { 628 if (rule->regex_compiled) {
557 int regex_match = regexec(&rule->match, str_to_match, ARRAY_SIZE(off), off, 0); 629 int regex_match = regexec(&rule->match, str_to_match, ARRAY_SIZE(off), off, 0);
558 dbg("regex_match for '%s':%d", str_to_match, regex_match); 630 dbg3("regex_match for '%s':%d", str_to_match, regex_match);
559 //bb_error_msg("matches:"); 631 //bb_error_msg("matches:");
560 //for (int i = 0; i < ARRAY_SIZE(off); i++) { 632 //for (int i = 0; i < ARRAY_SIZE(off); i++) {
561 // if (off[i].rm_so < 0) continue; 633 // if (off[i].rm_so < 0) continue;
@@ -574,9 +646,8 @@ static void make_device(char *device_name, char *path, int operation)
574 } 646 }
575 /* else: it's final implicit "match-all" rule */ 647 /* else: it's final implicit "match-all" rule */
576 rule_matches: 648 rule_matches:
649 dbg2("rule matched, line %d", G.parser ? G.parser->lineno : -1);
577#endif 650#endif
578 dbg("rule matched");
579
580 /* Build alias name */ 651 /* Build alias name */
581 alias = NULL; 652 alias = NULL;
582 if (ENABLE_FEATURE_MDEV_RENAME && rule->ren_mov) { 653 if (ENABLE_FEATURE_MDEV_RENAME && rule->ren_mov) {
@@ -619,34 +690,30 @@ static void make_device(char *device_name, char *path, int operation)
619 } 690 }
620 } 691 }
621 } 692 }
622 dbg("alias:'%s'", alias); 693 dbg3("alias:'%s'", alias);
623 694
624 command = NULL; 695 command = NULL;
625 IF_FEATURE_MDEV_EXEC(command = rule->r_cmd;) 696 IF_FEATURE_MDEV_EXEC(command = rule->r_cmd;)
626 if (command) { 697 if (command) {
627 const char *s = "$@*";
628 const char *s2 = strchr(s, command[0]);
629
630 /* Are we running this command now? 698 /* Are we running this command now?
631 * Run $cmd on delete, @cmd on create, *cmd on both 699 * Run @cmd on create, $cmd on delete, *cmd on any
632 */ 700 */
633 if (s2 - s != (operation == OP_remove) || *s2 == '*') { 701 if ((command[0] == '@' && operation == OP_add)
634 /* We are here if: '*', 702 || (command[0] == '$' && operation == OP_remove)
635 * or: '@' and delete = 0, 703 || (command[0] == '*')
636 * or: '$' and delete = 1 704 ) {
637 */
638 command++; 705 command++;
639 } else { 706 } else {
640 command = NULL; 707 command = NULL;
641 } 708 }
642 } 709 }
643 dbg("command:'%s'", command); 710 dbg3("command:'%s'", command);
644 711
645 /* "Execute" the line we found */ 712 /* "Execute" the line we found */
646 node_name = device_name; 713 node_name = device_name;
647 if (ENABLE_FEATURE_MDEV_RENAME && alias) { 714 if (ENABLE_FEATURE_MDEV_RENAME && alias) {
648 node_name = alias = build_alias(alias, device_name); 715 node_name = alias = build_alias(alias, device_name);
649 dbg("alias2:'%s'", alias); 716 dbg3("alias2:'%s'", alias);
650 } 717 }
651 718
652 if (operation == OP_add && major >= 0) { 719 if (operation == OP_add && major >= 0) {
@@ -656,13 +723,20 @@ static void make_device(char *device_name, char *path, int operation)
656 mkdir_recursive(node_name); 723 mkdir_recursive(node_name);
657 *slash = '/'; 724 *slash = '/';
658 } 725 }
659 if (G.verbose) 726 if (ENABLE_FEATURE_MDEV_CONF) {
660 bb_error_msg("mknod: %s (%d,%d) %o", node_name, major, minor, rule->mode | type); 727 dbg1("mknod %s (%d,%d) %o"
728 " %u:%u",
729 node_name, major, minor, rule->mode | type,
730 rule->ugid.uid, rule->ugid.gid
731 );
732 } else {
733 dbg1("mknod %s (%d,%d) %o",
734 node_name, major, minor, rule->mode | type
735 );
736 }
661 if (mknod(node_name, rule->mode | type, makedev(major, minor)) && errno != EEXIST) 737 if (mknod(node_name, rule->mode | type, makedev(major, minor)) && errno != EEXIST)
662 bb_perror_msg("can't create '%s'", node_name); 738 bb_perror_msg("can't create '%s'", node_name);
663 if (ENABLE_FEATURE_MDEV_CONF) { 739 if (ENABLE_FEATURE_MDEV_CONF) {
664 if (G.verbose)
665 bb_error_msg("chmod: %o chown: %u:%u", rule->mode, rule->ugid.uid, rule->ugid.gid);
666 chmod(node_name, rule->mode); 740 chmod(node_name, rule->mode);
667 chown(node_name, rule->ugid.uid, rule->ugid.gid); 741 chown(node_name, rule->ugid.uid, rule->ugid.gid);
668 } 742 }
@@ -673,8 +747,7 @@ static void make_device(char *device_name, char *path, int operation)
673//TODO: on devtmpfs, device_name already exists and symlink() fails. 747//TODO: on devtmpfs, device_name already exists and symlink() fails.
674//End result is that instead of symlink, we have two nodes. 748//End result is that instead of symlink, we have two nodes.
675//What should be done? 749//What should be done?
676 if (G.verbose) 750 dbg1("symlink: %s", device_name);
677 bb_error_msg("symlink: %s", device_name);
678 symlink(node_name, device_name); 751 symlink(node_name, device_name);
679 } 752 }
680 } 753 }
@@ -683,27 +756,21 @@ static void make_device(char *device_name, char *path, int operation)
683 if (ENABLE_FEATURE_MDEV_EXEC && command) { 756 if (ENABLE_FEATURE_MDEV_EXEC && command) {
684 /* setenv will leak memory, use putenv/unsetenv/free */ 757 /* setenv will leak memory, use putenv/unsetenv/free */
685 char *s = xasprintf("%s=%s", "MDEV", node_name); 758 char *s = xasprintf("%s=%s", "MDEV", node_name);
686 char *s1 = xasprintf("%s=%s", "SUBSYSTEM", G.subsystem);
687 putenv(s); 759 putenv(s);
688 putenv(s1); 760 dbg1("running: %s", command);
689 if (G.verbose)
690 bb_error_msg("running: %s", command);
691 if (system(command) == -1) 761 if (system(command) == -1)
692 bb_perror_msg("can't run '%s'", command); 762 bb_perror_msg("can't run '%s'", command);
693 bb_unsetenv_and_free(s1);
694 bb_unsetenv_and_free(s); 763 bb_unsetenv_and_free(s);
695 } 764 }
696 765
697 if (operation == OP_remove && major >= -1) { 766 if (operation == OP_remove && major >= -1) {
698 if (ENABLE_FEATURE_MDEV_RENAME && alias) { 767 if (ENABLE_FEATURE_MDEV_RENAME && alias) {
699 if (aliaslink == '>') { 768 if (aliaslink == '>') {
700 if (G.verbose) 769 dbg1("unlink: %s", device_name);
701 bb_error_msg("unlink: %s", device_name);
702 unlink(device_name); 770 unlink(device_name);
703 } 771 }
704 } 772 }
705 if (G.verbose) 773 dbg1("unlink: %s", node_name);
706 bb_error_msg("unlink: %s", node_name);
707 unlink(node_name); 774 unlink(node_name);
708 } 775 }
709 776
@@ -748,9 +815,16 @@ static int FAST_FUNC dirAction(const char *fileName UNUSED_PARAM,
748 * under /sys/class/ */ 815 * under /sys/class/ */
749 if (1 == depth) { 816 if (1 == depth) {
750 free(G.subsystem); 817 free(G.subsystem);
818 if (G.subsys_env) {
819 bb_unsetenv_and_free(G.subsys_env);
820 G.subsys_env = NULL;
821 }
751 G.subsystem = strrchr(fileName, '/'); 822 G.subsystem = strrchr(fileName, '/');
752 if (G.subsystem) 823 if (G.subsystem) {
753 G.subsystem = xstrdup(G.subsystem + 1); 824 G.subsystem = xstrdup(G.subsystem + 1);
825 G.subsys_env = xasprintf("%s=%s", "SUBSYSTEM", G.subsystem);
826 putenv(G.subsys_env);
827 }
754 } 828 }
755 829
756 return (depth >= MAX_SYSFS_DEPTH ? SKIP : TRUE); 830 return (depth >= MAX_SYSFS_DEPTH ? SKIP : TRUE);
@@ -822,6 +896,100 @@ static void load_firmware(const char *firmware, const char *sysfs_path)
822 } 896 }
823} 897}
824 898
899static char *curtime(void)
900{
901 struct timeval tv;
902 gettimeofday(&tv, NULL);
903 sprintf(G.timestr, "%u.%06u", (unsigned)tv.tv_sec % 60, (unsigned)tv.tv_usec);
904 return G.timestr;
905}
906
907static void open_mdev_log(const char *seq, unsigned my_pid)
908{
909 int logfd = open("mdev.log", O_WRONLY | O_APPEND);
910 if (logfd >= 0) {
911 xmove_fd(logfd, STDERR_FILENO);
912 G.verbose = 2;
913 applet_name = xasprintf("%s[%s]", applet_name, seq ? seq : utoa(my_pid));
914 }
915}
916
917/* If it exists, does /dev/mdev.seq match $SEQNUM?
918 * If it does not match, earlier mdev is running
919 * in parallel, and we need to wait.
920 * Active mdev pokes us with SIGCHLD to check the new file.
921 */
922static int
923wait_for_seqfile(const char *seq)
924{
925 /* We time out after 2 sec */
926 static const struct timespec ts = { 0, 32*1000*1000 };
927 int timeout = 2000 / 32;
928 int seq_fd = -1;
929 int do_once = 1;
930 sigset_t set_CHLD;
931
932 sigemptyset(&set_CHLD);
933 sigaddset(&set_CHLD, SIGCHLD);
934 sigprocmask(SIG_BLOCK, &set_CHLD, NULL);
935
936 for (;;) {
937 int seqlen;
938 char seqbuf[sizeof(int)*3 + 2];
939
940 if (seq_fd < 0) {
941 seq_fd = open("mdev.seq", O_RDWR);
942 if (seq_fd < 0)
943 break;
944 }
945 seqlen = pread(seq_fd, seqbuf, sizeof(seqbuf) - 1, 0);
946 if (seqlen < 0) {
947 close(seq_fd);
948 seq_fd = -1;
949 break;
950 }
951 seqbuf[seqlen] = '\0';
952 if (seqbuf[0] == '\n') {
953 /* seed file: write out seq ASAP */
954 xwrite_str(seq_fd, seq);
955 xlseek(seq_fd, 0, SEEK_SET);
956 dbg2("first seq written");
957 break;
958 }
959 if (strcmp(seq, seqbuf) == 0) {
960 /* correct idx */
961 break;
962 }
963 if (do_once) {
964 dbg2("%s waiting for '%s'", curtime(), seqbuf);
965 do_once = 0;
966 }
967 if (sigtimedwait(&set_CHLD, NULL, &ts) >= 0) {
968 dbg3("woken up");
969 continue; /* don't decrement timeout! */
970 }
971 if (--timeout == 0) {
972 dbg1("%s waiting for '%s'", "timed out", seqbuf);
973 break;
974 }
975 }
976 sigprocmask(SIG_UNBLOCK, &set_CHLD, NULL);
977 return seq_fd;
978}
979
980static void signal_mdevs(unsigned my_pid)
981{
982 procps_status_t* p = NULL;
983 while ((p = procps_scan(p, PSSCAN_ARGV0)) != NULL) {
984 if (p->pid != my_pid
985 && p->argv0
986 && strcmp(bb_basename(p->argv0), "mdev") == 0
987 ) {
988 kill(p->pid, SIGCHLD);
989 }
990 }
991}
992
825int mdev_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 993int mdev_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
826int mdev_main(int argc UNUSED_PARAM, char **argv) 994int mdev_main(int argc UNUSED_PARAM, char **argv)
827{ 995{
@@ -843,8 +1011,8 @@ int mdev_main(int argc UNUSED_PARAM, char **argv)
843 xchdir("/dev"); 1011 xchdir("/dev");
844 1012
845 if (argv[1] && strcmp(argv[1], "-s") == 0) { 1013 if (argv[1] && strcmp(argv[1], "-s") == 0) {
846 /* Scan: 1014 /*
847 * mdev -s 1015 * Scan: mdev -s
848 */ 1016 */
849 struct stat st; 1017 struct stat st;
850 1018
@@ -856,6 +1024,8 @@ int mdev_main(int argc UNUSED_PARAM, char **argv)
856 G.root_major = major(st.st_dev); 1024 G.root_major = major(st.st_dev);
857 G.root_minor = minor(st.st_dev); 1025 G.root_minor = minor(st.st_dev);
858 1026
1027 putenv((char*)"ACTION=add");
1028
859 /* ACTION_FOLLOWLINKS is needed since in newer kernels 1029 /* ACTION_FOLLOWLINKS is needed since in newer kernels
860 * /sys/block/loop* (for example) are symlinks to dirs, 1030 * /sys/block/loop* (for example) are symlinks to dirs,
861 * not real directories. 1031 * not real directories.
@@ -881,11 +1051,13 @@ int mdev_main(int argc UNUSED_PARAM, char **argv)
881 char *action; 1051 char *action;
882 char *env_devname; 1052 char *env_devname;
883 char *env_devpath; 1053 char *env_devpath;
1054 unsigned my_pid;
1055 int seq_fd;
884 smalluint op; 1056 smalluint op;
885 1057
886 /* Hotplug: 1058 /* Hotplug:
887 * env ACTION=... DEVPATH=... SUBSYSTEM=... [SEQNUM=...] mdev 1059 * env ACTION=... DEVPATH=... SUBSYSTEM=... [SEQNUM=...] mdev
888 * ACTION can be "add" or "remove" 1060 * ACTION can be "add", "remove", "change"
889 * DEVPATH is like "/block/sda" or "/class/input/mice" 1061 * DEVPATH is like "/block/sda" or "/class/input/mice"
890 */ 1062 */
891 action = getenv("ACTION"); 1063 action = getenv("ACTION");
@@ -896,41 +1068,20 @@ int mdev_main(int argc UNUSED_PARAM, char **argv)
896 if (!action || !env_devpath /*|| !G.subsystem*/) 1068 if (!action || !env_devpath /*|| !G.subsystem*/)
897 bb_show_usage(); 1069 bb_show_usage();
898 fw = getenv("FIRMWARE"); 1070 fw = getenv("FIRMWARE");
899 /* If it exists, does /dev/mdev.seq match $SEQNUM?
900 * If it does not match, earlier mdev is running
901 * in parallel, and we need to wait */
902 seq = getenv("SEQNUM"); 1071 seq = getenv("SEQNUM");
903 if (seq) {
904 int timeout = 2000 / 32; /* 2000 msec */
905 do {
906 int seqlen;
907 char seqbuf[sizeof(int)*3 + 2];
908
909 seqlen = open_read_close("mdev.seq", seqbuf, sizeof(seqbuf) - 1);
910 if (seqlen < 0) {
911 seq = NULL;
912 break;
913 }
914 seqbuf[seqlen] = '\0';
915 if (seqbuf[0] == '\n' /* seed file? */
916 || strcmp(seq, seqbuf) == 0 /* correct idx? */
917 ) {
918 break;
919 }
920 usleep(32*1000);
921 } while (--timeout);
922 }
923 1072
924 { 1073 my_pid = getpid();
925 int logfd = open("mdev.log", O_WRONLY | O_APPEND); 1074 open_mdev_log(seq, my_pid);
926 if (logfd >= 0) { 1075
927 xmove_fd(logfd, STDERR_FILENO); 1076 seq_fd = seq ? wait_for_seqfile(seq) : -1;
928 G.verbose = 1; 1077
929 if (seq) 1078 dbg1("%s "
930 applet_name = xasprintf("%s[%s]", applet_name, seq); 1079 "ACTION:%s SUBSYSTEM:%s DEVNAME:%s DEVPATH:%s"
931 bb_error_msg("action: %s", action); 1080 "%s%s",
932 } 1081 curtime(),
933 } 1082 action, G.subsystem, env_devname, env_devpath,
1083 fw ? " FW:" : "", fw ? fw : ""
1084 );
934 1085
935 snprintf(temp, PATH_MAX, "/sys%s", env_devpath); 1086 snprintf(temp, PATH_MAX, "/sys%s", env_devpath);
936 if (op == OP_remove) { 1087 if (op == OP_remove) {
@@ -940,16 +1091,18 @@ int mdev_main(int argc UNUSED_PARAM, char **argv)
940 if (!fw) 1091 if (!fw)
941 make_device(env_devname, temp, op); 1092 make_device(env_devname, temp, op);
942 } 1093 }
943 else if (op == OP_add) { 1094 else {
944 make_device(env_devname, temp, op); 1095 make_device(env_devname, temp, op);
945 if (ENABLE_FEATURE_MDEV_LOAD_FIRMWARE) { 1096 if (ENABLE_FEATURE_MDEV_LOAD_FIRMWARE) {
946 if (fw) 1097 if (op == OP_add && fw)
947 load_firmware(fw, temp); 1098 load_firmware(fw, temp);
948 } 1099 }
949 } 1100 }
950 1101
951 if (seq) { 1102 dbg1("%s exiting", curtime());
952 xopen_xwrite_close("mdev.seq", utoa(xatou(seq) + 1)); 1103 if (seq_fd >= 0) {
1104 xwrite_str(seq_fd, utoa(xatou(seq) + 1));
1105 signal_mdevs(my_pid);
953 } 1106 }
954 } 1107 }
955 1108