• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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