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, "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.
path2mod(char * file,char * mod)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.
add_opts(char * opts,char * 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.
llist_popme(struct arg_list ** head)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.
llist_add(struct arg_list ** old,void * data)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.
llist_add_tail(struct arg_list ** head,void * data)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.
llist_rev(struct arg_list * list)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 */
get_mod(char * mod,uint8_t add)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 */
read_line(FILE * fl,char ** li)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 */
config_action(struct dirtree * node)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.
depmode_read_entry(char * cmdname)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.
find_dep(void)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.
rm_mod(char * modules,uint32_t flags)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.
ins_mod(char * modules,char * flags)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.
add_mod(char * name)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.
add_cmdopt(char ** argv)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.
go_probe(struct module_s * m)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 errno = 0;
484 rc = ins_mod(fn, options);
485 if (toys.optflags&FLAG_v)
486 printf("loaded %s '%s': %s\n", fn, options, strerror(errno));
487 if (errno == EEXIST) rc = 0;
488 free(options);
489 if (rc) {
490 perror_msg("can't load module %s (%s)", m2->name, fn);
491 break;
492 }
493 m2->flags |= MOD_ALOADED;
494 }
495 return rc;
496 }
497
modprobe_main(void)498 void modprobe_main(void)
499 {
500 struct utsname uts;
501 char **argv = toys.optargs, *procline = NULL;
502 FILE *fs;
503 struct module_s *module;
504 unsigned flags = toys.optflags;
505 struct arg_list *dirs;
506
507 if ((toys.optc < 1) && (((flags & FLAG_r) && (flags & FLAG_l))
508 ||(!((flags & FLAG_r)||(flags & FLAG_l)))))
509 {
510 help_exit("bad syntax");
511 }
512 // Check for -r flag without arg if yes then do auto remove.
513 if ((flags & FLAG_r) && !toys.optc) {
514 if (rm_mod(NULL, O_NONBLOCK | O_EXCL)) perror_exit("rmmod");
515 return;
516 }
517
518 if (!TT.dirs) {
519 uname(&uts);
520 TT.dirs = xzalloc(sizeof(struct arg_list));
521 TT.dirs->arg = xmprintf("/lib/modules/%s", uts.release);
522 }
523
524 // modules.dep processing for dependency check.
525 if (flags & FLAG_l) {
526 for (dirs = TT.dirs; dirs; dirs = dirs->next) {
527 xchdir(dirs->arg);
528 if (!depmode_read_entry(toys.optargs[0])) 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_flagread("/etc/modprobe.conf", DIRTREE_SHUTUP, config_action);
556 dirtree_flagread("/etc/modprobe.d", DIRTREE_SHUTUP, 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