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