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