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    -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
  32GLOBALS(
  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
  55struct 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.
  62static 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.
  79static 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.
  92static 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.
 106static 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.
 116static 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.
 124static 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 */
 142static 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 */
 167static 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 */
 213static 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.
 285static 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.
 313static 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.
 349static 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.
 364static 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.
 396static 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.
 413static 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.
 433static 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    errno = 0;
 484    rc = ins_mod(fn, options);
 485    if (toys.optflags&FLAG_v)
 486      printf("loaded %s '%s': %s\n", fn, options, strerror(errno));
 487    if (errno == EEXIST) rc = 0;
 488    free(options);
 489    if (rc) {
 490      perror_msg("can't load module %s (%s)", m2->name, fn);
 491      break;
 492    }
 493    m2->flags |= MOD_ALOADED;
 494  }
 495  return rc;
 496}
 497
 498void modprobe_main(void)
 499{
 500  struct utsname uts;
 501  char **argv = toys.optargs, *procline = NULL;
 502  FILE *fs;
 503  struct module_s *module;
 504  unsigned flags = toys.optflags;
 505  struct arg_list *dirs;
 506
 507  if ((toys.optc < 1) && (((flags & FLAG_r) && (flags & FLAG_l))
 508        ||(!((flags & FLAG_r)||(flags & FLAG_l)))))
 509  {
 510    help_exit("bad syntax");
 511  }
 512  // Check for -r flag without arg if yes then do auto remove.
 513  if ((flags & FLAG_r) && !toys.optc) {
 514    if (rm_mod(NULL, O_NONBLOCK | O_EXCL)) perror_exit("rmmod");
 515    return;
 516  }
 517
 518  if (!TT.dirs) {
 519    uname(&uts);
 520    TT.dirs = xzalloc(sizeof(struct arg_list));
 521    TT.dirs->arg = xmprintf("/lib/modules/%s", uts.release);
 522  }
 523
 524  // modules.dep processing for dependency check.
 525  if (flags & FLAG_l) {
 526    for (dirs = TT.dirs; dirs; dirs = dirs->next) {
 527      xchdir(dirs->arg);
 528      if (!depmode_read_entry(toys.optargs[0])) 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_flagread("/etc/modprobe.conf", DIRTREE_SHUTUP, config_action);
 556  dirtree_flagread("/etc/modprobe.d", DIRTREE_SHUTUP, 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