• 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     -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