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