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