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