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
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
GLOBALS(struct arg_list * probes;struct arg_list * dbase[256];char * cmdopts;int nudeps;uint8_t symreq;void (* dbg)(char * format,...);)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.
path2mod(char * file,char * mod)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.
add_opts(char * opts,char * 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.
llist_popme(struct arg_list ** head)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.
llist_add(struct arg_list ** old,void * data)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.
llist_add_tail(struct arg_list ** head,void * data)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.
llist_rev(struct arg_list * list)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 */
get_mod(char * mod,uint8_t add)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 */
read_line(FILE * fl,char ** li)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 */
config_action(struct dirtree * node)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.
depmode_read_entry(char * cmdname)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.
find_dep(void)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.
rm_mod(char * modules,uint32_t flags)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.
ins_mod(char * modules,char * flags)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.
add_mod(char * name)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.
add_cmdopt(char ** argv)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.
go_probe(struct module_s * m)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
modprobe_main(void)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