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