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