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