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 /modutils | |
| 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>
Diffstat (limited to 'modutils')
| -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. |
