Revert back to upstream 0.6.0 and remove all except for dhcp related
[platform/upstream/toybox.git] / toys / pending / modprobe.c
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
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.
65 static char *path2mod(char *file, char *mod)
66 {
67   int i;
68   char *from, *lslash;
69
70   if (!file) return NULL;
71   if (!mod) mod = xmalloc(MODNAME_LEN);
72         
73   lslash = strrchr(file, '/');
74   if (!lslash || (lslash == file && !lslash[1])) from = file;
75   else from = lslash + 1;
76   
77   for (i = 0; i < (MODNAME_LEN-1) && from[i] && from[i] != '.'; i++)
78     mod[i] = (from[i] == '-') ? '_' : from[i];
79   mod[i] = '\0';
80   return mod;
81 }
82
83 // Add options in opts from toadd.
84 static char *add_opts(char *opts, char *toadd)
85 {
86   if (toadd) {
87     int optlen = 0;
88
89     if (opts) optlen = strlen(opts);
90     opts = xrealloc(opts, optlen + strlen(toadd) + 2);
91     sprintf(opts + optlen, " %s", toadd);
92   }
93   return opts;
94 }
95
96 // Remove first element from the list and return it.
97 static void *llist_popme(struct arg_list **head)
98 {
99   char *data = NULL;
100   struct arg_list *temp = *head;
101
102   if (temp) {
103     data = temp->arg;
104     *head = temp->next;
105     free(temp);
106   }
107   return data;
108 }
109
110 // Add new node at the beginning of the list.
111 static void llist_add(struct arg_list **old, void *data)
112 {
113   struct arg_list *new = xmalloc(sizeof(struct arg_list));
114
115   new->arg = (char*)data;
116   new->next = *old;
117   *old = new;
118 }
119
120 // Add new node at tail of list.
121 static void llist_add_tail(struct arg_list **head, void *data)
122 {
123   while (*head) head = &(*head)->next;
124   *head = xzalloc(sizeof(struct arg_list));
125   (*head)->arg = (char*)data;
126 }
127
128 // Reverse list order.
129 static struct arg_list *llist_rev(struct arg_list *list)
130 {
131   struct arg_list *rev = NULL;
132
133   while (list) {
134     struct arg_list *next = list->next;
135
136     list->next = rev;
137     rev = list;
138     list = next;
139   }
140   return rev;
141 }
142
143 /*
144  * Returns struct module_s from the data base if found, NULL otherwise.
145  * if add - create module entry, add it to data base and return the same mod.
146  */
147 static struct module_s *get_mod(char *mod, uint8_t add)
148 {
149   char name[MODNAME_LEN];
150   struct module_s *modentry;
151   struct arg_list *temp;
152   unsigned i, hash = 0;
153
154   path2mod(mod, name);
155   for (i = 0; name[i]; i++) hash = ((hash*31) + hash) + name[i];
156   hash %= DBASE_SIZE;
157   for (temp = TT.dbase[hash]; temp; temp = temp->next) {
158     modentry = (struct module_s *) temp->arg;
159     if (!strcmp(modentry->name, name)) return modentry;
160   }
161   if (!add) return NULL;
162   modentry = xzalloc(sizeof(*modentry));
163   modentry->name = xstrdup(name);
164   llist_add(&TT.dbase[hash], modentry);
165   return modentry;
166 }
167
168 /*
169  * Read a line from file with \ continuation and escape commented line.
170  * Return the line in allocated string (*li)
171  */
172 static int read_line(FILE *fl, char **li)
173 {
174   char *nxtline = NULL, *line;
175   int len, nxtlen, linelen, nxtlinelen;
176
177   while (1) {
178     line = NULL;
179     linelen = nxtlinelen = 0;
180     len = getline(&line, (size_t*)&linelen, fl);
181     if (len <= 0) {
182       free(line);
183       return len;
184     }
185     // checking for commented lines.
186     if (line[0] != '#') break;
187     free(line);
188   }
189   for (;;) {
190     if (line[len - 1] == '\n') len--;
191     if (!len) { 
192       free(line);
193       return len;
194     } else if (line[len - 1] != '\\') break;
195     
196     len--;
197     nxtlen = getline(&nxtline, (size_t*)&nxtlinelen, fl);
198     if (nxtlen <= 0) break;
199     if (linelen < len + nxtlen + 1) {
200       linelen = len + nxtlen + 1;
201       line = xrealloc(line, linelen);
202     }
203     memcpy(&line[len], nxtline, nxtlen);
204     len += nxtlen;
205   }
206   line[len] = '\0';
207   *li = xstrdup(line);
208   free(line);
209   if (nxtline) free(nxtline);
210   return len;
211 }
212
213 /*
214  * Action to be taken on all config files in default directories
215  * checks for aliases, options, install, remove and blacklist
216  */
217 static int config_action(struct dirtree *node)
218 {
219   FILE *fc;
220   char *filename, *tokens[3], *line, *linecp;
221   struct module_s *modent;
222   int tcount = 0;
223
224   if (!dirtree_notdotdot(node)) return 0;
225   if (S_ISDIR(node->st.st_mode)) return DIRTREE_RECURSE;
226
227   if (!S_ISREG(node->st.st_mode)) return 0; // process only regular file
228   filename = dirtree_path(node, NULL);
229   if (!(fc = fopen(filename, "r"))) {
230     free(filename);
231     return 0;
232   }
233   for (line = linecp = NULL; read_line(fc, &line) > 0; 
234       free(line), free(linecp), line = linecp = NULL) {
235     char *tk = NULL;
236
237     if (!strlen(line)) continue; 
238     linecp = xstrdup(line);
239     for (tk = strtok(linecp, "# \t"), tcount = 0; tk;
240         tk = strtok(NULL, "# \t"), tcount++) {
241       tokens[tcount] = tk;
242       if (tcount == 2) {
243         tokens[2] = line + strlen(tokens[0]) + strlen(tokens[1]) + 2;
244         break;
245       }
246     }
247     if (!tk) continue; 
248     // process the tokens[0] contains first word of config line.
249     if (!strcmp(tokens[0], "alias")) {
250       struct arg_list *temp;
251       char aliase[MODNAME_LEN], *realname;
252
253       if (!tokens[2]) continue;
254       path2mod(tokens[1], aliase);
255       for (temp = TT.probes; temp; temp = temp->next) {
256         modent = (struct module_s *) temp->arg;
257         if (fnmatch(aliase, modent->name, 0)) continue;
258         realname = path2mod(tokens[2], NULL);
259         llist_add(&modent->rnames, realname);
260         if (modent->flags & MOD_NDDEPS) {
261           modent->flags &= ~MOD_NDDEPS;
262           TT.nudeps--;
263         }
264         modent = get_mod(realname, 1);
265         if (!(modent->flags & MOD_NDDEPS)) {
266           modent->flags |= MOD_NDDEPS;
267           TT.nudeps++;
268         }
269       }
270     } else if (!strcmp(tokens[0], "options")) {
271       if (!tokens[2]) continue;
272       modent = get_mod(tokens[1], 1);
273       modent->opts = add_opts(modent->opts, tokens[2]);
274     } else if (!strcmp(tokens[0], "include"))
275       dirtree_read(tokens[1], config_action);
276     else if (!strcmp(tokens[0], "blacklist"))
277       get_mod(tokens[1], 1)->flags |= MOD_BLACKLIST;
278     else if (!strcmp(tokens[0], "install")) continue;
279     else if (!strcmp(tokens[0], "remove")) continue;
280     else error_msg("Invalid option %s found in file %s", tokens[0], filename);
281   }
282   fclose(fc);
283   free(filename);
284   return 0;
285 }
286
287 // Show matched modules else return -1 on failure.
288 static int depmode_read_entry(char *cmdname)
289 {
290   char *line;
291   int ret = -1;
292   FILE *fe = xfopen("modules.dep", "r");
293
294   while (read_line(fe, &line) > 0) {
295     char *tmp = strchr(line, ':');
296
297     if (tmp) {
298       *tmp = '\0';
299      char *name = basename(line);
300
301       tmp = strchr(name, '.');
302       if (tmp) *tmp = '\0';
303       if (!cmdname || !fnmatch(cmdname, name, 0)) {
304         if (tmp) *tmp = '.';
305         TT.dbg("%s\n", line);
306         ret = 0;
307       }
308     }
309     free(line);
310   }
311   fclose(fe);
312   return ret;
313 }
314
315 // Finds dependencies for modules from the modules.dep file.
316 static void find_dep(void)
317 {
318   char *line = NULL;
319   struct module_s *mod;
320   FILE *fe = xfopen("modules.dep", "r");
321
322   for (; read_line(fe, &line) > 0; free(line)) {
323     char *tmp = strchr(line, ':');
324
325     if (tmp) {
326       *tmp = '\0';
327       mod = get_mod(line, 0);
328       if (!mod) continue;
329       if ((mod->flags & MOD_ALOADED) &&
330           !(toys.optflags & (FLAG_r | FLAG_D))) continue;
331       
332       mod->flags |= MOD_FNDDEPMOD;
333       if ((mod->flags & MOD_NDDEPS) && (!mod->dep)) {
334         TT.nudeps--;
335         llist_add(&mod->dep, xstrdup(line));
336         tmp++;
337         if (*tmp) {
338           char *tok;
339
340           while ((tok = strsep(&tmp, " \t"))) {
341             if (!*tok) continue;
342             llist_add_tail(&mod->dep, xstrdup(tok));
343           }
344         }
345       }
346     }
347   }
348   fclose(fe);
349 }
350
351 // Remove a module from the Linux Kernel. if !modules does auto remove.
352 static int rm_mod(char *modules, uint32_t flags)
353 {
354   if (modules) {
355     int len = strlen(modules);
356
357     if (len > 3 && !strcmp(modules+len-3, ".ko" )) modules[len-3] = 0;
358   }
359
360   errno = 0;
361   syscall(__NR_delete_module, modules, flags ? flags : O_NONBLOCK|O_EXCL);
362
363   return errno;
364 }
365
366 // Insert module same as insmod implementation.
367 static int ins_mod(char *modules, char *flags)
368 {
369   char *buf = NULL;
370   int len, res;
371   int fd = xopen(modules, O_RDONLY);
372
373   len = fdlength(fd);
374   buf = xmalloc(len);
375   xreadall(fd, buf, len);
376   xclose(fd);
377
378   while (flags && strlen(toybuf) + strlen(flags) + 2 < sizeof(toybuf)) {
379     strcat(toybuf, flags);
380     strcat(toybuf, " ");
381   }
382   res = syscall(__NR_init_module, buf, len, toybuf);
383   if (CFG_TOYBOX_FREE && buf != toybuf) free(buf);
384   if (res) perror_exit("failed to load %s ", toys.optargs[0]);
385   return res;
386 }
387
388 // Add module in probes list, if not loaded.
389 static void add_mod(char *name)
390 {
391   struct module_s *mod = get_mod(name, 1);
392
393   if (!(toys.optflags & (FLAG_r | FLAG_D)) && (mod->flags & MOD_ALOADED)) {
394     TT.dbg("skipping %s, it is already loaded\n", name);
395     return;
396   }
397   TT.dbg("queuing %s\n", name);
398   mod->cmdname = name;
399   mod->flags |= MOD_NDDEPS;
400   llist_add_tail(&TT.probes, mod);
401   TT.nudeps++;
402   if (!strncmp(mod->name, "symbol:", 7)) TT.symreq = 1;
403 }
404
405 // Parse cmdline options suplied for module.
406 static char *add_cmdopt(char **argv)
407 {
408   char *opt = xzalloc(1);
409   int lopt = 0;
410
411   while (*++argv) {
412     char *fmt, *var, *val;
413
414     var = *argv;
415     opt = xrealloc(opt, lopt + 2 + strlen(var) + 2);
416     // check for key=val or key = val.
417     fmt = "%.*s%s ";
418     for (val = var; *val && *val != '='; val++);
419     if (*val && strchr(++val, ' ')) fmt = "%.*s\"%s\" ";
420     lopt += sprintf(opt + lopt, fmt, (int) (val - var), var, val);
421   }
422   return opt;
423 }
424
425 // Probes a single module and loads all its dependencies.
426 static int go_probe(struct module_s *m)
427 {
428   int rc = 0, first = 1;
429
430   if (!(m->flags & MOD_FNDDEPMOD)) {
431     if (!(toys.optflags & FLAG_s))
432       error_msg("module %s not found in modules.dep", m->name);
433     return -ENOENT;
434   }
435   TT.dbg("go_prob'ing %s\n", m->name);
436   if (!(toys.optflags & 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 (toys.optflags & 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     options = m2->opts;
459     m2->opts = NULL;
460     if (m == m2) options = add_opts(options, TT.cmdopts);
461
462     // are we only checking dependencies ?
463     if (toys.optflags & FLAG_D) {
464       TT.dbg(options ? "insmod %s %s\n" : "insmod %s\n", fn, options);
465       if (options) free(options);
466       continue;
467     }
468     if (m2->flags & MOD_ALOADED) {
469       TT.dbg("%s is already loaded, skipping\n", fn);
470       if (options) free(options);
471       continue;
472     }
473     // none of above is true insert the module.
474     rc = ins_mod(fn, options);
475     TT.dbg("loaded %s '%s', rc:%d\n", fn, options, rc);
476     if (rc == EEXIST) rc = 0;
477     if (options) free(options);
478     if (rc) {
479       perror_msg("can't load module %s (%s)", m2->name, fn);
480       break;
481     }
482     m2->flags |= MOD_ALOADED;
483   }
484   return rc;
485 }
486
487 void modprobe_main(void)
488 {
489   struct utsname uts;
490   char **argv = toys.optargs, *procline = NULL;
491   FILE *fs;
492   struct module_s *module;
493   unsigned flags = toys.optflags;
494
495   TT.dbg = (flags & FLAG_v) ? xprintf : dummy;
496
497   if ((toys.optc < 1) && (((flags & FLAG_r) && (flags & FLAG_l))
498         ||(!((flags & FLAG_r)||(flags & FLAG_l)))))
499   {
500           toys.exithelp++;
501           error_exit("bad syntax");
502   }
503   // Check for -r flag without arg if yes then do auto remove.
504   if ((flags & FLAG_r) && !toys.optc) {
505     if (rm_mod(NULL, O_NONBLOCK | O_EXCL)) perror_exit("rmmod");
506     return;
507   }
508
509   // change directory to /lib/modules/<release>/ 
510   xchdir("/lib/modules");
511   uname(&uts);
512   xchdir(uts.release);
513
514   // modules.dep processing for dependency check.
515   if (flags & FLAG_l) {
516     if (depmode_read_entry(toys.optargs[0])) error_exit("no module found.");
517     return;
518   }
519   // Read /proc/modules to get loaded modules.
520   fs = xfopen("/proc/modules", "r");
521   
522   while (read_line(fs, &procline) > 0) {
523     *(strchr(procline, ' ')) = '\0';
524     get_mod(procline, 1)->flags = MOD_ALOADED;
525     free(procline);
526     procline = NULL;
527   }
528   fclose(fs);
529   if ((flags & FLAG_a) || (flags & FLAG_r)) {
530     do {
531       add_mod(*argv++);
532     } while (*argv);
533   } else {
534     add_mod(argv[0]);
535     TT.cmdopts = add_cmdopt(argv);
536   }
537   if (!TT.probes) {
538     TT.dbg("All modules loaded\n");
539     return;
540   }
541   dirtree_read("/etc/modprobe.conf", config_action);
542   dirtree_read("/etc/modprobe.d", config_action);
543   if (TT.symreq) dirtree_read("modules.symbols", config_action);
544   if (TT.nudeps) dirtree_read("modules.alias", config_action);
545   find_dep();
546   while ((module = llist_popme(&TT.probes))) {
547     if (!module->rnames) {
548       TT.dbg("probing by module name\n");
549       /* This is not an alias. Literal names are blacklisted
550        * only if '-b' is given.
551        */
552       if (!(flags & FLAG_b) || !(module->flags & MOD_BLACKLIST))
553         go_probe(module);
554       continue;
555     }
556     do { // Probe all real names for the alias.
557       char *real = ((struct arg_list*)llist_pop(&module->rnames))->arg;
558       struct module_s *m2 = get_mod(real, 0);
559       
560       TT.dbg("probing alias %s by realname %s\n", module->name, real);
561       if (!m2) continue;
562       if (!(m2->flags & MOD_BLACKLIST) 
563           && (!(m2->flags & MOD_ALOADED) || (flags & (FLAG_r | FLAG_D))))
564         go_probe(m2);
565       free(real);
566     } while (module->rnames);
567   }
568 }