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