Home | History | Annotate | Download | only in pending
      1 /* modprobe.c - modprobe utility.
      2  *
      3  * Copyright 2012 Madhur Verma <mad.flexi (at) gmail.com>
      4  * Copyright 2013 Kyungwan Han <asura321 (at) gmail.com>
      5  *
      6  * No Standard.
      7 
      8 USE_MODPROBE(NEWTOY(modprobe, "alrqvsDb", TOYFLAG_SBIN))
      9 
     10 config MODPROBE
     11   bool "modprobe"
     12   default n
     13   help
     14     usage: modprobe [-alrqvsDb] MODULE [symbol=value][...]
     15 
     16     modprobe utility - inserts modules and dependencies.
     17 
     18     -a  Load multiple MODULEs
     19     -l  List (MODULE is a pattern)
     20     -r  Remove MODULE (stacks) or do autoclean
     21     -q  Quiet
     22     -v  Verbose
     23     -s  Log to syslog
     24     -D  Show dependencies
     25     -b  Apply blacklist to module names too
     26 */
     27 #define FOR_modprobe
     28 #include "toys.h"
     29 #include <sys/syscall.h>
     30 
     31 GLOBALS(
     32   struct arg_list *probes;
     33   struct arg_list *dbase[256];
     34   char *cmdopts;
     35   int nudeps;
     36   uint8_t symreq;
     37   void (*dbg)(char *format, ...);
     38 )
     39 
     40 /* Note: if "#define DBASE_SIZE" modified,
     41  * Please update GLOBALS dbase[256] accordingly.
     42  */
     43 #define DBASE_SIZE  256
     44 #define MODNAME_LEN 256
     45 
     46 // Modules flag definations
     47 #define MOD_ALOADED   0x0001
     48 #define MOD_BLACKLIST 0x0002
     49 #define MOD_FNDDEPMOD 0x0004
     50 #define MOD_NDDEPS    0x0008
     51 
     52 // dummy interface for debugging.
     53 static void dummy(char *format, ...)
     54 {
     55 }
     56 
     57 // Current probing modules info
     58 struct module_s {
     59   uint32_t flags;
     60   char *cmdname, *name, *depent, *opts;
     61   struct arg_list *rnames, *dep;
     62 };
     63 
     64 // Converts path name FILE to module name.
     65 static char *path2mod(char *file, char *mod)
     66 {
     67   int i;
     68   char *from;
     69 
     70   if (!file) return NULL;
     71   if (!mod) mod = xmalloc(MODNAME_LEN);
     72 
     73   from = basename_r(file);
     74 
     75   for (i = 0; i < (MODNAME_LEN-1) && from[i] && from[i] != '.'; i++)
     76     mod[i] = (from[i] == '-') ? '_' : from[i];
     77   mod[i] = '\0';
     78   return mod;
     79 }
     80 
     81 // Add options in opts from toadd.
     82 static char *add_opts(char *opts, char *toadd)
     83 {
     84   if (toadd) {
     85     int optlen = 0;
     86 
     87     if (opts) optlen = strlen(opts);
     88     opts = xrealloc(opts, optlen + strlen(toadd) + 2);
     89     sprintf(opts + optlen, " %s", toadd);
     90   }
     91   return opts;
     92 }
     93 
     94 // Remove first element from the list and return it.
     95 static void *llist_popme(struct arg_list **head)
     96 {
     97   char *data = NULL;
     98   struct arg_list *temp = *head;
     99 
    100   if (temp) {
    101     data = temp->arg;
    102     *head = temp->next;
    103     free(temp);
    104   }
    105   return data;
    106 }
    107 
    108 // Add new node at the beginning of the list.
    109 static void llist_add(struct arg_list **old, void *data)
    110 {
    111   struct arg_list *new = xmalloc(sizeof(struct arg_list));
    112 
    113   new->arg = (char*)data;
    114   new->next = *old;
    115   *old = new;
    116 }
    117 
    118 // Add new node at tail of list.
    119 static void llist_add_tail(struct arg_list **head, void *data)
    120 {
    121   while (*head) head = &(*head)->next;
    122   *head = xzalloc(sizeof(struct arg_list));
    123   (*head)->arg = (char*)data;
    124 }
    125 
    126 // Reverse list order.
    127 static struct arg_list *llist_rev(struct arg_list *list)
    128 {
    129   struct arg_list *rev = NULL;
    130 
    131   while (list) {
    132     struct arg_list *next = list->next;
    133 
    134     list->next = rev;
    135     rev = list;
    136     list = next;
    137   }
    138   return rev;
    139 }
    140 
    141 /*
    142  * Returns struct module_s from the data base if found, NULL otherwise.
    143  * if add - create module entry, add it to data base and return the same mod.
    144  */
    145 static struct module_s *get_mod(char *mod, uint8_t add)
    146 {
    147   char name[MODNAME_LEN];
    148   struct module_s *modentry;
    149   struct arg_list *temp;
    150   unsigned i, hash = 0;
    151 
    152   path2mod(mod, name);
    153   for (i = 0; name[i]; i++) hash = ((hash*31) + hash) + name[i];
    154   hash %= DBASE_SIZE;
    155   for (temp = TT.dbase[hash]; temp; temp = temp->next) {
    156     modentry = (struct module_s *) temp->arg;
    157     if (!strcmp(modentry->name, name)) return modentry;
    158   }
    159   if (!add) return NULL;
    160   modentry = xzalloc(sizeof(*modentry));
    161   modentry->name = xstrdup(name);
    162   llist_add(&TT.dbase[hash], modentry);
    163   return modentry;
    164 }
    165 
    166 /*
    167  * Read a line from file with \ continuation and escape commented line.
    168  * Return the line in allocated string (*li)
    169  */
    170 static int read_line(FILE *fl, char **li)
    171 {
    172   char *nxtline = NULL, *line;
    173   int len, nxtlen, linelen, nxtlinelen;
    174 
    175   while (1) {
    176     line = NULL;
    177     linelen = nxtlinelen = 0;
    178     len = getline(&line, (size_t*)&linelen, fl);
    179     if (len <= 0) {
    180       free(line);
    181       return len;
    182     }
    183     // checking for commented lines.
    184     if (line[0] != '#') break;
    185     free(line);
    186   }
    187   for (;;) {
    188     if (line[len - 1] == '\n') len--;
    189     if (!len) {
    190       free(line);
    191       return len;
    192     } else if (line[len - 1] != '\\') break;
    193 
    194     len--;
    195     nxtlen = getline(&nxtline, (size_t*)&nxtlinelen, fl);
    196     if (nxtlen <= 0) break;
    197     if (linelen < len + nxtlen + 1) {
    198       linelen = len + nxtlen + 1;
    199       line = xrealloc(line, linelen);
    200     }
    201     memcpy(&line[len], nxtline, nxtlen);
    202     len += nxtlen;
    203   }
    204   line[len] = '\0';
    205   *li = xstrdup(line);
    206   free(line);
    207   if (nxtline) free(nxtline);
    208   return len;
    209 }
    210 
    211 /*
    212  * Action to be taken on all config files in default directories
    213  * checks for aliases, options, install, remove and blacklist
    214  */
    215 static int config_action(struct dirtree *node)
    216 {
    217   FILE *fc;
    218   char *filename, *tokens[3], *line, *linecp;
    219   struct module_s *modent;
    220   int tcount = 0;
    221 
    222   if (!dirtree_notdotdot(node)) return 0;
    223   if (S_ISDIR(node->st.st_mode)) return DIRTREE_RECURSE;
    224 
    225   if (!S_ISREG(node->st.st_mode)) return 0; // process only regular file
    226   filename = dirtree_path(node, NULL);
    227   if (!(fc = fopen(filename, "r"))) {
    228     free(filename);
    229     return 0;
    230   }
    231   for (line = linecp = NULL; read_line(fc, &line) > 0;
    232       free(line), free(linecp), line = linecp = NULL) {
    233     char *tk = NULL;
    234 
    235     if (!strlen(line)) continue;
    236     linecp = xstrdup(line);
    237     for (tk = strtok(linecp, "# \t"), tcount = 0; tk;
    238         tk = strtok(NULL, "# \t"), tcount++) {
    239       tokens[tcount] = tk;
    240       if (tcount == 2) {
    241         tokens[2] = line + strlen(tokens[0]) + strlen(tokens[1]) + 2;
    242         break;
    243       }
    244     }
    245     if (!tk) continue;
    246     // process the tokens[0] contains first word of config line.
    247     if (!strcmp(tokens[0], "alias")) {
    248       struct arg_list *temp;
    249       char aliase[MODNAME_LEN], *realname;
    250 
    251       if (!tokens[2]) continue;
    252       path2mod(tokens[1], aliase);
    253       for (temp = TT.probes; temp; temp = temp->next) {
    254         modent = (struct module_s *) temp->arg;
    255         if (fnmatch(aliase, modent->name, 0)) continue;
    256         realname = path2mod(tokens[2], NULL);
    257         llist_add(&modent->rnames, realname);
    258         if (modent->flags & MOD_NDDEPS) {
    259           modent->flags &= ~MOD_NDDEPS;
    260           TT.nudeps--;
    261         }
    262         modent = get_mod(realname, 1);
    263         if (!(modent->flags & MOD_NDDEPS)) {
    264           modent->flags |= MOD_NDDEPS;
    265           TT.nudeps++;
    266         }
    267       }
    268     } else if (!strcmp(tokens[0], "options")) {
    269       if (!tokens[2]) continue;
    270       modent = get_mod(tokens[1], 1);
    271       modent->opts = add_opts(modent->opts, tokens[2]);
    272     } else if (!strcmp(tokens[0], "include"))
    273       dirtree_read(tokens[1], config_action);
    274     else if (!strcmp(tokens[0], "blacklist"))
    275       get_mod(tokens[1], 1)->flags |= MOD_BLACKLIST;
    276     else if (!strcmp(tokens[0], "install")) continue;
    277     else if (!strcmp(tokens[0], "remove")) continue;
    278     else if (toys.optflags & FLAG_q)
    279       error_msg("Invalid option %s found in file %s", tokens[0], filename);
    280   }
    281   fclose(fc);
    282   free(filename);
    283   return 0;
    284 }
    285 
    286 // Show matched modules else return -1 on failure.
    287 static int depmode_read_entry(char *cmdname)
    288 {
    289   char *line;
    290   int ret = -1;
    291   FILE *fe = xfopen("modules.dep", "r");
    292 
    293   while (read_line(fe, &line) > 0) {
    294     char *tmp = strchr(line, ':');
    295 
    296     if (tmp) {
    297       *tmp = '\0';
    298      char *name = basename(line);
    299 
    300       tmp = strchr(name, '.');
    301       if (tmp) *tmp = '\0';
    302       if (!cmdname || !fnmatch(cmdname, name, 0)) {
    303         if (tmp) *tmp = '.';
    304         TT.dbg("%s\n", line);
    305         ret = 0;
    306       }
    307     }
    308     free(line);
    309   }
    310   fclose(fe);
    311   return ret;
    312 }
    313 
    314 // Finds dependencies for modules from the modules.dep file.
    315 static void find_dep(void)
    316 {
    317   char *line = NULL;
    318   struct module_s *mod;
    319   FILE *fe = xfopen("modules.dep", "r");
    320 
    321   for (; read_line(fe, &line) > 0; free(line)) {
    322     char *tmp = strchr(line, ':');
    323 
    324     if (tmp) {
    325       *tmp = '\0';
    326       mod = get_mod(line, 0);
    327       if (!mod) continue;
    328       if ((mod->flags & MOD_ALOADED) &&
    329           !(toys.optflags & (FLAG_r | FLAG_D))) continue;
    330 
    331       mod->flags |= MOD_FNDDEPMOD;
    332       if ((mod->flags & MOD_NDDEPS) && (!mod->dep)) {
    333         TT.nudeps--;
    334         llist_add(&mod->dep, xstrdup(line));
    335         tmp++;
    336         if (*tmp) {
    337           char *tok;
    338 
    339           while ((tok = strsep(&tmp, " \t"))) {
    340             if (!*tok) continue;
    341             llist_add_tail(&mod->dep, xstrdup(tok));
    342           }
    343         }
    344       }
    345     }
    346   }
    347   fclose(fe);
    348 }
    349 
    350 // Remove a module from the Linux Kernel. if !modules does auto remove.
    351 static int rm_mod(char *modules, uint32_t flags)
    352 {
    353   if (modules) {
    354     int len = strlen(modules);
    355 
    356     if (len > 3 && !strcmp(modules+len-3, ".ko" )) modules[len-3] = 0;
    357   }
    358 
    359   errno = 0;
    360   syscall(__NR_delete_module, modules, flags ? flags : O_NONBLOCK|O_EXCL);
    361 
    362   return errno;
    363 }
    364 
    365 // Insert module same as insmod implementation.
    366 static int ins_mod(char *modules, char *flags)
    367 {
    368   char *buf = NULL;
    369   int len, res;
    370   int fd = xopen(modules, O_RDONLY);
    371 
    372   len = fdlength(fd);
    373   buf = xmalloc(len);
    374   xreadall(fd, buf, len);
    375   xclose(fd);
    376 
    377   while (flags && strlen(toybuf) + strlen(flags) + 2 < sizeof(toybuf)) {
    378     strcat(toybuf, flags);
    379     strcat(toybuf, " ");
    380   }
    381   res = syscall(__NR_init_module, buf, len, toybuf);
    382   if (CFG_TOYBOX_FREE && buf != toybuf) free(buf);
    383   return res;
    384 }
    385 
    386 // Add module in probes list, if not loaded.
    387 static void add_mod(char *name)
    388 {
    389   struct module_s *mod = get_mod(name, 1);
    390 
    391   if (!(toys.optflags & (FLAG_r | FLAG_D)) && (mod->flags & MOD_ALOADED)) {
    392     TT.dbg("skipping %s, it is already loaded\n", name);
    393     return;
    394   }
    395   TT.dbg("queuing %s\n", name);
    396   mod->cmdname = name;
    397   mod->flags |= MOD_NDDEPS;
    398   llist_add_tail(&TT.probes, mod);
    399   TT.nudeps++;
    400   if (!strncmp(mod->name, "symbol:", 7)) TT.symreq = 1;
    401 }
    402 
    403 // Parse cmdline options suplied for module.
    404 static char *add_cmdopt(char **argv)
    405 {
    406   char *opt = xzalloc(1);
    407   int lopt = 0;
    408 
    409   while (*++argv) {
    410     char *fmt, *var, *val;
    411 
    412     var = *argv;
    413     opt = xrealloc(opt, lopt + 2 + strlen(var) + 2);
    414     // check for key=val or key = val.
    415     fmt = "%.*s%s ";
    416     for (val = var; *val && *val != '='; val++);
    417     if (*val && strchr(++val, ' ')) fmt = "%.*s\"%s\" ";
    418     lopt += sprintf(opt + lopt, fmt, (int) (val - var), var, val);
    419   }
    420   return opt;
    421 }
    422 
    423 // Probes a single module and loads all its dependencies.
    424 static int go_probe(struct module_s *m)
    425 {
    426   int rc = 0, first = 1;
    427 
    428   if (!(m->flags & MOD_FNDDEPMOD)) {
    429     if (!(toys.optflags & FLAG_q))
    430       error_msg("module %s not found in modules.dep", m->name);
    431     return -ENOENT;
    432   }
    433   TT.dbg("go_prob'ing %s\n", m->name);
    434   if (!(toys.optflags & FLAG_r)) m->dep = llist_rev(m->dep);
    435 
    436   while (m->dep) {
    437     struct module_s *m2;
    438     char *fn, *options;
    439 
    440     rc = 0;
    441     fn = llist_popme(&m->dep);
    442     m2 = get_mod(fn, 1);
    443     // are we removing ?
    444     if (toys.optflags & FLAG_r) {
    445       if (m2->flags & MOD_ALOADED) {
    446         if ((rc = rm_mod(m2->name, O_EXCL))) {
    447           if (first) {
    448             perror_msg("can't unload module %s", m2->name);
    449             break;
    450           }
    451         } else m2->flags &= ~MOD_ALOADED;
    452       }
    453       first = 0;
    454       continue;
    455     }
    456     options = m2->opts;
    457     m2->opts = NULL;
    458     if (m == m2) options = add_opts(options, TT.cmdopts);
    459 
    460     // are we only checking dependencies ?
    461     if (toys.optflags & FLAG_D) {
    462       TT.dbg(options ? "insmod %s %s\n" : "insmod %s\n", fn, options);
    463       if (options) free(options);
    464       continue;
    465     }
    466     if (m2->flags & MOD_ALOADED) {
    467       TT.dbg("%s is already loaded, skipping\n", fn);
    468       if (options) free(options);
    469       continue;
    470     }
    471     // none of above is true insert the module.
    472     rc = ins_mod(fn, options);
    473     TT.dbg("loaded %s '%s', rc:%d\n", fn, options, rc);
    474     if (rc == EEXIST) rc = 0;
    475     if (options) free(options);
    476     if (rc) {
    477       perror_msg("can't load module %s (%s)", m2->name, fn);
    478       break;
    479     }
    480     m2->flags |= MOD_ALOADED;
    481   }
    482   return rc;
    483 }
    484 
    485 void modprobe_main(void)
    486 {
    487   struct utsname uts;
    488   char **argv = toys.optargs, *procline = NULL;
    489   FILE *fs;
    490   struct module_s *module;
    491   unsigned flags = toys.optflags;
    492 
    493   TT.dbg = (flags & FLAG_v) ? xprintf : dummy;
    494 
    495   if ((toys.optc < 1) && (((flags & FLAG_r) && (flags & FLAG_l))
    496         ||(!((flags & FLAG_r)||(flags & FLAG_l)))))
    497   {
    498 	  help_exit("bad syntax");
    499   }
    500   // Check for -r flag without arg if yes then do auto remove.
    501   if ((flags & FLAG_r) && !toys.optc) {
    502     if (rm_mod(NULL, O_NONBLOCK | O_EXCL)) perror_exit("rmmod");
    503     return;
    504   }
    505 
    506   // change directory to /lib/modules/<release>/
    507   xchdir("/lib/modules");
    508   uname(&uts);
    509   xchdir(uts.release);
    510 
    511   // modules.dep processing for dependency check.
    512   if (flags & FLAG_l) {
    513     if (depmode_read_entry(toys.optargs[0])) error_exit("no module found.");
    514     return;
    515   }
    516   // Read /proc/modules to get loaded modules.
    517   fs = xfopen("/proc/modules", "r");
    518 
    519   while (read_line(fs, &procline) > 0) {
    520     *(strchr(procline, ' ')) = '\0';
    521     get_mod(procline, 1)->flags = MOD_ALOADED;
    522     free(procline);
    523     procline = NULL;
    524   }
    525   fclose(fs);
    526   if ((flags & FLAG_a) || (flags & FLAG_r)) {
    527     do {
    528       add_mod(*argv++);
    529     } while (*argv);
    530   } else {
    531     add_mod(argv[0]);
    532     TT.cmdopts = add_cmdopt(argv);
    533   }
    534   if (!TT.probes) {
    535     TT.dbg("All modules loaded\n");
    536     return;
    537   }
    538   dirtree_read("/etc/modprobe.conf", config_action);
    539   dirtree_read("/etc/modprobe.d", config_action);
    540   if (TT.symreq) dirtree_read("modules.symbols", config_action);
    541   if (TT.nudeps) dirtree_read("modules.alias", config_action);
    542   find_dep();
    543   while ((module = llist_popme(&TT.probes))) {
    544     if (!module->rnames) {
    545       TT.dbg("probing by module name\n");
    546       /* This is not an alias. Literal names are blacklisted
    547        * only if '-b' is given.
    548        */
    549       if (!(flags & FLAG_b) || !(module->flags & MOD_BLACKLIST))
    550         go_probe(module);
    551       continue;
    552     }
    553     do { // Probe all real names for the alias.
    554       char *real = ((struct arg_list*)llist_pop(&module->rnames))->arg;
    555       struct module_s *m2 = get_mod(real, 0);
    556 
    557       TT.dbg("probing alias %s by realname %s\n", module->name, real);
    558       if (!m2) continue;
    559       if (!(m2->flags & MOD_BLACKLIST)
    560           && (!(m2->flags & MOD_ALOADED) || (flags & (FLAG_r | FLAG_D))))
    561         go_probe(m2);
    562       free(real);
    563     } while (module->rnames);
    564   }
    565 }
    566