1
2
3
4
5
6
7
8
9
10
11
12
13
14
15#include "libbb.h"
16#include "modutils.h"
17#include <sys/utsname.h>
18#include <fnmatch.h>
19
20
21#define DBG(...) ((void)0)
22
23#define MODULE_FLAG_LOADED 0x0001
24#define MODULE_FLAG_NEED_DEPS 0x0002
25
26#define MODULE_FLAG_FOUND_IN_MODDEP 0x0004
27#define MODULE_FLAG_BLACKLISTED 0x0008
28
29struct module_entry {
30 unsigned flags;
31 char *modname;
32 const char *probed_name;
33 char *options;
34 llist_t *realnames;
35
36
37 llist_t *deps;
38};
39
40
41
42
43
44
45
46
47
48
49
50
51
52#define MODPROBE_OPTS "acdlnrt:VC:" IF_FEATURE_MODPROBE_BLACKLIST("b")
53enum {
54 MODPROBE_OPT_INSERT_ALL = (INSMOD_OPT_UNUSED << 0),
55 MODPROBE_OPT_DUMP_ONLY = (INSMOD_OPT_UNUSED << 1),
56 MODPROBE_OPT_D = (INSMOD_OPT_UNUSED << 2),
57 MODPROBE_OPT_LIST_ONLY = (INSMOD_OPT_UNUSED << 3),
58 MODPROBE_OPT_SHOW_ONLY = (INSMOD_OPT_UNUSED << 4),
59 MODPROBE_OPT_REMOVE = (INSMOD_OPT_UNUSED << 5),
60 MODPROBE_OPT_RESTRICT = (INSMOD_OPT_UNUSED << 6),
61 MODPROBE_OPT_VERONLY = (INSMOD_OPT_UNUSED << 7),
62 MODPROBE_OPT_CONFIGFILE = (INSMOD_OPT_UNUSED << 8),
63 MODPROBE_OPT_BLACKLIST = (INSMOD_OPT_UNUSED << 9) * ENABLE_FEATURE_MODPROBE_BLACKLIST,
64};
65
66struct globals {
67 llist_t *db;
68 llist_t *probes;
69 char *cmdline_mopts;
70 int num_unresolved_deps;
71
72 smallint need_symbols;
73};
74#define G (*(struct globals*)&bb_common_bufsiz1)
75#define INIT_G() do { } while (0)
76
77
78static int read_config(const char *path);
79
80static char *gather_options_str(char *opts, const char *append)
81{
82
83 if (opts == NULL) {
84 opts = xstrdup(append);
85 } else {
86 int optlen = strlen(opts);
87 opts = xrealloc(opts, optlen + strlen(append) + 2);
88 sprintf(opts + optlen, " %s", append);
89 }
90 return opts;
91}
92
93static struct module_entry *helper_get_module(const char *module, int create)
94{
95 char modname[MODULE_NAME_LEN];
96 struct module_entry *e;
97 llist_t *l;
98
99 filename2modname(module, modname);
100 for (l = G.db; l != NULL; l = l->link) {
101 e = (struct module_entry *) l->data;
102 if (strcmp(e->modname, modname) == 0)
103 return e;
104 }
105 if (!create)
106 return NULL;
107
108 e = xzalloc(sizeof(*e));
109 e->modname = xstrdup(modname);
110 llist_add_to(&G.db, e);
111
112 return e;
113}
114static struct module_entry *get_or_add_modentry(const char *module)
115{
116 return helper_get_module(module, 1);
117}
118static struct module_entry *get_modentry(const char *module)
119{
120 return helper_get_module(module, 0);
121}
122
123static void add_probe(const char *name)
124{
125 struct module_entry *m;
126
127 m = get_or_add_modentry(name);
128 if (!(option_mask32 & MODPROBE_OPT_REMOVE)
129 && (m->flags & MODULE_FLAG_LOADED)
130 ) {
131 DBG("skipping %s, it is already loaded", name);
132 return;
133 }
134
135 DBG("queuing %s", name);
136 m->probed_name = name;
137 m->flags |= MODULE_FLAG_NEED_DEPS;
138 llist_add_to_end(&G.probes, m);
139 G.num_unresolved_deps++;
140 if (ENABLE_FEATURE_MODUTILS_SYMBOLS
141 && strncmp(m->modname, "symbol:", 7) == 0
142 ) {
143 G.need_symbols = 1;
144 }
145}
146
147static int FAST_FUNC config_file_action(const char *filename,
148 struct stat *statbuf UNUSED_PARAM,
149 void *userdata UNUSED_PARAM,
150 int depth UNUSED_PARAM)
151{
152 char *tokens[3];
153 parser_t *p;
154 struct module_entry *m;
155 int rc = TRUE;
156
157 if (bb_basename(filename)[0] == '.')
158 goto error;
159
160 p = config_open2(filename, fopen_for_read);
161 if (p == NULL) {
162 rc = FALSE;
163 goto error;
164 }
165
166 while (config_read(p, tokens, 3, 2, "# \t", PARSE_NORMAL)) {
167
168 if (strcmp(tokens[0], "alias") == 0) {
169
170 llist_t *l;
171 char wildcard[MODULE_NAME_LEN];
172 char *rmod;
173
174 if (tokens[2] == NULL)
175 continue;
176 filename2modname(tokens[1], wildcard);
177
178 for (l = G.probes; l != NULL; l = l->link) {
179 m = (struct module_entry *) l->data;
180 if (fnmatch(wildcard, m->modname, 0) != 0)
181 continue;
182 rmod = filename2modname(tokens[2], NULL);
183 llist_add_to(&m->realnames, rmod);
184
185 if (m->flags & MODULE_FLAG_NEED_DEPS) {
186 m->flags &= ~MODULE_FLAG_NEED_DEPS;
187 G.num_unresolved_deps--;
188 }
189
190 m = get_or_add_modentry(rmod);
191 if (!(m->flags & MODULE_FLAG_NEED_DEPS)) {
192 m->flags |= MODULE_FLAG_NEED_DEPS;
193 G.num_unresolved_deps++;
194 }
195 }
196 } else if (strcmp(tokens[0], "options") == 0) {
197
198 if (tokens[2] == NULL)
199 continue;
200 m = get_or_add_modentry(tokens[1]);
201 m->options = gather_options_str(m->options, tokens[2]);
202 } else if (strcmp(tokens[0], "include") == 0) {
203
204 read_config(tokens[1]);
205 } else if (ENABLE_FEATURE_MODPROBE_BLACKLIST
206 && strcmp(tokens[0], "blacklist") == 0
207 ) {
208
209 get_or_add_modentry(tokens[1])->flags |= MODULE_FLAG_BLACKLISTED;
210 }
211 }
212 config_close(p);
213 error:
214 return rc;
215}
216
217static int read_config(const char *path)
218{
219 return recursive_action(path, ACTION_RECURSE | ACTION_QUIET,
220 config_file_action, NULL, NULL, 1);
221}
222
223static const char *humanly_readable_name(struct module_entry *m)
224{
225
226 return m->probed_name ? m->probed_name : m->modname;
227}
228
229
230
231
232
233
234static int do_modprobe(struct module_entry *m)
235{
236 struct module_entry *m2 = m2;
237 char *fn, *options;
238 int rc, first;
239 llist_t *l;
240
241 if (!(m->flags & MODULE_FLAG_FOUND_IN_MODDEP)) {
242 if (!(option_mask32 & INSMOD_OPT_SILENT))
243 bb_error_msg("module %s not found in modules.dep",
244 humanly_readable_name(m));
245 return -ENOENT;
246 }
247 DBG("do_modprob'ing %s", m->modname);
248
249 if (!(option_mask32 & MODPROBE_OPT_REMOVE))
250 m->deps = llist_rev(m->deps);
251
252 for (l = m->deps; l != NULL; l = l->link)
253 DBG("dep: %s", l->data);
254
255 first = 1;
256 rc = 0;
257 while (m->deps) {
258 rc = 0;
259 fn = llist_pop(&m->deps);
260 m2 = get_or_add_modentry(fn);
261
262 if (option_mask32 & MODPROBE_OPT_REMOVE) {
263
264 if (m2->flags & MODULE_FLAG_LOADED) {
265 rc = bb_delete_module(m2->modname, O_EXCL);
266 if (rc) {
267 if (first) {
268 bb_error_msg("failed to unload module %s: %s",
269 humanly_readable_name(m2),
270 moderror(rc));
271 break;
272 }
273 } else {
274 m2->flags &= ~MODULE_FLAG_LOADED;
275 }
276 }
277
278 first = 0;
279 continue;
280 }
281
282 if (m2->flags & MODULE_FLAG_LOADED) {
283 DBG("%s is already loaded, skipping", fn);
284 continue;
285 }
286
287 options = m2->options;
288 m2->options = NULL;
289 if (m == m2)
290 options = gather_options_str(options, G.cmdline_mopts);
291 rc = bb_init_module(fn, options);
292 DBG("loaded %s '%s', rc:%d", fn, options, rc);
293 free(options);
294 if (rc) {
295 bb_error_msg("failed to load module %s (%s): %s",
296 humanly_readable_name(m2),
297 fn,
298 moderror(rc)
299 );
300 break;
301 }
302 m2->flags |= MODULE_FLAG_LOADED;
303 }
304
305 return rc;
306}
307
308static void load_modules_dep(void)
309{
310 struct module_entry *m;
311 char *colon, *tokens[2];
312 parser_t *p;
313
314
315
316
317
318
319
320 p = config_open2(CONFIG_DEFAULT_DEPMOD_FILE, xfopen_for_read);
321
322 while (G.num_unresolved_deps
323 && config_read(p, tokens, 2, 1, "# \t", PARSE_NORMAL)
324 ) {
325 colon = last_char_is(tokens[0], ':');
326 if (colon == NULL)
327 continue;
328 *colon = 0;
329
330 m = get_modentry(tokens[0]);
331 if (m == NULL)
332 continue;
333
334
335 if ((m->flags & MODULE_FLAG_LOADED)
336 && !(option_mask32 & MODPROBE_OPT_REMOVE)
337 ) {
338 DBG("skip deps of %s, it's already loaded", tokens[0]);
339 continue;
340 }
341
342 m->flags |= MODULE_FLAG_FOUND_IN_MODDEP;
343 if ((m->flags & MODULE_FLAG_NEED_DEPS) && (m->deps == NULL)) {
344 G.num_unresolved_deps--;
345 llist_add_to(&m->deps, xstrdup(tokens[0]));
346 if (tokens[1])
347 string_to_llist(tokens[1], &m->deps, " \t");
348 } else
349 DBG("skipping dep line");
350 }
351 config_close(p);
352}
353
354int modprobe_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
355int modprobe_main(int argc UNUSED_PARAM, char **argv)
356{
357 struct utsname uts;
358 int rc;
359 unsigned opt;
360 struct module_entry *me;
361
362 opt_complementary = "q-v:v-q";
363 opt = getopt32(argv, INSMOD_OPTS MODPROBE_OPTS INSMOD_ARGS, NULL, NULL);
364 argv += optind;
365
366 if (opt & (MODPROBE_OPT_DUMP_ONLY | MODPROBE_OPT_LIST_ONLY |
367 MODPROBE_OPT_SHOW_ONLY))
368 bb_error_msg_and_die("not supported");
369
370 if (!argv[0]) {
371 if (opt & MODPROBE_OPT_REMOVE) {
372
373
374
375
376 if (bb_delete_module(NULL, O_NONBLOCK|O_EXCL) != 0)
377 bb_perror_msg_and_die("rmmod");
378 }
379 return EXIT_SUCCESS;
380 }
381
382
383 xchdir(CONFIG_DEFAULT_MODULES_DIR);
384 uname(&uts);
385 xchdir(uts.release);
386
387
388 {
389 char *s;
390 parser_t *parser = config_open2("/proc/modules", fopen_for_read);
391 while (config_read(parser, &s, 1, 1, "# \t", PARSE_NORMAL & ~PARSE_GREEDY))
392 get_or_add_modentry(s)->flags |= MODULE_FLAG_LOADED;
393 config_close(parser);
394 }
395
396 if (opt & (MODPROBE_OPT_INSERT_ALL | MODPROBE_OPT_REMOVE)) {
397
398 do {
399 DBG("adding module %s", *argv);
400 add_probe(*argv++);
401 } while (*argv);
402 } else {
403
404 DBG("probing just module %s", *argv);
405 add_probe(argv[0]);
406 G.cmdline_mopts = parse_cmdline_module_options(argv);
407 }
408
409
410 if (G.probes == NULL)
411 return EXIT_SUCCESS;
412
413 read_config("/etc/modprobe.conf");
414 read_config("/etc/modprobe.d");
415 if (ENABLE_FEATURE_MODUTILS_SYMBOLS && G.need_symbols)
416 read_config("modules.symbols");
417 load_modules_dep();
418 if (ENABLE_FEATURE_MODUTILS_ALIAS && G.num_unresolved_deps) {
419 read_config("modules.alias");
420 load_modules_dep();
421 }
422
423 rc = 0;
424 while ((me = llist_pop(&G.probes)) != NULL) {
425 if (me->realnames == NULL) {
426 DBG("probing by module name");
427
428
429
430 if (!(opt & MODPROBE_OPT_BLACKLIST)
431 || !(me->flags & MODULE_FLAG_BLACKLISTED)
432 ) {
433 rc |= do_modprobe(me);
434 }
435 continue;
436 }
437
438
439 do {
440 char *realname = llist_pop(&me->realnames);
441 struct module_entry *m2;
442
443 DBG("probing alias %s by realname %s", me->modname, realname);
444 m2 = get_or_add_modentry(realname);
445 if (!(m2->flags & MODULE_FLAG_BLACKLISTED)
446 && (!(m2->flags & MODULE_FLAG_LOADED)
447 || (opt & MODPROBE_OPT_REMOVE))
448 ) {
449
450
451
452 rc |= do_modprobe(m2);
453 }
454 free(realname);
455 } while (me->realnames != NULL);
456 }
457
458 return (rc != 0);
459}
460