diff options
Diffstat (limited to 'modutils/modprobe.c')
-rw-r--r-- | modutils/modprobe.c | 159 |
1 files changed, 95 insertions, 64 deletions
diff --git a/modutils/modprobe.c b/modutils/modprobe.c index d000c9123..cfc16cb34 100644 --- a/modutils/modprobe.c +++ b/modutils/modprobe.c | |||
@@ -9,10 +9,9 @@ | |||
9 | */ | 9 | */ |
10 | 10 | ||
11 | /* Note that unlike older versions of modules.dep/depmod (busybox and m-i-t), | 11 | /* Note that unlike older versions of modules.dep/depmod (busybox and m-i-t), |
12 | * we expect the full dependency list to be specified in modules.dep. Older | 12 | * we expect the full dependency list to be specified in modules.dep. |
13 | * versions would only export the direct dependency list. | 13 | * Older versions would only export the direct dependency list. |
14 | */ | 14 | */ |
15 | |||
16 | #include "libbb.h" | 15 | #include "libbb.h" |
17 | #include "modutils.h" | 16 | #include "modutils.h" |
18 | #include <sys/utsname.h> | 17 | #include <sys/utsname.h> |
@@ -21,11 +20,11 @@ | |||
21 | //#define DBG(fmt, ...) bb_error_msg("%s: " fmt, __func__, ## __VA_ARGS__) | 20 | //#define DBG(fmt, ...) bb_error_msg("%s: " fmt, __func__, ## __VA_ARGS__) |
22 | #define DBG(...) ((void)0) | 21 | #define DBG(...) ((void)0) |
23 | 22 | ||
24 | #define MODULE_FLAG_LOADED 0x0001 | 23 | #define MODULE_FLAG_LOADED 0x0001 |
25 | #define MODULE_FLAG_NEED_DEPS 0x0002 | 24 | #define MODULE_FLAG_NEED_DEPS 0x0002 |
26 | /* "was seen in modules.dep": */ | 25 | /* "was seen in modules.dep": */ |
27 | #define MODULE_FLAG_FOUND_IN_MODDEP 0x0004 | 26 | #define MODULE_FLAG_FOUND_IN_MODDEP 0x0004 |
28 | #define MODULE_FLAG_BLACKLISTED 0x0008 | 27 | #define MODULE_FLAG_BLACKLISTED 0x0008 |
29 | 28 | ||
30 | struct module_entry { /* I'll call it ME. */ | 29 | struct module_entry { /* I'll call it ME. */ |
31 | unsigned flags; | 30 | unsigned flags; |
@@ -38,18 +37,30 @@ struct module_entry { /* I'll call it ME. */ | |||
38 | llist_t *deps; /* strings. modules we depend on */ | 37 | llist_t *deps; /* strings. modules we depend on */ |
39 | }; | 38 | }; |
40 | 39 | ||
40 | /* NB: INSMOD_OPT_SILENT bit suppresses ONLY non-existent modules, | ||
41 | * not deleted ones (those are still listed in modules.dep). | ||
42 | * module-init-tools version 3.4: | ||
43 | * # modprobe bogus | ||
44 | * FATAL: Module bogus not found. [exitcode 1] | ||
45 | * # modprobe -q bogus [silent, exitcode still 1] | ||
46 | * but: | ||
47 | * # rm kernel/drivers/net/dummy.ko | ||
48 | * # modprobe -q dummy | ||
49 | * FATAL: Could not open '/lib/modules/xxx/kernel/drivers/net/dummy.ko': No such file or directory | ||
50 | * [exitcode 1] | ||
51 | */ | ||
41 | #define MODPROBE_OPTS "acdlnrt:VC:" IF_FEATURE_MODPROBE_BLACKLIST("b") | 52 | #define MODPROBE_OPTS "acdlnrt:VC:" IF_FEATURE_MODPROBE_BLACKLIST("b") |
42 | enum { | 53 | enum { |
43 | MODPROBE_OPT_INSERT_ALL = (INSMOD_OPT_UNUSED << 0), /* a */ | 54 | MODPROBE_OPT_INSERT_ALL = (INSMOD_OPT_UNUSED << 0), /* a */ |
44 | MODPROBE_OPT_DUMP_ONLY = (INSMOD_OPT_UNUSED << 1), /* c */ | 55 | MODPROBE_OPT_DUMP_ONLY = (INSMOD_OPT_UNUSED << 1), /* c */ |
45 | MODPROBE_OPT_D = (INSMOD_OPT_UNUSED << 2), /* d */ | 56 | MODPROBE_OPT_D = (INSMOD_OPT_UNUSED << 2), /* d */ |
46 | MODPROBE_OPT_LIST_ONLY = (INSMOD_OPT_UNUSED << 3), /* l */ | 57 | MODPROBE_OPT_LIST_ONLY = (INSMOD_OPT_UNUSED << 3), /* l */ |
47 | MODPROBE_OPT_SHOW_ONLY = (INSMOD_OPT_UNUSED << 4), /* n */ | 58 | MODPROBE_OPT_SHOW_ONLY = (INSMOD_OPT_UNUSED << 4), /* n */ |
48 | MODPROBE_OPT_REMOVE = (INSMOD_OPT_UNUSED << 5), /* r */ | 59 | MODPROBE_OPT_REMOVE = (INSMOD_OPT_UNUSED << 5), /* r */ |
49 | MODPROBE_OPT_RESTRICT = (INSMOD_OPT_UNUSED << 6), /* t */ | 60 | MODPROBE_OPT_RESTRICT = (INSMOD_OPT_UNUSED << 6), /* t */ |
50 | MODPROBE_OPT_VERONLY = (INSMOD_OPT_UNUSED << 7), /* V */ | 61 | MODPROBE_OPT_VERONLY = (INSMOD_OPT_UNUSED << 7), /* V */ |
51 | MODPROBE_OPT_CONFIGFILE = (INSMOD_OPT_UNUSED << 8), /* C */ | 62 | MODPROBE_OPT_CONFIGFILE = (INSMOD_OPT_UNUSED << 8), /* C */ |
52 | MODPROBE_OPT_BLACKLIST = (INSMOD_OPT_UNUSED << 9) * ENABLE_FEATURE_MODPROBE_BLACKLIST, | 63 | MODPROBE_OPT_BLACKLIST = (INSMOD_OPT_UNUSED << 9) * ENABLE_FEATURE_MODPROBE_BLACKLIST, |
53 | }; | 64 | }; |
54 | 65 | ||
55 | struct globals { | 66 | struct globals { |
@@ -140,7 +151,7 @@ static int FAST_FUNC config_file_action(const char *filename, | |||
140 | { | 151 | { |
141 | char *tokens[3]; | 152 | char *tokens[3]; |
142 | parser_t *p; | 153 | parser_t *p; |
143 | struct module_entry *m; | 154 | struct module_entry *m; |
144 | int rc = TRUE; | 155 | int rc = TRUE; |
145 | 156 | ||
146 | if (bb_basename(filename)[0] == '.') | 157 | if (bb_basename(filename)[0] == '.') |
@@ -199,7 +210,7 @@ static int FAST_FUNC config_file_action(const char *filename, | |||
199 | } | 210 | } |
200 | } | 211 | } |
201 | config_close(p); | 212 | config_close(p); |
202 | error: | 213 | error: |
203 | return rc; | 214 | return rc; |
204 | } | 215 | } |
205 | 216 | ||
@@ -209,6 +220,11 @@ static int read_config(const char *path) | |||
209 | config_file_action, NULL, NULL, 1); | 220 | config_file_action, NULL, NULL, 1); |
210 | } | 221 | } |
211 | 222 | ||
223 | /* Return: similar to bb_init_module: | ||
224 | * 0 on success, | ||
225 | * -errno on open/read error, | ||
226 | * errno on init_module() error | ||
227 | */ | ||
212 | static int do_modprobe(struct module_entry *m) | 228 | static int do_modprobe(struct module_entry *m) |
213 | { | 229 | { |
214 | struct module_entry *m2 = m2; /* for compiler */ | 230 | struct module_entry *m2 = m2; /* for compiler */ |
@@ -217,7 +233,8 @@ static int do_modprobe(struct module_entry *m) | |||
217 | llist_t *l; | 233 | llist_t *l; |
218 | 234 | ||
219 | if (!(m->flags & MODULE_FLAG_FOUND_IN_MODDEP)) { | 235 | if (!(m->flags & MODULE_FLAG_FOUND_IN_MODDEP)) { |
220 | DBG("skipping %s, not found in modules.dep", m->modname); | 236 | if (!(option_mask32 & INSMOD_OPT_SILENT)) |
237 | bb_error_msg("module %s not found in modules.dep", m->probed_name); | ||
221 | return -ENOENT; | 238 | return -ENOENT; |
222 | } | 239 | } |
223 | DBG("do_modprob'ing %s", m->modname); | 240 | DBG("do_modprob'ing %s", m->modname); |
@@ -230,43 +247,52 @@ static int do_modprobe(struct module_entry *m) | |||
230 | 247 | ||
231 | first = 1; | 248 | first = 1; |
232 | rc = 0; | 249 | rc = 0; |
233 | while (m->deps && rc == 0) { | 250 | while (m->deps) { |
234 | fn = llist_pop(&m->deps); | 251 | rc = 0; |
252 | fn = llist_pop(&m->deps); /* we leak it */ | ||
235 | m2 = get_or_add_modentry(fn); | 253 | m2 = get_or_add_modentry(fn); |
254 | |||
236 | if (option_mask32 & MODPROBE_OPT_REMOVE) { | 255 | if (option_mask32 & MODPROBE_OPT_REMOVE) { |
256 | /* modprobe -r */ | ||
237 | if (m2->flags & MODULE_FLAG_LOADED) { | 257 | if (m2->flags & MODULE_FLAG_LOADED) { |
238 | if (bb_delete_module(m2->modname, O_EXCL) != 0) { | 258 | rc = bb_delete_module(m2->modname, O_EXCL); |
239 | if (first) | 259 | if (rc) { |
240 | rc = errno; | 260 | if (first) { |
261 | bb_error_msg("failed to unload module %s: %s", | ||
262 | m2->probed_name ? m2->probed_name : m2->modname, | ||
263 | moderror(rc)); | ||
264 | break; | ||
265 | } | ||
241 | } else { | 266 | } else { |
242 | m2->flags &= ~MODULE_FLAG_LOADED; | 267 | m2->flags &= ~MODULE_FLAG_LOADED; |
243 | } | 268 | } |
244 | } | 269 | } |
245 | /* do not error out if *deps* fail to unload */ | 270 | /* do not error out if *deps* fail to unload */ |
246 | first = 0; | 271 | first = 0; |
247 | } else if (!(m2->flags & MODULE_FLAG_LOADED)) { | 272 | continue; |
248 | options = m2->options; | ||
249 | m2->options = NULL; | ||
250 | if (m == m2) | ||
251 | options = gather_options_str(options, G.cmdline_mopts); | ||
252 | rc = bb_init_module(fn, options); | ||
253 | DBG("loaded %s '%s', rc:%d", fn, options, rc); | ||
254 | if (rc == 0) | ||
255 | m2->flags |= MODULE_FLAG_LOADED; | ||
256 | free(options); | ||
257 | } else { | ||
258 | DBG("%s is already loaded, skipping", fn); | ||
259 | } | 273 | } |
260 | 274 | ||
261 | free(fn); | 275 | if (m2->flags & MODULE_FLAG_LOADED) { |
262 | } | 276 | DBG("%s is already loaded, skipping", fn); |
277 | continue; | ||
278 | } | ||
263 | 279 | ||
264 | if (rc && !(option_mask32 & INSMOD_OPT_SILENT)) { | 280 | options = m2->options; |
265 | bb_error_msg("failed to %sload module %s: %s", | 281 | m2->options = NULL; |
266 | (option_mask32 & MODPROBE_OPT_REMOVE) ? "un" : "", | 282 | if (m == m2) |
283 | options = gather_options_str(options, G.cmdline_mopts); | ||
284 | rc = bb_init_module(fn, options); | ||
285 | DBG("loaded %s '%s', rc:%d", fn, options, rc); | ||
286 | free(options); | ||
287 | if (rc) { | ||
288 | bb_error_msg("failed to load module %s (%s): %s", | ||
267 | m2->probed_name ? m2->probed_name : m2->modname, | 289 | m2->probed_name ? m2->probed_name : m2->modname, |
268 | moderror(rc) | 290 | fn, |
269 | ); | 291 | moderror(rc) |
292 | ); | ||
293 | break; | ||
294 | } | ||
295 | m2->flags |= MODULE_FLAG_LOADED; | ||
270 | } | 296 | } |
271 | 297 | ||
272 | return rc; | 298 | return rc; |
@@ -278,7 +304,7 @@ static void load_modules_dep(void) | |||
278 | char *colon, *tokens[2]; | 304 | char *colon, *tokens[2]; |
279 | parser_t *p; | 305 | parser_t *p; |
280 | 306 | ||
281 | /* Modprobe does not work at all without modprobe.dep, | 307 | /* Modprobe does not work at all without modules.dep, |
282 | * even if the full module name is given. Returning error here | 308 | * even if the full module name is given. Returning error here |
283 | * was making us later confuse user with this message: | 309 | * was making us later confuse user with this message: |
284 | * "module /full/path/to/existing/file/module.ko not found". | 310 | * "module /full/path/to/existing/file/module.ko not found". |
@@ -387,35 +413,40 @@ int modprobe_main(int argc UNUSED_PARAM, char **argv) | |||
387 | load_modules_dep(); | 413 | load_modules_dep(); |
388 | } | 414 | } |
389 | 415 | ||
416 | rc = 0; | ||
390 | while ((me = llist_pop(&G.probes)) != NULL) { | 417 | while ((me = llist_pop(&G.probes)) != NULL) { |
391 | if (me->realnames == NULL) { | 418 | if (me->realnames == NULL) { |
419 | DBG("probing by module name"); | ||
392 | /* This is not an alias. Literal names are blacklisted | 420 | /* This is not an alias. Literal names are blacklisted |
393 | * only if '-b' is given. | 421 | * only if '-b' is given. |
394 | */ | 422 | */ |
395 | if (!(opt & MODPROBE_OPT_BLACKLIST) | 423 | if (!(opt & MODPROBE_OPT_BLACKLIST) |
396 | || !(me->flags & MODULE_FLAG_BLACKLISTED) | 424 | || !(me->flags & MODULE_FLAG_BLACKLISTED) |
397 | ) { | 425 | ) { |
398 | rc = do_modprobe(me); | 426 | rc |= do_modprobe(me); |
399 | //FIXME: what if rc > 0? | ||
400 | if (rc < 0 && !(opt & INSMOD_OPT_SILENT)) | ||
401 | bb_error_msg("module %s not found", | ||
402 | me->probed_name); | ||
403 | } | 427 | } |
404 | } else { | 428 | continue; |
405 | /* Probe all realnames */ | ||
406 | do { | ||
407 | char *realname = llist_pop(&me->realnames); | ||
408 | struct module_entry *m2; | ||
409 | |||
410 | DBG("probing %s by realname %s", me->modname, realname); | ||
411 | m2 = get_or_add_modentry(realname); | ||
412 | if (!(m2->flags & MODULE_FLAG_BLACKLISTED)) | ||
413 | do_modprobe(m2); | ||
414 | //FIXME: error check? | ||
415 | free(realname); | ||
416 | } while (me->realnames != NULL); | ||
417 | } | 429 | } |
430 | |||
431 | /* Probe all real names for the alias */ | ||
432 | do { | ||
433 | char *realname = llist_pop(&me->realnames); | ||
434 | struct module_entry *m2; | ||
435 | |||
436 | DBG("probing alias %s by realname %s", me->modname, realname); | ||
437 | m2 = get_or_add_modentry(realname); | ||
438 | if (!(m2->flags & MODULE_FLAG_BLACKLISTED) | ||
439 | && (!(m2->flags & MODULE_FLAG_LOADED) | ||
440 | || (opt & MODPROBE_OPT_REMOVE)) | ||
441 | ) { | ||
442 | //TODO: we can pass "me" as 2nd param to do_modprobe, | ||
443 | //and make do_modprobe emit more meaningful error messages | ||
444 | //with alias name included, not just module name alias resolves to. | ||
445 | rc |= do_modprobe(m2); | ||
446 | } | ||
447 | free(realname); | ||
448 | } while (me->realnames != NULL); | ||
418 | } | 449 | } |
419 | 450 | ||
420 | return EXIT_SUCCESS; | 451 | return (rc != 0); |
421 | } | 452 | } |