diff options
author | Denys Vlasenko <vda.linux@googlemail.com> | 2014-04-21 16:59:36 +0200 |
---|---|---|
committer | Denys Vlasenko <vda.linux@googlemail.com> | 2014-04-21 16:59:36 +0200 |
commit | 07e5555a8f7469f6f45cacd7fc188816ae644f74 (patch) | |
tree | 3a7d88959d5ed5d0ed5ab74922e1c16a18b2a35b | |
parent | 5a21c8550ef9ed9cfcc4abed7d59e58017a306e5 (diff) | |
download | busybox-w32-07e5555a8f7469f6f45cacd7fc188816ae644f74.tar.gz busybox-w32-07e5555a8f7469f6f45cacd7fc188816ae644f74.tar.bz2 busybox-w32-07e5555a8f7469f6f45cacd7fc188816ae644f74.zip |
modprobe-small: (un)load all modules which match the alias, not only first one
Closes 627 and 7034.
Commonly seen case is (un)loading of an alias
which matches ata_generic and a more specific ata module.
For example:
modprobe [-r] pci:v00008086d00007010sv00000000sd00000000bc01sc01i80
(ata_generic and pata_acpi)
modprobe [-r] pci:v00001106d00000571sv00001509sd00009022bc01sc01i8a
(ata_generic and pata_via)
function old new delta
process_module 615 728 +113
parse_module 309 395 +86
find_alias 621 653 +32
pathname_matches_modname 78 79 +1
------------------------------------------------------------------------------
(add/remove: 0/0 grow/shrink: 4/0 up/down: 232/0) Total: 232 bytes
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
-rw-r--r-- | modutils/modprobe-small.c | 162 |
1 files changed, 101 insertions, 61 deletions
diff --git a/modutils/modprobe-small.c b/modutils/modprobe-small.c index 223eba915..91e0c1380 100644 --- a/modutils/modprobe-small.c +++ b/modutils/modprobe-small.c | |||
@@ -22,6 +22,9 @@ | |||
22 | extern int init_module(void *module, unsigned long len, const char *options); | 22 | extern int init_module(void *module, unsigned long len, const char *options); |
23 | extern int delete_module(const char *module, unsigned flags); | 23 | extern int delete_module(const char *module, unsigned flags); |
24 | extern int query_module(const char *name, int which, void *buf, size_t bufsize, size_t *ret); | 24 | extern int query_module(const char *name, int which, void *buf, size_t bufsize, size_t *ret); |
25 | /* linux/include/linux/module.h has limit of 64 chars on module names */ | ||
26 | #undef MODULE_NAME_LEN | ||
27 | #define MODULE_NAME_LEN 64 | ||
25 | 28 | ||
26 | 29 | ||
27 | #if 1 | 30 | #if 1 |
@@ -143,6 +146,19 @@ static void replace(char *s, char what, char with) | |||
143 | } | 146 | } |
144 | } | 147 | } |
145 | 148 | ||
149 | static char *filename2modname(const char *filename, char *modname) | ||
150 | { | ||
151 | int i; | ||
152 | char *from; | ||
153 | |||
154 | from = bb_get_last_path_component_nostrip(filename); | ||
155 | for (i = 0; i < (MODULE_NAME_LEN-1) && from[i] != '\0' && from[i] != '.'; i++) | ||
156 | modname[i] = (from[i] == '-') ? '_' : from[i]; | ||
157 | modname[i] = '\0'; | ||
158 | |||
159 | return modname; | ||
160 | } | ||
161 | |||
146 | /* Take "word word", return malloced "word",NUL,"word",NUL,NUL */ | 162 | /* Take "word word", return malloced "word",NUL,"word",NUL,NUL */ |
147 | static char* str_2_list(const char *str) | 163 | static char* str_2_list(const char *str) |
148 | { | 164 | { |
@@ -277,14 +293,13 @@ static void parse_module(module_info *info, const char *pathname) | |||
277 | 293 | ||
278 | static int pathname_matches_modname(const char *pathname, const char *modname) | 294 | static int pathname_matches_modname(const char *pathname, const char *modname) |
279 | { | 295 | { |
296 | int r; | ||
297 | char name[MODULE_NAME_LEN]; | ||
280 | const char *fname = bb_get_last_path_component_nostrip(pathname); | 298 | const char *fname = bb_get_last_path_component_nostrip(pathname); |
281 | const char *suffix = strrstr(fname, ".ko"); | 299 | const char *suffix = strrstr(fname, ".ko"); |
282 | //TODO: can do without malloc? | 300 | safe_strncpy(name, fname, suffix - fname); |
283 | char *name = xstrndup(fname, suffix - fname); | ||
284 | int r; | ||
285 | replace(name, '-', '_'); | 301 | replace(name, '-', '_'); |
286 | r = (strcmp(name, modname) == 0); | 302 | r = (strcmp(name, modname) == 0); |
287 | free(name); | ||
288 | return r; | 303 | return r; |
289 | } | 304 | } |
290 | 305 | ||
@@ -447,11 +462,12 @@ static void write_out_dep_bb(int fd) | |||
447 | } | 462 | } |
448 | } | 463 | } |
449 | 464 | ||
450 | static module_info* find_alias(const char *alias) | 465 | static module_info** find_alias(const char *alias) |
451 | { | 466 | { |
452 | int i; | 467 | int i; |
453 | int dep_bb_fd; | 468 | int dep_bb_fd; |
454 | module_info *result; | 469 | int infoidx; |
470 | module_info **infovec; | ||
455 | dbg1_error_msg("find_alias('%s')", alias); | 471 | dbg1_error_msg("find_alias('%s')", alias); |
456 | 472 | ||
457 | try_again: | 473 | try_again: |
@@ -464,7 +480,9 @@ static module_info* find_alias(const char *alias) | |||
464 | if (!modinfo[i].aliases) { | 480 | if (!modinfo[i].aliases) { |
465 | parse_module(&modinfo[i], modinfo[i].pathname); | 481 | parse_module(&modinfo[i], modinfo[i].pathname); |
466 | } | 482 | } |
467 | return &modinfo[i]; | 483 | infovec = xzalloc(2 * sizeof(infovec[0])); |
484 | infovec[0] = &modinfo[i]; | ||
485 | return infovec; | ||
468 | } | 486 | } |
469 | i++; | 487 | i++; |
470 | } | 488 | } |
@@ -477,16 +495,13 @@ static module_info* find_alias(const char *alias) | |||
477 | 495 | ||
478 | /* Scan all module bodies, extract modinfo (it contains aliases) */ | 496 | /* Scan all module bodies, extract modinfo (it contains aliases) */ |
479 | i = 0; | 497 | i = 0; |
480 | result = NULL; | 498 | infoidx = 0; |
499 | infovec = NULL; | ||
481 | while (modinfo[i].pathname) { | 500 | while (modinfo[i].pathname) { |
482 | char *desc, *s; | 501 | char *desc, *s; |
483 | if (!modinfo[i].aliases) { | 502 | if (!modinfo[i].aliases) { |
484 | parse_module(&modinfo[i], modinfo[i].pathname); | 503 | parse_module(&modinfo[i], modinfo[i].pathname); |
485 | } | 504 | } |
486 | if (result) { | ||
487 | i++; | ||
488 | continue; | ||
489 | } | ||
490 | /* "alias1 symbol:sym1 alias2 symbol:sym2" */ | 505 | /* "alias1 symbol:sym1 alias2 symbol:sym2" */ |
491 | desc = str_2_list(modinfo[i].aliases); | 506 | desc = str_2_list(modinfo[i].aliases); |
492 | /* Does matching substring exist? */ | 507 | /* Does matching substring exist? */ |
@@ -498,13 +513,12 @@ static module_info* find_alias(const char *alias) | |||
498 | if (fnmatch(s, alias, 0) == 0) { | 513 | if (fnmatch(s, alias, 0) == 0) { |
499 | dbg1_error_msg("found alias '%s' in module '%s'", | 514 | dbg1_error_msg("found alias '%s' in module '%s'", |
500 | alias, modinfo[i].pathname); | 515 | alias, modinfo[i].pathname); |
501 | result = &modinfo[i]; | 516 | infovec = xrealloc_vector(infovec, 1, infoidx); |
517 | infovec[infoidx++] = &modinfo[i]; | ||
502 | break; | 518 | break; |
503 | } | 519 | } |
504 | } | 520 | } |
505 | free(desc); | 521 | free(desc); |
506 | if (result && dep_bb_fd < 0) | ||
507 | return result; | ||
508 | i++; | 522 | i++; |
509 | } | 523 | } |
510 | 524 | ||
@@ -513,8 +527,8 @@ static module_info* find_alias(const char *alias) | |||
513 | write_out_dep_bb(dep_bb_fd); | 527 | write_out_dep_bb(dep_bb_fd); |
514 | } | 528 | } |
515 | 529 | ||
516 | dbg1_error_msg("find_alias '%s' returns %p", alias, result); | 530 | dbg1_error_msg("find_alias '%s' returns %d results", alias, infoidx); |
517 | return result; | 531 | return infovec; |
518 | } | 532 | } |
519 | 533 | ||
520 | #if ENABLE_FEATURE_MODPROBE_SMALL_CHECK_ALREADY_LOADED | 534 | #if ENABLE_FEATURE_MODPROBE_SMALL_CHECK_ALREADY_LOADED |
@@ -550,14 +564,23 @@ static int already_loaded(const char *name) | |||
550 | static void process_module(char *name, const char *cmdline_options) | 564 | static void process_module(char *name, const char *cmdline_options) |
551 | { | 565 | { |
552 | char *s, *deps, *options; | 566 | char *s, *deps, *options; |
567 | module_info **infovec; | ||
553 | module_info *info; | 568 | module_info *info; |
569 | int infoidx; | ||
554 | int is_rmmod = (option_mask32 & OPT_r) != 0; | 570 | int is_rmmod = (option_mask32 & OPT_r) != 0; |
571 | |||
555 | dbg1_error_msg("process_module('%s','%s')", name, cmdline_options); | 572 | dbg1_error_msg("process_module('%s','%s')", name, cmdline_options); |
556 | 573 | ||
557 | replace(name, '-', '_'); | 574 | replace(name, '-', '_'); |
558 | 575 | ||
559 | dbg1_error_msg("already_loaded:%d is_rmmod:%d", already_loaded(name), is_rmmod); | 576 | dbg1_error_msg("already_loaded:%d is_rmmod:%d", already_loaded(name), is_rmmod); |
560 | if (already_loaded(name) != is_rmmod) { | 577 | /* |
578 | * We used to have "is_rmmod != already_loaded(name)" check here, but | ||
579 | * modprobe -r pci:v00008086d00007010sv00000000sd00000000bc01sc01i80 | ||
580 | * won't unload modules (there are more than one) | ||
581 | * which have this alias. | ||
582 | */ | ||
583 | if (!is_rmmod && already_loaded(name)) { | ||
561 | dbg1_error_msg("nothing to do for '%s'", name); | 584 | dbg1_error_msg("nothing to do for '%s'", name); |
562 | return; | 585 | return; |
563 | } | 586 | } |
@@ -586,39 +609,51 @@ static void process_module(char *name, const char *cmdline_options) | |||
586 | if (!module_count) { | 609 | if (!module_count) { |
587 | /* Scan module directory. This is done only once. | 610 | /* Scan module directory. This is done only once. |
588 | * It will attempt module load, and will exit(EXIT_SUCCESS) | 611 | * It will attempt module load, and will exit(EXIT_SUCCESS) |
589 | * on success. */ | 612 | * on success. |
613 | */ | ||
590 | module_found_idx = -1; | 614 | module_found_idx = -1; |
591 | recursive_action(".", | 615 | recursive_action(".", |
592 | ACTION_RECURSE, /* flags */ | 616 | ACTION_RECURSE, /* flags */ |
593 | fileAction, /* file action */ | 617 | fileAction, /* file action */ |
594 | NULL, /* dir action */ | 618 | NULL, /* dir action */ |
595 | name, /* user data */ | 619 | name, /* user data */ |
596 | 0); /* depth */ | 620 | 0 /* depth */ |
621 | ); | ||
597 | dbg1_error_msg("dirscan complete"); | 622 | dbg1_error_msg("dirscan complete"); |
598 | /* Module was not found, or load failed, or is_rmmod */ | 623 | /* Module was not found, or load failed, or is_rmmod */ |
599 | if (module_found_idx >= 0) { /* module was found */ | 624 | if (module_found_idx >= 0) { /* module was found */ |
600 | info = &modinfo[module_found_idx]; | 625 | infovec = xzalloc(2 * sizeof(infovec[0])); |
626 | infovec[0] = &modinfo[module_found_idx]; | ||
601 | } else { /* search for alias, not a plain module name */ | 627 | } else { /* search for alias, not a plain module name */ |
602 | info = find_alias(name); | 628 | infovec = find_alias(name); |
603 | } | 629 | } |
604 | } else { | 630 | } else { |
605 | info = find_alias(name); | 631 | infovec = find_alias(name); |
606 | } | 632 | } |
607 | 633 | ||
608 | // Problem here: there can be more than one module | 634 | /* There can be more than one module for the given alias. For example, |
609 | // for the given alias. For example, | 635 | * "pci:v00008086d00007010sv00000000sd00000000bc01sc01i80" matches |
610 | // "pci:v00008086d00007010sv00000000sd00000000bc01sc01i80" matches | 636 | * ata_piix because it has alias "pci:v00008086d00007010sv*sd*bc*sc*i*" |
611 | // ata_piix because it has an alias "pci:v00008086d00007010sv*sd*bc*sc*i*" | 637 | * and ata_generic, it has alias "pci:v*d*sv*sd*bc01sc01i*" |
612 | // and ata_generic, it has an alias "pci:v*d*sv*sd*bc01sc01i*" | 638 | * Standard modprobe loads them both. We achieve it by returning |
613 | // Standard modprobe would load them both. | 639 | * a *list* of modinfo pointers from find_alias(). |
614 | // In this code, find_alias() returns only the first matching module. | 640 | */ |
615 | 641 | ||
616 | /* rmmod? unload it by name */ | 642 | /* rmmod or modprobe -r? unload module(s) */ |
617 | if (is_rmmod) { | 643 | if (is_rmmod) { |
618 | if (delete_module(name, O_NONBLOCK | O_EXCL) != 0) { | 644 | infoidx = 0; |
619 | if (!(option_mask32 & OPT_q)) | 645 | while ((info = infovec[infoidx++]) != NULL) { |
620 | bb_perror_msg("remove '%s'", name); | 646 | int r; |
621 | goto ret; | 647 | char modname[MODULE_NAME_LEN]; |
648 | |||
649 | filename2modname(info->pathname, modname); | ||
650 | r = delete_module(modname, O_NONBLOCK | O_EXCL); | ||
651 | dbg1_error_msg("delete_module('%s', O_NONBLOCK | O_EXCL):%d", modname, r); | ||
652 | if (r != 0) { | ||
653 | if (!(option_mask32 & OPT_q)) | ||
654 | bb_perror_msg("remove '%s'", modname); | ||
655 | goto ret; | ||
656 | } | ||
622 | } | 657 | } |
623 | 658 | ||
624 | if (applet_name[0] == 'r') { | 659 | if (applet_name[0] == 'r') { |
@@ -634,7 +669,7 @@ static void process_module(char *name, const char *cmdline_options) | |||
634 | */ | 669 | */ |
635 | } | 670 | } |
636 | 671 | ||
637 | if (!info) { | 672 | if (!infovec) { |
638 | /* both dirscan and find_alias found nothing */ | 673 | /* both dirscan and find_alias found nothing */ |
639 | if (!is_rmmod && applet_name[0] != 'd') /* it wasn't rmmod or depmod */ | 674 | if (!is_rmmod && applet_name[0] != 'd') /* it wasn't rmmod or depmod */ |
640 | bb_error_msg("module '%s' not found", name); | 675 | bb_error_msg("module '%s' not found", name); |
@@ -642,36 +677,41 @@ static void process_module(char *name, const char *cmdline_options) | |||
642 | goto ret; | 677 | goto ret; |
643 | } | 678 | } |
644 | 679 | ||
645 | /* Iterate thru dependencies, trying to (un)load them */ | 680 | infoidx = 0; |
646 | deps = str_2_list(info->deps); | 681 | while ((info = infovec[infoidx++]) != NULL) { |
647 | for (s = deps; *s; s += strlen(s) + 1) { | 682 | /* Iterate thru dependencies, trying to (un)load them */ |
648 | //if (strcmp(name, s) != 0) // N.B. do loops exist? | 683 | deps = str_2_list(info->deps); |
649 | dbg1_error_msg("recurse on dep '%s'", s); | 684 | for (s = deps; *s; s += strlen(s) + 1) { |
650 | process_module(s, NULL); | 685 | //if (strcmp(name, s) != 0) // N.B. do loops exist? |
651 | dbg1_error_msg("recurse on dep '%s' done", s); | 686 | dbg1_error_msg("recurse on dep '%s'", s); |
652 | } | 687 | process_module(s, NULL); |
653 | free(deps); | 688 | dbg1_error_msg("recurse on dep '%s' done", s); |
689 | } | ||
690 | free(deps); | ||
654 | 691 | ||
655 | /* modprobe -> load it */ | 692 | if (is_rmmod) |
656 | if (!is_rmmod) { | 693 | continue; |
657 | if (!options || strstr(options, "blacklist") == NULL) { | 694 | |
658 | errno = 0; | 695 | /* We are modprobe: load it */ |
659 | if (load_module(info->pathname, options) != 0) { | 696 | if (options && strstr(options, "blacklist")) { |
660 | if (EEXIST != errno) { | ||
661 | bb_error_msg("'%s': %s", | ||
662 | info->pathname, | ||
663 | moderror(errno)); | ||
664 | } else { | ||
665 | dbg1_error_msg("'%s': %s", | ||
666 | info->pathname, | ||
667 | moderror(errno)); | ||
668 | } | ||
669 | } | ||
670 | } else { | ||
671 | dbg1_error_msg("'%s': blacklisted", info->pathname); | 697 | dbg1_error_msg("'%s': blacklisted", info->pathname); |
698 | continue; | ||
699 | } | ||
700 | errno = 0; | ||
701 | if (load_module(info->pathname, options) != 0) { | ||
702 | if (EEXIST != errno) { | ||
703 | bb_error_msg("'%s': %s", | ||
704 | info->pathname, | ||
705 | moderror(errno)); | ||
706 | } else { | ||
707 | dbg1_error_msg("'%s': %s", | ||
708 | info->pathname, | ||
709 | moderror(errno)); | ||
710 | } | ||
672 | } | 711 | } |
673 | } | 712 | } |
674 | ret: | 713 | ret: |
714 | free(infovec); | ||
675 | free(options); | 715 | free(options); |
676 | //TODO: return load attempt result from process_module. | 716 | //TODO: return load attempt result from process_module. |
677 | //If dep didn't load ok, continuing makes little sense. | 717 | //If dep didn't load ok, continuing makes little sense. |