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 /util-linux | |
| 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>
Diffstat (limited to 'util-linux')
| -rw-r--r-- | util-linux/mdev.c | 265 |
1 files changed, 177 insertions, 88 deletions
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 | ||
