diff options
Diffstat (limited to 'modutils/modprobe-small.c')
-rw-r--r-- | modutils/modprobe-small.c | 182 |
1 files changed, 114 insertions, 68 deletions
diff --git a/modutils/modprobe-small.c b/modutils/modprobe-small.c index b7990bff1..9c941064b 100644 --- a/modutils/modprobe-small.c +++ b/modutils/modprobe-small.c | |||
@@ -116,21 +116,21 @@ static char* copy_stringbuf(void) | |||
116 | 116 | ||
117 | static char* find_keyword(char *ptr, size_t len, const char *word) | 117 | static char* find_keyword(char *ptr, size_t len, const char *word) |
118 | { | 118 | { |
119 | int wlen; | ||
120 | |||
121 | if (!ptr) /* happens if xmalloc_open_zipped_read_close cannot read it */ | 119 | if (!ptr) /* happens if xmalloc_open_zipped_read_close cannot read it */ |
122 | return NULL; | 120 | return NULL; |
123 | 121 | ||
124 | wlen = strlen(word); | 122 | len -= strlen(word) - 1; |
125 | len -= wlen - 1; | ||
126 | while ((ssize_t)len > 0) { | 123 | while ((ssize_t)len > 0) { |
127 | char *old = ptr; | 124 | char *old = ptr; |
125 | char *after_word; | ||
126 | |||
128 | /* search for the first char in word */ | 127 | /* search for the first char in word */ |
129 | ptr = memchr(ptr, *word, len); | 128 | ptr = memchr(ptr, word[0], len); |
130 | if (ptr == NULL) /* no occurance left, done */ | 129 | if (ptr == NULL) /* no occurance left, done */ |
131 | break; | 130 | break; |
132 | if (strncmp(ptr, word, wlen) == 0) | 131 | after_word = is_prefixed_with(ptr, word); |
133 | return ptr + wlen; /* found, return ptr past it */ | 132 | if (after_word) |
133 | return after_word; /* found, return ptr past it */ | ||
134 | ++ptr; | 134 | ++ptr; |
135 | len -= (ptr - old); | 135 | len -= (ptr - old); |
136 | } | 136 | } |
@@ -149,9 +149,13 @@ static void replace(char *s, char what, char with) | |||
149 | static char *filename2modname(const char *filename, char *modname) | 149 | static char *filename2modname(const char *filename, char *modname) |
150 | { | 150 | { |
151 | int i; | 151 | int i; |
152 | char *from; | 152 | const char *from; |
153 | 153 | ||
154 | from = bb_get_last_path_component_nostrip(filename); | 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; | ||
155 | for (i = 0; i < (MODULE_NAME_LEN-1) && from[i] != '\0' && from[i] != '.'; i++) | 159 | for (i = 0; i < (MODULE_NAME_LEN-1) && from[i] != '\0' && from[i] != '.'; i++) |
156 | modname[i] = (from[i] == '-') ? '_' : from[i]; | 160 | modname[i] = (from[i] == '-') ? '_' : from[i]; |
157 | modname[i] = '\0'; | 161 | modname[i] = '\0'; |
@@ -159,6 +163,15 @@ static char *filename2modname(const char *filename, char *modname) | |||
159 | return modname; | 163 | return modname; |
160 | } | 164 | } |
161 | 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; | ||
173 | } | ||
174 | |||
162 | /* Take "word word", return malloced "word",NUL,"word",NUL,NUL */ | 175 | /* Take "word word", return malloced "word",NUL,"word",NUL,NUL */ |
163 | static char* str_2_list(const char *str) | 176 | static char* str_2_list(const char *str) |
164 | { | 177 | { |
@@ -291,18 +304,6 @@ static void parse_module(module_info *info, const char *pathname) | |||
291 | free(module_image); | 304 | free(module_image); |
292 | } | 305 | } |
293 | 306 | ||
294 | static int pathname_matches_modname(const char *pathname, const char *modname) | ||
295 | { | ||
296 | int r; | ||
297 | char name[MODULE_NAME_LEN]; | ||
298 | const char *fname = bb_get_last_path_component_nostrip(pathname); | ||
299 | const char *suffix = strrstr(fname, ".ko"); | ||
300 | safe_strncpy(name, fname, suffix - fname + 1); | ||
301 | replace(name, '-', '_'); | ||
302 | r = (strcmp(name, modname) == 0); | ||
303 | return r; | ||
304 | } | ||
305 | |||
306 | static FAST_FUNC int fileAction(const char *pathname, | 307 | static FAST_FUNC int fileAction(const char *pathname, |
307 | struct stat *sb UNUSED_PARAM, | 308 | struct stat *sb UNUSED_PARAM, |
308 | void *modname_to_match, | 309 | void *modname_to_match, |
@@ -535,22 +536,69 @@ static module_info** find_alias(const char *alias) | |||
535 | // TODO: open only once, invent config_rewind() | 536 | // TODO: open only once, invent config_rewind() |
536 | static int already_loaded(const char *name) | 537 | static int already_loaded(const char *name) |
537 | { | 538 | { |
538 | int ret = 0; | 539 | int ret; |
539 | char *s; | 540 | char *line; |
540 | parser_t *parser = config_open2("/proc/modules", xfopen_for_read); | 541 | FILE *fp; |
541 | while (config_read(parser, &s, 1, 1, "# \t", PARSE_NORMAL & ~PARSE_GREEDY)) { | 542 | |
542 | if (strcmp(s, name) == 0) { | 543 | ret = 5 * 2; |
543 | ret = 1; | 544 | again: |
544 | break; | 545 | fp = fopen_for_read("/proc/modules"); |
546 | if (!fp) | ||
547 | return 0; | ||
548 | while ((line = xmalloc_fgetline(fp)) != NULL) { | ||
549 | char *live; | ||
550 | char *after_name; | ||
551 | |||
552 | // Examples from kernel 3.14.6: | ||
553 | //pcspkr 12718 0 - Live 0xffffffffa017e000 | ||
554 | //snd_timer 28690 2 snd_seq,snd_pcm, Live 0xffffffffa025e000 | ||
555 | //i915 801405 2 - Live 0xffffffffa0096000 | ||
556 | after_name = is_prefixed_with(line, name); | ||
557 | if (!after_name || *after_name != ' ') { | ||
558 | free(line); | ||
559 | continue; | ||
545 | } | 560 | } |
561 | live = strstr(line, " Live"); | ||
562 | free(line); | ||
563 | if (!live) { | ||
564 | /* State can be Unloading, Loading, or Live. | ||
565 | * modprobe must not return prematurely if we see "Loading": | ||
566 | * it can cause further programs to assume load completed, | ||
567 | * but it did not (yet)! | ||
568 | * Wait up to 5*20 ms for it to resolve. | ||
569 | */ | ||
570 | ret -= 2; | ||
571 | if (ret == 0) | ||
572 | break; /* huh? report as "not loaded" */ | ||
573 | fclose(fp); | ||
574 | usleep(20*1000); | ||
575 | goto again; | ||
576 | } | ||
577 | ret = 1; | ||
578 | break; | ||
546 | } | 579 | } |
547 | config_close(parser); | 580 | fclose(fp); |
548 | return ret; | 581 | |
582 | return ret & 1; | ||
549 | } | 583 | } |
550 | #else | 584 | #else |
551 | #define already_loaded(name) is_rmmod | 585 | #define already_loaded(name) 0 |
552 | #endif | 586 | #endif |
553 | 587 | ||
588 | static int rmmod(const char *filename) | ||
589 | { | ||
590 | int r; | ||
591 | char modname[MODULE_NAME_LEN]; | ||
592 | |||
593 | filename2modname(filename, modname); | ||
594 | r = delete_module(modname, O_NONBLOCK | O_EXCL); | ||
595 | dbg1_error_msg("delete_module('%s', O_NONBLOCK | O_EXCL):%d", modname, r); | ||
596 | if (r != 0 && !(option_mask32 & OPT_q)) { | ||
597 | bb_perror_msg("remove '%s'", modname); | ||
598 | } | ||
599 | return r; | ||
600 | } | ||
601 | |||
554 | /* | 602 | /* |
555 | * Given modules definition and module name (or alias, or symbol) | 603 | * Given modules definition and module name (or alias, or symbol) |
556 | * load/remove the module respecting dependencies. | 604 | * load/remove the module respecting dependencies. |
@@ -567,26 +615,36 @@ static void process_module(char *name, const char *cmdline_options) | |||
567 | module_info **infovec; | 615 | module_info **infovec; |
568 | module_info *info; | 616 | module_info *info; |
569 | int infoidx; | 617 | int infoidx; |
570 | int is_rmmod = (option_mask32 & OPT_r) != 0; | 618 | int is_remove = (option_mask32 & OPT_r) != 0; |
571 | 619 | ||
572 | dbg1_error_msg("process_module('%s','%s')", name, cmdline_options); | 620 | dbg1_error_msg("process_module('%s','%s')", name, cmdline_options); |
573 | 621 | ||
574 | replace(name, '-', '_'); | 622 | replace(name, '-', '_'); |
575 | 623 | ||
576 | dbg1_error_msg("already_loaded:%d is_rmmod:%d", already_loaded(name), is_rmmod); | 624 | dbg1_error_msg("already_loaded:%d is_remove:%d", already_loaded(name), is_remove); |
625 | |||
626 | if (applet_name[0] == 'r') { | ||
627 | /* rmmod. | ||
628 | * Does not remove dependencies, no need to scan, just remove. | ||
629 | * (compat note: this allows and strips .ko suffix) | ||
630 | */ | ||
631 | rmmod(name); | ||
632 | return; | ||
633 | } | ||
634 | |||
577 | /* | 635 | /* |
578 | * We used to have "is_rmmod != already_loaded(name)" check here, but | 636 | * We used to have "is_remove != already_loaded(name)" check here, but |
579 | * modprobe -r pci:v00008086d00007010sv00000000sd00000000bc01sc01i80 | 637 | * modprobe -r pci:v00008086d00007010sv00000000sd00000000bc01sc01i80 |
580 | * won't unload modules (there are more than one) | 638 | * won't unload modules (there are more than one) |
581 | * which have this alias. | 639 | * which have this alias. |
582 | */ | 640 | */ |
583 | if (!is_rmmod && already_loaded(name)) { | 641 | if (!is_remove && already_loaded(name)) { |
584 | dbg1_error_msg("nothing to do for '%s'", name); | 642 | dbg1_error_msg("nothing to do for '%s'", name); |
585 | return; | 643 | return; |
586 | } | 644 | } |
587 | 645 | ||
588 | options = NULL; | 646 | options = NULL; |
589 | if (!is_rmmod) { | 647 | if (!is_remove) { |
590 | char *opt_filename = xasprintf("/etc/modules/%s", name); | 648 | char *opt_filename = xasprintf("/etc/modules/%s", name); |
591 | options = xmalloc_open_read_close(opt_filename, NULL); | 649 | options = xmalloc_open_read_close(opt_filename, NULL); |
592 | if (options) | 650 | if (options) |
@@ -620,7 +678,7 @@ static void process_module(char *name, const char *cmdline_options) | |||
620 | 0 /* depth */ | 678 | 0 /* depth */ |
621 | ); | 679 | ); |
622 | dbg1_error_msg("dirscan complete"); | 680 | dbg1_error_msg("dirscan complete"); |
623 | /* Module was not found, or load failed, or is_rmmod */ | 681 | /* Module was not found, or load failed, or is_remove */ |
624 | if (module_found_idx >= 0) { /* module was found */ | 682 | if (module_found_idx >= 0) { /* module was found */ |
625 | infovec = xzalloc(2 * sizeof(infovec[0])); | 683 | infovec = xzalloc(2 * sizeof(infovec[0])); |
626 | infovec[0] = &modinfo[module_found_idx]; | 684 | infovec[0] = &modinfo[module_found_idx]; |
@@ -631,6 +689,14 @@ static void process_module(char *name, const char *cmdline_options) | |||
631 | infovec = find_alias(name); | 689 | infovec = find_alias(name); |
632 | } | 690 | } |
633 | 691 | ||
692 | if (!infovec) { | ||
693 | /* both dirscan and find_alias found nothing */ | ||
694 | if (!is_remove && applet_name[0] != 'd') /* it wasn't rmmod or depmod */ | ||
695 | bb_error_msg("module '%s' not found", name); | ||
696 | //TODO: _and_die()? or should we continue (un)loading modules listed on cmdline? | ||
697 | goto ret; | ||
698 | } | ||
699 | |||
634 | /* There can be more than one module for the given alias. For example, | 700 | /* There can be more than one module for the given alias. For example, |
635 | * "pci:v00008086d00007010sv00000000sd00000000bc01sc01i80" matches | 701 | * "pci:v00008086d00007010sv00000000sd00000000bc01sc01i80" matches |
636 | * ata_piix because it has alias "pci:v00008086d00007010sv*sd*bc*sc*i*" | 702 | * ata_piix because it has alias "pci:v00008086d00007010sv*sd*bc*sc*i*" |
@@ -639,28 +705,15 @@ static void process_module(char *name, const char *cmdline_options) | |||
639 | * a *list* of modinfo pointers from find_alias(). | 705 | * a *list* of modinfo pointers from find_alias(). |
640 | */ | 706 | */ |
641 | 707 | ||
642 | /* rmmod or modprobe -r? unload module(s) */ | 708 | /* modprobe -r? unload module(s) */ |
643 | if (is_rmmod) { | 709 | if (is_remove) { |
644 | infoidx = 0; | 710 | infoidx = 0; |
645 | while ((info = infovec[infoidx++]) != NULL) { | 711 | while ((info = infovec[infoidx++]) != NULL) { |
646 | int r; | 712 | int r = rmmod(bb_get_last_path_component_nostrip(info->pathname)); |
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) { | 713 | if (r != 0) { |
653 | if (!(option_mask32 & OPT_q)) | 714 | goto ret; /* error */ |
654 | bb_perror_msg("remove '%s'", modname); | ||
655 | goto ret; | ||
656 | } | 715 | } |
657 | } | 716 | } |
658 | |||
659 | if (applet_name[0] == 'r') { | ||
660 | /* rmmod: do not remove dependencies, exit */ | ||
661 | goto ret; | ||
662 | } | ||
663 | |||
664 | /* modprobe -r: we do not stop here - | 717 | /* modprobe -r: we do not stop here - |
665 | * continue to unload modules on which the module depends: | 718 | * continue to unload modules on which the module depends: |
666 | * "-r --remove: option causes modprobe to remove a module. | 719 | * "-r --remove: option causes modprobe to remove a module. |
@@ -669,14 +722,6 @@ static void process_module(char *name, const char *cmdline_options) | |||
669 | */ | 722 | */ |
670 | } | 723 | } |
671 | 724 | ||
672 | if (!infovec) { | ||
673 | /* both dirscan and find_alias found nothing */ | ||
674 | if (!is_rmmod && applet_name[0] != 'd') /* it wasn't rmmod or depmod */ | ||
675 | bb_error_msg("module '%s' not found", name); | ||
676 | //TODO: _and_die()? or should we continue (un)loading modules listed on cmdline? | ||
677 | goto ret; | ||
678 | } | ||
679 | |||
680 | infoidx = 0; | 725 | infoidx = 0; |
681 | while ((info = infovec[infoidx++]) != NULL) { | 726 | while ((info = infovec[infoidx++]) != NULL) { |
682 | /* Iterate thru dependencies, trying to (un)load them */ | 727 | /* Iterate thru dependencies, trying to (un)load them */ |
@@ -689,7 +734,7 @@ static void process_module(char *name, const char *cmdline_options) | |||
689 | } | 734 | } |
690 | free(deps); | 735 | free(deps); |
691 | 736 | ||
692 | if (is_rmmod) | 737 | if (is_remove) |
693 | continue; | 738 | continue; |
694 | 739 | ||
695 | /* We are modprobe: load it */ | 740 | /* We are modprobe: load it */ |
@@ -892,10 +937,10 @@ int modprobe_main(int argc UNUSED_PARAM, char **argv) | |||
892 | } | 937 | } |
893 | 938 | ||
894 | #if ENABLE_FEATURE_MODPROBE_SMALL_OPTIONS_ON_CMDLINE | 939 | #if ENABLE_FEATURE_MODPROBE_SMALL_OPTIONS_ON_CMDLINE |
895 | /* If not rmmod, parse possible module options given on command line. | 940 | /* If not rmmod/-r, parse possible module options given on command line. |
896 | * insmod/modprobe takes one module name, the rest are parameters. */ | 941 | * insmod/modprobe takes one module name, the rest are parameters. */ |
897 | options = NULL; | 942 | options = NULL; |
898 | if ('r' != applet0) { | 943 | if (!(option_mask32 & OPT_r)) { |
899 | char **arg = argv; | 944 | char **arg = argv; |
900 | while (*++arg) { | 945 | while (*++arg) { |
901 | /* Enclose options in quotes */ | 946 | /* Enclose options in quotes */ |
@@ -906,7 +951,7 @@ int modprobe_main(int argc UNUSED_PARAM, char **argv) | |||
906 | } | 951 | } |
907 | } | 952 | } |
908 | #else | 953 | #else |
909 | if ('r' != applet0) | 954 | if (!(option_mask32 & OPT_r)) |
910 | argv[1] = NULL; | 955 | argv[1] = NULL; |
911 | #endif | 956 | #endif |
912 | 957 | ||
@@ -930,10 +975,11 @@ int modprobe_main(int argc UNUSED_PARAM, char **argv) | |||
930 | } | 975 | } |
931 | 976 | ||
932 | /* Try to load modprobe.dep.bb */ | 977 | /* Try to load modprobe.dep.bb */ |
933 | load_dep_bb(); | 978 | if ('r' != applet0) /* not rmmod */ |
979 | load_dep_bb(); | ||
934 | 980 | ||
935 | /* Load/remove modules. | 981 | /* Load/remove modules. |
936 | * Only rmmod loops here, modprobe has only argv[0] */ | 982 | * Only rmmod/modprobe -r loops here, insmod/modprobe has only argv[0] */ |
937 | do { | 983 | do { |
938 | process_module(*argv, options); | 984 | process_module(*argv, options); |
939 | } while (*++argv); | 985 | } while (*++argv); |