Annotation of /trunk/mkinitrd-magellan/busybox/modutils/modprobe-small.c
Parent Directory | Revision Log
Revision 984 -
(hide annotations)
(download)
Sun May 30 11:32:42 2010 UTC (14 years ago) by niro
File MIME type: text/plain
File size: 22534 byte(s)
Sun May 30 11:32:42 2010 UTC (14 years ago) by niro
File MIME type: text/plain
File size: 22534 byte(s)
-updated to busybox-1.16.1 and enabled blkid/uuid support in default config
1 | niro | 816 | /* 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 | niro | 984 | /* After libbb.h, since it needs sys/types.h on some systems */ |
13 | niro | 816 | #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 | niro | 984 | unsigned module_count; |
48 | niro | 816 | int module_found_idx; |
49 | niro | 984 | unsigned stringbuf_idx; |
50 | unsigned stringbuf_size; | ||
51 | char *stringbuf; /* some modules have lots of stuff */ | ||
52 | niro | 816 | /* for example, drivers/media/video/saa7134/saa7134.ko */ |
53 | niro | 984 | /* therefore having a fixed biggish buffer is not wise */ |
54 | niro | 816 | }; |
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 | niro | 984 | #define stringbuf_size (G.stringbuf_size ) |
64 | niro | 816 | #define stringbuf (G.stringbuf ) |
65 | #define INIT_G() do { \ | ||
66 | SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \ | ||
67 | } while (0) | ||
68 | |||
69 | niro | 984 | 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 | niro | 816 | |
81 | static void appendc(char c) | ||
82 | { | ||
83 | niro | 984 | /* We appendc() only after append(), + 15 trick in append() |
84 | * makes it unnecessary to check for overflow here */ | ||
85 | stringbuf[stringbuf_idx++] = c; | ||
86 | niro | 816 | } |
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 | niro | 984 | char *copy = xzalloc(stringbuf_idx + 1); /* terminating NUL */ |
102 | niro | 816 | 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 | niro | 984 | replace(info->aliases, '-', '_'); |
227 | niro | 816 | |
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 | niro | 984 | char* linebuf; |
323 | niro | 816 | 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 | niro | 984 | linebuf = xmalloc_fgetline(fp); |
338 | modinfo[cur].deps = linebuf ? linebuf : xzalloc(1); | ||
339 | niro | 816 | 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 | niro | 984 | fp = xfdopen_for_write(fd); |
388 | niro | 816 | 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 | niro | 984 | // 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 | niro | 816 | /* 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 | niro | 984 | depmod will output a dependency list suitable for the modprobe utility. |
671 | niro | 816 | 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 | int modprobe_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | ||
689 | int modprobe_main(int argc UNUSED_PARAM, char **argv) | ||
690 | { | ||
691 | struct utsname uts; | ||
692 | char applet0 = applet_name[0]; | ||
693 | niro | 984 | IF_FEATURE_MODPROBE_SMALL_OPTIONS_ON_CMDLINE(char *options;) |
694 | niro | 816 | |
695 | /* are we lsmod? -> just dump /proc/modules */ | ||
696 | if ('l' == applet0) { | ||
697 | xprint_and_close_file(xfopen_for_read("/proc/modules")); | ||
698 | return EXIT_SUCCESS; | ||
699 | } | ||
700 | |||
701 | INIT_G(); | ||
702 | |||
703 | /* Prevent ugly corner cases with no modules at all */ | ||
704 | modinfo = xzalloc(sizeof(modinfo[0])); | ||
705 | |||
706 | if ('i' != applet0) { /* not insmod */ | ||
707 | /* Goto modules directory */ | ||
708 | xchdir(CONFIG_DEFAULT_MODULES_DIR); | ||
709 | } | ||
710 | uname(&uts); /* never fails */ | ||
711 | |||
712 | /* depmod? */ | ||
713 | if ('d' == applet0) { | ||
714 | /* Supported: | ||
715 | * -n: print result to stdout | ||
716 | * -a: process all modules (default) | ||
717 | * optional VERSION parameter | ||
718 | * Ignored: | ||
719 | * -A: do work only if a module is newer than depfile | ||
720 | * -e: report any symbols which a module needs | ||
721 | * which are not supplied by other modules or the kernel | ||
722 | * -F FILE: System.map (symbols for -e) | ||
723 | * -q, -r, -u: noop? | ||
724 | * Not supported: | ||
725 | * -b BASEDIR: (TODO!) modules are in | ||
726 | * $BASEDIR/lib/modules/$VERSION | ||
727 | * -v: human readable deps to stdout | ||
728 | * -V: version (don't want to support it - people may depend | ||
729 | * on it as an indicator of "standard" depmod) | ||
730 | * -h: help (well duh) | ||
731 | * module1.o module2.o parameters (just ignored for now) | ||
732 | */ | ||
733 | getopt32(argv, "na" "AeF:qru" /* "b:vV", NULL */, NULL); | ||
734 | argv += optind; | ||
735 | /* if (argv[0] && argv[1]) bb_show_usage(); */ | ||
736 | /* Goto $VERSION directory */ | ||
737 | xchdir(argv[0] ? argv[0] : uts.release); | ||
738 | /* Force full module scan by asking to find a bogus module. | ||
739 | * This will generate modules.dep.bb as a side effect. */ | ||
740 | process_module((char*)"/", NULL); | ||
741 | return !wrote_dep_bb_ok; | ||
742 | } | ||
743 | |||
744 | /* insmod, modprobe, rmmod require at least one argument */ | ||
745 | opt_complementary = "-1"; | ||
746 | /* only -q (quiet) and -r (rmmod), | ||
747 | * the rest are accepted and ignored (compat) */ | ||
748 | getopt32(argv, "qrfsvw"); | ||
749 | argv += optind; | ||
750 | |||
751 | /* are we rmmod? -> simulate modprobe -r */ | ||
752 | if ('r' == applet0) { | ||
753 | option_mask32 |= OPT_r; | ||
754 | } | ||
755 | |||
756 | if ('i' != applet0) { /* not insmod */ | ||
757 | /* Goto $VERSION directory */ | ||
758 | xchdir(uts.release); | ||
759 | } | ||
760 | |||
761 | #if ENABLE_FEATURE_MODPROBE_SMALL_OPTIONS_ON_CMDLINE | ||
762 | /* If not rmmod, parse possible module options given on command line. | ||
763 | * insmod/modprobe takes one module name, the rest are parameters. */ | ||
764 | options = NULL; | ||
765 | if ('r' != applet0) { | ||
766 | char **arg = argv; | ||
767 | while (*++arg) { | ||
768 | /* Enclose options in quotes */ | ||
769 | char *s = options; | ||
770 | options = xasprintf("%s \"%s\"", s ? s : "", *arg); | ||
771 | free(s); | ||
772 | *arg = NULL; | ||
773 | } | ||
774 | } | ||
775 | #else | ||
776 | if ('r' != applet0) | ||
777 | argv[1] = NULL; | ||
778 | #endif | ||
779 | |||
780 | if ('i' == applet0) { /* insmod */ | ||
781 | size_t len; | ||
782 | void *map; | ||
783 | |||
784 | len = MAXINT(ssize_t); | ||
785 | map = xmalloc_xopen_read_close(*argv, &len); | ||
786 | if (init_module(map, len, | ||
787 | niro | 984 | IF_FEATURE_MODPROBE_SMALL_OPTIONS_ON_CMDLINE(options ? options : "") |
788 | IF_NOT_FEATURE_MODPROBE_SMALL_OPTIONS_ON_CMDLINE("") | ||
789 | niro | 816 | ) != 0) |
790 | niro | 984 | bb_error_msg_and_die("can't insert '%s': %s", |
791 | niro | 816 | *argv, moderror(errno)); |
792 | return 0; | ||
793 | } | ||
794 | |||
795 | /* Try to load modprobe.dep.bb */ | ||
796 | load_dep_bb(); | ||
797 | |||
798 | /* Load/remove modules. | ||
799 | * Only rmmod loops here, modprobe has only argv[0] */ | ||
800 | do { | ||
801 | process_module(*argv++, options); | ||
802 | } while (*argv); | ||
803 | |||
804 | if (ENABLE_FEATURE_CLEAN_UP) { | ||
805 | niro | 984 | IF_FEATURE_MODPROBE_SMALL_OPTIONS_ON_CMDLINE(free(options);) |
806 | niro | 816 | } |
807 | return EXIT_SUCCESS; | ||
808 | } |