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