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