1
2#include "arg.h"
3#include "fs.h"
4#include "util.h"
5
6#include <ctype.h>
7#include <dirent.h>
8#include <errno.h>
9#include <fcntl.h>
10#include <fnmatch.h>
11#include <stdarg.h>
12#include <stdio.h>
13#include <stdlib.h>
14#include <string.h>
15#include <sys/stat.h>
16#include <sys/syscall.h>
17#include <sys/types.h>
18#include <sys/utsname.h>
19#include <unistd.h>
20
21#if FEATURE_MODPROBE_SYSLOG
22#include <syslog.h>
23#endif
24
25#define HASH_SIZE 256
26#ifndef LINE_MAX
27#define LINE_MAX 4096
28#endif
29
30enum ModFlags {
31 MOD_LOADED = 1 << 0,
32 MOD_BLACKLISTED = 1 << 1,
33 MOD_QUEUED = 1 << 2,
34 MOD_SEEN_DEP = 1 << 3,
35};
36
37struct StrNode {
38 char *str;
39 struct StrNode *next;
40};
41
42struct Module {
43 char *name;
44 char *path;
45 char *options;
46 struct StrNode *deps;
47 struct StrNode *aliases;
48 int flags;
49 struct Module *next;
50};
51
52static struct Module *mod_db[HASH_SIZE];
53static struct StrNode *probes = NULL;
54static struct StrNode *moddirs = NULL;
55static char *cmdopts = NULL;
56
57static int aflag = 0;
58static int rflag = 0;
59static int qflag = 0;
60static int vflag = 0;
61static int lflag = 0;
62
63#if FEATURE_MODPROBE_SHOW_DEPENDS
64static int Dflag = 0;
65#endif
66
67#if FEATURE_MODPROBE_BLACKLIST
68static int bflag = 0;
69#endif
70
71#if FEATURE_MODPROBE_SYSLOG
72static int sflag = 0;
73#endif
74
75/* logging wrappers: handles syslog delegation and quiet mode suppression */
76
77static void
78pr_warn(const char *fmt, ...)
79{
80 va_list ap;
81 int saved_errno = errno;
82#if FEATURE_MODPROBE_SYSLOG
83 char buf[1024];
84#endif
85
86 if (qflag)
87 return;
88
89 va_start(ap, fmt);
90#if FEATURE_MODPROBE_SYSLOG
91 if (sflag) {
92 vsnprintf(buf, sizeof(buf), fmt, ap);
93 if (fmt[0] && fmt[strlen(fmt)-1] == ':')
94 syslog(LOG_ERR, "%s %s", buf, strerror(saved_errno));
95 else
96 syslog(LOG_ERR, "%s", buf);
97 va_end(ap);
98 return;
99 }
100#endif
101 fprintf(stderr, "%s: ", argv0);
102 vfprintf(stderr, fmt, ap);
103 if (fmt[0] && fmt[strlen(fmt)-1] == ':')
104 fprintf(stderr, " %s\n", strerror(saved_errno));
105 else
106 fputc('\n', stderr);
107 va_end(ap);
108}
109
110static void
111pr_info(const char *fmt, ...)
112{
113 va_list ap;
114
115 if (qflag)
116 return;
117
118 va_start(ap, fmt);
119#if FEATURE_MODPROBE_SYSLOG
120 if (sflag) {
121 vsyslog(LOG_INFO, fmt, ap);
122 va_end(ap);
123 return;
124 }
125#endif
126 vprintf(fmt, ap);
127 putchar('\n');
128 va_end(ap);
129}
130
131/* string structures: singly linked lists used for directories, deps, and aliases */
132
133static void
134strlist_append(struct StrNode **list, const char *str)
135{
136 struct StrNode *n, *tail;
137
138 n = ecalloc(1, sizeof(*n));
139 n->str = estrdup(str);
140
141 if (!*list) {
142 *list = n;
143 return;
144 }
145 for (tail = *list; tail->next; tail = tail->next)
146 ;
147 tail->next = n;
148}
149
150static char *
151append_opts(char *opts, const char *add)
152{
153 size_t olen, alen;
154 char *newopts;
155
156 if (!add)
157 return opts;
158 if (!opts)
159 return estrdup(add);
160
161 olen = strlen(opts);
162 alen = strlen(add);
163 newopts = ecalloc(1, olen + alen + 2);
164 memcpy(newopts, opts, olen);
165 newopts[olen] = ' ';
166 memcpy(newopts + olen + 1, add, alen);
167
168 return newopts;
169}
170
171/* module database: hash table allows fast realname queries for aliases */
172
173static unsigned int
174hash(const char *s)
175{
176 unsigned int h = 5381;
177
178 while (*s)
179 h = ((h << 5) + h) + *s++;
180 return h % HASH_SIZE;
181}
182
183static void
184normalize_name(char *dst, const char *src)
185{
186 const char *base;
187 int i;
188
189 base = strrchr(src, '/');
190 if (base)
191 base++;
192 else
193 base = src;
194
195 for (i = 0; i < 255 && base[i] && base[i] != '.'; i++)
196 dst[i] = (base[i] == '-') ? '_' : base[i];
197 dst[i] = '\0';
198}
199
200static struct Module *
201get_module(const char *path_or_name, int create)
202{
203 char name[256];
204 unsigned int h;
205 struct Module *m;
206
207 normalize_name(name, path_or_name);
208 h = hash(name);
209
210 for (m = mod_db[h]; m; m = m->next) {
211 if (strcmp(m->name, name) == 0)
212 return m;
213 }
214
215 if (!create)
216 return NULL;
217
218 m = ecalloc(1, sizeof(*m));
219 m->name = estrdup(name);
220 m->next = mod_db[h];
221 mod_db[h] = m;
222
223 return m;
224}
225
226/* config parsing: recursively loads aliases and module options from dir tree */
227
228static void
229parse_config_file(const char *path)
230{
231 FILE *fp;
232 char line[LINE_MAX];
233 char *p, *cmd, *arg1, *arg2;
234 struct Module *m;
235
236 if (!(fp = fopen(path, "r")))
237 return;
238
239 while (fgets(line, sizeof(line), fp)) {
240 p = strchr(line, '#');
241 if (p)
242 *p = '\0';
243
244 cmd = strtok(line, " \t\n");
245 if (!cmd)
246 continue;
247
248 arg1 = strtok(NULL, " \t\n");
249 if (!arg1)
250 continue;
251
252 if (strcmp(cmd, "alias") == 0) {
253 arg2 = strtok(NULL, " \t\n");
254 if (!arg2)
255 continue;
256 m = get_module(arg1, 1);
257 strlist_append(&m->aliases, arg2);
258 } else if (strcmp(cmd, "options") == 0) {
259 arg2 = strtok(NULL, "\n");
260 if (!arg2)
261 continue;
262 while (*arg2 == ' ' || *arg2 == '\t')
263 arg2++;
264 m = get_module(arg1, 1);
265 m->options = append_opts(m->options, arg2);
266 }
267#if FEATURE_MODPROBE_BLACKLIST
268 else if (strcmp(cmd, "blacklist") == 0) {
269 m = get_module(arg1, 1);
270 m->flags |= MOD_BLACKLISTED;
271 }
272#endif
273 }
274 fclose(fp);
275}
276
277static void
278config_cb(int fd, const char *path, struct stat *st, void *data, struct recursor *r)
279{
280 size_t len;
281
282 (void)fd;
283 (void)data;
284 (void)r;
285
286 if (S_ISREG(st->st_mode)) {
287 len = strlen(path);
288 if (len > 5 && strcmp(path + len - 5, ".conf") == 0)
289 parse_config_file(path);
290 }
291}
292
293static void
294read_configs(void)
295{
296 struct recursor r = { .fn = config_cb, .maxdepth = 1, .follow = 'H', .flags = DIRFIRST };
297 struct stat st;
298
299 parse_config_file("/etc/modprobe.conf");
300
301 if (stat("/etc/modprobe.d", &st) == 0 && S_ISDIR(st.st_mode))
302 recurse(AT_FDCWD, "/etc/modprobe.d", NULL, &r);
303}
304
305static void
306parse_dep_file(const char *path)
307{
308 FILE *fp;
309 char line[LINE_MAX];
310 char *p, *tok;
311 struct Module *m;
312
313 if (!(fp = fopen(path, "r"))) {
314 pr_warn("fopen %s:", path);
315 return;
316 }
317
318 while (fgets(line, sizeof(line), fp)) {
319 p = strchr(line, ':');
320 if (!p)
321 continue;
322 *p = '\0';
323 p++;
324
325 m = get_module(line, 1);
326 if (!m->path)
327 m->path = estrdup(line);
328 m->flags |= MOD_SEEN_DEP;
329
330 while ((tok = strtok(p, " \t\n"))) {
331 p = NULL;
332 if (*tok)
333 strlist_append(&m->deps, tok);
334 }
335 }
336 fclose(fp);
337}
338
339static void
340mark_loaded(void)
341{
342 FILE *fp;
343 char line[LINE_MAX];
344 char *p;
345 struct Module *m;
346
347 if (!(fp = fopen("/proc/modules", "r")))
348 return;
349
350 while (fgets(line, sizeof(line), fp)) {
351 p = strchr(line, ' ');
352 if (p)
353 *p = '\0';
354 else {
355 p = strchr(line, '\n');
356 if (p)
357 *p = '\0';
358 }
359 m = get_module(line, 1);
360 m->flags |= MOD_LOADED;
361 }
362 fclose(fp);
363}
364
365static void
366list_modules(const char *pattern)
367{
368 FILE *fp;
369 char line[LINE_MAX];
370 char *p, *name;
371
372 if (!(fp = fopen("modules.dep", "r"))) {
373 pr_warn("fopen modules.dep:");
374 return;
375 }
376
377 while (fgets(line, sizeof(line), fp)) {
378 p = strchr(line, ':');
379 if (!p)
380 continue;
381 *p = '\0';
382
383 name = strrchr(line, '/');
384 name = name ? name + 1 : line;
385
386 p = strrchr(name, '.');
387 if (p)
388 *p = '\0';
389
390 if (!pattern || fnmatch(pattern, name, 0) == 0) {
391 if (p)
392 *p = '.';
393 printf("%s\n", line); /* intended output, not a log */
394 }
395 }
396 fclose(fp);
397}
398
399/* kernel interaction */
400
401static int
402load_module(const char *path, const char *opts)
403{
404 int fd, ret;
405
406 fd = open(path, O_RDONLY | O_CLOEXEC);
407 if (fd < 0) {
408 pr_warn("open %s:", path);
409 return -1;
410 }
411 ret = syscall(__NR_finit_module, fd, opts ? opts : "", 0);
412 close(fd);
413 return ret;
414}
415
416static int
417unload_module(const char *name)
418{
419 return syscall(__NR_delete_module, name, O_NONBLOCK);
420}
421
422/* modprobe actions: load and unload requested module graph */
423
424static void
425process_module(struct Module *m)
426{
427 struct StrNode *dep;
428 struct Module *dm;
429 char *opts;
430
431 if (!m->path) {
432 pr_warn("module %s not found in modules.dep", m->name);
433 return;
434 }
435
436 if (rflag) {
437 if (m->flags & MOD_LOADED) {
438 if (unload_module(m->name) == 0)
439 m->flags &= ~MOD_LOADED;
440 else
441 pr_warn("unload %s:", m->name);
442 }
443 return;
444 }
445
446 for (dep = m->deps; dep; dep = dep->next) {
447 dm = get_module(dep->str, 0);
448 if (dm && !(dm->flags & MOD_LOADED))
449 process_module(dm);
450 }
451
452 if (m->flags & MOD_LOADED) {
453 if (vflag)
454 pr_info("%s already loaded", m->name);
455 return;
456 }
457
458 opts = m->options;
459 if (cmdopts && probes && strcmp(probes->str, m->name) == 0)
460 opts = append_opts(opts, cmdopts);
461
462#if FEATURE_MODPROBE_SHOW_DEPENDS
463 if (Dflag) {
464 printf(opts ? "insmod %s %s\n" : "insmod %s\n", m->path, opts); /* output data */
465 if (opts != m->options)
466 free(opts);
467 return;
468 }
469#endif
470
471 if (load_module(m->path, opts) == 0) {
472 m->flags |= MOD_LOADED;
473 if (vflag)
474 pr_info("loaded %s '%s'", m->path, opts ? opts : "");
475 } else {
476 pr_warn("load %s:", m->path);
477 }
478
479 if (opts != m->options)
480 free(opts);
481}
482
483static void
484do_probe(const char *name)
485{
486 struct Module *m, *am;
487 struct StrNode *alias;
488 char norm[256];
489
490 normalize_name(norm, name);
491 m = get_module(norm, 1);
492
493#if FEATURE_MODPROBE_BLACKLIST
494 if (bflag && (m->flags & MOD_BLACKLISTED))
495 return;
496#endif
497
498 if (!m->aliases) {
499 if (vflag)
500 pr_info("probing %s by name", norm);
501 process_module(m);
502 return;
503 }
504
505 for (alias = m->aliases; alias; alias = alias->next) {
506 am = get_module(alias->str, 1);
507#if FEATURE_MODPROBE_BLACKLIST
508 if (am->flags & MOD_BLACKLISTED)
509 continue;
510#endif
511 if (vflag)
512 pr_info("probing alias %s -> %s", norm, alias->str);
513 process_module(am);
514 }
515}
516
517static void
518usage(void)
519{
520 eprintf("usage: %s [-alqrv"
521#if FEATURE_MODPROBE_SHOW_DEPENDS
522 "D"
523#endif
524#if FEATURE_MODPROBE_BLACKLIST
525 "b"
526#endif
527#if FEATURE_MODPROBE_SYSLOG
528 "s"
529#endif
530 "] "
531#if FEATURE_MODPROBE_DIR_OVERRIDE
532 "[-d dir] "
533#endif
534 "module [symbol=value ...]\n", argv0);
535}
536
537// ?man modprobe: add or remove modules from the Linux kernel
538// ?man arguments: module [symbol=value ...
539// ?man modprobe loads or removes kernel modules from the running system.
540// ?man It reads modules.dep, modules.alias, and modules.symbols from the appropriate
541// ?man /lib/modules/release directory to resolve module names and dependencies,
542// ?man loading prerequisites first.
543// ?man Without -r, modprobe loads the named module (and any required dependencies)
544// ?man into the kernel.
545int
546main(int argc, char *argv[])
547{
548 struct utsname uts;
549 struct StrNode *pn;
550 char path[PATH_MAX];
551 int i, ret = 0;
552
553 ARGBEGIN {
554 // ?man -a: specify a option
555 case 'a':
556 // ?man -a: Load all modules named on the command line (rather than stopping after the first).
557 aflag = 1; break;
558 // ?man -r: specify r option
559 case 'r':
560 // ?man -r: Remove the named modules from the kernel. Dependencies are not automatically removed.
561 rflag = 1; break;
562 // ?man -q: specify q option
563 case 'q':
564 // ?man -q: Quiet mode. Suppress error messages.
565 qflag = 1; break;
566 // ?man -v: specify v option
567 case 'v':
568 // ?man -v: Verbose mode. Print each action taken.
569 vflag = 1; break;
570 // ?man -l: specify l option
571 case 'l':
572 // ?man -l: List available modules matching the optional _pattern_ (a fnmatch(3) glob).
573 lflag = 1; break;
574#if FEATURE_MODPROBE_SHOW_DEPENDS
575 // ?man -D: specify D option
576 case 'D':
577 // ?man -D: Print the sequence of insmod commands that would be used to load the module, without actually loading anything.
578 Dflag = 1; break;
579#endif
580#if FEATURE_MODPROBE_BLACKLIST
581 // ?man -b: specify b option
582 case 'b':
583 // ?man -b: Skip modules listed as blacklist in /etc/modprobe.d/.
584 bflag = 1; break;
585#endif
586#if FEATURE_MODPROBE_SYSLOG
587 // ?man -s: specify s option
588 case 's':
589 // ?man -s: Log messages to syslog(3) (facility LOG_DAEMON) instead of standard error.
590 sflag = 1; break;
591#endif
592#if FEATURE_MODPROBE_DIR_OVERRIDE
593 // ?man -d:file: specify d option
594 case 'd':
595 // ?man -d dir: Use dir as the base directory for module files instead of /lib/modules/release.
596 strlist_append(&moddirs, EARGF(usage())); break;
597#endif
598 default:
599 usage();
600 } ARGEND
601
602 if (!argc && !rflag && !lflag)
603 usage();
604
605#if FEATURE_MODPROBE_SYSLOG
606 if (sflag)
607 openlog("modprobe", LOG_PID, LOG_DAEMON);
608#endif
609
610 if (!moddirs) {
611 if (uname(&uts) < 0)
612 eprintf("uname:");
613 snprintf(path, sizeof(path), "/lib/modules/%s", uts.release);
614 strlist_append(&moddirs, path);
615 }
616
617 mark_loaded();
618 read_configs();
619
620 /* traverse all provided or default base directories for symbol, alias, and dependency tracking */
621 for (pn = moddirs; pn; pn = pn->next) {
622 if (chdir(pn->str) < 0) {
623 pr_warn("chdir %s:", pn->str);
624 continue;
625 }
626
627 if (lflag) {
628 list_modules(argc ? argv[0] : NULL);
629 continue;
630 }
631
632 parse_config_file("modules.symbols");
633 parse_config_file("modules.alias");
634 parse_dep_file("modules.dep");
635 }
636
637 if (lflag)
638 goto end;
639
640 if (aflag || rflag) {
641 for (i = 0; i < argc; i++)
642 strlist_append(&probes, argv[i]);
643 } else if (argc > 0) {
644 strlist_append(&probes, argv[0]);
645 for (i = 1; i < argc; i++)
646 cmdopts = append_opts(cmdopts, argv[i]);
647 }
648
649 if (rflag && !argc) {
650 if (syscall(__NR_delete_module, NULL, O_NONBLOCK) < 0)
651 eprintf("delete_module:");
652 goto end;
653 }
654
655 for (pn = probes; pn; pn = pn->next) {
656 do_probe(pn->str);
657 }
658
659end:
660#if FEATURE_MODPROBE_SYSLOG
661 if (sflag)
662 closelog();
663#endif
664 if (fshut(stdin, "<stdin>") | fshut(stdout, "<stdout>"))
665 ret = 2;
666
667 // ?man
668 // ?man ## FILES
669 // ?man
670 // ?man `/etc/modprobe.conf`
671 // ?man : Global configuration file.
672 // ?man
673 // ?man Files under `/etc/modprobe.d/`
674 // ?man : Per-module configuration snippets.
675 // ?man
676 // ?man `/lib/modules/`_release_`/modules.dep`
677 // ?man : Module dependency map generated by `depmod(8)`.
678 // ?man
679 // ?man ## EXIT STATUS
680 // ?man
681 // ?man 0
682 // ?man : Success.
683 // ?man
684 // ?man 1
685 // ?man : Module not found or kernel rejected the operation.
686 // ?man
687 // ?man 2
688 // ?man : I/O error on stdout or stdin.
689 // ?man
690 // ?man ## SEE ALSO
691 // ?man
692 // ?man insmod(8), rmmod(8), lsmod(8), depmod(8)
693 // ?man
694
695 return ret;
696}