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