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 -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
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 #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
49 struct 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.
path2mod(char * file,char * mod)56 static 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.
add_opts(char * opts,char * toadd)73 static 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.
llist_popme(struct arg_list ** head)86 static 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.
llist_add(struct arg_list ** old,void * data)100 static 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.
llist_add_tail(struct arg_list ** head,void * data)110 static 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.
llist_rev(struct arg_list * list)118 static 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 */
get_mod(char * mod,uint8_t add)136 static 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 */
read_line(FILE * fl,char ** li)161 static 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 */
config_action(struct dirtree * node)207 static 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.
depmode_read_entry(char * cmdname)280 static 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.
find_dep(void)307 static 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.
rm_mod(char * modules)342 static 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.
ins_mod(char * modules,char * flags)352 static 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.
add_mod(char * name)361 static 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.
add_cmdopt(char ** argv)378 static 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.
go_probe(struct module_s * m)398 static 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
modprobe_main(void)461 void 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