X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;f=modutils%2Fmodprobe.c;h=0a372a049062aae973654203c62b9c3fb3a55fda;hb=981b2eff814bd186188ad66a32990a6d17b37a3e;hp=314a7a1cbea8acf2978fef0c5b59929682bbbfb3;hpb=875297378cdbebb1278a4595f9fffffca3fc2303;p=oweals%2Fbusybox.git diff --git a/modutils/modprobe.c b/modutils/modprobe.c index 314a7a1cb..0a372a049 100644 --- a/modutils/modprobe.c +++ b/modutils/modprobe.c @@ -7,8 +7,30 @@ * * 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" @@ -90,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)" @@ -111,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 { @@ -149,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 { \ @@ -195,51 +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) -{ - 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); -} -/* So far this function always gets a module pathname, never an alias name. - * The crucial difference is that pathname needs dirname stripping, - * while alias name must NOT do it! - * Testcase where dirname stripping is likely to go wrong: "modprobe devname:snd/timer" - */ -static ALWAYS_INLINE struct module_entry *get_modentry(const char *pathname) +static struct module_entry *get_or_add_modentry(const char *module) { - return helper_get_module(bb_get_last_path_component_nostrip(pathname), 0); + return moddb_get_or_create(&G.db, module); } static void add_probe(const char *name) @@ -248,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; @@ -269,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; @@ -322,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 @@ -339,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) @@ -430,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); @@ -461,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 { @@ -478,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" @@ -537,7 +532,7 @@ static void load_modules_dep(void) continue; *colon = '\0'; - m = get_modentry(tokens[0]); + m = moddb_get(&G.db, bb_get_last_path_component_nostrip(tokens[0])); if (m == NULL) continue; @@ -570,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 */ @@ -622,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; } @@ -634,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)) { @@ -646,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 */ @@ -698,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); }