hush: preparatory patch, no code changes
[oweals/busybox.git] / modutils / modprobe-small.c
1 /* vi: set sw=4 ts=4: */
2 /*
3  * simplified modprobe
4  *
5  * Copyright (c) 2008 Vladimir Dronnikov
6  * Copyright (c) 2008 Bernhard Reutner-Fischer (initial depmod code)
7  *
8  * Licensed under GPLv2, see file LICENSE in this source tree.
9  */
10
11 #include "libbb.h"
12 /* After libbb.h, since it needs sys/types.h on some systems */
13 #include <sys/utsname.h> /* uname() */
14 #include <fnmatch.h>
15
16 extern int init_module(void *module, unsigned long len, const char *options);
17 extern int delete_module(const char *module, unsigned flags);
18 extern int query_module(const char *name, int which, void *buf, size_t bufsize, size_t *ret);
19
20
21 #if 1
22 # define dbg1_error_msg(...) ((void)0)
23 # define dbg2_error_msg(...) ((void)0)
24 #else
25 # define dbg1_error_msg(...) bb_error_msg(__VA_ARGS__)
26 # define dbg2_error_msg(...) bb_error_msg(__VA_ARGS__)
27 #endif
28
29 #define DEPFILE_BB CONFIG_DEFAULT_DEPMOD_FILE".bb"
30
31 enum {
32         OPT_q = (1 << 0), /* be quiet */
33         OPT_r = (1 << 1), /* module removal instead of loading */
34 };
35
36 typedef struct module_info {
37         char *pathname;
38         char *aliases;
39         char *deps;
40 } module_info;
41
42 /*
43  * GLOBALS
44  */
45 struct globals {
46         module_info *modinfo;
47         char *module_load_options;
48         smallint dep_bb_seen;
49         smallint wrote_dep_bb_ok;
50         unsigned module_count;
51         int module_found_idx;
52         unsigned stringbuf_idx;
53         unsigned stringbuf_size;
54         char *stringbuf; /* some modules have lots of stuff */
55         /* for example, drivers/media/video/saa7134/saa7134.ko */
56         /* therefore having a fixed biggish buffer is not wise */
57 };
58 #define G (*ptr_to_globals)
59 #define modinfo             (G.modinfo            )
60 #define dep_bb_seen         (G.dep_bb_seen        )
61 #define wrote_dep_bb_ok     (G.wrote_dep_bb_ok    )
62 #define module_count        (G.module_count       )
63 #define module_found_idx    (G.module_found_idx   )
64 #define module_load_options (G.module_load_options)
65 #define stringbuf_idx       (G.stringbuf_idx      )
66 #define stringbuf_size      (G.stringbuf_size     )
67 #define stringbuf           (G.stringbuf          )
68 #define INIT_G() do { \
69         SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
70 } while (0)
71
72 static void append(const char *s)
73 {
74         unsigned len = strlen(s);
75         if (stringbuf_idx + len + 15 > stringbuf_size) {
76                 stringbuf_size = stringbuf_idx + len + 127;
77                 dbg2_error_msg("grow stringbuf to %u", stringbuf_size);
78                 stringbuf = xrealloc(stringbuf, stringbuf_size);
79         }
80         memcpy(stringbuf + stringbuf_idx, s, len);
81         stringbuf_idx += len;
82 }
83
84 static void appendc(char c)
85 {
86         /* We appendc() only after append(), + 15 trick in append()
87          * makes it unnecessary to check for overflow here */
88         stringbuf[stringbuf_idx++] = c;
89 }
90
91 static void bksp(void)
92 {
93         if (stringbuf_idx)
94                 stringbuf_idx--;
95 }
96
97 static void reset_stringbuf(void)
98 {
99         stringbuf_idx = 0;
100 }
101
102 static char* copy_stringbuf(void)
103 {
104         char *copy = xzalloc(stringbuf_idx + 1); /* terminating NUL */
105         return memcpy(copy, stringbuf, stringbuf_idx);
106 }
107
108 static char* find_keyword(char *ptr, size_t len, const char *word)
109 {
110         int wlen;
111
112         if (!ptr) /* happens if xmalloc_open_zipped_read_close cannot read it */
113                 return NULL;
114
115         wlen = strlen(word);
116         len -= wlen - 1;
117         while ((ssize_t)len > 0) {
118                 char *old = ptr;
119                 /* search for the first char in word */
120                 ptr = memchr(ptr, *word, len);
121                 if (ptr == NULL) /* no occurance left, done */
122                         break;
123                 if (strncmp(ptr, word, wlen) == 0)
124                         return ptr + wlen; /* found, return ptr past it */
125                 ++ptr;
126                 len -= (ptr - old);
127         }
128         return NULL;
129 }
130
131 static void replace(char *s, char what, char with)
132 {
133         while (*s) {
134                 if (what == *s)
135                         *s = with;
136                 ++s;
137         }
138 }
139
140 /* Take "word word", return malloced "word",NUL,"word",NUL,NUL */
141 static char* str_2_list(const char *str)
142 {
143         int len = strlen(str) + 1;
144         char *dst = xmalloc(len + 1);
145
146         dst[len] = '\0';
147         memcpy(dst, str, len);
148 //TODO: protect against 2+ spaces: "word  word"
149         replace(dst, ' ', '\0');
150         return dst;
151 }
152
153 /* We use error numbers in a loose translation... */
154 static const char *moderror(int err)
155 {
156         switch (err) {
157         case ENOEXEC:
158                 return "invalid module format";
159         case ENOENT:
160                 return "unknown symbol in module or invalid parameter";
161         case ESRCH:
162                 return "module has wrong symbol version";
163         case EINVAL: /* "invalid parameter" */
164                 return "unknown symbol in module or invalid parameter"
165                 + sizeof("unknown symbol in module or");
166         default:
167                 return strerror(err);
168         }
169 }
170
171 static int load_module(const char *fname, const char *options)
172 {
173 #if 1
174         int r;
175         size_t len = MAXINT(ssize_t);
176         char *module_image;
177         dbg1_error_msg("load_module('%s','%s')", fname, options);
178
179         module_image = xmalloc_open_zipped_read_close(fname, &len);
180         r = (!module_image || init_module(module_image, len, options ? options : "") != 0);
181         free(module_image);
182         dbg1_error_msg("load_module:%d", r);
183         return r; /* 0 = success */
184 #else
185         /* For testing */
186         dbg1_error_msg("load_module('%s','%s')", fname, options);
187         return 1;
188 #endif
189 }
190
191 static void parse_module(module_info *info, const char *pathname)
192 {
193         char *module_image;
194         char *ptr;
195         size_t len;
196         size_t pos;
197         dbg1_error_msg("parse_module('%s')", pathname);
198
199         /* Read (possibly compressed) module */
200         len = 64 * 1024 * 1024; /* 64 Mb at most */
201         module_image = xmalloc_open_zipped_read_close(pathname, &len);
202 //TODO: optimize redundant module body reads
203
204         /* "alias1 symbol:sym1 alias2 symbol:sym2" */
205         reset_stringbuf();
206         pos = 0;
207         while (1) {
208                 ptr = find_keyword(module_image + pos, len - pos, "alias=");
209                 if (!ptr) {
210                         ptr = find_keyword(module_image + pos, len - pos, "__ksymtab_");
211                         if (!ptr)
212                                 break;
213                         /* DOCME: __ksymtab_gpl and __ksymtab_strings occur
214                          * in many modules. What do they mean? */
215                         if (strcmp(ptr, "gpl") == 0 || strcmp(ptr, "strings") == 0)
216                                 goto skip;
217                         dbg2_error_msg("alias:'symbol:%s'", ptr);
218                         append("symbol:");
219                 } else {
220                         dbg2_error_msg("alias:'%s'", ptr);
221                 }
222                 append(ptr);
223                 appendc(' ');
224  skip:
225                 pos = (ptr - module_image);
226         }
227         bksp(); /* remove last ' ' */
228         info->aliases = copy_stringbuf();
229         replace(info->aliases, '-', '_');
230
231         /* "dependency1 depandency2" */
232         reset_stringbuf();
233         ptr = find_keyword(module_image, len, "depends=");
234         if (ptr && *ptr) {
235                 replace(ptr, ',', ' ');
236                 replace(ptr, '-', '_');
237                 dbg2_error_msg("dep:'%s'", ptr);
238                 append(ptr);
239         }
240         info->deps = copy_stringbuf();
241
242         free(module_image);
243 }
244
245 static int pathname_matches_modname(const char *pathname, const char *modname)
246 {
247         const char *fname = bb_get_last_path_component_nostrip(pathname);
248         const char *suffix = strrstr(fname, ".ko");
249 //TODO: can do without malloc?
250         char *name = xstrndup(fname, suffix - fname);
251         int r;
252         replace(name, '-', '_');
253         r = (strcmp(name, modname) == 0);
254         free(name);
255         return r;
256 }
257
258 static FAST_FUNC int fileAction(const char *pathname,
259                 struct stat *sb UNUSED_PARAM,
260                 void *modname_to_match,
261                 int depth UNUSED_PARAM)
262 {
263         int cur;
264         const char *fname;
265
266         pathname += 2; /* skip "./" */
267         fname = bb_get_last_path_component_nostrip(pathname);
268         if (!strrstr(fname, ".ko")) {
269                 dbg1_error_msg("'%s' is not a module", pathname);
270                 return TRUE; /* not a module, continue search */
271         }
272
273         cur = module_count++;
274         modinfo = xrealloc_vector(modinfo, 12, cur);
275         modinfo[cur].pathname = xstrdup(pathname);
276         /*modinfo[cur].aliases = NULL; - xrealloc_vector did it */
277         /*modinfo[cur+1].pathname = NULL;*/
278
279         if (!pathname_matches_modname(fname, modname_to_match)) {
280                 dbg1_error_msg("'%s' module name doesn't match", pathname);
281                 return TRUE; /* module name doesn't match, continue search */
282         }
283
284         dbg1_error_msg("'%s' module name matches", pathname);
285         module_found_idx = cur;
286         parse_module(&modinfo[cur], pathname);
287
288         if (!(option_mask32 & OPT_r)) {
289                 if (load_module(pathname, module_load_options) == 0) {
290                         /* Load was successful, there is nothing else to do.
291                          * This can happen ONLY for "top-level" module load,
292                          * not a dep, because deps dont do dirscan. */
293                         exit(EXIT_SUCCESS);
294                 }
295         }
296
297         return TRUE;
298 }
299
300 static int load_dep_bb(void)
301 {
302         char *line;
303         FILE *fp = fopen_for_read(DEPFILE_BB);
304
305         if (!fp)
306                 return 0;
307
308         dep_bb_seen = 1;
309         dbg1_error_msg("loading "DEPFILE_BB);
310
311         /* Why? There is a rare scenario: we did not find modprobe.dep.bb,
312          * we scanned the dir and found no module by name, then we search
313          * for alias (full scan), and we decided to generate modprobe.dep.bb.
314          * But we see modprobe.dep.bb.new! Other modprobe is at work!
315          * We wait and other modprobe renames it to modprobe.dep.bb.
316          * Now we can use it.
317          * But we already have modinfo[] filled, and "module_count = 0"
318          * makes us start anew. Yes, we leak modinfo[].xxx pointers -
319          * there is not much of data there anyway. */
320         module_count = 0;
321         memset(&modinfo[0], 0, sizeof(modinfo[0]));
322
323         while ((line = xmalloc_fgetline(fp)) != NULL) {
324                 char* space;
325                 char* linebuf;
326                 int cur;
327
328                 if (!line[0]) {
329                         free(line);
330                         continue;
331                 }
332                 space = strchrnul(line, ' ');
333                 cur = module_count++;
334                 modinfo = xrealloc_vector(modinfo, 12, cur);
335                 /*modinfo[cur+1].pathname = NULL; - xrealloc_vector did it */
336                 modinfo[cur].pathname = line; /* we take ownership of malloced block here */
337                 if (*space)
338                         *space++ = '\0';
339                 modinfo[cur].aliases = space;
340                 linebuf = xmalloc_fgetline(fp);
341                 modinfo[cur].deps = linebuf ? linebuf : xzalloc(1);
342                 if (modinfo[cur].deps[0]) {
343                         /* deps are not "", so next line must be empty */
344                         line = xmalloc_fgetline(fp);
345                         /* Refuse to work with damaged config file */
346                         if (line && line[0])
347                                 bb_error_msg_and_die("error in %s at '%s'", DEPFILE_BB, line);
348                         free(line);
349                 }
350         }
351         return 1;
352 }
353
354 static int start_dep_bb_writeout(void)
355 {
356         int fd;
357
358         /* depmod -n: write result to stdout */
359         if (applet_name[0] == 'd' && (option_mask32 & 1))
360                 return STDOUT_FILENO;
361
362         fd = open(DEPFILE_BB".new", O_WRONLY | O_CREAT | O_TRUNC | O_EXCL, 0644);
363         if (fd < 0) {
364                 if (errno == EEXIST) {
365                         int count = 5 * 20;
366                         dbg1_error_msg(DEPFILE_BB".new exists, waiting for "DEPFILE_BB);
367                         while (1) {
368                                 usleep(1000*1000 / 20);
369                                 if (load_dep_bb()) {
370                                         dbg1_error_msg(DEPFILE_BB" appeared");
371                                         return -2; /* magic number */
372                                 }
373                                 if (!--count)
374                                         break;
375                         }
376                         bb_error_msg("deleting stale %s", DEPFILE_BB".new");
377                         fd = open_or_warn(DEPFILE_BB".new", O_WRONLY | O_CREAT | O_TRUNC);
378                 }
379         }
380         dbg1_error_msg("opened "DEPFILE_BB".new:%d", fd);
381         return fd;
382 }
383
384 static void write_out_dep_bb(int fd)
385 {
386         int i;
387         FILE *fp;
388
389         /* We want good error reporting. fdprintf is not good enough. */
390         fp = xfdopen_for_write(fd);
391         i = 0;
392         while (modinfo[i].pathname) {
393                 fprintf(fp, "%s%s%s\n" "%s%s\n",
394                         modinfo[i].pathname, modinfo[i].aliases[0] ? " " : "", modinfo[i].aliases,
395                         modinfo[i].deps, modinfo[i].deps[0] ? "\n" : "");
396                 i++;
397         }
398         /* Badly formatted depfile is a no-no. Be paranoid. */
399         errno = 0;
400         if (ferror(fp) | fclose(fp)) /* | instead of || is intended */
401                 goto err;
402
403         if (fd == STDOUT_FILENO) /* it was depmod -n */
404                 goto ok;
405
406         if (rename(DEPFILE_BB".new", DEPFILE_BB) != 0) {
407  err:
408                 bb_perror_msg("can't create '%s'", DEPFILE_BB);
409                 unlink(DEPFILE_BB".new");
410         } else {
411  ok:
412                 wrote_dep_bb_ok = 1;
413                 dbg1_error_msg("created "DEPFILE_BB);
414         }
415 }
416
417 static module_info* find_alias(const char *alias)
418 {
419         int i;
420         int dep_bb_fd;
421         module_info *result;
422         dbg1_error_msg("find_alias('%s')", alias);
423
424  try_again:
425         /* First try to find by name (cheaper) */
426         i = 0;
427         while (modinfo[i].pathname) {
428                 if (pathname_matches_modname(modinfo[i].pathname, alias)) {
429                         dbg1_error_msg("found '%s' in module '%s'",
430                                         alias, modinfo[i].pathname);
431                         if (!modinfo[i].aliases) {
432                                 parse_module(&modinfo[i], modinfo[i].pathname);
433                         }
434                         return &modinfo[i];
435                 }
436                 i++;
437         }
438
439         /* Ok, we definitely have to scan module bodies. This is a good
440          * moment to generate modprobe.dep.bb, if it does not exist yet */
441         dep_bb_fd = dep_bb_seen ? -1 : start_dep_bb_writeout();
442         if (dep_bb_fd == -2) /* modprobe.dep.bb appeared? */
443                 goto try_again;
444
445         /* Scan all module bodies, extract modinfo (it contains aliases) */
446         i = 0;
447         result = NULL;
448         while (modinfo[i].pathname) {
449                 char *desc, *s;
450                 if (!modinfo[i].aliases) {
451                         parse_module(&modinfo[i], modinfo[i].pathname);
452                 }
453                 if (result) {
454                         i++;
455                         continue;
456                 }
457                 /* "alias1 symbol:sym1 alias2 symbol:sym2" */
458                 desc = str_2_list(modinfo[i].aliases);
459                 /* Does matching substring exist? */
460                 for (s = desc; *s; s += strlen(s) + 1) {
461                         /* Aliases in module bodies can be defined with
462                          * shell patterns. Example:
463                          * "pci:v000010DEd000000D9sv*sd*bc*sc*i*".
464                          * Plain strcmp() won't catch that */
465                         if (fnmatch(s, alias, 0) == 0) {
466                                 dbg1_error_msg("found alias '%s' in module '%s'",
467                                                 alias, modinfo[i].pathname);
468                                 result = &modinfo[i];
469                                 break;
470                         }
471                 }
472                 free(desc);
473                 if (result && dep_bb_fd < 0)
474                         return result;
475                 i++;
476         }
477
478         /* Create module.dep.bb if needed */
479         if (dep_bb_fd >= 0) {
480                 write_out_dep_bb(dep_bb_fd);
481         }
482
483         dbg1_error_msg("find_alias '%s' returns %p", alias, result);
484         return result;
485 }
486
487 #if ENABLE_FEATURE_MODPROBE_SMALL_CHECK_ALREADY_LOADED
488 // TODO: open only once, invent config_rewind()
489 static int already_loaded(const char *name)
490 {
491         int ret = 0;
492         char *s;
493         parser_t *parser = config_open2("/proc/modules", xfopen_for_read);
494         while (config_read(parser, &s, 1, 1, "# \t", PARSE_NORMAL & ~PARSE_GREEDY)) {
495                 if (strcmp(s, name) == 0) {
496                         ret = 1;
497                         break;
498                 }
499         }
500         config_close(parser);
501         return ret;
502 }
503 #else
504 #define already_loaded(name) is_rmmod
505 #endif
506
507 /*
508  * Given modules definition and module name (or alias, or symbol)
509  * load/remove the module respecting dependencies.
510  * NB: also called by depmod with bogus name "/",
511  * just in order to force modprobe.dep.bb creation.
512 */
513 #if !ENABLE_FEATURE_MODPROBE_SMALL_OPTIONS_ON_CMDLINE
514 #define process_module(a,b) process_module(a)
515 #define cmdline_options ""
516 #endif
517 static void process_module(char *name, const char *cmdline_options)
518 {
519         char *s, *deps, *options;
520         module_info *info;
521         int is_rmmod = (option_mask32 & OPT_r) != 0;
522         dbg1_error_msg("process_module('%s','%s')", name, cmdline_options);
523
524         replace(name, '-', '_');
525
526         dbg1_error_msg("already_loaded:%d is_rmmod:%d", already_loaded(name), is_rmmod);
527         if (already_loaded(name) != is_rmmod) {
528                 dbg1_error_msg("nothing to do for '%s'", name);
529                 return;
530         }
531
532         options = NULL;
533         if (!is_rmmod) {
534                 char *opt_filename = xasprintf("/etc/modules/%s", name);
535                 options = xmalloc_open_read_close(opt_filename, NULL);
536                 if (options)
537                         replace(options, '\n', ' ');
538 #if ENABLE_FEATURE_MODPROBE_SMALL_OPTIONS_ON_CMDLINE
539                 if (cmdline_options) {
540                         /* NB: cmdline_options always have one leading ' '
541                          * (see main()), we remove it here */
542                         char *op = xasprintf(options ? "%s %s" : "%s %s" + 3,
543                                                 cmdline_options + 1, options);
544                         free(options);
545                         options = op;
546                 }
547 #endif
548                 free(opt_filename);
549                 module_load_options = options;
550                 dbg1_error_msg("process_module('%s'): options:'%s'", name, options);
551         }
552
553         if (!module_count) {
554                 /* Scan module directory. This is done only once.
555                  * It will attempt module load, and will exit(EXIT_SUCCESS)
556                  * on success. */
557                 module_found_idx = -1;
558                 recursive_action(".",
559                         ACTION_RECURSE, /* flags */
560                         fileAction, /* file action */
561                         NULL, /* dir action */
562                         name, /* user data */
563                         0); /* depth */
564                 dbg1_error_msg("dirscan complete");
565                 /* Module was not found, or load failed, or is_rmmod */
566                 if (module_found_idx >= 0) { /* module was found */
567                         info = &modinfo[module_found_idx];
568                 } else { /* search for alias, not a plain module name */
569                         info = find_alias(name);
570                 }
571         } else {
572                 info = find_alias(name);
573         }
574
575 // Problem here: there can be more than one module
576 // for the given alias. For example,
577 // "pci:v00008086d00007010sv00000000sd00000000bc01sc01i80" matches
578 // ata_piix because it has an alias "pci:v00008086d00007010sv*sd*bc*sc*i*"
579 // and ata_generic, it has an alias "alias=pci:v*d*sv*sd*bc01sc01i*"
580 // Standard modprobe would load them both.
581 // In this code, find_alias() returns only the first matching module.
582
583         /* rmmod? unload it by name */
584         if (is_rmmod) {
585                 if (delete_module(name, O_NONBLOCK | O_EXCL) != 0) {
586                         if (!(option_mask32 & OPT_q))
587                                 bb_perror_msg("remove '%s'", name);
588                         goto ret;
589                 }
590                 /* N.B. we do not stop here -
591                  * continue to unload modules on which the module depends:
592                  * "-r --remove: option causes modprobe to remove a module.
593                  * If the modules it depends on are also unused, modprobe
594                  * will try to remove them, too." */
595         }
596
597         if (!info) {
598                 /* both dirscan and find_alias found nothing */
599                 if (!is_rmmod && applet_name[0] != 'd') /* it wasn't rmmod or depmod */
600                         bb_error_msg("module '%s' not found", name);
601 //TODO: _and_die()? or should we continue (un)loading modules listed on cmdline?
602                 goto ret;
603         }
604
605         /* Iterate thru dependencies, trying to (un)load them */
606         deps = str_2_list(info->deps);
607         for (s = deps; *s; s += strlen(s) + 1) {
608                 //if (strcmp(name, s) != 0) // N.B. do loops exist?
609                 dbg1_error_msg("recurse on dep '%s'", s);
610                 process_module(s, NULL);
611                 dbg1_error_msg("recurse on dep '%s' done", s);
612         }
613         free(deps);
614
615         /* modprobe -> load it */
616         if (!is_rmmod) {
617                 if (!options || strstr(options, "blacklist") == NULL) {
618                         errno = 0;
619                         if (load_module(info->pathname, options) != 0) {
620                                 if (EEXIST != errno) {
621                                         bb_error_msg("'%s': %s",
622                                                 info->pathname,
623                                                 moderror(errno));
624                                 } else {
625                                         dbg1_error_msg("'%s': %s",
626                                                 info->pathname,
627                                                 moderror(errno));
628                                 }
629                         }
630                 } else {
631                         dbg1_error_msg("'%s': blacklisted", info->pathname);
632                 }
633         }
634  ret:
635         free(options);
636 //TODO: return load attempt result from process_module.
637 //If dep didn't load ok, continuing makes little sense.
638 }
639 #undef cmdline_options
640
641
642 /* For reference, module-init-tools v3.4 options:
643
644 # insmod
645 Usage: insmod filename [args]
646
647 # rmmod --help
648 Usage: rmmod [-fhswvV] modulename ...
649  -f (or --force) forces a module unload, and may crash your
650     machine. This requires the Forced Module Removal option
651     when the kernel was compiled.
652  -h (or --help) prints this help text
653  -s (or --syslog) says use syslog, not stderr
654  -v (or --verbose) enables more messages
655  -V (or --version) prints the version code
656  -w (or --wait) begins module removal even if it is used
657     and will stop new users from accessing the module (so it
658     should eventually fall to zero).
659
660 # modprobe
661 Usage: modprobe [-v] [-V] [-C config-file] [-n] [-i] [-q] [-b]
662     [-o <modname>] [ --dump-modversions ] <modname> [parameters...]
663 modprobe -r [-n] [-i] [-v] <modulename> ...
664 modprobe -l -t <dirname> [ -a <modulename> ...]
665
666 # depmod --help
667 depmod 3.4 -- part of module-init-tools
668 depmod -[aA] [-n -e -v -q -V -r -u]
669       [-b basedirectory] [forced_version]
670 depmod [-n -e -v -q -r -u] [-F kernelsyms] module1.ko module2.ko ...
671 If no arguments (except options) are given, "depmod -a" is assumed.
672 depmod will output a dependency list suitable for the modprobe utility.
673 Options:
674     -a, --all           Probe all modules
675     -A, --quick         Only does the work if there's a new module
676     -n, --show          Write the dependency file on stdout only
677     -e, --errsyms       Report not supplied symbols
678     -V, --version       Print the release version
679     -v, --verbose       Enable verbose mode
680     -h, --help          Print this usage message
681 The following options are useful for people managing distributions:
682     -b basedirectory
683     --basedir basedirectory
684                         Use an image of a module tree
685     -F kernelsyms
686     --filesyms kernelsyms
687                         Use the file instead of the current kernel symbols
688 */
689
690 //usage:#if ENABLE_MODPROBE_SMALL
691 //usage:#define modprobe_trivial_usage
692 //usage:        "[-qfwrsv] MODULE [symbol=value]..."
693 //usage:#define modprobe_full_usage "\n\n"
694 //usage:       "Options:"
695 //usage:     "\n        -r      Remove MODULE (stacks) or do autoclean"
696 //usage:     "\n        -q      Quiet"
697 //usage:     "\n        -v      Verbose"
698 //usage:     "\n        -f      Force"
699 //usage:     "\n        -w      Wait for unload"
700 //usage:     "\n        -s      Report via syslog instead of stderr"
701 //usage:#endif /* ENABLE_MODPROBE_SMALL */
702
703 int modprobe_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
704 int modprobe_main(int argc UNUSED_PARAM, char **argv)
705 {
706         struct utsname uts;
707         char applet0 = applet_name[0];
708         IF_FEATURE_MODPROBE_SMALL_OPTIONS_ON_CMDLINE(char *options;)
709
710         /* are we lsmod? -> just dump /proc/modules */
711         if ('l' == applet0) {
712                 xprint_and_close_file(xfopen_for_read("/proc/modules"));
713                 return EXIT_SUCCESS;
714         }
715
716         INIT_G();
717
718         /* Prevent ugly corner cases with no modules at all */
719         modinfo = xzalloc(sizeof(modinfo[0]));
720
721         if ('i' != applet0) { /* not insmod */
722                 /* Goto modules directory */
723                 xchdir(CONFIG_DEFAULT_MODULES_DIR);
724         }
725         uname(&uts); /* never fails */
726
727         /* depmod? */
728         if ('d' == applet0) {
729                 /* Supported:
730                  * -n: print result to stdout
731                  * -a: process all modules (default)
732                  * optional VERSION parameter
733                  * Ignored:
734                  * -A: do work only if a module is newer than depfile
735                  * -e: report any symbols which a module needs
736                  *  which are not supplied by other modules or the kernel
737                  * -F FILE: System.map (symbols for -e)
738                  * -q, -r, -u: noop?
739                  * Not supported:
740                  * -b BASEDIR: (TODO!) modules are in
741                  *  $BASEDIR/lib/modules/$VERSION
742                  * -v: human readable deps to stdout
743                  * -V: version (don't want to support it - people may depend
744                  *  on it as an indicator of "standard" depmod)
745                  * -h: help (well duh)
746                  * module1.o module2.o parameters (just ignored for now)
747                  */
748                 getopt32(argv, "na" "AeF:qru" /* "b:vV", NULL */, NULL);
749                 argv += optind;
750                 /* if (argv[0] && argv[1]) bb_show_usage(); */
751                 /* Goto $VERSION directory */
752                 xchdir(argv[0] ? argv[0] : uts.release);
753                 /* Force full module scan by asking to find a bogus module.
754                  * This will generate modules.dep.bb as a side effect. */
755                 process_module((char*)"/", NULL);
756                 return !wrote_dep_bb_ok;
757         }
758
759         /* insmod, modprobe, rmmod require at least one argument */
760         opt_complementary = "-1";
761         /* only -q (quiet) and -r (rmmod),
762          * the rest are accepted and ignored (compat) */
763         getopt32(argv, "qrfsvw");
764         argv += optind;
765
766         /* are we rmmod? -> simulate modprobe -r */
767         if ('r' == applet0) {
768                 option_mask32 |= OPT_r;
769         }
770
771         if ('i' != applet0) { /* not insmod */
772                 /* Goto $VERSION directory */
773                 xchdir(uts.release);
774         }
775
776 #if ENABLE_FEATURE_MODPROBE_SMALL_OPTIONS_ON_CMDLINE
777         /* If not rmmod, parse possible module options given on command line.
778          * insmod/modprobe takes one module name, the rest are parameters. */
779         options = NULL;
780         if ('r' != applet0) {
781                 char **arg = argv;
782                 while (*++arg) {
783                         /* Enclose options in quotes */
784                         char *s = options;
785                         options = xasprintf("%s \"%s\"", s ? s : "", *arg);
786                         free(s);
787                         *arg = NULL;
788                 }
789         }
790 #else
791         if ('r' != applet0)
792                 argv[1] = NULL;
793 #endif
794
795         if ('i' == applet0) { /* insmod */
796                 size_t len;
797                 void *map;
798
799                 len = MAXINT(ssize_t);
800                 map = xmalloc_xopen_read_close(*argv, &len);
801                 if (init_module(map, len,
802                         IF_FEATURE_MODPROBE_SMALL_OPTIONS_ON_CMDLINE(options ? options : "")
803                         IF_NOT_FEATURE_MODPROBE_SMALL_OPTIONS_ON_CMDLINE("")
804                                 ) != 0)
805                         bb_error_msg_and_die("can't insert '%s': %s",
806                                         *argv, moderror(errno));
807                 return 0;
808         }
809
810         /* Try to load modprobe.dep.bb */
811         load_dep_bb();
812
813         /* Load/remove modules.
814          * Only rmmod loops here, modprobe has only argv[0] */
815         do {
816                 process_module(*argv, options);
817         } while (*++argv);
818
819         if (ENABLE_FEATURE_CLEAN_UP) {
820                 IF_FEATURE_MODPROBE_SMALL_OPTIONS_ON_CMDLINE(free(options);)
821         }
822         return EXIT_SUCCESS;
823 }