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