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