diff options
Diffstat (limited to 'util-linux/mdev.c')
-rw-r--r-- | util-linux/mdev.c | 341 |
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 | ||
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 | ||
255 | struct envmatch { | ||
256 | struct envmatch *next; | ||
257 | char *envname; | ||
258 | regex_t match; | ||
259 | }; | ||
260 | |||
236 | struct rule { | 261 | struct 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 | ||
248 | struct globals { | 274 | struct 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 | ||
282 | static void make_default_cur_rule(void) | 303 | static void make_default_cur_rule(void) |
@@ -288,14 +309,48 @@ static void make_default_cur_rule(void) | |||
288 | 309 | ||
289 | static void clean_up_cur_rule(void) | 310 | static 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 | ||
328 | static 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 | |||
299 | static void parse_next_rule(void) | 354 | static 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 | ||
484 | static 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 | ||
899 | static 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 | |||
907 | static 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 | */ | ||
922 | static int | ||
923 | wait_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 | |||
980 | static 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 | |||
825 | int mdev_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | 993 | int mdev_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
826 | int mdev_main(int argc UNUSED_PARAM, char **argv) | 994 | int 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 | ||