busybox/modutils/modprobe.c
<<
>>
Prefs
   1/* vi: set sw=4 ts=4: */
   2/*
   3 * Modprobe written from scratch for BusyBox
   4 *
   5 * Copyright (c) 2008 Timo Teras <timo.teras@iki.fi>
   6 * Copyright (c) 2008 Vladimir Dronnikov
   7 *
   8 * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
   9 */
  10
  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.
  13 * Older versions would only export the direct dependency list.
  14 */
  15#include "libbb.h"
  16#include "modutils.h"
  17#include <sys/utsname.h>
  18#include <fnmatch.h>
  19
  20//#define DBG(fmt, ...) bb_error_msg("%s: " fmt, __func__, ## __VA_ARGS__)
  21#define DBG(...) ((void)0)
  22
  23#define MODULE_FLAG_LOADED              0x0001
  24#define MODULE_FLAG_NEED_DEPS           0x0002
  25/* "was seen in modules.dep": */
  26#define MODULE_FLAG_FOUND_IN_MODDEP     0x0004
  27#define MODULE_FLAG_BLACKLISTED         0x0008
  28
  29struct module_entry { /* I'll call it ME. */
  30        unsigned flags;
  31        char *modname; /* stripped of /path/, .ext and s/-/_/g */
  32        const char *probed_name; /* verbatim as seen on cmdline */
  33        char *options; /* options from config files */
  34        llist_t *realnames; /* strings. if this module is an alias, */
  35        /* real module name is one of these. */
  36//Can there really be more than one? Example from real kernel?
  37        llist_t *deps; /* strings. modules we depend on */
  38};
  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 */
  52#define MODPROBE_OPTS  "acdlnrt:VC:" IF_FEATURE_MODPROBE_BLACKLIST("b")
  53enum {
  54        MODPROBE_OPT_INSERT_ALL = (INSMOD_OPT_UNUSED << 0), /* a */
  55        MODPROBE_OPT_DUMP_ONLY  = (INSMOD_OPT_UNUSED << 1), /* c */
  56        MODPROBE_OPT_D          = (INSMOD_OPT_UNUSED << 2), /* d */
  57        MODPROBE_OPT_LIST_ONLY  = (INSMOD_OPT_UNUSED << 3), /* l */
  58        MODPROBE_OPT_SHOW_ONLY  = (INSMOD_OPT_UNUSED << 4), /* n */
  59        MODPROBE_OPT_REMOVE     = (INSMOD_OPT_UNUSED << 5), /* r */
  60        MODPROBE_OPT_RESTRICT   = (INSMOD_OPT_UNUSED << 6), /* t */
  61        MODPROBE_OPT_VERONLY    = (INSMOD_OPT_UNUSED << 7), /* V */
  62        MODPROBE_OPT_CONFIGFILE = (INSMOD_OPT_UNUSED << 8), /* C */
  63        MODPROBE_OPT_BLACKLIST  = (INSMOD_OPT_UNUSED << 9) * ENABLE_FEATURE_MODPROBE_BLACKLIST,
  64};
  65
  66struct globals {
  67        llist_t *db; /* MEs of all modules ever seen (caching for speed) */
  68        llist_t *probes; /* MEs of module(s) requested on cmdline */
  69        char *cmdline_mopts; /* module options from cmdline */
  70        int num_unresolved_deps;
  71        /* bool. "Did we have 'symbol:FOO' requested on cmdline?" */
  72        smallint need_symbols;
  73};
  74#define G (*(struct globals*)&bb_common_bufsiz1)
  75#define INIT_G() do { } while (0)
  76
  77
  78static int read_config(const char *path);
  79
  80static char *gather_options_str(char *opts, const char *append)
  81{
  82        /* Speed-optimized. We call gather_options_str many times. */
  83        if (opts == NULL) {
  84                opts = xstrdup(append);
  85        } else {
  86                int optlen = strlen(opts);
  87                opts = xrealloc(opts, optlen + strlen(append) + 2);
  88                sprintf(opts + optlen, " %s", append);
  89        }
  90        return opts;
  91}
  92
  93static struct module_entry *helper_get_module(const char *module, int create)
  94{
  95        char modname[MODULE_NAME_LEN];
  96        struct module_entry *e;
  97        llist_t *l;
  98
  99        filename2modname(module, modname);
 100        for (l = G.db; l != NULL; l = l->link) {
 101                e = (struct module_entry *) l->data;
 102                if (strcmp(e->modname, modname) == 0)
 103                        return e;
 104        }
 105        if (!create)
 106                return NULL;
 107
 108        e = xzalloc(sizeof(*e));
 109        e->modname = xstrdup(modname);
 110        llist_add_to(&G.db, e);
 111
 112        return e;
 113}
 114static struct module_entry *get_or_add_modentry(const char *module)
 115{
 116        return helper_get_module(module, 1);
 117}
 118static struct module_entry *get_modentry(const char *module)
 119{
 120        return helper_get_module(module, 0);
 121}
 122
 123static void add_probe(const char *name)
 124{
 125        struct module_entry *m;
 126
 127        m = get_or_add_modentry(name);
 128        if (!(option_mask32 & MODPROBE_OPT_REMOVE)
 129         && (m->flags & MODULE_FLAG_LOADED)
 130        ) {
 131                DBG("skipping %s, it is already loaded", name);
 132                return;
 133        }
 134
 135        DBG("queuing %s", name);
 136        m->probed_name = name;
 137        m->flags |= MODULE_FLAG_NEED_DEPS;
 138        llist_add_to_end(&G.probes, m);
 139        G.num_unresolved_deps++;
 140        if (ENABLE_FEATURE_MODUTILS_SYMBOLS
 141         && strncmp(m->modname, "symbol:", 7) == 0
 142        ) {
 143                G.need_symbols = 1;
 144        }
 145}
 146
 147static int FAST_FUNC config_file_action(const char *filename,
 148                                        struct stat *statbuf UNUSED_PARAM,
 149                                        void *userdata UNUSED_PARAM,
 150                                        int depth UNUSED_PARAM)
 151{
 152        char *tokens[3];
 153        parser_t *p;
 154        struct module_entry *m;
 155        int rc = TRUE;
 156
 157        if (bb_basename(filename)[0] == '.')
 158                goto error;
 159
 160        p = config_open2(filename, fopen_for_read);
 161        if (p == NULL) {
 162                rc = FALSE;
 163                goto error;
 164        }
 165
 166        while (config_read(p, tokens, 3, 2, "# \t", PARSE_NORMAL)) {
 167//Use index_in_strings?
 168                if (strcmp(tokens[0], "alias") == 0) {
 169                        /* alias <wildcard> <modulename> */
 170                        llist_t *l;
 171                        char wildcard[MODULE_NAME_LEN];
 172                        char *rmod;
 173
 174                        if (tokens[2] == NULL)
 175                                continue;
 176                        filename2modname(tokens[1], wildcard);
 177
 178                        for (l = G.probes; l != NULL; l = l->link) {
 179                                m = (struct module_entry *) l->data;
 180                                if (fnmatch(wildcard, m->modname, 0) != 0)
 181                                        continue;
 182                                rmod = filename2modname(tokens[2], NULL);
 183                                llist_add_to(&m->realnames, rmod);
 184
 185                                if (m->flags & MODULE_FLAG_NEED_DEPS) {
 186                                        m->flags &= ~MODULE_FLAG_NEED_DEPS;
 187                                        G.num_unresolved_deps--;
 188                                }
 189
 190                                m = get_or_add_modentry(rmod);
 191                                if (!(m->flags & MODULE_FLAG_NEED_DEPS)) {
 192                                        m->flags |= MODULE_FLAG_NEED_DEPS;
 193                                        G.num_unresolved_deps++;
 194                                }
 195                        }
 196                } else if (strcmp(tokens[0], "options") == 0) {
 197                        /* options <modulename> <option...> */
 198                        if (tokens[2] == NULL)
 199                                continue;
 200                        m = get_or_add_modentry(tokens[1]);
 201                        m->options = gather_options_str(m->options, tokens[2]);
 202                } else if (strcmp(tokens[0], "include") == 0) {
 203                        /* include <filename> */
 204                        read_config(tokens[1]);
 205                } else if (ENABLE_FEATURE_MODPROBE_BLACKLIST
 206                 && strcmp(tokens[0], "blacklist") == 0
 207                ) {
 208                        /* blacklist <modulename> */
 209                        get_or_add_modentry(tokens[1])->flags |= MODULE_FLAG_BLACKLISTED;
 210                }
 211        }
 212        config_close(p);
 213 error:
 214        return rc;
 215}
 216
 217static int read_config(const char *path)
 218{
 219        return recursive_action(path, ACTION_RECURSE | ACTION_QUIET,
 220                                config_file_action, NULL, NULL, 1);
 221}
 222
 223static const char *humanly_readable_name(struct module_entry *m)
 224{
 225        /* probed_name may be NULL. modname always exists. */
 226        return m->probed_name ? m->probed_name : m->modname;
 227}
 228
 229/* Return: similar to bb_init_module:
 230 * 0 on success,
 231 * -errno on open/read error,
 232 * errno on init_module() error
 233 */
 234static int do_modprobe(struct module_entry *m)
 235{
 236        struct module_entry *m2 = m2; /* for compiler */
 237        char *fn, *options;
 238        int rc, first;
 239        llist_t *l;
 240
 241        if (!(m->flags & MODULE_FLAG_FOUND_IN_MODDEP)) {
 242                if (!(option_mask32 & INSMOD_OPT_SILENT))
 243                        bb_error_msg("module %s not found in modules.dep",
 244                                humanly_readable_name(m));
 245                return -ENOENT;
 246        }
 247        DBG("do_modprob'ing %s", m->modname);
 248
 249        if (!(option_mask32 & MODPROBE_OPT_REMOVE))
 250                m->deps = llist_rev(m->deps);
 251
 252        for (l = m->deps; l != NULL; l = l->link)
 253                DBG("dep: %s", l->data);
 254
 255        first = 1;
 256        rc = 0;
 257        while (m->deps) {
 258                rc = 0;
 259                fn = llist_pop(&m->deps); /* we leak it */
 260                m2 = get_or_add_modentry(fn);
 261
 262                if (option_mask32 & MODPROBE_OPT_REMOVE) {
 263                        /* modprobe -r */
 264                        if (m2->flags & MODULE_FLAG_LOADED) {
 265                                rc = bb_delete_module(m2->modname, O_EXCL);
 266                                if (rc) {
 267                                        if (first) {
 268                                                bb_error_msg("failed to unload module %s: %s",
 269                                                        humanly_readable_name(m2),
 270                                                        moderror(rc));
 271                                                break;
 272                                        }
 273                                } else {
 274                                        m2->flags &= ~MODULE_FLAG_LOADED;
 275                                }
 276                        }
 277                        /* do not error out if *deps* fail to unload */
 278                        first = 0;
 279                        continue;
 280                }
 281
 282                if (m2->flags & MODULE_FLAG_LOADED) {
 283                        DBG("%s is already loaded, skipping", fn);
 284                        continue;
 285                }
 286
 287                options = m2->options;
 288                m2->options = NULL;
 289                if (m == m2)
 290                        options = gather_options_str(options, G.cmdline_mopts);
 291                rc = bb_init_module(fn, options);
 292                DBG("loaded %s '%s', rc:%d", fn, options, rc);
 293                free(options);
 294                if (rc) {
 295                        bb_error_msg("failed to load module %s (%s): %s",
 296                                humanly_readable_name(m2),
 297                                fn,
 298                                moderror(rc)
 299                        );
 300                        break;
 301                }
 302                m2->flags |= MODULE_FLAG_LOADED;
 303        }
 304
 305        return rc;
 306}
 307
 308static void load_modules_dep(void)
 309{
 310        struct module_entry *m;
 311        char *colon, *tokens[2];
 312        parser_t *p;
 313
 314        /* Modprobe does not work at all without modules.dep,
 315         * even if the full module name is given. Returning error here
 316         * was making us later confuse user with this message:
 317         * "module /full/path/to/existing/file/module.ko not found".
 318         * It's better to die immediately, with good message.
 319         * xfopen_for_read provides that. */
 320        p = config_open2(CONFIG_DEFAULT_DEPMOD_FILE, xfopen_for_read);
 321
 322        while (G.num_unresolved_deps
 323         && config_read(p, tokens, 2, 1, "# \t", PARSE_NORMAL)
 324        ) {
 325                colon = last_char_is(tokens[0], ':');
 326                if (colon == NULL)
 327                        continue;
 328                *colon = 0;
 329
 330                m = get_modentry(tokens[0]);
 331                if (m == NULL)
 332                        continue;
 333
 334                /* Optimization... */
 335                if ((m->flags & MODULE_FLAG_LOADED)
 336                 && !(option_mask32 & MODPROBE_OPT_REMOVE)
 337                ) {
 338                        DBG("skip deps of %s, it's already loaded", tokens[0]);
 339                        continue;
 340                }
 341
 342                m->flags |= MODULE_FLAG_FOUND_IN_MODDEP;
 343                if ((m->flags & MODULE_FLAG_NEED_DEPS) && (m->deps == NULL)) {
 344                        G.num_unresolved_deps--;
 345                        llist_add_to(&m->deps, xstrdup(tokens[0]));
 346                        if (tokens[1])
 347                                string_to_llist(tokens[1], &m->deps, " \t");
 348                } else
 349                        DBG("skipping dep line");
 350        }
 351        config_close(p);
 352}
 353
 354int modprobe_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 355int modprobe_main(int argc UNUSED_PARAM, char **argv)
 356{
 357        struct utsname uts;
 358        int rc;
 359        unsigned opt;
 360        struct module_entry *me;
 361
 362        opt_complementary = "q-v:v-q";
 363        opt = getopt32(argv, INSMOD_OPTS MODPROBE_OPTS INSMOD_ARGS, NULL, NULL);
 364        argv += optind;
 365
 366        if (opt & (MODPROBE_OPT_DUMP_ONLY | MODPROBE_OPT_LIST_ONLY |
 367                                MODPROBE_OPT_SHOW_ONLY))
 368                bb_error_msg_and_die("not supported");
 369
 370        if (!argv[0]) {
 371                if (opt & MODPROBE_OPT_REMOVE) {
 372                        /* "modprobe -r" (w/o params).
 373                         * "If name is NULL, all unused modules marked
 374                         * autoclean will be removed".
 375                         */
 376                        if (bb_delete_module(NULL, O_NONBLOCK|O_EXCL) != 0)
 377                                bb_perror_msg_and_die("rmmod");
 378                }
 379                return EXIT_SUCCESS;
 380        }
 381
 382        /* Goto modules location */
 383        xchdir(CONFIG_DEFAULT_MODULES_DIR);
 384        uname(&uts);
 385        xchdir(uts.release);
 386
 387        /* Retrieve module names of already loaded modules */
 388        {
 389                char *s;
 390                parser_t *parser = config_open2("/proc/modules", fopen_for_read);
 391                while (config_read(parser, &s, 1, 1, "# \t", PARSE_NORMAL & ~PARSE_GREEDY))
 392                        get_or_add_modentry(s)->flags |= MODULE_FLAG_LOADED;
 393                config_close(parser);
 394        }
 395
 396        if (opt & (MODPROBE_OPT_INSERT_ALL | MODPROBE_OPT_REMOVE)) {
 397                /* Each argument is a module name */
 398                do {
 399                        DBG("adding module %s", *argv);
 400                        add_probe(*argv++);
 401                } while (*argv);
 402        } else {
 403                /* First argument is module name, rest are parameters */
 404                DBG("probing just module %s", *argv);
 405                add_probe(argv[0]);
 406                G.cmdline_mopts = parse_cmdline_module_options(argv);
 407        }
 408
 409        /* Happens if all requested modules are already loaded */
 410        if (G.probes == NULL)
 411                return EXIT_SUCCESS;
 412
 413        read_config("/etc/modprobe.conf");
 414        read_config("/etc/modprobe.d");
 415        if (ENABLE_FEATURE_MODUTILS_SYMBOLS && G.need_symbols)
 416                read_config("modules.symbols");
 417        load_modules_dep();
 418        if (ENABLE_FEATURE_MODUTILS_ALIAS && G.num_unresolved_deps) {
 419                read_config("modules.alias");
 420                load_modules_dep();
 421        }
 422
 423        rc = 0;
 424        while ((me = llist_pop(&G.probes)) != NULL) {
 425                if (me->realnames == NULL) {
 426                        DBG("probing by module name");
 427                        /* This is not an alias. Literal names are blacklisted
 428                         * only if '-b' is given.
 429                         */
 430                        if (!(opt & MODPROBE_OPT_BLACKLIST)
 431                         || !(me->flags & MODULE_FLAG_BLACKLISTED)
 432                        ) {
 433                                rc |= do_modprobe(me);
 434                        }
 435                        continue;
 436                }
 437
 438                /* Probe all real names for the alias */
 439                do {
 440                        char *realname = llist_pop(&me->realnames);
 441                        struct module_entry *m2;
 442
 443                        DBG("probing alias %s by realname %s", me->modname, realname);
 444                        m2 = get_or_add_modentry(realname);
 445                        if (!(m2->flags & MODULE_FLAG_BLACKLISTED)
 446                         && (!(m2->flags & MODULE_FLAG_LOADED)
 447                            || (opt & MODPROBE_OPT_REMOVE))
 448                        ) {
 449//TODO: we can pass "me" as 2nd param to do_modprobe,
 450//and make do_modprobe emit more meaningful error messages
 451//with alias name included, not just module name alias resolves to.
 452                                rc |= do_modprobe(m2);
 453                        }
 454                        free(realname);
 455                } while (me->realnames != NULL);
 456        }
 457
 458        return (rc != 0);
 459}
 460