1 /* modprobe.c - modprobe utility.
3 * Copyright 2012 Madhur Verma <mad.flexi@gmail.com>
4 * Copyright 2013 Kyungwan Han <asura321@gmail.com>
8 USE_MODPROBE(NEWTOY(modprobe, "alrqvsDb", TOYFLAG_SBIN))
14 usage: modprobe [-alrqvsDb] MODULE [symbol=value][...]
16 modprobe utility - inserts modules and dependencies.
18 -a Load multiple MODULEs
19 -l List (MODULE is a pattern)
20 -r Remove MODULE (stacks) or do autoclean
25 -b Apply blacklist to module names too
29 #include <sys/syscall.h>
32 struct arg_list *probes;
33 struct arg_list *dbase[256];
37 void (*dbg)(char *format, ...);
40 /* Note: if "#define DBASE_SIZE" modified,
41 * Please update GLOBALS dbase[256] accordingly.
43 #define DBASE_SIZE 256
44 #define MODNAME_LEN 256
46 // Modules flag definations
47 #define MOD_ALOADED 0x0001
48 #define MOD_BLACKLIST 0x0002
49 #define MOD_FNDDEPMOD 0x0004
50 #define MOD_NDDEPS 0x0008
52 // dummy interface for debugging.
53 static void dummy(char *format, ...)
57 // Current probing modules info
60 char *cmdname, *name, *depent, *opts;
61 struct arg_list *rnames, *dep;
64 // Converts path name FILE to module name.
65 static char *path2mod(char *file, char *mod)
70 if (!file) return NULL;
71 if (!mod) mod = xmalloc(MODNAME_LEN);
73 lslash = strrchr(file, '/');
74 if (!lslash || (lslash == file && !lslash[1])) from = file;
75 else from = lslash + 1;
77 for (i = 0; i < (MODNAME_LEN-1) && from[i] && from[i] != '.'; i++)
78 mod[i] = (from[i] == '-') ? '_' : from[i];
83 // Add options in opts from toadd.
84 static char *add_opts(char *opts, char *toadd)
89 if (opts) optlen = strlen(opts);
90 opts = xrealloc(opts, optlen + strlen(toadd) + 2);
91 sprintf(opts + optlen, " %s", toadd);
96 // Remove first element from the list and return it.
97 static void *llist_popme(struct arg_list **head)
100 struct arg_list *temp = *head;
110 // Add new node at the beginning of the list.
111 static void llist_add(struct arg_list **old, void *data)
113 struct arg_list *new = xmalloc(sizeof(struct arg_list));
115 new->arg = (char*)data;
120 // Add new node at tail of list.
121 static void llist_add_tail(struct arg_list **head, void *data)
123 while (*head) head = &(*head)->next;
124 *head = xzalloc(sizeof(struct arg_list));
125 (*head)->arg = (char*)data;
128 // Reverse list order.
129 static struct arg_list *llist_rev(struct arg_list *list)
131 struct arg_list *rev = NULL;
134 struct arg_list *next = list->next;
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.
147 static struct module_s *get_mod(char *mod, uint8_t add)
149 char name[MODNAME_LEN];
150 struct module_s *modentry;
151 struct arg_list *temp;
152 unsigned i, hash = 0;
155 for (i = 0; name[i]; i++) hash = ((hash*31) + hash) + name[i];
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;
161 if (!add) return NULL;
162 modentry = xzalloc(sizeof(*modentry));
163 modentry->name = xstrdup(name);
164 llist_add(&TT.dbase[hash], modentry);
169 * Read a line from file with \ continuation and escape commented line.
170 * Return the line in allocated string (*li)
172 static int read_line(FILE *fl, char **li)
174 char *nxtline = NULL, *line;
175 int len, nxtlen, linelen, nxtlinelen;
179 linelen = nxtlinelen = 0;
180 len = getline(&line, (size_t*)&linelen, fl);
185 // checking for commented lines.
186 if (line[0] != '#') break;
190 if (line[len - 1] == '\n') len--;
194 } else if (line[len - 1] != '\\') break;
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);
203 memcpy(&line[len], nxtline, nxtlen);
209 if (nxtline) free(nxtline);
214 * Action to be taken on all config files in default directories
215 * checks for aliases, options, install, remove and blacklist
217 static int config_action(struct dirtree *node)
220 char *filename, *tokens[3], *line, *linecp;
221 struct module_s *modent;
224 if (!dirtree_notdotdot(node)) return 0;
225 if (S_ISDIR(node->st.st_mode)) return DIRTREE_RECURSE;
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"))) {
233 for (line = linecp = NULL; read_line(fc, &line) > 0;
234 free(line), free(linecp), line = linecp = NULL) {
237 if (!strlen(line)) continue;
238 linecp = xstrdup(line);
239 for (tk = strtok(linecp, "# \t"), tcount = 0; tk;
240 tk = strtok(NULL, "# \t"), tcount++) {
243 tokens[2] = line + strlen(tokens[0]) + strlen(tokens[1]) + 2;
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;
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;
264 modent = get_mod(realname, 1);
265 if (!(modent->flags & MOD_NDDEPS)) {
266 modent->flags |= MOD_NDDEPS;
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);
287 // Show matched modules else return -1 on failure.
288 static int depmode_read_entry(char *cmdname)
292 FILE *fe = xfopen("modules.dep", "r");
294 while (read_line(fe, &line) > 0) {
295 char *tmp = strchr(line, ':');
299 char *name = basename(line);
301 tmp = strchr(name, '.');
302 if (tmp) *tmp = '\0';
303 if (!cmdname || !fnmatch(cmdname, name, 0)) {
305 TT.dbg("%s\n", line);
315 // Finds dependencies for modules from the modules.dep file.
316 static void find_dep(void)
319 struct module_s *mod;
320 FILE *fe = xfopen("modules.dep", "r");
322 for (; read_line(fe, &line) > 0; free(line)) {
323 char *tmp = strchr(line, ':');
327 mod = get_mod(line, 0);
329 if ((mod->flags & MOD_ALOADED) &&
330 !(toys.optflags & (FLAG_r | FLAG_D))) continue;
332 mod->flags |= MOD_FNDDEPMOD;
333 if ((mod->flags & MOD_NDDEPS) && (!mod->dep)) {
335 llist_add(&mod->dep, xstrdup(line));
340 while ((tok = strsep(&tmp, " \t"))) {
342 llist_add_tail(&mod->dep, xstrdup(tok));
351 // Remove a module from the Linux Kernel. if !modules does auto remove.
352 static int rm_mod(char *modules, uint32_t flags)
355 int len = strlen(modules);
357 if (len > 3 && !strcmp(modules+len-3, ".ko" )) modules[len-3] = 0;
361 syscall(__NR_delete_module, modules, flags ? flags : O_NONBLOCK|O_EXCL);
366 // Insert module same as insmod implementation.
367 static int ins_mod(char *modules, char *flags)
371 int fd = xopen(modules, O_RDONLY);
375 xreadall(fd, buf, len);
378 while (flags && strlen(toybuf) + strlen(flags) + 2 < sizeof(toybuf)) {
379 strcat(toybuf, flags);
382 res = syscall(__NR_init_module, buf, len, toybuf);
383 if (CFG_TOYBOX_FREE && buf != toybuf) free(buf);
387 // Add module in probes list, if not loaded.
388 static void add_mod(char *name)
390 struct module_s *mod = get_mod(name, 1);
392 if (!(toys.optflags & (FLAG_r | FLAG_D)) && (mod->flags & MOD_ALOADED)) {
393 TT.dbg("skipping %s, it is already loaded\n", name);
396 TT.dbg("queuing %s\n", name);
398 mod->flags |= MOD_NDDEPS;
399 llist_add_tail(&TT.probes, mod);
401 if (!strncmp(mod->name, "symbol:", 7)) TT.symreq = 1;
404 // Parse cmdline options suplied for module.
405 static char *add_cmdopt(char **argv)
407 char *opt = xzalloc(1);
411 char *fmt, *var, *val;
414 opt = xrealloc(opt, lopt + 2 + strlen(var) + 2);
415 // check for key=val or key = val.
417 for (val = var; *val && *val != '='; val++);
418 if (*val && strchr(++val, ' ')) fmt = "%.*s\"%s\" ";
419 lopt += sprintf(opt + lopt, fmt, (int) (val - var), var, val);
424 // Probes a single module and loads all its dependencies.
425 static int go_probe(struct module_s *m)
427 int rc = 0, first = 1;
429 if (!(m->flags & MOD_FNDDEPMOD)) {
430 if (!(toys.optflags & FLAG_s))
431 error_msg("module %s not found in modules.dep", m->name);
434 TT.dbg("go_prob'ing %s\n", m->name);
435 if (!(toys.optflags & FLAG_r)) m->dep = llist_rev(m->dep);
442 fn = llist_popme(&m->dep);
445 if (toys.optflags & FLAG_r) {
446 if (m2->flags & MOD_ALOADED) {
447 if ((rc = rm_mod(m2->name, O_EXCL))) {
449 perror_msg("can't unload module %s", m2->name);
452 } else m2->flags &= ~MOD_ALOADED;
459 if (m == m2) options = add_opts(options, TT.cmdopts);
461 // are we only checking dependencies ?
462 if (toys.optflags & FLAG_D) {
463 TT.dbg(options ? "insmod %s %s\n" : "insmod %s\n", fn, options);
464 if (options) free(options);
467 if (m2->flags & MOD_ALOADED) {
468 TT.dbg("%s is already loaded, skipping\n", fn);
469 if (options) free(options);
472 // none of above is true insert the module.
473 rc = ins_mod(fn, options);
474 TT.dbg("loaded %s '%s', rc:%d\n", fn, options, rc);
475 if (rc == EEXIST) rc = 0;
476 if (options) free(options);
478 perror_msg("can't load module %s (%s)", m2->name, fn);
481 m2->flags |= MOD_ALOADED;
486 void modprobe_main(void)
489 char **argv = toys.optargs, *procline = NULL;
491 struct module_s *module;
492 unsigned flags = toys.optflags;
494 TT.dbg = (flags & FLAG_v) ? xprintf : dummy;
496 if ((toys.optc < 1) && (((flags & FLAG_r) && (flags & FLAG_l))
497 ||(!((flags & FLAG_r)||(flags & FLAG_l)))))
500 error_exit("bad syntax");
502 // Check for -r flag without arg if yes then do auto remove.
503 if ((flags & FLAG_r) && !toys.optc) {
504 if (rm_mod(NULL, O_NONBLOCK | O_EXCL)) perror_exit("rmmod");
508 // change directory to /lib/modules/<release>/
509 xchdir("/lib/modules");
513 // modules.dep processing for dependency check.
514 if (flags & FLAG_l) {
515 if (depmode_read_entry(toys.optargs[0])) error_exit("no module found.");
518 // Read /proc/modules to get loaded modules.
519 fs = xfopen("/proc/modules", "r");
521 while (read_line(fs, &procline) > 0) {
522 *(strchr(procline, ' ')) = '\0';
523 get_mod(procline, 1)->flags = MOD_ALOADED;
528 if ((flags & FLAG_a) || (flags & FLAG_r)) {
534 TT.cmdopts = add_cmdopt(argv);
537 TT.dbg("All modules loaded\n");
540 dirtree_read("/etc/modprobe.conf", config_action);
541 dirtree_read("/etc/modprobe.d", config_action);
542 if (TT.symreq) dirtree_read("modules.symbols", config_action);
543 if (TT.nudeps) dirtree_read("modules.alias", config_action);
545 while ((module = llist_popme(&TT.probes))) {
546 if (!module->rnames) {
547 TT.dbg("probing by module name\n");
548 /* This is not an alias. Literal names are blacklisted
549 * only if '-b' is given.
551 if (!(flags & FLAG_b) || !(module->flags & MOD_BLACKLIST))
555 do { // Probe all real names for the alias.
556 char *real = ((struct arg_list*)llist_pop(&module->rnames))->arg;
557 struct module_s *m2 = get_mod(real, 0);
559 TT.dbg("probing alias %s by realname %s\n", module->name, real);
561 if (!(m2->flags & MOD_BLACKLIST)
562 && (!(m2->flags & MOD_ALOADED) || (flags & (FLAG_r | FLAG_D))))
565 } while (module->rnames);