diff options
author | Denys Vlasenko <vda.linux@googlemail.com> | 2013-02-27 10:51:41 +0100 |
---|---|---|
committer | Denys Vlasenko <vda.linux@googlemail.com> | 2013-02-27 10:51:41 +0100 |
commit | 5bce135e36800a34a273376d5ea1f052ed2d4212 (patch) | |
tree | 00a35271b59d2e6e029555296ff6e0ee79451e30 | |
parent | 9fed24c031a885264a9249eed3b6c654c32ce139 (diff) | |
download | busybox-w32-5bce135e36800a34a273376d5ea1f052ed2d4212.tar.gz busybox-w32-5bce135e36800a34a273376d5ea1f052ed2d4212.tar.bz2 busybox-w32-5bce135e36800a34a273376d5ea1f052ed2d4212.zip |
mdev: improve $SEQ handling; improve debug logging
Sequential run of concurrent mdev's was too simplistic:
they waited for /dev/mdev.seq to match. This could sometimes
cause cumulative loss of time on the order of a second.
Added SIGCHLD signaling from exiting mdev to all other mdev's.
Added debugging required to see that code actually works as intended.
Example of /dev/mdev.log (with "woken up" elevated from dbg lvl 3 to 2):
mdev[1023]: first seq written
^^^^ seq, not pid
mdev[1023]: 35.022395 ACTION:add SUBSYSTEM:module DEVNAME:(null) DEVPATH:/module/lib80211
mdev[1023]: rule matched, line -1
^^^^^^^ means "default rule"
mdev[1023]: 35.022676 exiting
^^^^^^^^^ second,usec timestamp
mdev[1024]: 35.069691 ACTION:add SUBSYSTEM:vc DEVNAME:vcs9 DEVPATH:/devices/virtual/vc/vcs9
mdev[1024]: dev 7,9
mdev[1025]: 35.069889 waiting for '1024'
mdev[1026]: 35.069946 waiting for '1024'
mdev[1027]: 35.070151 waiting for '1024'
mdev[1024]: rule matched, line -1
mdev[1024]: mknod vcs9 (7,9) 20660 0:0
mdev[1024]: 35.070346 exiting
mdev[1025]: woken up
mdev[1026]: woken up
mdev[1025]: 35.071213 ACTION:add SUBSYSTEM:vc DEVNAME:vcsa9 DEVPATH:/devices/virtual/vc/vcsa9
^^^^^^^^^ took only a millisecond to start running after prev mdev exited
mdev[1025]: dev 7,137
mdev[1027]: woken up
mdev[1025]: rule matched, line -1
mdev[1025]: mknod vcsa9 (7,137) 20660 0:0
mdev[1025]: 35.072109 exiting
function old new delta
mdev_main 849 1372 +523
curtime - 59 +59
dirAction 87 134 +47
static.ts - 8 +8
keywords 19 12 -7
make_device 2189 2119 -70
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
-rw-r--r-- | examples/mdev_fat.conf | 7 | ||||
-rw-r--r-- | util-linux/mdev.c | 265 |
2 files changed, 181 insertions, 91 deletions
diff --git a/examples/mdev_fat.conf b/examples/mdev_fat.conf index ceba3a797..bceddb2d7 100644 --- a/examples/mdev_fat.conf +++ b/examples/mdev_fat.conf | |||
@@ -7,9 +7,9 @@ | |||
7 | # instead of the default 0:0 660. | 7 | # instead of the default 0:0 660. |
8 | # | 8 | # |
9 | # Syntax: | 9 | # Syntax: |
10 | # [-]devicename_regex user:group mode [=path]|[>path]|[!] [@|$|*cmd args...] | 10 | # [-][ENVVAR=regex;]...devicename_regex user:group mode [=path]|[>path]|[!] [@|$|*cmd args...] |
11 | # [-]$ENVVAR=regex user:group mode [=path]|[>path]|[!] [@|$|*cmd args...] | 11 | # [-][ENVVAR=regex;]...@maj,min[-min2] user:group mode [=path]|[>path]|[!] [@|$|*cmd args...] |
12 | # [-]@maj,min[-min2] user:group mode [=path]|[>path]|[!] [@|$|*cmd args...] | 12 | # [-]$ENVVAR=regex user:group mode [=path]|[>path]|[!] [@|$|*cmd args...] |
13 | # | 13 | # |
14 | # [-]: do not stop on this match, continue reading mdev.conf | 14 | # [-]: do not stop on this match, continue reading mdev.conf |
15 | # =: move, >: move and create a symlink | 15 | # =: move, >: move and create a symlink |
@@ -53,6 +53,7 @@ sr[0-9]* root:cdrom 660 @ln -sf $MDEV cdrom | |||
53 | fd[0-9]* root:floppy 660 | 53 | fd[0-9]* root:floppy 660 |
54 | 54 | ||
55 | # net devices | 55 | # net devices |
56 | SUBSYSTEM=net;.* root:root 600 @nameif | ||
56 | tun[0-9]* root:root 600 =net/ | 57 | tun[0-9]* root:root 600 =net/ |
57 | tap[0-9]* root:root 600 =net/ | 58 | tap[0-9]* root:root 600 =net/ |
58 | 59 | ||
diff --git a/util-linux/mdev.c b/util-linux/mdev.c index 775e5c241..c5c0d613c 100644 --- a/util-linux/mdev.c +++ b/util-linux/mdev.c | |||
@@ -230,7 +230,26 @@ | |||
230 | * SUBSYSTEM=block | 230 | * SUBSYSTEM=block |
231 | */ | 231 | */ |
232 | 232 | ||
233 | static 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 | |||
252 | static const char keywords[] ALIGN1 = "add\0remove\0"; // "change\0" | ||
234 | enum { OP_add, OP_remove }; | 253 | enum { OP_add, OP_remove }; |
235 | 254 | ||
236 | struct envmatch { | 255 | struct envmatch { |
@@ -256,6 +275,7 @@ struct globals { | |||
256 | int root_major, root_minor; | 275 | int root_major, root_minor; |
257 | smallint verbose; | 276 | smallint verbose; |
258 | char *subsystem; | 277 | char *subsystem; |
278 | char *subsys_env; /* for putenv("SUBSYSTEM=subsystem") */ | ||
259 | #if ENABLE_FEATURE_MDEV_CONF | 279 | #if ENABLE_FEATURE_MDEV_CONF |
260 | const char *filename; | 280 | const char *filename; |
261 | parser_t *parser; | 281 | parser_t *parser; |
@@ -263,6 +283,7 @@ struct globals { | |||
263 | unsigned rule_idx; | 283 | unsigned rule_idx; |
264 | #endif | 284 | #endif |
265 | struct rule cur_rule; | 285 | struct rule cur_rule; |
286 | char timestr[sizeof("60.123456")]; | ||
266 | } FIX_ALIASING; | 287 | } FIX_ALIASING; |
267 | #define G (*(struct globals*)&bb_common_bufsiz1) | 288 | #define G (*(struct globals*)&bb_common_bufsiz1) |
268 | #define INIT_G() do { \ | 289 | #define INIT_G() do { \ |
@@ -277,13 +298,6 @@ struct globals { | |||
277 | /* We use additional 64+ bytes in make_device() */ | 298 | /* We use additional 64+ bytes in make_device() */ |
278 | #define SCRATCH_SIZE 80 | 299 | #define SCRATCH_SIZE 80 |
279 | 300 | ||
280 | #if 0 | ||
281 | # define dbg(...) bb_error_msg(__VA_ARGS__) | ||
282 | #else | ||
283 | # define dbg(...) ((void)0) | ||
284 | #endif | ||
285 | |||
286 | |||
287 | #if ENABLE_FEATURE_MDEV_CONF | 301 | #if ENABLE_FEATURE_MDEV_CONF |
288 | 302 | ||
289 | static void make_default_cur_rule(void) | 303 | static void make_default_cur_rule(void) |
@@ -349,7 +363,7 @@ static void parse_next_rule(void) | |||
349 | break; | 363 | break; |
350 | 364 | ||
351 | /* Fields: [-]regex uid:gid mode [alias] [cmd] */ | 365 | /* Fields: [-]regex uid:gid mode [alias] [cmd] */ |
352 | dbg("token1:'%s'", tokens[1]); | 366 | dbg3("token1:'%s'", tokens[1]); |
353 | 367 | ||
354 | /* 1st field */ | 368 | /* 1st field */ |
355 | val = tokens[0]; | 369 | val = tokens[0]; |
@@ -417,7 +431,7 @@ static void parse_next_rule(void) | |||
417 | clean_up_cur_rule(); | 431 | clean_up_cur_rule(); |
418 | } /* while (config_read) */ | 432 | } /* while (config_read) */ |
419 | 433 | ||
420 | dbg("config_close(G.parser)"); | 434 | dbg3("config_close(G.parser)"); |
421 | config_close(G.parser); | 435 | config_close(G.parser); |
422 | G.parser = NULL; | 436 | G.parser = NULL; |
423 | 437 | ||
@@ -434,7 +448,7 @@ static const struct rule *next_rule(void) | |||
434 | 448 | ||
435 | /* Open conf file if we didn't do it yet */ | 449 | /* Open conf file if we didn't do it yet */ |
436 | if (!G.parser && G.filename) { | 450 | if (!G.parser && G.filename) { |
437 | dbg("config_open('%s')", G.filename); | 451 | dbg3("config_open('%s')", G.filename); |
438 | G.parser = config_open2(G.filename, fopen_for_read); | 452 | G.parser = config_open2(G.filename, fopen_for_read); |
439 | G.filename = NULL; | 453 | G.filename = NULL; |
440 | } | 454 | } |
@@ -443,7 +457,7 @@ static const struct rule *next_rule(void) | |||
443 | /* mdev -s */ | 457 | /* mdev -s */ |
444 | /* Do we have rule parsed already? */ | 458 | /* Do we have rule parsed already? */ |
445 | if (G.rule_vec[G.rule_idx]) { | 459 | if (G.rule_vec[G.rule_idx]) { |
446 | 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]); |
447 | return G.rule_vec[G.rule_idx++]; | 461 | return G.rule_vec[G.rule_idx++]; |
448 | } | 462 | } |
449 | make_default_cur_rule(); | 463 | make_default_cur_rule(); |
@@ -460,7 +474,7 @@ static const struct rule *next_rule(void) | |||
460 | 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)); |
461 | 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); |
462 | G.rule_vec[G.rule_idx++] = rule; | 476 | G.rule_vec[G.rule_idx++] = rule; |
463 | 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]); |
464 | } | 478 | } |
465 | } | 479 | } |
466 | 480 | ||
@@ -538,9 +552,6 @@ static void make_device(char *device_name, char *path, int operation) | |||
538 | { | 552 | { |
539 | int major, minor, type, len; | 553 | int major, minor, type, len; |
540 | 554 | ||
541 | if (G.verbose) | ||
542 | bb_error_msg("device: %s, %s", device_name, path); | ||
543 | |||
544 | /* 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 |
545 | * 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 |
546 | * because sscanf() will stop at the first nondigit, which \n is. | 557 | * because sscanf() will stop at the first nondigit, which \n is. |
@@ -559,8 +570,7 @@ static void make_device(char *device_name, char *path, int operation) | |||
559 | /* no "dev" file, but we can still run scripts | 570 | /* no "dev" file, but we can still run scripts |
560 | * based on device name */ | 571 | * based on device name */ |
561 | } else if (sscanf(++dev_maj_min, "%u:%u", &major, &minor) == 2) { | 572 | } else if (sscanf(++dev_maj_min, "%u:%u", &major, &minor) == 2) { |
562 | if (G.verbose) | 573 | dbg1("dev %u,%u", major, minor); |
563 | bb_error_msg("maj,min: %u,%u", major, minor); | ||
564 | } else { | 574 | } else { |
565 | major = -1; | 575 | major = -1; |
566 | } | 576 | } |
@@ -570,7 +580,8 @@ static void make_device(char *device_name, char *path, int operation) | |||
570 | /* Determine device name, type, major and minor */ | 580 | /* Determine device name, type, major and minor */ |
571 | if (!device_name) | 581 | if (!device_name) |
572 | device_name = (char*) bb_basename(path); | 582 | device_name = (char*) bb_basename(path); |
573 | /* http://kernel.org/doc/pending/hotplug.txt says that only | 583 | /* |
584 | * http://kernel.org/doc/pending/hotplug.txt says that only | ||
574 | * "/sys/block/..." is for block devices. "/sys/bus" etc is not. | 585 | * "/sys/block/..." is for block devices. "/sys/bus" etc is not. |
575 | * 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. |
576 | * We use strstr("/block/") to forestall future surprises. | 587 | * We use strstr("/block/") to forestall future surprises. |
@@ -608,7 +619,7 @@ static void make_device(char *device_name, char *path, int operation) | |||
608 | } | 619 | } |
609 | if (rule->envvar) { /* $envvar=regex rule */ | 620 | if (rule->envvar) { /* $envvar=regex rule */ |
610 | str_to_match = getenv(rule->envvar); | 621 | str_to_match = getenv(rule->envvar); |
611 | dbg("getenv('%s'):'%s'", rule->envvar, str_to_match); | 622 | dbg3("getenv('%s'):'%s'", rule->envvar, str_to_match); |
612 | if (!str_to_match) | 623 | if (!str_to_match) |
613 | continue; | 624 | continue; |
614 | } | 625 | } |
@@ -616,7 +627,7 @@ static void make_device(char *device_name, char *path, int operation) | |||
616 | 627 | ||
617 | if (rule->regex_compiled) { | 628 | if (rule->regex_compiled) { |
618 | 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); |
619 | dbg("regex_match for '%s':%d", str_to_match, regex_match); | 630 | dbg3("regex_match for '%s':%d", str_to_match, regex_match); |
620 | //bb_error_msg("matches:"); | 631 | //bb_error_msg("matches:"); |
621 | //for (int i = 0; i < ARRAY_SIZE(off); i++) { | 632 | //for (int i = 0; i < ARRAY_SIZE(off); i++) { |
622 | // if (off[i].rm_so < 0) continue; | 633 | // if (off[i].rm_so < 0) continue; |
@@ -636,7 +647,7 @@ static void make_device(char *device_name, char *path, int operation) | |||
636 | /* else: it's final implicit "match-all" rule */ | 647 | /* else: it's final implicit "match-all" rule */ |
637 | rule_matches: | 648 | rule_matches: |
638 | #endif | 649 | #endif |
639 | dbg("rule matched"); | 650 | dbg2("rule matched, line %d", G.parser ? G.parser->lineno : -1); |
640 | 651 | ||
641 | /* Build alias name */ | 652 | /* Build alias name */ |
642 | alias = NULL; | 653 | alias = NULL; |
@@ -680,34 +691,30 @@ static void make_device(char *device_name, char *path, int operation) | |||
680 | } | 691 | } |
681 | } | 692 | } |
682 | } | 693 | } |
683 | dbg("alias:'%s'", alias); | 694 | dbg3("alias:'%s'", alias); |
684 | 695 | ||
685 | command = NULL; | 696 | command = NULL; |
686 | IF_FEATURE_MDEV_EXEC(command = rule->r_cmd;) | 697 | IF_FEATURE_MDEV_EXEC(command = rule->r_cmd;) |
687 | if (command) { | 698 | if (command) { |
688 | const char *s = "$@*"; | ||
689 | const char *s2 = strchr(s, command[0]); | ||
690 | |||
691 | /* Are we running this command now? | 699 | /* Are we running this command now? |
692 | * Run $cmd on delete, @cmd on create, *cmd on both | 700 | * Run @cmd on create, $cmd on delete, *cmd on any |
693 | */ | 701 | */ |
694 | if (s2 - s != (operation == OP_remove) || *s2 == '*') { | 702 | if ((command[0] == '@' && operation == OP_add) |
695 | /* We are here if: '*', | 703 | || (command[0] == '$' && operation == OP_remove) |
696 | * or: '@' and delete = 0, | 704 | || (command[0] == '*') |
697 | * or: '$' and delete = 1 | 705 | ) { |
698 | */ | ||
699 | command++; | 706 | command++; |
700 | } else { | 707 | } else { |
701 | command = NULL; | 708 | command = NULL; |
702 | } | 709 | } |
703 | } | 710 | } |
704 | dbg("command:'%s'", command); | 711 | dbg3("command:'%s'", command); |
705 | 712 | ||
706 | /* "Execute" the line we found */ | 713 | /* "Execute" the line we found */ |
707 | node_name = device_name; | 714 | node_name = device_name; |
708 | if (ENABLE_FEATURE_MDEV_RENAME && alias) { | 715 | if (ENABLE_FEATURE_MDEV_RENAME && alias) { |
709 | node_name = alias = build_alias(alias, device_name); | 716 | node_name = alias = build_alias(alias, device_name); |
710 | dbg("alias2:'%s'", alias); | 717 | dbg3("alias2:'%s'", alias); |
711 | } | 718 | } |
712 | 719 | ||
713 | if (operation == OP_add && major >= 0) { | 720 | if (operation == OP_add && major >= 0) { |
@@ -717,13 +724,20 @@ static void make_device(char *device_name, char *path, int operation) | |||
717 | mkdir_recursive(node_name); | 724 | mkdir_recursive(node_name); |
718 | *slash = '/'; | 725 | *slash = '/'; |
719 | } | 726 | } |
720 | if (G.verbose) | 727 | if (ENABLE_FEATURE_MDEV_CONF) { |
721 | bb_error_msg("mknod: %s (%d,%d) %o", node_name, major, minor, rule->mode | type); | 728 | dbg1("mknod %s (%d,%d) %o" |
729 | " %u:%u", | ||
730 | node_name, major, minor, rule->mode | type, | ||
731 | rule->ugid.uid, rule->ugid.gid | ||
732 | ); | ||
733 | } else { | ||
734 | dbg1("mknod %s (%d,%d) %o", | ||
735 | node_name, major, minor, rule->mode | type | ||
736 | ); | ||
737 | } | ||
722 | if (mknod(node_name, rule->mode | type, makedev(major, minor)) && errno != EEXIST) | 738 | if (mknod(node_name, rule->mode | type, makedev(major, minor)) && errno != EEXIST) |
723 | bb_perror_msg("can't create '%s'", node_name); | 739 | bb_perror_msg("can't create '%s'", node_name); |
724 | if (ENABLE_FEATURE_MDEV_CONF) { | 740 | if (ENABLE_FEATURE_MDEV_CONF) { |
725 | if (G.verbose) | ||
726 | bb_error_msg("chmod: %o chown: %u:%u", rule->mode, rule->ugid.uid, rule->ugid.gid); | ||
727 | chmod(node_name, rule->mode); | 741 | chmod(node_name, rule->mode); |
728 | chown(node_name, rule->ugid.uid, rule->ugid.gid); | 742 | chown(node_name, rule->ugid.uid, rule->ugid.gid); |
729 | } | 743 | } |
@@ -734,8 +748,7 @@ static void make_device(char *device_name, char *path, int operation) | |||
734 | //TODO: on devtmpfs, device_name already exists and symlink() fails. | 748 | //TODO: on devtmpfs, device_name already exists and symlink() fails. |
735 | //End result is that instead of symlink, we have two nodes. | 749 | //End result is that instead of symlink, we have two nodes. |
736 | //What should be done? | 750 | //What should be done? |
737 | if (G.verbose) | 751 | dbg1("symlink: %s", device_name); |
738 | bb_error_msg("symlink: %s", device_name); | ||
739 | symlink(node_name, device_name); | 752 | symlink(node_name, device_name); |
740 | } | 753 | } |
741 | } | 754 | } |
@@ -744,27 +757,21 @@ static void make_device(char *device_name, char *path, int operation) | |||
744 | if (ENABLE_FEATURE_MDEV_EXEC && command) { | 757 | if (ENABLE_FEATURE_MDEV_EXEC && command) { |
745 | /* setenv will leak memory, use putenv/unsetenv/free */ | 758 | /* setenv will leak memory, use putenv/unsetenv/free */ |
746 | char *s = xasprintf("%s=%s", "MDEV", node_name); | 759 | char *s = xasprintf("%s=%s", "MDEV", node_name); |
747 | char *s1 = xasprintf("%s=%s", "SUBSYSTEM", G.subsystem); | ||
748 | putenv(s); | 760 | putenv(s); |
749 | putenv(s1); | 761 | dbg1("running: %s", command); |
750 | if (G.verbose) | ||
751 | bb_error_msg("running: %s", command); | ||
752 | if (system(command) == -1) | 762 | if (system(command) == -1) |
753 | bb_perror_msg("can't run '%s'", command); | 763 | bb_perror_msg("can't run '%s'", command); |
754 | bb_unsetenv_and_free(s1); | ||
755 | bb_unsetenv_and_free(s); | 764 | bb_unsetenv_and_free(s); |
756 | } | 765 | } |
757 | 766 | ||
758 | if (operation == OP_remove && major >= -1) { | 767 | if (operation == OP_remove && major >= -1) { |
759 | if (ENABLE_FEATURE_MDEV_RENAME && alias) { | 768 | if (ENABLE_FEATURE_MDEV_RENAME && alias) { |
760 | if (aliaslink == '>') { | 769 | if (aliaslink == '>') { |
761 | if (G.verbose) | 770 | dbg1("unlink: %s", device_name); |
762 | bb_error_msg("unlink: %s", device_name); | ||
763 | unlink(device_name); | 771 | unlink(device_name); |
764 | } | 772 | } |
765 | } | 773 | } |
766 | if (G.verbose) | 774 | dbg1("unlink: %s", node_name); |
767 | bb_error_msg("unlink: %s", node_name); | ||
768 | unlink(node_name); | 775 | unlink(node_name); |
769 | } | 776 | } |
770 | 777 | ||
@@ -809,10 +816,15 @@ static int FAST_FUNC dirAction(const char *fileName UNUSED_PARAM, | |||
809 | * under /sys/class/ */ | 816 | * under /sys/class/ */ |
810 | if (1 == depth) { | 817 | if (1 == depth) { |
811 | free(G.subsystem); | 818 | free(G.subsystem); |
819 | if (G.subsys_env) { | ||
820 | bb_unsetenv_and_free(G.subsys_env); | ||
821 | G.subsys_env = NULL; | ||
822 | } | ||
812 | G.subsystem = strrchr(fileName, '/'); | 823 | G.subsystem = strrchr(fileName, '/'); |
813 | if (G.subsystem) { | 824 | if (G.subsystem) { |
814 | G.subsystem = xstrdup(G.subsystem + 1); | 825 | G.subsystem = xstrdup(G.subsystem + 1); |
815 | xsetenv("SUBSYSTEM", G.subsystem); | 826 | G.subsys_env = xasprintf("%s=%s", "SUBSYSTEM", G.subsystem); |
827 | putenv(G.subsys_env); | ||
816 | } | 828 | } |
817 | } | 829 | } |
818 | 830 | ||
@@ -885,6 +897,100 @@ static void load_firmware(const char *firmware, const char *sysfs_path) | |||
885 | } | 897 | } |
886 | } | 898 | } |
887 | 899 | ||
900 | static char *curtime(void) | ||
901 | { | ||
902 | struct timeval tv; | ||
903 | gettimeofday(&tv, NULL); | ||
904 | sprintf(G.timestr, "%u.%06u", (unsigned)tv.tv_sec % 60, (unsigned)tv.tv_usec); | ||
905 | return G.timestr; | ||
906 | } | ||
907 | |||
908 | static void open_mdev_log(const char *seq, unsigned my_pid) | ||
909 | { | ||
910 | int logfd = open("mdev.log", O_WRONLY | O_APPEND); | ||
911 | if (logfd >= 0) { | ||
912 | xmove_fd(logfd, STDERR_FILENO); | ||
913 | G.verbose = 2; | ||
914 | applet_name = xasprintf("%s[%s]", applet_name, seq ? seq : utoa(my_pid)); | ||
915 | } | ||
916 | } | ||
917 | |||
918 | /* If it exists, does /dev/mdev.seq match $SEQNUM? | ||
919 | * If it does not match, earlier mdev is running | ||
920 | * in parallel, and we need to wait. | ||
921 | * Active mdev pokes us with SIGCHLD to check the new file. | ||
922 | */ | ||
923 | static int | ||
924 | wait_for_seqfile(const char *seq) | ||
925 | { | ||
926 | /* We time out after 2 sec */ | ||
927 | static const struct timespec ts = { 0, 32*1000*1000 }; | ||
928 | int timeout = 2000 / 32; | ||
929 | int seq_fd = -1; | ||
930 | int do_once = 1; | ||
931 | sigset_t set_CHLD; | ||
932 | |||
933 | sigemptyset(&set_CHLD); | ||
934 | sigaddset(&set_CHLD, SIGCHLD); | ||
935 | sigprocmask(SIG_BLOCK, &set_CHLD, NULL); | ||
936 | |||
937 | for (;;) { | ||
938 | int seqlen; | ||
939 | char seqbuf[sizeof(int)*3 + 2]; | ||
940 | |||
941 | if (seq_fd < 0) { | ||
942 | seq_fd = open("mdev.seq", O_RDWR); | ||
943 | if (seq_fd < 0) | ||
944 | break; | ||
945 | } | ||
946 | seqlen = pread(seq_fd, seqbuf, sizeof(seqbuf) - 1, 0); | ||
947 | if (seqlen < 0) { | ||
948 | close(seq_fd); | ||
949 | seq_fd = -1; | ||
950 | break; | ||
951 | } | ||
952 | seqbuf[seqlen] = '\0'; | ||
953 | if (seqbuf[0] == '\n') { | ||
954 | /* seed file: write out seq ASAP */ | ||
955 | xwrite_str(seq_fd, seq); | ||
956 | xlseek(seq_fd, 0, SEEK_SET); | ||
957 | dbg2("first seq written"); | ||
958 | break; | ||
959 | } | ||
960 | if (strcmp(seq, seqbuf) == 0) { | ||
961 | /* correct idx */ | ||
962 | break; | ||
963 | } | ||
964 | if (do_once) { | ||
965 | dbg2("%s waiting for '%s'", curtime(), seqbuf); | ||
966 | do_once = 0; | ||
967 | } | ||
968 | if (sigtimedwait(&set_CHLD, NULL, &ts) >= 0) { | ||
969 | dbg3("woken up"); | ||
970 | continue; /* don't decrement timeout! */ | ||
971 | } | ||
972 | if (--timeout == 0) { | ||
973 | dbg1("%s waiting for '%s'", "timed out", seqbuf); | ||
974 | break; | ||
975 | } | ||
976 | } | ||
977 | sigprocmask(SIG_UNBLOCK, &set_CHLD, NULL); | ||
978 | return seq_fd; | ||
979 | } | ||
980 | |||
981 | static void signal_mdevs(unsigned my_pid) | ||
982 | { | ||
983 | procps_status_t* p = NULL; | ||
984 | while ((p = procps_scan(p, PSSCAN_ARGV0)) != NULL) { | ||
985 | if (p->pid != my_pid | ||
986 | && p->argv0 | ||
987 | && strcmp(bb_basename(p->argv0), "mdev") == 0 | ||
988 | ) { | ||
989 | kill(p->pid, SIGCHLD); | ||
990 | } | ||
991 | } | ||
992 | } | ||
993 | |||
888 | int mdev_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | 994 | int mdev_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
889 | int mdev_main(int argc UNUSED_PARAM, char **argv) | 995 | int mdev_main(int argc UNUSED_PARAM, char **argv) |
890 | { | 996 | { |
@@ -946,11 +1052,13 @@ int mdev_main(int argc UNUSED_PARAM, char **argv) | |||
946 | char *action; | 1052 | char *action; |
947 | char *env_devname; | 1053 | char *env_devname; |
948 | char *env_devpath; | 1054 | char *env_devpath; |
1055 | unsigned my_pid; | ||
1056 | int seq_fd; | ||
949 | smalluint op; | 1057 | smalluint op; |
950 | 1058 | ||
951 | /* Hotplug: | 1059 | /* Hotplug: |
952 | * env ACTION=... DEVPATH=... SUBSYSTEM=... [SEQNUM=...] mdev | 1060 | * env ACTION=... DEVPATH=... SUBSYSTEM=... [SEQNUM=...] mdev |
953 | * ACTION can be "add" or "remove" | 1061 | * ACTION can be "add", "remove", "change" |
954 | * DEVPATH is like "/block/sda" or "/class/input/mice" | 1062 | * DEVPATH is like "/block/sda" or "/class/input/mice" |
955 | */ | 1063 | */ |
956 | action = getenv("ACTION"); | 1064 | action = getenv("ACTION"); |
@@ -961,41 +1069,20 @@ int mdev_main(int argc UNUSED_PARAM, char **argv) | |||
961 | if (!action || !env_devpath /*|| !G.subsystem*/) | 1069 | if (!action || !env_devpath /*|| !G.subsystem*/) |
962 | bb_show_usage(); | 1070 | bb_show_usage(); |
963 | fw = getenv("FIRMWARE"); | 1071 | fw = getenv("FIRMWARE"); |
964 | /* If it exists, does /dev/mdev.seq match $SEQNUM? | ||
965 | * If it does not match, earlier mdev is running | ||
966 | * in parallel, and we need to wait */ | ||
967 | seq = getenv("SEQNUM"); | 1072 | seq = getenv("SEQNUM"); |
968 | if (seq) { | ||
969 | int timeout = 2000 / 32; /* 2000 msec */ | ||
970 | do { | ||
971 | int seqlen; | ||
972 | char seqbuf[sizeof(int)*3 + 2]; | ||
973 | |||
974 | seqlen = open_read_close("mdev.seq", seqbuf, sizeof(seqbuf) - 1); | ||
975 | if (seqlen < 0) { | ||
976 | seq = NULL; | ||
977 | break; | ||
978 | } | ||
979 | seqbuf[seqlen] = '\0'; | ||
980 | if (seqbuf[0] == '\n' /* seed file? */ | ||
981 | || strcmp(seq, seqbuf) == 0 /* correct idx? */ | ||
982 | ) { | ||
983 | break; | ||
984 | } | ||
985 | usleep(32*1000); | ||
986 | } while (--timeout); | ||
987 | } | ||
988 | 1073 | ||
989 | { | 1074 | my_pid = getpid(); |
990 | int logfd = open("mdev.log", O_WRONLY | O_APPEND); | 1075 | open_mdev_log(seq, my_pid); |
991 | if (logfd >= 0) { | 1076 | |
992 | xmove_fd(logfd, STDERR_FILENO); | 1077 | seq_fd = seq ? wait_for_seqfile(seq) : -1; |
993 | G.verbose = 1; | 1078 | |
994 | if (seq) | 1079 | dbg1("%s " |
995 | applet_name = xasprintf("%s[%s]", applet_name, seq); | 1080 | "ACTION:%s SUBSYSTEM:%s DEVNAME:%s DEVPATH:%s" |
996 | bb_error_msg("action: %s", action); | 1081 | "%s%s", |
997 | } | 1082 | curtime(), |
998 | } | 1083 | action, G.subsystem, env_devname, env_devpath, |
1084 | fw ? " FW:" : "", fw ? fw : "" | ||
1085 | ); | ||
999 | 1086 | ||
1000 | snprintf(temp, PATH_MAX, "/sys%s", env_devpath); | 1087 | snprintf(temp, PATH_MAX, "/sys%s", env_devpath); |
1001 | if (op == OP_remove) { | 1088 | if (op == OP_remove) { |
@@ -1005,16 +1092,18 @@ int mdev_main(int argc UNUSED_PARAM, char **argv) | |||
1005 | if (!fw) | 1092 | if (!fw) |
1006 | make_device(env_devname, temp, op); | 1093 | make_device(env_devname, temp, op); |
1007 | } | 1094 | } |
1008 | else if (op == OP_add) { | 1095 | else { |
1009 | make_device(env_devname, temp, op); | 1096 | make_device(env_devname, temp, op); |
1010 | if (ENABLE_FEATURE_MDEV_LOAD_FIRMWARE) { | 1097 | if (ENABLE_FEATURE_MDEV_LOAD_FIRMWARE) { |
1011 | if (fw) | 1098 | if (op == OP_add && fw) |
1012 | load_firmware(fw, temp); | 1099 | load_firmware(fw, temp); |
1013 | } | 1100 | } |
1014 | } | 1101 | } |
1015 | 1102 | ||
1016 | if (seq) { | 1103 | dbg1("%s exiting", curtime()); |
1017 | xopen_xwrite_close("mdev.seq", utoa(xatou(seq) + 1)); | 1104 | if (seq_fd >= 0) { |
1105 | xwrite_str(seq_fd, utoa(xatou(seq) + 1)); | ||
1106 | signal_mdevs(my_pid); | ||
1018 | } | 1107 | } |
1019 | } | 1108 | } |
1020 | 1109 | ||