Changeset 3621 in MondoRescue for branches/3.3/mindi-busybox/modutils/modprobe-small.c
- Timestamp:
- Dec 20, 2016, 4:07:32 PM (7 years ago)
- Location:
- branches/3.3
- Files:
-
- 1 edited
- 1 copied
Legend:
- Unmodified
- Added
- Removed
-
branches/3.3/mindi-busybox/modutils/modprobe-small.c
r3232 r3621 10 10 11 11 //applet:IF_MODPROBE_SMALL(APPLET(modprobe, BB_DIR_SBIN, BB_SUID_DROP)) 12 //applet:IF_MODPROBE_SMALL(APPLET_ODDNAME(depmod, modprobe, BB_DIR_SBIN, BB_SUID_DROP, modprobe))13 //applet:IF_MODPROBE_SMALL(APPLET_ODDNAME(insmod, modprobe, BB_DIR_SBIN, BB_SUID_DROP, modprobe))14 //applet:IF_MODPROBE_SMALL(APPLET_ODDNAME(lsmod, modprobe, BB_DIR_SBIN, BB_SUID_DROP, modprobe))15 //applet:IF_MODPROBE_SMALL(APPLET_ODDNAME(rmmod, modprobe, BB_DIR_SBIN, BB_SUID_DROP, modprobe))12 //applet:IF_MODPROBE_SMALL(APPLET_ODDNAME(depmod, modprobe, BB_DIR_SBIN, BB_SUID_DROP, depmod)) 13 //applet:IF_MODPROBE_SMALL(APPLET_ODDNAME(insmod, modprobe, BB_DIR_SBIN, BB_SUID_DROP, insmod)) 14 //applet:IF_MODPROBE_SMALL(APPLET_ODDNAME(lsmod, modprobe, BB_DIR_SBIN, BB_SUID_DROP, lsmod)) 15 //applet:IF_MODPROBE_SMALL(APPLET_ODDNAME(rmmod, modprobe, BB_DIR_SBIN, BB_SUID_DROP, rmmod)) 16 16 17 17 #include "libbb.h" … … 22 22 extern int init_module(void *module, unsigned long len, const char *options); 23 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 /* linux/include/linux/module.h has limit of 64 chars on module names */ 25 #undef MODULE_NAME_LEN 26 #define MODULE_NAME_LEN 64 25 27 26 28 … … 44 46 char *aliases; 45 47 char *deps; 48 smallint open_read_failed; 46 49 } module_info; 47 50 … … 114 117 static char* find_keyword(char *ptr, size_t len, const char *word) 115 118 { 116 int wlen;117 118 119 if (!ptr) /* happens if xmalloc_open_zipped_read_close cannot read it */ 119 120 return NULL; 120 121 121 wlen = strlen(word); 122 len -= wlen - 1; 122 len -= strlen(word) - 1; 123 123 while ((ssize_t)len > 0) { 124 124 char *old = ptr; 125 char *after_word; 126 125 127 /* search for the first char in word */ 126 ptr = memchr(ptr, *word, len);128 ptr = memchr(ptr, word[0], len); 127 129 if (ptr == NULL) /* no occurance left, done */ 128 130 break; 129 if (strncmp(ptr, word, wlen) == 0) 130 return ptr + wlen; /* found, return ptr past it */ 131 after_word = is_prefixed_with(ptr, word); 132 if (after_word) 133 return after_word; /* found, return ptr past it */ 131 134 ++ptr; 132 135 len -= (ptr - old); … … 142 145 ++s; 143 146 } 147 } 148 149 static char *filename2modname(const char *filename, char *modname) 150 { 151 int i; 152 const char *from; 153 154 // Disabled since otherwise "modprobe dir/name" would work 155 // as if it is "modprobe name". It is unclear why 156 // 'basenamization' was here in the first place. 157 //from = bb_get_last_path_component_nostrip(filename); 158 from = filename; 159 for (i = 0; i < (MODULE_NAME_LEN-1) && from[i] != '\0' && from[i] != '.'; i++) 160 modname[i] = (from[i] == '-') ? '_' : from[i]; 161 modname[i] = '\0'; 162 163 return modname; 164 } 165 166 static int pathname_matches_modname(const char *pathname, const char *modname) 167 { 168 int r; 169 char name[MODULE_NAME_LEN]; 170 filename2modname(bb_get_last_path_component_nostrip(pathname), name); 171 r = (strcmp(name, modname) == 0); 172 return r; 144 173 } 145 174 … … 195 224 } 196 225 197 static void parse_module(module_info *info, const char *pathname) 226 /* Returns !0 if open/read was unsuccessful */ 227 static int parse_module(module_info *info, const char *pathname) 198 228 { 199 229 char *module_image; … … 204 234 205 235 /* Read (possibly compressed) module */ 236 errno = 0; 206 237 len = 64 * 1024 * 1024; /* 64 Mb at most */ 207 238 module_image = xmalloc_open_zipped_read_close(pathname, &len); … … 213 244 pos = 0; 214 245 while (1) { 246 unsigned start = stringbuf_idx; 215 247 ptr = find_keyword(module_image + pos, len - pos, "alias="); 216 248 if (!ptr) { … … 229 261 append(ptr); 230 262 appendc(' '); 263 /* 264 * Don't add redundant aliases, such as: 265 * libcrc32c.ko symbol:crc32c symbol:crc32c 266 */ 267 if (start) { /* "if we aren't the first alias" */ 268 char *found, *last; 269 stringbuf[stringbuf_idx] = '\0'; 270 last = stringbuf + start; 271 /* 272 * String at last-1 is " symbol:crc32c " 273 * (with both leading and trailing spaces). 274 */ 275 if (strncmp(stringbuf, last, stringbuf_idx - start) == 0) 276 /* First alias matches us */ 277 found = stringbuf; 278 else 279 /* Does any other alias match? */ 280 found = strstr(stringbuf, last-1); 281 if (found < last-1) { 282 /* There is absolutely the same string before us */ 283 dbg2_error_msg("redundant:'%s'", last); 284 stringbuf_idx = start; 285 goto skip; 286 } 287 } 231 288 skip: 232 289 pos = (ptr - module_image); … … 245 302 append(ptr); 246 303 } 304 free(module_image); 247 305 info->deps = copy_stringbuf(); 248 306 249 free(module_image); 250 } 251 252 static int pathname_matches_modname(const char *pathname, const char *modname) 253 { 254 const char *fname = bb_get_last_path_component_nostrip(pathname); 255 const char *suffix = strrstr(fname, ".ko"); 256 //TODO: can do without malloc? 257 char *name = xstrndup(fname, suffix - fname); 258 int r; 259 replace(name, '-', '_'); 260 r = (strcmp(name, modname) == 0); 261 free(name); 262 return r; 307 info->open_read_failed = (module_image == NULL); 308 return info->open_read_failed; 263 309 } 264 310 … … 291 337 dbg1_error_msg("'%s' module name matches", pathname); 292 338 module_found_idx = cur; 293 parse_module(&modinfo[cur], pathname); 339 if (parse_module(&modinfo[cur], pathname) != 0) 340 return TRUE; /* failed to open/read it, no point in trying loading */ 294 341 295 342 if (!(option_mask32 & OPT_r)) { … … 422 469 } 423 470 424 static module_info* find_alias(const char *alias)471 static module_info** find_alias(const char *alias) 425 472 { 426 473 int i; 427 474 int dep_bb_fd; 428 module_info *result; 475 int infoidx; 476 module_info **infovec; 429 477 dbg1_error_msg("find_alias('%s')", alias); 430 478 … … 439 487 parse_module(&modinfo[i], modinfo[i].pathname); 440 488 } 441 return &modinfo[i]; 489 infovec = xzalloc(2 * sizeof(infovec[0])); 490 infovec[0] = &modinfo[i]; 491 return infovec; 442 492 } 443 493 i++; … … 452 502 /* Scan all module bodies, extract modinfo (it contains aliases) */ 453 503 i = 0; 454 result = NULL; 504 infoidx = 0; 505 infovec = NULL; 455 506 while (modinfo[i].pathname) { 456 507 char *desc, *s; 457 508 if (!modinfo[i].aliases) { 458 509 parse_module(&modinfo[i], modinfo[i].pathname); 459 }460 if (result) {461 i++;462 continue;463 510 } 464 511 /* "alias1 symbol:sym1 alias2 symbol:sym2" */ … … 473 520 dbg1_error_msg("found alias '%s' in module '%s'", 474 521 alias, modinfo[i].pathname); 475 result = &modinfo[i]; 522 infovec = xrealloc_vector(infovec, 1, infoidx); 523 infovec[infoidx++] = &modinfo[i]; 476 524 break; 477 525 } 478 526 } 479 527 free(desc); 480 if (result && dep_bb_fd < 0)481 return result;482 528 i++; 483 529 } … … 488 534 } 489 535 490 dbg1_error_msg("find_alias '%s' returns % p", alias, result);491 return result;536 dbg1_error_msg("find_alias '%s' returns %d results", alias, infoidx); 537 return infovec; 492 538 } 493 539 … … 496 542 static int already_loaded(const char *name) 497 543 { 498 int ret = 0; 499 char *s; 500 parser_t *parser = config_open2("/proc/modules", xfopen_for_read); 501 while (config_read(parser, &s, 1, 1, "# \t", PARSE_NORMAL & ~PARSE_GREEDY)) { 502 if (strcmp(s, name) == 0) { 503 ret = 1; 504 break; 505 } 506 } 507 config_close(parser); 508 return ret; 544 int ret; 545 char *line; 546 FILE *fp; 547 548 ret = 5 * 2; 549 again: 550 fp = fopen_for_read("/proc/modules"); 551 if (!fp) 552 return 0; 553 while ((line = xmalloc_fgetline(fp)) != NULL) { 554 char *live; 555 char *after_name; 556 557 // Examples from kernel 3.14.6: 558 //pcspkr 12718 0 - Live 0xffffffffa017e000 559 //snd_timer 28690 2 snd_seq,snd_pcm, Live 0xffffffffa025e000 560 //i915 801405 2 - Live 0xffffffffa0096000 561 after_name = is_prefixed_with(line, name); 562 if (!after_name || *after_name != ' ') { 563 free(line); 564 continue; 565 } 566 live = strstr(line, " Live"); 567 free(line); 568 if (!live) { 569 /* State can be Unloading, Loading, or Live. 570 * modprobe must not return prematurely if we see "Loading": 571 * it can cause further programs to assume load completed, 572 * but it did not (yet)! 573 * Wait up to 5*20 ms for it to resolve. 574 */ 575 ret -= 2; 576 if (ret == 0) 577 break; /* huh? report as "not loaded" */ 578 fclose(fp); 579 usleep(20*1000); 580 goto again; 581 } 582 ret = 1; 583 break; 584 } 585 fclose(fp); 586 587 return ret & 1; 509 588 } 510 589 #else 511 #define already_loaded(name) is_rmmod590 #define already_loaded(name) 0 512 591 #endif 592 593 static int rmmod(const char *filename) 594 { 595 int r; 596 char modname[MODULE_NAME_LEN]; 597 598 filename2modname(filename, modname); 599 r = delete_module(modname, O_NONBLOCK | O_EXCL); 600 dbg1_error_msg("delete_module('%s', O_NONBLOCK | O_EXCL):%d", modname, r); 601 if (r != 0 && !(option_mask32 & OPT_q)) { 602 bb_perror_msg("remove '%s'", modname); 603 } 604 return r; 605 } 513 606 514 607 /* … … 522 615 #define cmdline_options "" 523 616 #endif 524 static voidprocess_module(char *name, const char *cmdline_options)617 static int process_module(char *name, const char *cmdline_options) 525 618 { 526 619 char *s, *deps, *options; 620 module_info **infovec; 527 621 module_info *info; 528 int is_rmmod = (option_mask32 & OPT_r) != 0; 622 int infoidx; 623 int is_remove = (option_mask32 & OPT_r) != 0; 624 int exitcode = EXIT_SUCCESS; 625 529 626 dbg1_error_msg("process_module('%s','%s')", name, cmdline_options); 530 627 531 628 replace(name, '-', '_'); 532 629 533 dbg1_error_msg("already_loaded:%d is_rmmod:%d", already_loaded(name), is_rmmod); 534 if (already_loaded(name) != is_rmmod) { 630 dbg1_error_msg("already_loaded:%d is_remove:%d", already_loaded(name), is_remove); 631 632 if (applet_name[0] == 'r') { 633 /* rmmod. 634 * Does not remove dependencies, no need to scan, just remove. 635 * (compat note: this allows and strips .ko suffix) 636 */ 637 rmmod(name); 638 return EXIT_SUCCESS; 639 } 640 641 /* 642 * We used to have "is_remove != already_loaded(name)" check here, but 643 * modprobe -r pci:v00008086d00007010sv00000000sd00000000bc01sc01i80 644 * won't unload modules (there are more than one) 645 * which have this alias. 646 */ 647 if (!is_remove && already_loaded(name)) { 535 648 dbg1_error_msg("nothing to do for '%s'", name); 536 return ;649 return EXIT_SUCCESS; 537 650 } 538 651 539 652 options = NULL; 540 if (!is_r mmod) {653 if (!is_remove) { 541 654 char *opt_filename = xasprintf("/etc/modules/%s", name); 542 655 options = xmalloc_open_read_close(opt_filename, NULL); … … 561 674 /* Scan module directory. This is done only once. 562 675 * It will attempt module load, and will exit(EXIT_SUCCESS) 563 * on success. */ 676 * on success. 677 */ 564 678 module_found_idx = -1; 565 679 recursive_action(".", … … 568 682 NULL, /* dir action */ 569 683 name, /* user data */ 570 0); /* depth */ 684 0 /* depth */ 685 ); 571 686 dbg1_error_msg("dirscan complete"); 572 /* Module was not found, or load failed, or is_r mmod*/687 /* Module was not found, or load failed, or is_remove */ 573 688 if (module_found_idx >= 0) { /* module was found */ 574 info = &modinfo[module_found_idx]; 689 infovec = xzalloc(2 * sizeof(infovec[0])); 690 infovec[0] = &modinfo[module_found_idx]; 575 691 } else { /* search for alias, not a plain module name */ 576 info = find_alias(name);692 infovec = find_alias(name); 577 693 } 578 694 } else { 579 info = find_alias(name); 580 } 581 582 // Problem here: there can be more than one module 583 // for the given alias. For example, 584 // "pci:v00008086d00007010sv00000000sd00000000bc01sc01i80" matches 585 // ata_piix because it has an alias "pci:v00008086d00007010sv*sd*bc*sc*i*" 586 // and ata_generic, it has an alias "alias=pci:v*d*sv*sd*bc01sc01i*" 587 // Standard modprobe would load them both. 588 // In this code, find_alias() returns only the first matching module. 589 590 /* rmmod? unload it by name */ 591 if (is_rmmod) { 592 if (delete_module(name, O_NONBLOCK | O_EXCL) != 0) { 593 if (!(option_mask32 & OPT_q)) 594 bb_perror_msg("remove '%s'", name); 595 goto ret; 596 } 597 598 if (applet_name[0] == 'r') { 599 /* rmmod: do not remove dependencies, exit */ 600 goto ret; 601 } 602 695 infovec = find_alias(name); 696 } 697 698 if (!infovec) { 699 /* both dirscan and find_alias found nothing */ 700 if (!is_remove && applet_name[0] != 'd') /* it wasn't rmmod or depmod */ 701 bb_error_msg("module '%s' not found", name); 702 //TODO: _and_die()? or should we continue (un)loading modules listed on cmdline? 703 goto ret; 704 } 705 706 /* There can be more than one module for the given alias. For example, 707 * "pci:v00008086d00007010sv00000000sd00000000bc01sc01i80" matches 708 * ata_piix because it has alias "pci:v00008086d00007010sv*sd*bc*sc*i*" 709 * and ata_generic, it has alias "pci:v*d*sv*sd*bc01sc01i*" 710 * Standard modprobe loads them both. We achieve it by returning 711 * a *list* of modinfo pointers from find_alias(). 712 */ 713 714 /* modprobe -r? unload module(s) */ 715 if (is_remove) { 716 infoidx = 0; 717 while ((info = infovec[infoidx++]) != NULL) { 718 int r = rmmod(bb_get_last_path_component_nostrip(info->pathname)); 719 if (r != 0) { 720 goto ret; /* error */ 721 } 722 } 603 723 /* modprobe -r: we do not stop here - 604 724 * continue to unload modules on which the module depends: … … 609 729 } 610 730 611 if (!info) { 612 /* both dirscan and find_alias found nothing */ 613 if (!is_rmmod && applet_name[0] != 'd') /* it wasn't rmmod or depmod */ 614 bb_error_msg("module '%s' not found", name); 615 //TODO: _and_die()? or should we continue (un)loading modules listed on cmdline? 616 goto ret; 617 } 618 619 /* Iterate thru dependencies, trying to (un)load them */ 620 deps = str_2_list(info->deps); 621 for (s = deps; *s; s += strlen(s) + 1) { 622 //if (strcmp(name, s) != 0) // N.B. do loops exist? 623 dbg1_error_msg("recurse on dep '%s'", s); 624 process_module(s, NULL); 625 dbg1_error_msg("recurse on dep '%s' done", s); 626 } 627 free(deps); 628 629 /* modprobe -> load it */ 630 if (!is_rmmod) { 631 if (!options || strstr(options, "blacklist") == NULL) { 632 errno = 0; 633 if (load_module(info->pathname, options) != 0) { 634 if (EEXIST != errno) { 635 bb_error_msg("'%s': %s", 636 info->pathname, 637 moderror(errno)); 638 } else { 639 dbg1_error_msg("'%s': %s", 640 info->pathname, 641 moderror(errno)); 642 } 731 infoidx = 0; 732 while ((info = infovec[infoidx++]) != NULL) { 733 /* Iterate thru dependencies, trying to (un)load them */ 734 deps = str_2_list(info->deps); 735 for (s = deps; *s; s += strlen(s) + 1) { 736 //if (strcmp(name, s) != 0) // N.B. do loops exist? 737 dbg1_error_msg("recurse on dep '%s'", s); 738 process_module(s, NULL); 739 dbg1_error_msg("recurse on dep '%s' done", s); 740 } 741 free(deps); 742 743 if (is_remove) 744 continue; 745 746 /* We are modprobe: load it */ 747 if (options && strstr(options, "blacklist")) { 748 dbg1_error_msg("'%s': blacklisted", info->pathname); 749 continue; 750 } 751 if (info->open_read_failed) { 752 /* We already tried it, didn't work. Don't try load again */ 753 exitcode = EXIT_FAILURE; 754 continue; 755 } 756 errno = 0; 757 if (load_module(info->pathname, options) != 0) { 758 if (EEXIST != errno) { 759 bb_error_msg("'%s': %s", 760 info->pathname, 761 moderror(errno)); 762 } else { 763 dbg1_error_msg("'%s': %s", 764 info->pathname, 765 moderror(errno)); 643 766 } 644 } else { 645 dbg1_error_msg("'%s': blacklisted", info->pathname); 767 exitcode = EXIT_FAILURE; 646 768 } 647 769 } 648 770 ret: 771 free(infovec); 649 772 free(options); 650 //TODO: return load attempt result from process_module. 651 //If dep didn't load ok, continuing makes little sense. 773 774 return exitcode; 652 775 } 653 776 #undef cmdline_options … … 704 827 //usage:#if ENABLE_MODPROBE_SMALL 705 828 706 //// Note: currently, help system shows modprobe --help text for all aliased cmds707 //// (see APPLET_ODDNAME macro definition).708 //// All other help texts defined below are not used. FIXME?709 710 829 //usage:#define depmod_trivial_usage NOUSAGE_STR 711 830 //usage:#define depmod_full_usage "" … … 721 840 //usage: "[SYMBOL=VALUE]..." 722 841 //usage:#define insmod_full_usage "\n\n" 723 //usage: "Load the specified kernel modules into the kernel"842 //usage: "Load kernel module" 724 843 //usage: IF_FEATURE_2_4_MODULES( "\n" 725 844 //usage: "\n -f Force module to load into the wrong kernel version" … … 746 865 747 866 //usage:#define modprobe_trivial_usage 748 //usage: "[-qfwrsv] MODULE [ symbol=value]..."867 //usage: "[-qfwrsv] MODULE [SYMBOL=VALUE]..." 749 868 //usage:#define modprobe_full_usage "\n\n" 750 869 //usage: " -r Remove MODULE (stacks) or do autoclean" … … 760 879 int modprobe_main(int argc UNUSED_PARAM, char **argv) 761 880 { 881 int exitcode; 762 882 struct utsname uts; 763 883 char applet0 = applet_name[0]; … … 831 951 832 952 #if ENABLE_FEATURE_MODPROBE_SMALL_OPTIONS_ON_CMDLINE 833 /* If not rmmod , parse possible module options given on command line.953 /* If not rmmod/-r, parse possible module options given on command line. 834 954 * insmod/modprobe takes one module name, the rest are parameters. */ 835 955 options = NULL; 836 if ( 'r' != applet0) {956 if (!(option_mask32 & OPT_r)) { 837 957 char **arg = argv; 838 958 while (*++arg) { … … 845 965 } 846 966 #else 847 if ( 'r' != applet0)967 if (!(option_mask32 & OPT_r)) 848 968 argv[1] = NULL; 849 969 #endif … … 865 985 *argv, moderror(errno)); 866 986 } 867 return 0;987 return EXIT_SUCCESS; 868 988 } 869 989 870 990 /* Try to load modprobe.dep.bb */ 871 load_dep_bb(); 991 if ('r' != applet0) { /* not rmmod */ 992 load_dep_bb(); 993 } 872 994 873 995 /* Load/remove modules. 874 * Only rmmod loops here, modprobe has only argv[0] */ 996 * Only rmmod/modprobe -r loops here, insmod/modprobe has only argv[0] */ 997 exitcode = EXIT_SUCCESS; 875 998 do { 876 process_module(*argv, options);999 exitcode |= process_module(*argv, options); 877 1000 } while (*++argv); 878 1001 … … 880 1003 IF_FEATURE_MODPROBE_SMALL_OPTIONS_ON_CMDLINE(free(options);) 881 1004 } 882 return EXIT_SUCCESS;883 } 1005 return exitcode; 1006 }
Note:
See TracChangeset
for help on using the changeset viewer.