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