X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;f=modutils%2Fmodprobe.c;h=0a372a049062aae973654203c62b9c3fb3a55fda;hb=e17e8d4b7da27f28956253104218d53328f31995;hp=f08f0850d6543f1f83074cf7f8f55b076e17a78b;hpb=5fd3ddfb243f8f3a8ef471ff8c323a76cf815574;p=oweals%2Fbusybox.git diff --git a/modutils/modprobe.c b/modutils/modprobe.c index f08f0850d..0a372a049 100644 --- a/modutils/modprobe.c +++ b/modutils/modprobe.c @@ -7,16 +7,41 @@ * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ - -//applet:IF_MODPROBE(APPLET(modprobe, BB_DIR_SBIN, BB_SUID_DROP)) +//config:config MODPROBE +//config: bool "modprobe (28 kb)" +//config: default y +//config: select PLATFORM_LINUX +//config: help +//config: Handle the loading of modules, and their dependencies on a high +//config: level. +//config: +//config:config FEATURE_MODPROBE_BLACKLIST +//config: bool "Blacklist support" +//config: default y +//config: depends on MODPROBE && !MODPROBE_SMALL +//config: help +//config: Say 'y' here to enable support for the 'blacklist' command in +//config: modprobe.conf. This prevents the alias resolver to resolve +//config: blacklisted modules. This is useful if you want to prevent your +//config: hardware autodetection scripts to load modules like evdev, frame +//config: buffer drivers etc. + +//applet:IF_MODPROBE(IF_NOT_MODPROBE_SMALL(APPLET_NOEXEC(modprobe, modprobe, BB_DIR_SBIN, BB_SUID_DROP, modprobe))) + +//kbuild:ifneq ($(CONFIG_MODPROBE_SMALL),y) +//kbuild:lib-$(CONFIG_MODPROBE) += modprobe.o modutils.o +//kbuild:endif #include "libbb.h" #include "modutils.h" #include #include -//#define DBG(fmt, ...) bb_error_msg("%s: " fmt, __func__, ## __VA_ARGS__) +#if 1 #define DBG(...) ((void)0) +#else +#define DBG(fmt, ...) bb_error_msg("%s: " fmt, __func__, ## __VA_ARGS__) +#endif /* Note that unlike older versions of modules.dep/depmod (busybox and m-i-t), * we expect the full dependency list to be specified in modules.dep. @@ -87,7 +112,7 @@ //usage: //usage:#define modprobe_trivial_usage //usage: "[-alrqvsD" IF_FEATURE_MODPROBE_BLACKLIST("b") "]" -//usage: " MODULE [SYMBOL=VALUE]..." +//usage: " MODULE" IF_FEATURE_CMDLINE_MODULE_OPTIONS(" [SYMBOL=VALUE]...") //usage:#define modprobe_full_usage "\n\n" //usage: " -a Load multiple MODULEs" //usage: "\n -l List (MODULE is a pattern)" @@ -108,7 +133,7 @@ */ #define MODPROBE_OPTS "alrDb" /* -a and -D _are_ in fact compatible */ -#define MODPROBE_COMPLEMENTARY ("q-v:v-q:l--arD:r--alD:a--lr:D--rl") +#define MODPROBE_COMPLEMENTARY "q-v:v-q:l--arD:r--alD:a--lr:D--rl" //#define MODPROBE_OPTS "acd:lnrt:C:b" //#define MODPROBE_COMPLEMENTARY "q-v:v-q:l--acr:a--lr:r--al" enum { @@ -146,28 +171,18 @@ static const char modprobe_longopts[] ALIGN1 = /* "was seen in modules.dep": */ #define MODULE_FLAG_FOUND_IN_MODDEP 0x0004 #define MODULE_FLAG_BLACKLISTED 0x0008 - -struct module_entry { /* I'll call it ME. */ - unsigned flags; - char *modname; /* stripped of /path/, .ext and s/-/_/g */ - const char *probed_name; /* verbatim as seen on cmdline */ - char *options; /* options from config files */ - llist_t *realnames; /* strings. if this module is an alias, */ - /* real module name is one of these. */ -//Can there really be more than one? Example from real kernel? - llist_t *deps; /* strings. modules we depend on */ -}; - -#define DB_HASH_SIZE 256 +#define MODULE_FLAG_BUILTIN 0x0010 struct globals { llist_t *probes; /* MEs of module(s) requested on cmdline */ +#if ENABLE_FEATURE_CMDLINE_MODULE_OPTIONS char *cmdline_mopts; /* module options from cmdline */ +#endif int num_unresolved_deps; /* bool. "Did we have 'symbol:FOO' requested on cmdline?" */ smallint need_symbols; struct utsname uts; - llist_t *db[DB_HASH_SIZE]; /* MEs of all modules ever seen (caching for speed) */ + module_db db; } FIX_ALIASING; #define G (*ptr_to_globals) #define INIT_G() do { \ @@ -192,46 +207,9 @@ static char *gather_options_str(char *opts, const char *append) return opts; } -/* These three functions called many times, optimizing for speed. - * Users reported minute-long delays when they runn iptables repeatedly - * (iptables use modprobe to install needed kernel modules). - */ -static struct module_entry *helper_get_module(const char *module, int create) +static struct module_entry *get_or_add_modentry(const char *module) { - char modname[MODULE_NAME_LEN]; - struct module_entry *e; - llist_t *l; - unsigned i; - unsigned hash; - - filename2modname(module, modname); - - hash = 0; - for (i = 0; modname[i]; i++) - hash = ((hash << 5) + hash) + modname[i]; - hash %= DB_HASH_SIZE; - - for (l = G.db[hash]; l; l = l->link) { - e = (struct module_entry *) l->data; - if (strcmp(e->modname, modname) == 0) - return e; - } - if (!create) - return NULL; - - e = xzalloc(sizeof(*e)); - e->modname = xstrdup(modname); - llist_add_to(&G.db[hash], e); - - return e; -} -static ALWAYS_INLINE struct module_entry *get_or_add_modentry(const char *module) -{ - return helper_get_module(module, 1); -} -static ALWAYS_INLINE struct module_entry *get_modentry(const char *module) -{ - return helper_get_module(module, 0); + return moddb_get_or_create(&G.db, module); } static void add_probe(const char *name) @@ -240,7 +218,7 @@ static void add_probe(const char *name) m = get_or_add_modentry(name); if (!(option_mask32 & (OPT_REMOVE | OPT_SHOW_DEPS)) - && (m->flags & MODULE_FLAG_LOADED) + && (m->flags & (MODULE_FLAG_LOADED | MODULE_FLAG_BUILTIN)) ) { DBG("skipping %s, it is already loaded", name); return; @@ -252,7 +230,7 @@ static void add_probe(const char *name) llist_add_to_end(&G.probes, m); G.num_unresolved_deps++; if (ENABLE_FEATURE_MODUTILS_SYMBOLS - && strncmp(m->modname, "symbol:", 7) == 0 + && is_prefixed_with(m->modname, "symbol:") ) { G.need_symbols = 1; } @@ -261,16 +239,37 @@ static void add_probe(const char *name) static int FAST_FUNC config_file_action(const char *filename, struct stat *statbuf UNUSED_PARAM, void *userdata UNUSED_PARAM, - int depth UNUSED_PARAM) + int depth) { char *tokens[3]; parser_t *p; struct module_entry *m; int rc = TRUE; + const char *base; - if (bb_basename(filename)[0] == '.') + /* Skip files that begin with a "." */ + base = bb_basename(filename); + if (base[0] == '.') goto error; + /* "man modprobe.d" from kmod version 22 suggests + * that we shouldn't recurse into /etc/modprobe.d/dir/ + * _subdirectories_: + */ + if (depth > 1) + return SKIP; /* stop recursing */ +//TODO: instead, can use dirAction in recursive_action() to SKIP dirs +//on depth == 1 level. But that's more code... + + /* In dir recursion, skip files that do not end with a ".conf" + * depth==0: read_config("modules.{symbols,alias}") must work, + * "include FILE_NOT_ENDING_IN_CONF" must work too. + */ + if (depth != 0) { + if (!is_suffixed_with(base, ".conf")) + goto error; + } + p = config_open2(filename, fopen_for_read); if (p == NULL) { rc = FALSE; @@ -314,7 +313,7 @@ static int FAST_FUNC config_file_action(const char *filename, m = get_or_add_modentry(tokens[1]); m->options = gather_options_str(m->options, tokens[2]); } else if (strcmp(tokens[0], "include") == 0) { - /* include */ + /* include / (yes, directories also must work) */ read_config(tokens[1]); } else if (ENABLE_FEATURE_MODPROBE_BLACKLIST && strcmp(tokens[0], "blacklist") == 0 @@ -331,7 +330,8 @@ static int FAST_FUNC config_file_action(const char *filename, static int read_config(const char *path) { return recursive_action(path, ACTION_RECURSE | ACTION_QUIET, - config_file_action, NULL, NULL, 1); + config_file_action, NULL, NULL, + /*depth:*/ 0); } static const char *humanly_readable_name(struct module_entry *m) @@ -340,27 +340,55 @@ static const char *humanly_readable_name(struct module_entry *m) return m->probed_name ? m->probed_name : m->modname; } +/* Like strsep(&stringp, "\n\t ") but quoted text goes to single token + * even if it contains whitespace. + */ +static char *strsep_quotes(char **stringp) +{ + char *s, *start = *stringp; + + if (!start) + return NULL; + + for (s = start; ; s++) { + switch (*s) { + case '"': + s = strchrnul(s + 1, '"'); /* find trailing quote */ + if (*s != '\0') + s++; /* skip trailing quote */ + /* fall through */ + case '\0': + case '\n': + case '\t': + case ' ': + if (*s != '\0') { + *s = '\0'; + *stringp = s + 1; + } else { + *stringp = NULL; + } + return start; + } + } +} + static char *parse_and_add_kcmdline_module_options(char *options, const char *modulename) { char *kcmdline_buf; char *kcmdline; char *kptr; - int len; kcmdline_buf = xmalloc_open_read_close("/proc/cmdline", NULL); if (!kcmdline_buf) return options; kcmdline = kcmdline_buf; - len = strlen(modulename); - while ((kptr = strsep(&kcmdline, "\n\t ")) != NULL) { - if (strncmp(modulename, kptr, len) != 0) - continue; - kptr += len; - if (*kptr != '.') + while ((kptr = strsep_quotes(&kcmdline)) != NULL) { + char *after_modulename = is_prefixed_with(kptr, modulename); + if (!after_modulename || *after_modulename != '.') continue; /* It is "modulename.xxxx" */ - kptr++; + kptr = after_modulename + 1; if (strchr(kptr, '=') != NULL) { /* It is "modulename.opt=[val]" */ options = gather_options_str(options, kptr); @@ -394,8 +422,10 @@ static int do_modprobe(struct module_entry *m) if (!(m->flags & MODULE_FLAG_FOUND_IN_MODDEP)) { if (!(option_mask32 & INSMOD_OPT_SILENT)) - bb_error_msg("module %s not found in modules.dep", - humanly_readable_name(m)); + bb_error_msg((m->flags & MODULE_FLAG_BUILTIN) ? + "module %s is builtin" : + "module %s not found in modules.dep", + humanly_readable_name(m)); return -ENOENT; } DBG("do_modprob'ing %s", m->modname); @@ -417,7 +447,7 @@ static int do_modprobe(struct module_entry *m) rc = 0; fn = llist_pop(&m->deps); /* we leak it */ - m2 = get_or_add_modentry(fn); + m2 = get_or_add_modentry(bb_get_last_path_component_nostrip(fn)); if (option_mask32 & OPT_REMOVE) { /* modprobe -r */ @@ -425,9 +455,8 @@ static int do_modprobe(struct module_entry *m) rc = bb_delete_module(m2->modname, O_EXCL); if (rc) { if (first) { - bb_error_msg("can't unload module %s: %s", - humanly_readable_name(m2), - moderror(rc)); + bb_perror_msg("can't unload module '%s'", + humanly_readable_name(m2)); break; } } else { @@ -442,8 +471,10 @@ static int do_modprobe(struct module_entry *m) options = m2->options; m2->options = NULL; options = parse_and_add_kcmdline_module_options(options, m2->modname); +#if ENABLE_FEATURE_CMDLINE_MODULE_OPTIONS if (m == m2) options = gather_options_str(options, G.cmdline_mopts); +#endif if (option_mask32 & OPT_SHOW_DEPS) { printf(options ? "insmod %s/%s/%s %s\n" @@ -499,9 +530,9 @@ static void load_modules_dep(void) colon = last_char_is(tokens[0], ':'); if (colon == NULL) continue; - *colon = 0; + *colon = '\0'; - m = get_modentry(tokens[0]); + m = moddb_get(&G.db, bb_get_last_path_component_nostrip(tokens[0])); if (m == NULL) continue; @@ -534,9 +565,10 @@ int modprobe_main(int argc UNUSED_PARAM, char **argv) INIT_G(); - IF_LONG_OPTS(applet_long_options = modprobe_longopts;) - opt_complementary = MODPROBE_COMPLEMENTARY; - opt = getopt32(argv, INSMOD_OPTS MODPROBE_OPTS INSMOD_ARGS); + opt = getopt32long(argv, "^" INSMOD_OPTS MODPROBE_OPTS "\0" MODPROBE_COMPLEMENTARY, + modprobe_longopts + INSMOD_ARGS + ); argv += optind; /* Goto modules location */ @@ -546,7 +578,6 @@ int modprobe_main(int argc UNUSED_PARAM, char **argv) if (opt & OPT_LIST_ONLY) { int i; - char name[MODULE_NAME_LEN]; char *colon, *tokens[2]; parser_t *p = config_open2(CONFIG_DEFAULT_DEPMOD_FILE, xfopen_for_read); @@ -558,10 +589,14 @@ int modprobe_main(int argc UNUSED_PARAM, char **argv) if (!colon) continue; *colon = '\0'; - filename2modname(tokens[0], name); if (!argv[0]) puts(tokens[0]); else { + char name[MODULE_NAME_LEN]; + filename2modname( + bb_get_last_path_component_nostrip(tokens[0]), + name + ); for (i = 0; argv[i]; i++) { if (fnmatch(argv[i], name, 0) == 0) { puts(tokens[0]); @@ -583,7 +618,7 @@ int modprobe_main(int argc UNUSED_PARAM, char **argv) * autoclean will be removed". */ if (bb_delete_module(NULL, O_NONBLOCK | O_EXCL) != 0) - bb_perror_msg_and_die("rmmod"); + bb_perror_nomsg_and_die(); } return EXIT_SUCCESS; } @@ -595,6 +630,11 @@ int modprobe_main(int argc UNUSED_PARAM, char **argv) while (config_read(parser, &s, 1, 1, "# \t", PARSE_NORMAL & ~PARSE_GREEDY)) get_or_add_modentry(s)->flags |= MODULE_FLAG_LOADED; config_close(parser); + + parser = config_open2("modules.builtin", fopen_for_read); + while (config_read(parser, &s, 1, 1, "# \t", PARSE_NORMAL)) + get_or_add_modentry(s)->flags |= MODULE_FLAG_BUILTIN; + config_close(parser); } if (opt & (OPT_INSERT_ALL | OPT_REMOVE)) { @@ -607,7 +647,9 @@ int modprobe_main(int argc UNUSED_PARAM, char **argv) /* First argument is module name, rest are parameters */ DBG("probing just module %s", *argv); add_probe(argv[0]); +#if ENABLE_FEATURE_CMDLINE_MODULE_OPTIONS G.cmdline_mopts = parse_cmdline_module_options(argv, /*quote_spaces:*/ 1); +#endif } /* Happens if all requested modules are already loaded */ @@ -659,5 +701,8 @@ int modprobe_main(int argc UNUSED_PARAM, char **argv) } while (me->realnames != NULL); } + if (ENABLE_FEATURE_CLEAN_UP) + moddb_free(&G.db); + return (rc != 0); }