1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17#include "libbb.h"
18
19#include <sys/utsname.h>
20#include <fnmatch.h>
21
22extern int init_module(void *module, unsigned long len, const char *options);
23extern int delete_module(const char *module, unsigned flags);
24extern int query_module(const char *name, int which, void *buf, size_t bufsize, size_t *ret);
25
26#undef MODULE_NAME_LEN
27#define MODULE_NAME_LEN 64
28
29
30#if 1
31# define dbg1_error_msg(...) ((void)0)
32# define dbg2_error_msg(...) ((void)0)
33#else
34# define dbg1_error_msg(...) bb_error_msg(__VA_ARGS__)
35# define dbg2_error_msg(...) bb_error_msg(__VA_ARGS__)
36#endif
37
38#define DEPFILE_BB CONFIG_DEFAULT_DEPMOD_FILE".bb"
39
40enum {
41 OPT_q = (1 << 0),
42 OPT_r = (1 << 1),
43};
44
45typedef struct module_info {
46 char *pathname;
47 char *aliases;
48 char *deps;
49} module_info;
50
51
52
53
54struct globals {
55 module_info *modinfo;
56 char *module_load_options;
57 smallint dep_bb_seen;
58 smallint wrote_dep_bb_ok;
59 unsigned module_count;
60 int module_found_idx;
61 unsigned stringbuf_idx;
62 unsigned stringbuf_size;
63 char *stringbuf;
64
65
66};
67#define G (*ptr_to_globals)
68#define modinfo (G.modinfo )
69#define dep_bb_seen (G.dep_bb_seen )
70#define wrote_dep_bb_ok (G.wrote_dep_bb_ok )
71#define module_count (G.module_count )
72#define module_found_idx (G.module_found_idx )
73#define module_load_options (G.module_load_options)
74#define stringbuf_idx (G.stringbuf_idx )
75#define stringbuf_size (G.stringbuf_size )
76#define stringbuf (G.stringbuf )
77#define INIT_G() do { \
78 SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
79} while (0)
80
81static void append(const char *s)
82{
83 unsigned len = strlen(s);
84 if (stringbuf_idx + len + 15 > stringbuf_size) {
85 stringbuf_size = stringbuf_idx + len + 127;
86 dbg2_error_msg("grow stringbuf to %u", stringbuf_size);
87 stringbuf = xrealloc(stringbuf, stringbuf_size);
88 }
89 memcpy(stringbuf + stringbuf_idx, s, len);
90 stringbuf_idx += len;
91}
92
93static void appendc(char c)
94{
95
96
97 stringbuf[stringbuf_idx++] = c;
98}
99
100static void bksp(void)
101{
102 if (stringbuf_idx)
103 stringbuf_idx--;
104}
105
106static void reset_stringbuf(void)
107{
108 stringbuf_idx = 0;
109}
110
111static char* copy_stringbuf(void)
112{
113 char *copy = xzalloc(stringbuf_idx + 1);
114 return memcpy(copy, stringbuf, stringbuf_idx);
115}
116
117static char* find_keyword(char *ptr, size_t len, const char *word)
118{
119 int wlen;
120
121 if (!ptr)
122 return NULL;
123
124 wlen = strlen(word);
125 len -= wlen - 1;
126 while ((ssize_t)len > 0) {
127 char *old = ptr;
128
129 ptr = memchr(ptr, *word, len);
130 if (ptr == NULL)
131 break;
132 if (strncmp(ptr, word, wlen) == 0)
133 return ptr + wlen;
134 ++ptr;
135 len -= (ptr - old);
136 }
137 return NULL;
138}
139
140static void replace(char *s, char what, char with)
141{
142 while (*s) {
143 if (what == *s)
144 *s = with;
145 ++s;
146 }
147}
148
149static char *filename2modname(const char *filename, char *modname)
150{
151 int i;
152 const char *from;
153
154
155
156
157
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
167static char* str_2_list(const char *str)
168{
169 int len = strlen(str) + 1;
170 char *dst = xmalloc(len + 1);
171
172 dst[len] = '\0';
173 memcpy(dst, str, len);
174
175 replace(dst, ' ', '\0');
176 return dst;
177}
178
179
180static const char *moderror(int err)
181{
182 switch (err) {
183 case ENOEXEC:
184 return "invalid module format";
185 case ENOENT:
186 return "unknown symbol in module or invalid parameter";
187 case ESRCH:
188 return "module has wrong symbol version";
189 case EINVAL:
190 return "unknown symbol in module or invalid parameter"
191 + sizeof("unknown symbol in module or");
192 default:
193 return strerror(err);
194 }
195}
196
197static int load_module(const char *fname, const char *options)
198{
199#if 1
200 int r;
201 size_t len = MAXINT(ssize_t);
202 char *module_image;
203 dbg1_error_msg("load_module('%s','%s')", fname, options);
204
205 module_image = xmalloc_open_zipped_read_close(fname, &len);
206 r = (!module_image || init_module(module_image, len, options ? options : "") != 0);
207 free(module_image);
208 dbg1_error_msg("load_module:%d", r);
209 return r;
210#else
211
212 dbg1_error_msg("load_module('%s','%s')", fname, options);
213 return 1;
214#endif
215}
216
217static void parse_module(module_info *info, const char *pathname)
218{
219 char *module_image;
220 char *ptr;
221 size_t len;
222 size_t pos;
223 dbg1_error_msg("parse_module('%s')", pathname);
224
225
226 len = 64 * 1024 * 1024;
227 module_image = xmalloc_open_zipped_read_close(pathname, &len);
228
229
230
231
232 reset_stringbuf();
233 pos = 0;
234 while (1) {
235 unsigned start = stringbuf_idx;
236 ptr = find_keyword(module_image + pos, len - pos, "alias=");
237 if (!ptr) {
238 ptr = find_keyword(module_image + pos, len - pos, "__ksymtab_");
239 if (!ptr)
240 break;
241
242
243 if (strcmp(ptr, "gpl") == 0 || strcmp(ptr, "strings") == 0)
244 goto skip;
245 dbg2_error_msg("alias:'symbol:%s'", ptr);
246 append("symbol:");
247 } else {
248 dbg2_error_msg("alias:'%s'", ptr);
249 }
250 append(ptr);
251 appendc(' ');
252
253
254
255
256 if (start) {
257 char *found, *last;
258 stringbuf[stringbuf_idx] = '\0';
259 last = stringbuf + start;
260
261
262
263
264 if (strncmp(stringbuf, last, stringbuf_idx - start) == 0)
265
266 found = stringbuf;
267 else
268
269 found = strstr(stringbuf, last-1);
270 if (found < last-1) {
271
272 dbg2_error_msg("redundant:'%s'", last);
273 stringbuf_idx = start;
274 goto skip;
275 }
276 }
277 skip:
278 pos = (ptr - module_image);
279 }
280 bksp();
281 info->aliases = copy_stringbuf();
282 replace(info->aliases, '-', '_');
283
284
285 reset_stringbuf();
286 ptr = find_keyword(module_image, len, "depends=");
287 if (ptr && *ptr) {
288 replace(ptr, ',', ' ');
289 replace(ptr, '-', '_');
290 dbg2_error_msg("dep:'%s'", ptr);
291 append(ptr);
292 }
293 info->deps = copy_stringbuf();
294
295 free(module_image);
296}
297
298static int pathname_matches_modname(const char *pathname, const char *modname)
299{
300 int r;
301 char name[MODULE_NAME_LEN];
302 const char *fname = bb_get_last_path_component_nostrip(pathname);
303 const char *suffix = strrstr(fname, ".ko");
304 safe_strncpy(name, fname, suffix - fname + 1);
305 replace(name, '-', '_');
306 r = (strcmp(name, modname) == 0);
307 return r;
308}
309
310static FAST_FUNC int fileAction(const char *pathname,
311 struct stat *sb UNUSED_PARAM,
312 void *modname_to_match,
313 int depth UNUSED_PARAM)
314{
315 int cur;
316 const char *fname;
317
318 pathname += 2;
319 fname = bb_get_last_path_component_nostrip(pathname);
320 if (!strrstr(fname, ".ko")) {
321 dbg1_error_msg("'%s' is not a module", pathname);
322 return TRUE;
323 }
324
325 cur = module_count++;
326 modinfo = xrealloc_vector(modinfo, 12, cur);
327 modinfo[cur].pathname = xstrdup(pathname);
328
329
330
331 if (!pathname_matches_modname(fname, modname_to_match)) {
332 dbg1_error_msg("'%s' module name doesn't match", pathname);
333 return TRUE;
334 }
335
336 dbg1_error_msg("'%s' module name matches", pathname);
337 module_found_idx = cur;
338 parse_module(&modinfo[cur], pathname);
339
340 if (!(option_mask32 & OPT_r)) {
341 if (load_module(pathname, module_load_options) == 0) {
342
343
344
345 exit(EXIT_SUCCESS);
346 }
347 }
348
349 return TRUE;
350}
351
352static int load_dep_bb(void)
353{
354 char *line;
355 FILE *fp = fopen_for_read(DEPFILE_BB);
356
357 if (!fp)
358 return 0;
359
360 dep_bb_seen = 1;
361 dbg1_error_msg("loading "DEPFILE_BB);
362
363
364
365
366
367
368
369
370
371
372 module_count = 0;
373 memset(&modinfo[0], 0, sizeof(modinfo[0]));
374
375 while ((line = xmalloc_fgetline(fp)) != NULL) {
376 char* space;
377 char* linebuf;
378 int cur;
379
380 if (!line[0]) {
381 free(line);
382 continue;
383 }
384 space = strchrnul(line, ' ');
385 cur = module_count++;
386 modinfo = xrealloc_vector(modinfo, 12, cur);
387
388 modinfo[cur].pathname = line;
389 if (*space)
390 *space++ = '\0';
391 modinfo[cur].aliases = space;
392 linebuf = xmalloc_fgetline(fp);
393 modinfo[cur].deps = linebuf ? linebuf : xzalloc(1);
394 if (modinfo[cur].deps[0]) {
395
396 line = xmalloc_fgetline(fp);
397
398 if (line && line[0])
399 bb_error_msg_and_die("error in %s at '%s'", DEPFILE_BB, line);
400 free(line);
401 }
402 }
403 return 1;
404}
405
406static int start_dep_bb_writeout(void)
407{
408 int fd;
409
410
411 if (applet_name[0] == 'd' && (option_mask32 & 1))
412 return STDOUT_FILENO;
413
414 fd = open(DEPFILE_BB".new", O_WRONLY | O_CREAT | O_TRUNC | O_EXCL, 0644);
415 if (fd < 0) {
416 if (errno == EEXIST) {
417 int count = 5 * 20;
418 dbg1_error_msg(DEPFILE_BB".new exists, waiting for "DEPFILE_BB);
419 while (1) {
420 usleep(1000*1000 / 20);
421 if (load_dep_bb()) {
422 dbg1_error_msg(DEPFILE_BB" appeared");
423 return -2;
424 }
425 if (!--count)
426 break;
427 }
428 bb_error_msg("deleting stale %s", DEPFILE_BB".new");
429 fd = open_or_warn(DEPFILE_BB".new", O_WRONLY | O_CREAT | O_TRUNC);
430 }
431 }
432 dbg1_error_msg("opened "DEPFILE_BB".new:%d", fd);
433 return fd;
434}
435
436static void write_out_dep_bb(int fd)
437{
438 int i;
439 FILE *fp;
440
441
442 fp = xfdopen_for_write(fd);
443 i = 0;
444 while (modinfo[i].pathname) {
445 fprintf(fp, "%s%s%s\n" "%s%s\n",
446 modinfo[i].pathname, modinfo[i].aliases[0] ? " " : "", modinfo[i].aliases,
447 modinfo[i].deps, modinfo[i].deps[0] ? "\n" : "");
448 i++;
449 }
450
451 errno = 0;
452 if (ferror(fp) | fclose(fp))
453 goto err;
454
455 if (fd == STDOUT_FILENO)
456 goto ok;
457
458 if (rename(DEPFILE_BB".new", DEPFILE_BB) != 0) {
459 err:
460 bb_perror_msg("can't create '%s'", DEPFILE_BB);
461 unlink(DEPFILE_BB".new");
462 } else {
463 ok:
464 wrote_dep_bb_ok = 1;
465 dbg1_error_msg("created "DEPFILE_BB);
466 }
467}
468
469static module_info** find_alias(const char *alias)
470{
471 int i;
472 int dep_bb_fd;
473 int infoidx;
474 module_info **infovec;
475 dbg1_error_msg("find_alias('%s')", alias);
476
477 try_again:
478
479 i = 0;
480 while (modinfo[i].pathname) {
481 if (pathname_matches_modname(modinfo[i].pathname, alias)) {
482 dbg1_error_msg("found '%s' in module '%s'",
483 alias, modinfo[i].pathname);
484 if (!modinfo[i].aliases) {
485 parse_module(&modinfo[i], modinfo[i].pathname);
486 }
487 infovec = xzalloc(2 * sizeof(infovec[0]));
488 infovec[0] = &modinfo[i];
489 return infovec;
490 }
491 i++;
492 }
493
494
495
496 dep_bb_fd = dep_bb_seen ? -1 : start_dep_bb_writeout();
497 if (dep_bb_fd == -2)
498 goto try_again;
499
500
501 i = 0;
502 infoidx = 0;
503 infovec = NULL;
504 while (modinfo[i].pathname) {
505 char *desc, *s;
506 if (!modinfo[i].aliases) {
507 parse_module(&modinfo[i], modinfo[i].pathname);
508 }
509
510 desc = str_2_list(modinfo[i].aliases);
511
512 for (s = desc; *s; s += strlen(s) + 1) {
513
514
515
516
517 if (fnmatch(s, alias, 0) == 0) {
518 dbg1_error_msg("found alias '%s' in module '%s'",
519 alias, modinfo[i].pathname);
520 infovec = xrealloc_vector(infovec, 1, infoidx);
521 infovec[infoidx++] = &modinfo[i];
522 break;
523 }
524 }
525 free(desc);
526 i++;
527 }
528
529
530 if (dep_bb_fd >= 0) {
531 write_out_dep_bb(dep_bb_fd);
532 }
533
534 dbg1_error_msg("find_alias '%s' returns %d results", alias, infoidx);
535 return infovec;
536}
537
538#if ENABLE_FEATURE_MODPROBE_SMALL_CHECK_ALREADY_LOADED
539
540static int already_loaded(const char *name)
541{
542 int ret = 0;
543 char *s;
544 parser_t *parser = config_open2("/proc/modules", xfopen_for_read);
545 while (config_read(parser, &s, 1, 1, "# \t", PARSE_NORMAL & ~PARSE_GREEDY)) {
546 if (strcmp(s, name) == 0) {
547 ret = 1;
548 break;
549 }
550 }
551 config_close(parser);
552 return ret;
553}
554#else
555#define already_loaded(name) is_rmmod
556#endif
557
558
559
560
561
562
563
564#if !ENABLE_FEATURE_MODPROBE_SMALL_OPTIONS_ON_CMDLINE
565#define process_module(a,b) process_module(a)
566#define cmdline_options ""
567#endif
568static void process_module(char *name, const char *cmdline_options)
569{
570 char *s, *deps, *options;
571 module_info **infovec;
572 module_info *info;
573 int infoidx;
574 int is_rmmod = (option_mask32 & OPT_r) != 0;
575
576 dbg1_error_msg("process_module('%s','%s')", name, cmdline_options);
577
578 replace(name, '-', '_');
579
580 dbg1_error_msg("already_loaded:%d is_rmmod:%d", already_loaded(name), is_rmmod);
581
582
583
584
585
586
587 if (!is_rmmod && already_loaded(name)) {
588 dbg1_error_msg("nothing to do for '%s'", name);
589 return;
590 }
591
592 options = NULL;
593 if (!is_rmmod) {
594 char *opt_filename = xasprintf("/etc/modules/%s", name);
595 options = xmalloc_open_read_close(opt_filename, NULL);
596 if (options)
597 replace(options, '\n', ' ');
598#if ENABLE_FEATURE_MODPROBE_SMALL_OPTIONS_ON_CMDLINE
599 if (cmdline_options) {
600
601
602 char *op = xasprintf(options ? "%s %s" : "%s %s" + 3,
603 cmdline_options + 1, options);
604 free(options);
605 options = op;
606 }
607#endif
608 free(opt_filename);
609 module_load_options = options;
610 dbg1_error_msg("process_module('%s'): options:'%s'", name, options);
611 }
612
613 if (!module_count) {
614
615
616
617
618 module_found_idx = -1;
619 recursive_action(".",
620 ACTION_RECURSE,
621 fileAction,
622 NULL,
623 name,
624 0
625 );
626 dbg1_error_msg("dirscan complete");
627
628 if (module_found_idx >= 0) {
629 infovec = xzalloc(2 * sizeof(infovec[0]));
630 infovec[0] = &modinfo[module_found_idx];
631 } else {
632 infovec = find_alias(name);
633 }
634 } else {
635 infovec = find_alias(name);
636 }
637
638 if (!infovec) {
639
640 if (!is_rmmod && applet_name[0] != 'd')
641 bb_error_msg("module '%s' not found", name);
642
643 goto ret;
644 }
645
646
647
648
649
650
651
652
653
654
655 if (is_rmmod) {
656 infoidx = 0;
657 while ((info = infovec[infoidx++]) != NULL) {
658 int r;
659 char modname[MODULE_NAME_LEN];
660
661 filename2modname(
662 bb_get_last_path_component_nostrip(info->pathname), modname);
663 r = delete_module(modname, O_NONBLOCK | O_EXCL);
664 dbg1_error_msg("delete_module('%s', O_NONBLOCK | O_EXCL):%d", modname, r);
665 if (r != 0) {
666 if (!(option_mask32 & OPT_q))
667 bb_perror_msg("remove '%s'", modname);
668 goto ret;
669 }
670 }
671
672 if (applet_name[0] == 'r') {
673
674 goto ret;
675 }
676
677
678
679
680
681
682
683 }
684
685 infoidx = 0;
686 while ((info = infovec[infoidx++]) != NULL) {
687
688 deps = str_2_list(info->deps);
689 for (s = deps; *s; s += strlen(s) + 1) {
690
691 dbg1_error_msg("recurse on dep '%s'", s);
692 process_module(s, NULL);
693 dbg1_error_msg("recurse on dep '%s' done", s);
694 }
695 free(deps);
696
697 if (is_rmmod)
698 continue;
699
700
701 if (options && strstr(options, "blacklist")) {
702 dbg1_error_msg("'%s': blacklisted", info->pathname);
703 continue;
704 }
705 errno = 0;
706 if (load_module(info->pathname, options) != 0) {
707 if (EEXIST != errno) {
708 bb_error_msg("'%s': %s",
709 info->pathname,
710 moderror(errno));
711 } else {
712 dbg1_error_msg("'%s': %s",
713 info->pathname,
714 moderror(errno));
715 }
716 }
717 }
718 ret:
719 free(infovec);
720 free(options);
721
722
723}
724#undef cmdline_options
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826int modprobe_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
827int modprobe_main(int argc UNUSED_PARAM, char **argv)
828{
829 struct utsname uts;
830 char applet0 = applet_name[0];
831 IF_FEATURE_MODPROBE_SMALL_OPTIONS_ON_CMDLINE(char *options;)
832
833
834 if ('l' == applet0) {
835 xprint_and_close_file(xfopen_for_read("/proc/modules"));
836 return EXIT_SUCCESS;
837 }
838
839 INIT_G();
840
841
842 modinfo = xzalloc(sizeof(modinfo[0]));
843
844 if ('i' != applet0) {
845
846 xchdir(CONFIG_DEFAULT_MODULES_DIR);
847 }
848 uname(&uts);
849
850
851 if ('d' == applet0) {
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871 getopt32(argv, "na" "AeF:qru" , NULL);
872 argv += optind;
873
874
875 xchdir(argv[0] ? argv[0] : uts.release);
876
877
878 process_module((char*)"/", NULL);
879 return !wrote_dep_bb_ok;
880 }
881
882
883 opt_complementary = "-1";
884
885
886 getopt32(argv, "qrfsvwb");
887 argv += optind;
888
889
890 if ('r' == applet0) {
891 option_mask32 |= OPT_r;
892 }
893
894 if ('i' != applet0) {
895
896 xchdir(uts.release);
897 }
898
899#if ENABLE_FEATURE_MODPROBE_SMALL_OPTIONS_ON_CMDLINE
900
901
902 options = NULL;
903 if ('r' != applet0) {
904 char **arg = argv;
905 while (*++arg) {
906
907 char *s = options;
908 options = xasprintf("%s \"%s\"", s ? s : "", *arg);
909 free(s);
910 *arg = NULL;
911 }
912 }
913#else
914 if ('r' != applet0)
915 argv[1] = NULL;
916#endif
917
918 if ('i' == applet0) {
919 size_t len;
920 void *map;
921
922 len = MAXINT(ssize_t);
923 map = xmalloc_open_zipped_read_close(*argv, &len);
924 if (!map)
925 bb_perror_msg_and_die("can't read '%s'", *argv);
926 if (init_module(map, len,
927 IF_FEATURE_MODPROBE_SMALL_OPTIONS_ON_CMDLINE(options ? options : "")
928 IF_NOT_FEATURE_MODPROBE_SMALL_OPTIONS_ON_CMDLINE("")
929 ) != 0
930 ) {
931 bb_error_msg_and_die("can't insert '%s': %s",
932 *argv, moderror(errno));
933 }
934 return 0;
935 }
936
937
938 load_dep_bb();
939
940
941
942 do {
943 process_module(*argv, options);
944 } while (*++argv);
945
946 if (ENABLE_FEATURE_CLEAN_UP) {
947 IF_FEATURE_MODPROBE_SMALL_OPTIONS_ON_CMDLINE(free(options);)
948 }
949 return EXIT_SUCCESS;
950}
951