diff options
author | Denis Vlasenko <vda.linux@googlemail.com> | 2008-09-13 14:59:38 +0000 |
---|---|---|
committer | Denis Vlasenko <vda.linux@googlemail.com> | 2008-09-13 14:59:38 +0000 |
commit | ba1315d0fbe7fa43aa7481b5d6e92bd03b0152d5 (patch) | |
tree | b5b295f5382bd71c6184539feabf0ba061897131 /modutils/modprobe.c | |
parent | 4f3209b9d4b24ebe9b76e3bfe8ddd87af5228af9 (diff) | |
download | busybox-w32-ba1315d0fbe7fa43aa7481b5d6e92bd03b0152d5.tar.gz busybox-w32-ba1315d0fbe7fa43aa7481b5d6e92bd03b0152d5.tar.bz2 busybox-w32-ba1315d0fbe7fa43aa7481b5d6e92bd03b0152d5.zip |
modutils/*: rewrite by Timo Teras <timo.teras AT iki.fi>
- a lot faster (linear algorithmic complexity, smaller memory foot print)
- a lot smaller (the old code was overly complicated)
- loading of aliases is now module-init-tools compliant
- blacklisting is done correctly (-b option added)
- module argument quoting done right
- depmod now correctly generates modules.symbols and modules.alias
add/remove: 16/21 grow/shrink: 4/6 up/down: 6930/-9316 Total: -2386 bytes
text data bss dec hex filename
806039 592 6680 813311 c68ff busybox_old
803498 592 6676 810766 c5f0e busybox_unstripped
Diffstat (limited to 'modutils/modprobe.c')
-rw-r--r-- | modutils/modprobe.c | 1103 |
1 files changed, 218 insertions, 885 deletions
diff --git a/modutils/modprobe.c b/modutils/modprobe.c index 412e71d16..20b9a74aa 100644 --- a/modutils/modprobe.c +++ b/modutils/modprobe.c | |||
@@ -2,951 +2,284 @@ | |||
2 | /* | 2 | /* |
3 | * Modprobe written from scratch for BusyBox | 3 | * Modprobe written from scratch for BusyBox |
4 | * | 4 | * |
5 | * Copyright (c) 2002 by Robert Griebl, griebl@gmx.de | 5 | * Copyright (c) 2008 Timo Teras <timo.teras@iki.fi> |
6 | * Copyright (c) 2003 by Andrew Dennison, andrew.dennison@motec.com.au | 6 | * Copyright (c) 2008 Vladimir Dronnikov |
7 | * Copyright (c) 2005 by Jim Bauer, jfbauer@nfr.com | ||
8 | * | ||
9 | * Portions Copyright (c) 2005 by Yann E. MORIN, yann.morin.1998@anciens.enib.fr | ||
10 | * | 7 | * |
11 | * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. | 8 | * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. |
12 | */ | 9 | */ |
13 | 10 | ||
14 | #include "libbb.h" | 11 | #include "libbb.h" |
12 | #include "modutils.h" | ||
15 | #include <sys/utsname.h> | 13 | #include <sys/utsname.h> |
16 | #include <fnmatch.h> | 14 | #include <fnmatch.h> |
17 | 15 | ||
18 | #define line_buffer bb_common_bufsiz1 | 16 | struct modprobe_option { |
19 | 17 | char *module; | |
20 | struct mod_opt_t { /* one-way list of options to pass to a module */ | 18 | char *option; |
21 | char * m_opt_val; | ||
22 | struct mod_opt_t * m_next; | ||
23 | }; | ||
24 | |||
25 | struct dep_t { /* one-way list of dependency rules */ | ||
26 | /* a dependency rule */ | ||
27 | char * m_name; /* the module name*/ | ||
28 | char * m_path; /* the module file path */ | ||
29 | struct mod_opt_t * m_options; /* the module options */ | ||
30 | |||
31 | unsigned int m_isalias :1; /* the module is an alias */ | ||
32 | unsigned int m_isblacklisted:1; /* the module is blacklisted */ | ||
33 | unsigned int m_reserved :14; /* stuffin' */ | ||
34 | |||
35 | unsigned int m_depcnt :16; /* the number of dependable module(s) */ | ||
36 | char ** m_deparr; /* the list of dependable module(s) */ | ||
37 | |||
38 | struct dep_t * m_next; /* the next dependency rule */ | ||
39 | }; | 19 | }; |
40 | 20 | ||
41 | struct mod_list_t { /* two-way list of modules to process */ | 21 | struct modprobe_conf { |
42 | /* a module description */ | 22 | char probename[MODULE_NAME_LEN]; |
43 | const char * m_name; | 23 | llist_t *options; |
44 | char * m_path; | 24 | llist_t *aliases; |
45 | struct mod_opt_t * m_options; | 25 | #if ENABLE_FEATURE_MODPROBE_BLACKLIST |
46 | 26 | #define add_to_blacklist(conf, name) llist_add_to(&conf->blacklist, name) | |
47 | struct mod_list_t * m_prev; | 27 | #define check_blacklist(conf, name) (llist_find(conf->blacklist, name) == NULL) |
48 | struct mod_list_t * m_next; | 28 | llist_t *blacklist; |
29 | #else | ||
30 | #define add_to_blacklist(conf, name) do {} while (0) | ||
31 | #define check_blacklist(conf, name) (1) | ||
32 | #endif | ||
49 | }; | 33 | }; |
50 | 34 | ||
51 | struct include_conf_t { | 35 | #define MODPROBE_OPTS "acdlnrt:VC:" USE_FEATURE_MODPROBE_BLACKLIST("b") |
52 | struct dep_t *first; | 36 | enum { |
53 | struct dep_t *current; | 37 | MODPROBE_OPT_INSERT_ALL = (INSMOD_OPT_UNUSED << 0), /* a */ |
38 | MODPROBE_OPT_DUMP_ONLY = (INSMOD_OPT_UNUSED << 1), /* c */ | ||
39 | MODPROBE_OPT_D = (INSMOD_OPT_UNUSED << 2), /* d */ | ||
40 | MODPROBE_OPT_LIST_ONLY = (INSMOD_OPT_UNUSED << 3), /* l */ | ||
41 | MODPROBE_OPT_SHOW_ONLY = (INSMOD_OPT_UNUSED << 4), /* n */ | ||
42 | MODPROBE_OPT_REMOVE = (INSMOD_OPT_UNUSED << 5), /* r */ | ||
43 | MODPROBE_OPT_RESTRICT = (INSMOD_OPT_UNUSED << 6), /* t */ | ||
44 | MODPROBE_OPT_VERONLY = (INSMOD_OPT_UNUSED << 7), /* V */ | ||
45 | MODPROBE_OPT_CONFIGFILE = (INSMOD_OPT_UNUSED << 8), /* C */ | ||
46 | MODPROBE_OPT_BLACKLIST = (INSMOD_OPT_UNUSED << 9) * ENABLE_FEATURE_MODPROBE_BLACKLIST, | ||
54 | }; | 47 | }; |
55 | 48 | ||
56 | static struct dep_t *depend; | 49 | static llist_t *loaded; |
57 | |||
58 | #define MAIN_OPT_STR "acdklnqrst:vVC:" | ||
59 | #define INSERT_ALL 1 /* a */ | ||
60 | #define DUMP_CONF_EXIT 2 /* c */ | ||
61 | #define D_OPT_IGNORED 4 /* d */ | ||
62 | #define AUTOCLEAN_FLG 8 /* k */ | ||
63 | #define LIST_ALL 16 /* l */ | ||
64 | #define SHOW_ONLY 32 /* n */ | ||
65 | #define QUIET 64 /* q */ | ||
66 | #define REMOVE_OPT 128 /* r */ | ||
67 | #define DO_SYSLOG 256 /* s */ | ||
68 | #define RESTRICT_DIR 512 /* t */ | ||
69 | #define VERBOSE 1024 /* v */ | ||
70 | #define VERSION_ONLY 2048 /* V */ | ||
71 | #define CONFIG_FILE 4096 /* C */ | ||
72 | |||
73 | #define autoclean (option_mask32 & AUTOCLEAN_FLG) | ||
74 | #define show_only (option_mask32 & SHOW_ONLY) | ||
75 | #define quiet (option_mask32 & QUIET) | ||
76 | #define remove_opt (option_mask32 & REMOVE_OPT) | ||
77 | #define do_syslog (option_mask32 & DO_SYSLOG) | ||
78 | #define verbose (option_mask32 & VERBOSE) | ||
79 | |||
80 | static int parse_tag_value(char *buffer, char **ptag, char **pvalue) | ||
81 | { | ||
82 | char *tag, *value; | ||
83 | |||
84 | buffer = skip_whitespace(buffer); | ||
85 | tag = value = buffer; | ||
86 | while (!isspace(*value)) { | ||
87 | if (!*value) | ||
88 | return 0; | ||
89 | value++; | ||
90 | } | ||
91 | *value++ = '\0'; | ||
92 | value = skip_whitespace(value); | ||
93 | if (!*value) | ||
94 | return 0; | ||
95 | |||
96 | *ptag = tag; | ||
97 | *pvalue = value; | ||
98 | |||
99 | return 1; | ||
100 | } | ||
101 | |||
102 | /* | ||
103 | * This function appends an option to a list | ||
104 | */ | ||
105 | static struct mod_opt_t *append_option(struct mod_opt_t *opt_list, char *opt) | ||
106 | { | ||
107 | struct mod_opt_t *ol = opt_list; | ||
108 | |||
109 | if (ol) { | ||
110 | while (ol->m_next) { | ||
111 | ol = ol->m_next; | ||
112 | } | ||
113 | ol->m_next = xzalloc(sizeof(struct mod_opt_t)); | ||
114 | ol = ol->m_next; | ||
115 | } else { | ||
116 | ol = opt_list = xzalloc(sizeof(struct mod_opt_t)); | ||
117 | } | ||
118 | |||
119 | ol->m_opt_val = xstrdup(opt); | ||
120 | /*ol->m_next = NULL; - done by xzalloc*/ | ||
121 | |||
122 | return opt_list; | ||
123 | } | ||
124 | |||
125 | #if ENABLE_FEATURE_MODPROBE_MULTIPLE_OPTIONS | ||
126 | /* static char* parse_command_string(char* src, char **dst); | ||
127 | * src: pointer to string containing argument | ||
128 | * dst: pointer to where to store the parsed argument | ||
129 | * return value: the pointer to the first char after the parsed argument, | ||
130 | * NULL if there was no argument parsed (only trailing spaces). | ||
131 | * Note that memory is allocated with xstrdup when a new argument was | ||
132 | * parsed. Don't forget to free it! | ||
133 | */ | ||
134 | #define ARG_EMPTY 0x00 | ||
135 | #define ARG_IN_DQUOTES 0x01 | ||
136 | #define ARG_IN_SQUOTES 0x02 | ||
137 | static char *parse_command_string(char *src, char **dst) | ||
138 | { | ||
139 | int opt_status = ARG_EMPTY; | ||
140 | char* tmp_str; | ||
141 | |||
142 | /* Dumb you, I have nothing to do... */ | ||
143 | if (src == NULL) return src; | ||
144 | |||
145 | /* Skip leading spaces */ | ||
146 | while (*src == ' ') { | ||
147 | src++; | ||
148 | } | ||
149 | /* Is the end of string reached? */ | ||
150 | if (*src == '\0') { | ||
151 | return NULL; | ||
152 | } | ||
153 | /* Reached the start of an argument | ||
154 | * By the way, we duplicate a little too much | ||
155 | * here but what is too much is freed later. */ | ||
156 | *dst = tmp_str = xstrdup(src); | ||
157 | /* Get to the end of that argument */ | ||
158 | while (*tmp_str != '\0' | ||
159 | && (*tmp_str != ' ' || (opt_status & (ARG_IN_DQUOTES | ARG_IN_SQUOTES))) | ||
160 | ) { | ||
161 | switch (*tmp_str) { | ||
162 | case '\'': | ||
163 | if (opt_status & ARG_IN_DQUOTES) { | ||
164 | /* Already in double quotes, keep current char as is */ | ||
165 | } else { | ||
166 | /* shift left 1 char, until end of string: get rid of the opening/closing quotes */ | ||
167 | memmove(tmp_str, tmp_str + 1, strlen(tmp_str)); | ||
168 | /* mark me: we enter or leave single quotes */ | ||
169 | opt_status ^= ARG_IN_SQUOTES; | ||
170 | /* Back one char, as we need to re-scan the new char there. */ | ||
171 | tmp_str--; | ||
172 | } | ||
173 | break; | ||
174 | case '"': | ||
175 | if (opt_status & ARG_IN_SQUOTES) { | ||
176 | /* Already in single quotes, keep current char as is */ | ||
177 | } else { | ||
178 | /* shift left 1 char, until end of string: get rid of the opening/closing quotes */ | ||
179 | memmove(tmp_str, tmp_str + 1, strlen(tmp_str)); | ||
180 | /* mark me: we enter or leave double quotes */ | ||
181 | opt_status ^= ARG_IN_DQUOTES; | ||
182 | /* Back one char, as we need to re-scan the new char there. */ | ||
183 | tmp_str--; | ||
184 | } | ||
185 | break; | ||
186 | case '\\': | ||
187 | if (opt_status & ARG_IN_SQUOTES) { | ||
188 | /* Between single quotes: keep as is. */ | ||
189 | } else { | ||
190 | switch (*(tmp_str+1)) { | ||
191 | case 'a': | ||
192 | case 'b': | ||
193 | case 't': | ||
194 | case 'n': | ||
195 | case 'v': | ||
196 | case 'f': | ||
197 | case 'r': | ||
198 | case '0': | ||
199 | /* We escaped a special character. For now, keep | ||
200 | * both the back-slash and the following char. */ | ||
201 | tmp_str++; | ||
202 | src++; | ||
203 | break; | ||
204 | default: | ||
205 | /* We escaped a space or a single or double quote, | ||
206 | * or a back-slash, or a non-escapable char. Remove | ||
207 | * the '\' and keep the new current char as is. */ | ||
208 | memmove(tmp_str, tmp_str + 1, strlen(tmp_str)); | ||
209 | break; | ||
210 | } | ||
211 | } | ||
212 | break; | ||
213 | /* Any other char that is special shall appear here. | ||
214 | * Example: $ starts a variable | ||
215 | case '$': | ||
216 | do_variable_expansion(); | ||
217 | break; | ||
218 | * */ | ||
219 | default: | ||
220 | /* any other char is kept as is. */ | ||
221 | break; | ||
222 | } | ||
223 | tmp_str++; /* Go to next char */ | ||
224 | src++; /* Go to next char to find the end of the argument. */ | ||
225 | } | ||
226 | /* End of string, but still no ending quote */ | ||
227 | if (opt_status & (ARG_IN_DQUOTES | ARG_IN_SQUOTES)) { | ||
228 | bb_error_msg_and_die("unterminated (single or double) quote in options list: %s", src); | ||
229 | } | ||
230 | *tmp_str++ = '\0'; | ||
231 | *dst = xrealloc(*dst, (tmp_str - *dst)); | ||
232 | return src; | ||
233 | } | ||
234 | #else | ||
235 | #define parse_command_string(src, dst) (0) | ||
236 | #endif /* ENABLE_FEATURE_MODPROBE_MULTIPLE_OPTIONS */ | ||
237 | |||
238 | static int is_conf_command(char *buffer, const char *command) | ||
239 | { | ||
240 | int len = strlen(command); | ||
241 | return ((strstr(buffer, command) == buffer) && | ||
242 | isspace(buffer[len])); | ||
243 | } | ||
244 | |||
245 | /* | ||
246 | * This function reads aliases and default module options from a configuration file | ||
247 | * (/etc/modprobe.conf syntax). It supports includes (only files, no directories). | ||
248 | */ | ||
249 | 50 | ||
250 | static int FAST_FUNC include_conf_file_act(const char *filename, | 51 | static int read_config(struct modprobe_conf *conf, const char *path); |
251 | struct stat *statbuf UNUSED_PARAM, | ||
252 | void *userdata, | ||
253 | int depth UNUSED_PARAM); | ||
254 | 52 | ||
255 | static int FAST_FUNC include_conf_dir_act(const char *filename UNUSED_PARAM, | 53 | static void add_option(llist_t **all_opts, const char *module, const char *opts) |
256 | struct stat *statbuf UNUSED_PARAM, | ||
257 | void *userdata UNUSED_PARAM, | ||
258 | int depth) | ||
259 | { | 54 | { |
260 | if (depth > 1) | 55 | struct modprobe_option *o; |
261 | return SKIP; | ||
262 | 56 | ||
263 | return TRUE; | 57 | o = xzalloc(sizeof(struct modprobe_option)); |
58 | if (module) | ||
59 | o->module = filename2modname(module, NULL); | ||
60 | o->option = xstrdup(opts); | ||
61 | llist_add_to(all_opts, o); | ||
264 | } | 62 | } |
265 | 63 | ||
266 | static int include_conf_recursive(struct include_conf_t *conf, const char *filename) | 64 | static int FAST_FUNC config_file_action(const char *filename, |
65 | struct stat *statbuf UNUSED_PARAM, | ||
66 | void *userdata, | ||
67 | int depth UNUSED_PARAM) | ||
267 | { | 68 | { |
268 | return recursive_action(filename, ACTION_RECURSE, | 69 | struct modprobe_conf *conf = (struct modprobe_conf *) userdata; |
269 | include_conf_file_act, | 70 | RESERVE_CONFIG_BUFFER(modname, MODULE_NAME_LEN); |
270 | include_conf_dir_act, | 71 | char *tokens[3]; |
271 | conf, 1); | 72 | parser_t *p; |
272 | } | 73 | int rc = TRUE; |
273 | |||
274 | static int FAST_FUNC include_conf_file_act(const char *filename, | ||
275 | struct stat *statbuf UNUSED_PARAM, | ||
276 | void *userdata, | ||
277 | int depth UNUSED_PARAM) | ||
278 | { | ||
279 | struct include_conf_t *conf = (struct include_conf_t *) userdata; | ||
280 | struct dep_t **first = &conf->first; | ||
281 | struct dep_t **current = &conf->current; | ||
282 | int continuation_line = 0; | ||
283 | FILE *f; | ||
284 | 74 | ||
285 | if (bb_basename(filename)[0] == '.') | 75 | if (bb_basename(filename)[0] == '.') |
286 | return TRUE; | 76 | goto error; |
287 | |||
288 | f = fopen_for_read(filename); | ||
289 | if (f == NULL) | ||
290 | return FALSE; | ||
291 | |||
292 | // alias parsing is not 100% correct (no correct handling of continuation lines within an alias)! | ||
293 | |||
294 | while (fgets(line_buffer, sizeof(line_buffer), f)) { | ||
295 | int l; | ||
296 | 77 | ||
297 | *strchrnul(line_buffer, '#') = '\0'; | 78 | p = config_open2(filename, fopen_for_read); |
298 | 79 | if (p == NULL) { | |
299 | l = strlen(line_buffer); | 80 | rc = FALSE; |
300 | 81 | goto error; | |
301 | while (l && isspace(line_buffer[l-1])) { | 82 | } |
302 | line_buffer[l-1] = '\0'; | ||
303 | l--; | ||
304 | } | ||
305 | |||
306 | if (l == 0) { | ||
307 | continuation_line = 0; | ||
308 | continue; | ||
309 | } | ||
310 | |||
311 | if (continuation_line) | ||
312 | continue; | ||
313 | |||
314 | if (is_conf_command(line_buffer, "alias")) { | ||
315 | char *alias, *mod; | ||
316 | |||
317 | if (parse_tag_value(line_buffer + 6, &alias, &mod)) { | ||
318 | /* handle alias as a module dependent on the aliased module */ | ||
319 | if (!*current) { | ||
320 | (*first) = (*current) = xzalloc(sizeof(struct dep_t)); | ||
321 | } else { | ||
322 | (*current)->m_next = xzalloc(sizeof(struct dep_t)); | ||
323 | (*current) = (*current)->m_next; | ||
324 | } | ||
325 | (*current)->m_name = xstrdup(alias); | ||
326 | (*current)->m_isalias = 1; | ||
327 | |||
328 | if ((strcmp(mod, "off") == 0) || (strcmp(mod, "null") == 0)) { | ||
329 | /*(*current)->m_depcnt = 0; - done by xzalloc */ | ||
330 | /*(*current)->m_deparr = 0;*/ | ||
331 | } else { | ||
332 | (*current)->m_depcnt = 1; | ||
333 | (*current)->m_deparr = xmalloc(sizeof(char *)); | ||
334 | (*current)->m_deparr[0] = xstrdup(mod); | ||
335 | } | ||
336 | /*(*current)->m_next = NULL; - done by xzalloc */ | ||
337 | } | ||
338 | } else if (is_conf_command(line_buffer, "options")) { | ||
339 | char *mod, *opt; | ||
340 | |||
341 | /* split the line in the module/alias name, and options */ | ||
342 | if (parse_tag_value(line_buffer + 8, &mod, &opt)) { | ||
343 | struct dep_t *dt; | ||
344 | |||
345 | /* find the corresponding module */ | ||
346 | for (dt = *first; dt; dt = dt->m_next) { | ||
347 | if (strcmp(dt->m_name, mod) == 0) | ||
348 | break; | ||
349 | } | ||
350 | if (dt) { | ||
351 | if (ENABLE_FEATURE_MODPROBE_MULTIPLE_OPTIONS) { | ||
352 | char* new_opt = NULL; | ||
353 | while ((opt = parse_command_string(opt, &new_opt))) { | ||
354 | dt->m_options = append_option(dt->m_options, new_opt); | ||
355 | } | ||
356 | } else { | ||
357 | dt->m_options = append_option(dt->m_options, opt); | ||
358 | } | ||
359 | } | ||
360 | } | ||
361 | } else if (is_conf_command(line_buffer, "include")) { | ||
362 | char *includefile; | ||
363 | 83 | ||
364 | includefile = skip_whitespace(line_buffer + 8); | 84 | while (config_read(p, tokens, 3, 2, "# \t", PARSE_NORMAL)) { |
365 | include_conf_recursive(conf, includefile); | 85 | if (strcmp(tokens[0], "alias") == 0) { |
86 | filename2modname(tokens[1], modname); | ||
87 | if (tokens[2] && | ||
88 | fnmatch(modname, conf->probename, 0) == 0) | ||
89 | llist_add_to(&conf->aliases, | ||
90 | filename2modname(tokens[2], NULL)); | ||
91 | } else if (strcmp(tokens[0], "options") == 0) { | ||
92 | if (tokens[2]) | ||
93 | add_option(&conf->options, tokens[1], tokens[2]); | ||
94 | } else if (strcmp(tokens[0], "include") == 0) { | ||
95 | read_config(conf, tokens[1]); | ||
366 | } else if (ENABLE_FEATURE_MODPROBE_BLACKLIST && | 96 | } else if (ENABLE_FEATURE_MODPROBE_BLACKLIST && |
367 | (is_conf_command(line_buffer, "blacklist"))) { | 97 | strcmp(tokens[0], "blacklist") == 0) { |
368 | char *mod; | 98 | add_to_blacklist(conf, xstrdup(tokens[1])); |
369 | struct dep_t *dt; | ||
370 | |||
371 | mod = skip_whitespace(line_buffer + 10); | ||
372 | for (dt = *first; dt; dt = dt->m_next) { | ||
373 | if (strcmp(dt->m_name, mod) == 0) | ||
374 | break; | ||
375 | } | ||
376 | if (dt) | ||
377 | dt->m_isblacklisted = 1; | ||
378 | } | 99 | } |
379 | } /* while (fgets(...)) */ | ||
380 | |||
381 | fclose(f); | ||
382 | return TRUE; | ||
383 | } | ||
384 | |||
385 | static int include_conf_file(struct include_conf_t *conf, | ||
386 | const char *filename) | ||
387 | { | ||
388 | return include_conf_file_act(filename, NULL, conf, 0); | ||
389 | } | ||
390 | |||
391 | static int include_conf_file2(struct include_conf_t *conf, | ||
392 | const char *filename, const char *oldname) | ||
393 | { | ||
394 | if (include_conf_file(conf, filename) == TRUE) | ||
395 | return TRUE; | ||
396 | return include_conf_file(conf, oldname); | ||
397 | } | ||
398 | |||
399 | /* | ||
400 | * This function builds a list of dependency rules from /lib/modules/`uname -r`/modules.dep. | ||
401 | * It then fills every modules and aliases with their default options, found by parsing | ||
402 | * modprobe.conf (or modules.conf, or conf.modules). | ||
403 | */ | ||
404 | static struct dep_t *build_dep(void) | ||
405 | { | ||
406 | FILE *f; | ||
407 | struct utsname un; | ||
408 | struct include_conf_t conf = { NULL, NULL }; | ||
409 | char *filename; | ||
410 | int continuation_line = 0; | ||
411 | int k_version; | ||
412 | |||
413 | uname(&un); /* never fails */ | ||
414 | |||
415 | k_version = 0; | ||
416 | if (un.release[0] == '2') { | ||
417 | k_version = un.release[2] - '0'; | ||
418 | } | 100 | } |
419 | 101 | config_close(p); | |
420 | filename = xasprintf(CONFIG_DEFAULT_MODULES_DIR"/%s/"CONFIG_DEFAULT_DEPMOD_FILE, un.release); | 102 | error: |
421 | f = fopen_for_read(filename); | ||
422 | if (ENABLE_FEATURE_CLEAN_UP) | 103 | if (ENABLE_FEATURE_CLEAN_UP) |
423 | free(filename); | 104 | RELEASE_CONFIG_BUFFER(modname); |
424 | if (f == NULL) { | 105 | return rc; |
425 | /* Ok, that didn't work. Fall back to looking in /lib/modules */ | ||
426 | f = fopen_for_read(CONFIG_DEFAULT_MODULES_DIR"/"CONFIG_DEFAULT_DEPMOD_FILE); | ||
427 | if (f == NULL) { | ||
428 | bb_error_msg_and_die("cannot parse "CONFIG_DEFAULT_DEPMOD_FILE); | ||
429 | } | ||
430 | } | ||
431 | |||
432 | while (fgets(line_buffer, sizeof(line_buffer), f)) { | ||
433 | int l = strlen(line_buffer); | ||
434 | char *p = NULL; | ||
435 | |||
436 | while (l > 0 && isspace(line_buffer[l-1])) { | ||
437 | line_buffer[l-1] = '\0'; | ||
438 | l--; | ||
439 | } | ||
440 | |||
441 | if (l == 0) { | ||
442 | continuation_line = 0; | ||
443 | continue; | ||
444 | } | ||
445 | |||
446 | /* Is this a new module dep description? */ | ||
447 | if (!continuation_line) { | ||
448 | /* find the dep beginning */ | ||
449 | char *col = strchr(line_buffer, ':'); | ||
450 | char *dot = col; | ||
451 | |||
452 | if (col) { | ||
453 | /* This line is a dep description */ | ||
454 | const char *mods; | ||
455 | char *modpath; | ||
456 | char *mod; | ||
457 | |||
458 | /* Find the beginning of the module file name */ | ||
459 | *col = '\0'; | ||
460 | mods = bb_basename(line_buffer); | ||
461 | |||
462 | /* find the path of the module */ | ||
463 | modpath = strchr(line_buffer, '/'); /* ... and this is the path */ | ||
464 | if (!modpath) | ||
465 | modpath = line_buffer; /* module with no path */ | ||
466 | /* find the end of the module name in the file name */ | ||
467 | if (ENABLE_FEATURE_2_6_MODULES && | ||
468 | (k_version > 4) && (col[-3] == '.') && | ||
469 | (col[-2] == 'k') && (col[-1] == 'o')) | ||
470 | dot = col - 3; | ||
471 | else if ((col[-2] == '.') && (col[-1] == 'o')) | ||
472 | dot = col - 2; | ||
473 | |||
474 | mod = xstrndup(mods, dot - mods); | ||
475 | |||
476 | /* enqueue new module */ | ||
477 | if (!conf.current) { | ||
478 | conf.first = conf.current = xzalloc(sizeof(struct dep_t)); | ||
479 | } else { | ||
480 | conf.current->m_next = xzalloc(sizeof(struct dep_t)); | ||
481 | conf.current = conf.current->m_next; | ||
482 | } | ||
483 | conf.current->m_name = mod; | ||
484 | conf.current->m_path = xstrdup(modpath); | ||
485 | /*current->m_options = NULL; - xzalloc did it*/ | ||
486 | /*current->m_isalias = 0;*/ | ||
487 | /*current->m_depcnt = 0;*/ | ||
488 | /*current->m_deparr = 0;*/ | ||
489 | /*current->m_next = 0;*/ | ||
490 | |||
491 | p = col + 1; | ||
492 | } else | ||
493 | /* this line is not a dep description */ | ||
494 | p = NULL; | ||
495 | } else | ||
496 | /* It's a dep description continuation */ | ||
497 | p = line_buffer; | ||
498 | |||
499 | /* p points to the first dependable module; if NULL, no dependable module */ | ||
500 | if (p && (p = skip_whitespace(p))[0] != '\0') { | ||
501 | char *end = &line_buffer[l-1]; | ||
502 | const char *deps; | ||
503 | char *dep; | ||
504 | char *next; | ||
505 | int ext = 0; | ||
506 | |||
507 | while (isblank(*end) || (*end == '\\')) | ||
508 | end--; | ||
509 | |||
510 | do { | ||
511 | /* search the end of the dependency */ | ||
512 | next = strchr(p, ' '); | ||
513 | if (next) { | ||
514 | *next = '\0'; | ||
515 | next--; | ||
516 | } else | ||
517 | next = end; | ||
518 | |||
519 | /* find the beginning of the module file name */ | ||
520 | deps = bb_basename(p); | ||
521 | if (deps == p) | ||
522 | deps = skip_whitespace(deps); | ||
523 | |||
524 | /* find the end of the module name in the file name */ | ||
525 | if (ENABLE_FEATURE_2_6_MODULES | ||
526 | && (k_version > 4) && (next[-2] == '.') | ||
527 | && (next[-1] == 'k') && (next[0] == 'o')) | ||
528 | ext = 3; | ||
529 | else if ((next[-1] == '.') && (next[0] == 'o')) | ||
530 | ext = 2; | ||
531 | |||
532 | /* Cope with blank lines */ | ||
533 | if ((next - deps - ext + 1) <= 0) | ||
534 | continue; | ||
535 | dep = xstrndup(deps, next - deps - ext + 1); | ||
536 | |||
537 | /* Add the new dependable module name */ | ||
538 | conf.current->m_deparr = xrealloc_vector(conf.current->m_deparr, 2, conf.current->m_depcnt); | ||
539 | conf.current->m_deparr[conf.current->m_depcnt++] = dep; | ||
540 | |||
541 | p = next + 2; | ||
542 | } while (next < end); | ||
543 | } | ||
544 | |||
545 | /* is there other dependable module(s) ? */ | ||
546 | continuation_line = (line_buffer[l-1] == '\\'); | ||
547 | } /* while (fgets(...)) */ | ||
548 | fclose(f); | ||
549 | |||
550 | /* | ||
551 | * First parse system-specific options and aliases | ||
552 | * as they take precedence over the kernel ones. | ||
553 | * >=2.6: we only care about modprobe.conf | ||
554 | * <=2.4: we care about modules.conf and conf.modules | ||
555 | */ | ||
556 | { | ||
557 | int r = FALSE; | ||
558 | |||
559 | if (ENABLE_FEATURE_2_6_MODULES) { | ||
560 | if (include_conf_file(&conf, "/etc/modprobe.conf")) | ||
561 | r = TRUE; | ||
562 | if (include_conf_recursive(&conf, "/etc/modprobe.d")) | ||
563 | r = TRUE; | ||
564 | } | ||
565 | if (ENABLE_FEATURE_2_4_MODULES && !r) | ||
566 | include_conf_file2(&conf, | ||
567 | "/etc/modules.conf", | ||
568 | "/etc/conf.modules"); | ||
569 | } | ||
570 | |||
571 | /* Only 2.6 has a modules.alias file */ | ||
572 | if (ENABLE_FEATURE_2_6_MODULES) { | ||
573 | /* Parse kernel-declared module aliases */ | ||
574 | filename = xasprintf(CONFIG_DEFAULT_MODULES_DIR"/%s/modules.alias", un.release); | ||
575 | include_conf_file2(&conf, | ||
576 | filename, | ||
577 | CONFIG_DEFAULT_MODULES_DIR"/modules.alias"); | ||
578 | if (ENABLE_FEATURE_CLEAN_UP) | ||
579 | free(filename); | ||
580 | |||
581 | /* Parse kernel-declared symbol aliases */ | ||
582 | filename = xasprintf(CONFIG_DEFAULT_MODULES_DIR"/%s/modules.symbols", un.release); | ||
583 | include_conf_file2(&conf, | ||
584 | filename, | ||
585 | CONFIG_DEFAULT_MODULES_DIR"/modules.symbols"); | ||
586 | if (ENABLE_FEATURE_CLEAN_UP) | ||
587 | free(filename); | ||
588 | } | ||
589 | |||
590 | return conf.first; | ||
591 | } | ||
592 | |||
593 | /* return 1 = loaded, 0 = not loaded, -1 = can't tell */ | ||
594 | static int already_loaded(const char *name) | ||
595 | { | ||
596 | FILE *f; | ||
597 | int ret; | ||
598 | |||
599 | f = fopen_for_read("/proc/modules"); | ||
600 | if (f == NULL) | ||
601 | return -1; | ||
602 | |||
603 | ret = 0; | ||
604 | while (fgets(line_buffer, sizeof(line_buffer), f)) { | ||
605 | char *p = line_buffer; | ||
606 | const char *n = name; | ||
607 | |||
608 | while (1) { | ||
609 | char cn = *n; | ||
610 | char cp = *p; | ||
611 | if (cp == ' ' || cp == '\0') { | ||
612 | if (cn == '\0') { | ||
613 | ret = 1; /* match! */ | ||
614 | goto done; | ||
615 | } | ||
616 | break; /* no match on this line, take next one */ | ||
617 | } | ||
618 | if (cn == '-') cn = '_'; | ||
619 | if (cp == '-') cp = '_'; | ||
620 | if (cp != cn) | ||
621 | break; /* no match on this line, take next one */ | ||
622 | n++; | ||
623 | p++; | ||
624 | } | ||
625 | } | ||
626 | done: | ||
627 | fclose(f); | ||
628 | return ret; | ||
629 | } | 106 | } |
630 | 107 | ||
631 | static int mod_process(const struct mod_list_t *list, int do_insert) | 108 | static int read_config(struct modprobe_conf *conf, const char *path) |
632 | { | 109 | { |
633 | int rc = 0; | 110 | return recursive_action(path, ACTION_RECURSE | ACTION_QUIET, |
634 | char **argv = NULL; | 111 | config_file_action, NULL, conf, 1); |
635 | struct mod_opt_t *opts; | ||
636 | int argc_malloc; /* never used when CONFIG_FEATURE_CLEAN_UP not defined */ | ||
637 | int argc; | ||
638 | |||
639 | while (list) { | ||
640 | argc = 0; | ||
641 | if (ENABLE_FEATURE_CLEAN_UP) | ||
642 | argc_malloc = 0; | ||
643 | /* If CONFIG_FEATURE_CLEAN_UP is not defined, then we leak memory | ||
644 | * each time we allocate memory for argv. | ||
645 | * But it is (quite) small amounts of memory that leak each | ||
646 | * time a module is loaded, and it is reclaimed when modprobe | ||
647 | * exits anyway (even when standalone shell? Yes --vda). | ||
648 | * This could become a problem when loading a module with LOTS of | ||
649 | * dependencies, with LOTS of options for each dependencies, with | ||
650 | * very little memory on the target... But in that case, the module | ||
651 | * would not load because there is no more memory, so there's no | ||
652 | * problem. */ | ||
653 | /* enough for minimal insmod (5 args + NULL) or rmmod (3 args + NULL) */ | ||
654 | argv = xmalloc(6 * sizeof(char*)); | ||
655 | if (do_insert) { | ||
656 | if (already_loaded(list->m_name) != 1) { | ||
657 | argv[argc++] = (char*)"insmod"; | ||
658 | if (ENABLE_FEATURE_2_4_MODULES) { | ||
659 | if (do_syslog) | ||
660 | argv[argc++] = (char*)"-s"; | ||
661 | if (autoclean) | ||
662 | argv[argc++] = (char*)"-k"; | ||
663 | if (quiet) | ||
664 | argv[argc++] = (char*)"-q"; | ||
665 | else if (verbose) /* verbose and quiet are mutually exclusive */ | ||
666 | argv[argc++] = (char*)"-v"; | ||
667 | } | ||
668 | argv[argc++] = list->m_path; | ||
669 | if (ENABLE_FEATURE_CLEAN_UP) | ||
670 | argc_malloc = argc; | ||
671 | opts = list->m_options; | ||
672 | while (opts) { | ||
673 | /* Add one more option */ | ||
674 | argc++; | ||
675 | argv = xrealloc(argv, (argc + 1) * sizeof(char*)); | ||
676 | argv[argc-1] = opts->m_opt_val; | ||
677 | opts = opts->m_next; | ||
678 | } | ||
679 | } | ||
680 | } else { | ||
681 | /* modutils uses short name for removal */ | ||
682 | if (already_loaded(list->m_name) != 0) { | ||
683 | argv[argc++] = (char*)"rmmod"; | ||
684 | if (do_syslog) | ||
685 | argv[argc++] = (char*)"-s"; | ||
686 | argv[argc++] = (char*)list->m_name; | ||
687 | if (ENABLE_FEATURE_CLEAN_UP) | ||
688 | argc_malloc = argc; | ||
689 | } | ||
690 | } | ||
691 | argv[argc] = NULL; | ||
692 | |||
693 | if (argc) { | ||
694 | if (verbose) { | ||
695 | printf("%s module %s\n", do_insert?"Loading":"Unloading", list->m_name); | ||
696 | } | ||
697 | if (!show_only) { | ||
698 | int rc2 = wait4pid(spawn(argv)); | ||
699 | |||
700 | if (do_insert) { | ||
701 | rc = rc2; /* only last module matters */ | ||
702 | } else if (!rc2) { | ||
703 | rc = 0; /* success if remove any mod */ | ||
704 | } | ||
705 | } | ||
706 | if (ENABLE_FEATURE_CLEAN_UP) { | ||
707 | /* the last value in the array has index == argc, but | ||
708 | * it is the terminating NULL, so we must not free it. */ | ||
709 | while (argc_malloc < argc) { | ||
710 | free(argv[argc_malloc++]); | ||
711 | } | ||
712 | } | ||
713 | } | ||
714 | if (ENABLE_FEATURE_CLEAN_UP) { | ||
715 | free(argv); | ||
716 | argv = NULL; | ||
717 | } | ||
718 | list = do_insert ? list->m_prev : list->m_next; | ||
719 | } | ||
720 | return (show_only) ? 0 : rc; | ||
721 | } | 112 | } |
722 | 113 | ||
723 | /* | 114 | static char *gather_options(llist_t *first, const char *module, int usecmdline) |
724 | * Check the matching between a pattern and a module name. | ||
725 | * We need this as *_* is equivalent to *-*, even in pattern matching. | ||
726 | */ | ||
727 | static int check_pattern(const char* pat_src, const char* mod_src) | ||
728 | { | 115 | { |
729 | int ret; | 116 | struct modprobe_option *opt; |
730 | 117 | llist_t *n; | |
731 | if (ENABLE_FEATURE_MODPROBE_FANCY_ALIAS) { | 118 | char *opts = xstrdup(""); |
732 | char* pat; | 119 | int optlen = 0; |
733 | char* mod; | ||
734 | char* p; | ||
735 | 120 | ||
736 | pat = xstrdup(pat_src); | 121 | for (n = first; n != NULL; n = n->link) { |
737 | mod = xstrdup(mod_src); | 122 | opt = (struct modprobe_option *) n->data; |
738 | 123 | ||
739 | for (p = pat; (p = strchr(p, '-')); *p++ = '_'); | 124 | if (opt->module == NULL && !usecmdline) |
740 | for (p = mod; (p = strchr(p, '-')); *p++ = '_'); | 125 | continue; |
741 | 126 | if (opt->module != NULL && strcmp(opt->module, module) != 0) | |
742 | ret = fnmatch(pat, mod, 0); | 127 | continue; |
743 | |||
744 | if (ENABLE_FEATURE_CLEAN_UP) { | ||
745 | free(pat); | ||
746 | free(mod); | ||
747 | } | ||
748 | 128 | ||
749 | return ret; | 129 | opts = xrealloc(opts, optlen + strlen(opt->option) + 2); |
130 | optlen += sprintf(opts + optlen, "%s ", opt->option); | ||
750 | } | 131 | } |
751 | return fnmatch(pat_src, mod_src, 0); | 132 | return opts; |
752 | } | 133 | } |
753 | 134 | ||
754 | /* | 135 | static int do_modprobe(struct modprobe_conf *conf, const char *module) |
755 | * Builds the dependency list (aka stack) of a module. | ||
756 | * head: the highest module in the stack (last to insmod, first to rmmod) | ||
757 | * tail: the lowest module in the stack (first to insmod, last to rmmod) | ||
758 | */ | ||
759 | static void check_dep(char *mod, struct mod_list_t **head, struct mod_list_t **tail) | ||
760 | { | 136 | { |
761 | struct mod_list_t *find; | 137 | RESERVE_CONFIG_BUFFER(modname, MODULE_NAME_LEN); |
762 | struct dep_t *dt; | 138 | llist_t *deps = NULL; |
763 | struct mod_opt_t *opt = NULL; | 139 | char *fn, *options, *colon = NULL, *tokens[2]; |
764 | char *path = NULL; | 140 | parser_t *p; |
765 | 141 | int rc = -1; | |
766 | /* Search for the given module name amongst all dependency rules. | 142 | |
767 | * The module name in a dependency rule can be a shell pattern, | 143 | p = config_open2(CONFIG_DEFAULT_DEPMOD_FILE, fopen_for_read); |
768 | * so try to match the given module name against such a pattern. | 144 | if (p == NULL) |
769 | * Of course if the name in the dependency rule is a plain string, | 145 | goto error; |
770 | * then we consider it a pattern, and matching will still work. */ | 146 | |
771 | for (dt = depend; dt; dt = dt->m_next) { | 147 | while (config_read(p, tokens, 2, 1, "# \t", PARSE_NORMAL)) { |
772 | if (check_pattern(dt->m_name, mod) == 0) { | 148 | colon = last_char_is(tokens[0], ':'); |
773 | break; | 149 | if (colon == NULL) |
774 | } | 150 | continue; |
775 | } | ||
776 | |||
777 | if (!dt) { | ||
778 | bb_error_msg("module %s not found", mod); | ||
779 | return; | ||
780 | } | ||
781 | 151 | ||
782 | // resolve alias names | 152 | filename2modname(tokens[0], modname); |
783 | while (dt->m_isalias) { | 153 | if (strcmp(modname, module) == 0) |
784 | if (dt->m_depcnt == 1) { | 154 | break; |
785 | struct dep_t *adt; | ||
786 | 155 | ||
787 | for (adt = depend; adt; adt = adt->m_next) { | 156 | colon = NULL; |
788 | if (check_pattern(adt->m_name, dt->m_deparr[0]) == 0 && | ||
789 | !(ENABLE_FEATURE_MODPROBE_BLACKLIST && | ||
790 | adt->m_isblacklisted)) | ||
791 | break; | ||
792 | } | ||
793 | if (adt) { | ||
794 | /* This is the module we are aliased to */ | ||
795 | struct mod_opt_t *opts = dt->m_options; | ||
796 | /* Option of the alias are appended to the options of the module */ | ||
797 | while (opts) { | ||
798 | adt->m_options = append_option(adt->m_options, opts->m_opt_val); | ||
799 | opts = opts->m_next; | ||
800 | } | ||
801 | dt = adt; | ||
802 | } else { | ||
803 | bb_error_msg("module %s not found", mod); | ||
804 | return; | ||
805 | } | ||
806 | } else { | ||
807 | bb_error_msg("bad alias %s", dt->m_name); | ||
808 | return; | ||
809 | } | ||
810 | } | 157 | } |
811 | 158 | if (colon == NULL) | |
812 | mod = dt->m_name; | 159 | goto error_not_found; |
813 | path = dt->m_path; | 160 | |
814 | opt = dt->m_options; | 161 | colon[0] = '\0'; |
815 | 162 | llist_add_to(&deps, xstrdup(tokens[0])); | |
816 | // search for duplicates | 163 | if (tokens[1]) |
817 | for (find = *head; find; find = find->m_next) { | 164 | string_to_llist(tokens[1], &deps, " "); |
818 | if (strcmp(mod, find->m_name) == 0) { | 165 | |
819 | // found -> dequeue it | 166 | if (!(option_mask32 & MODPROBE_OPT_REMOVE)) |
820 | 167 | deps = llist_rev(deps); | |
821 | if (find->m_prev) | 168 | |
822 | find->m_prev->m_next = find->m_next; | 169 | rc = 0; |
823 | else | 170 | while (deps && rc == 0) { |
824 | *head = find->m_next; | 171 | fn = llist_pop(&deps); |
825 | 172 | filename2modname(fn, modname); | |
826 | if (find->m_next) | 173 | if (option_mask32 & MODPROBE_OPT_REMOVE) { |
827 | find->m_next->m_prev = find->m_prev; | 174 | if (bb_delete_module(modname, O_EXCL) != 0) |
828 | else | 175 | rc = errno; |
829 | *tail = find->m_prev; | 176 | } else if (llist_find(loaded, modname) == NULL) { |
830 | 177 | options = gather_options(conf->options, modname, | |
831 | break; // there can be only one duplicate | 178 | strcmp(modname, module) == 0); |
179 | rc = bb_init_module(fn, options); | ||
180 | if (rc == 0) | ||
181 | llist_add_to(&loaded, xstrdup(modname)); | ||
182 | if (ENABLE_FEATURE_CLEAN_UP) | ||
183 | free(options); | ||
832 | } | 184 | } |
833 | } | ||
834 | |||
835 | if (!find) { // did not find a duplicate | ||
836 | find = xzalloc(sizeof(struct mod_list_t)); | ||
837 | find->m_name = mod; | ||
838 | find->m_path = path; | ||
839 | find->m_options = opt; | ||
840 | } | ||
841 | |||
842 | // enqueue at tail | ||
843 | if (*tail) | ||
844 | (*tail)->m_next = find; | ||
845 | find->m_prev = *tail; | ||
846 | find->m_next = NULL; /* possibly NOT done by xzalloc! */ | ||
847 | |||
848 | if (!*head) | ||
849 | *head = find; | ||
850 | *tail = find; | ||
851 | |||
852 | if (dt) { | ||
853 | int i; | ||
854 | 185 | ||
855 | /* Add all dependable module for that new module */ | 186 | if (ENABLE_FEATURE_CLEAN_UP) |
856 | for (i = 0; i < dt->m_depcnt; i++) | 187 | free(fn); |
857 | check_dep(dt->m_deparr[i], head, tail); | ||
858 | } | ||
859 | } | ||
860 | |||
861 | static int mod_insert(char **argv) | ||
862 | { | ||
863 | struct mod_list_t *tail = NULL; | ||
864 | struct mod_list_t *head = NULL; | ||
865 | char *modname = *argv++; | ||
866 | int rc; | ||
867 | |||
868 | // get dep list for module mod | ||
869 | check_dep(modname, &head, &tail); | ||
870 | |||
871 | rc = 1; | ||
872 | if (head && tail) { | ||
873 | while (*argv) | ||
874 | head->m_options = append_option(head->m_options, *argv++); | ||
875 | |||
876 | // process tail ---> head | ||
877 | rc = mod_process(tail, 1); | ||
878 | if (rc) { | ||
879 | /* | ||
880 | * In case of using udev, multiple instances of modprobe can be | ||
881 | * spawned to load the same module (think of two same usb devices, | ||
882 | * for example; or cold-plugging at boot time). Thus we shouldn't | ||
883 | * fail if the module was loaded, and not by us. | ||
884 | */ | ||
885 | if (already_loaded(modname)) | ||
886 | rc = 0; | ||
887 | } | ||
888 | } | 188 | } |
889 | return rc; | ||
890 | } | ||
891 | |||
892 | static int mod_remove(char *modname) | ||
893 | { | ||
894 | static const struct mod_list_t rm_a_dummy = { "-a", NULL, NULL, NULL, NULL }; | ||
895 | |||
896 | int rc; | ||
897 | struct mod_list_t *head = NULL; | ||
898 | struct mod_list_t *tail = NULL; | ||
899 | 189 | ||
900 | if (modname) | 190 | error_not_found: |
901 | check_dep(modname, &head, &tail); | 191 | config_close(p); |
902 | else // autoclean | 192 | error: |
903 | head = tail = (struct mod_list_t*) &rm_a_dummy; | 193 | if (ENABLE_FEATURE_CLEAN_UP) |
904 | 194 | RELEASE_CONFIG_BUFFER(modname); | |
905 | rc = 1; | 195 | if (rc > 0 && !(option_mask32 & INSMOD_OPT_SILENT)) |
906 | if (head && tail) | 196 | bb_error_msg("Failed to %sload module %s: %s.", |
907 | rc = mod_process(head, 0); // process head ---> tail | 197 | (option_mask32 & MODPROBE_OPT_REMOVE) ? "un" : "", |
198 | module, moderror(rc)); | ||
908 | return rc; | 199 | return rc; |
909 | } | 200 | } |
910 | 201 | ||
911 | int modprobe_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | 202 | int modprobe_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
912 | int modprobe_main(int argc UNUSED_PARAM, char **argv) | 203 | int modprobe_main(int argc UNUSED_PARAM, char **argv) |
913 | { | 204 | { |
914 | int rc = EXIT_SUCCESS; | 205 | struct utsname uts; |
915 | unsigned opt; | 206 | int num_modules, i, rc; |
916 | char *unused; | 207 | llist_t *options = NULL; |
208 | parser_t *parser; | ||
917 | 209 | ||
918 | opt_complementary = "q-v:v-q"; | 210 | opt_complementary = "q-v:v-q"; |
919 | opt = getopt32(argv, MAIN_OPT_STR, &unused, &unused); | 211 | getopt32(argv, INSMOD_OPTS MODPROBE_OPTS INSMOD_ARGS, |
212 | NULL, NULL); | ||
920 | argv += optind; | 213 | argv += optind; |
921 | 214 | argc -= optind; | |
922 | if (opt & (DUMP_CONF_EXIT | LIST_ALL)) | 215 | |
923 | return EXIT_SUCCESS; | 216 | if (option_mask32 & (MODPROBE_OPT_DUMP_ONLY | MODPROBE_OPT_LIST_ONLY | |
924 | if (opt & (RESTRICT_DIR | CONFIG_FILE)) | 217 | MODPROBE_OPT_SHOW_ONLY)) |
925 | bb_error_msg_and_die("-t and -C not supported"); | 218 | bb_error_msg_and_die("not supported"); |
926 | 219 | ||
927 | depend = build_dep(); | 220 | /* goto modules location */ |
928 | 221 | xchdir(CONFIG_DEFAULT_MODULES_DIR); | |
929 | if (!depend) | 222 | uname(&uts); |
930 | bb_error_msg_and_die("cannot parse "CONFIG_DEFAULT_DEPMOD_FILE); | 223 | xchdir(uts.release); |
931 | 224 | ||
932 | if (remove_opt) { | 225 | if (option_mask32 & (MODPROBE_OPT_REMOVE | MODPROBE_OPT_INSERT_ALL)) { |
933 | do { | 226 | /* each parameter is a module name */ |
934 | /* (*argv) can be NULL here */ | 227 | num_modules = argc; |
935 | if (mod_remove(*argv)) { | 228 | if (num_modules == 0) { |
936 | bb_perror_msg("failed to %s module %s", "remove", | 229 | if (bb_delete_module(NULL, O_NONBLOCK|O_EXCL) != 0) |
937 | *argv); | 230 | bb_perror_msg_and_die("rmmod"); |
938 | rc = EXIT_FAILURE; | 231 | return EXIT_SUCCESS; |
939 | } | 232 | } |
940 | } while (*argv && *++argv); | ||
941 | } else { | 233 | } else { |
942 | if (!*argv) | 234 | /* the only module, the rest of parameters are options */ |
943 | bb_error_msg_and_die("no module or pattern provided"); | 235 | num_modules = 1; |
236 | add_option(&options, NULL, parse_cmdline_module_options(argv)); | ||
237 | } | ||
944 | 238 | ||
945 | if (mod_insert(argv)) | 239 | /* cache modules */ |
946 | bb_perror_msg_and_die("failed to %s module %s", "load", *argv); | 240 | parser = config_open2("/proc/modules", fopen_for_read); |
241 | if (parser) { | ||
242 | char *s; | ||
243 | while (config_read(parser, &s, 1, 1, "# \t", PARSE_NORMAL & ~PARSE_GREEDY)) | ||
244 | llist_add_to(&loaded, xstrdup(s)); | ||
245 | config_close(parser); | ||
947 | } | 246 | } |
948 | 247 | ||
949 | /* Here would be a good place to free up memory allocated during the dependencies build. */ | 248 | for (i = 0; i < num_modules; i++) { |
249 | struct modprobe_conf *conf; | ||
250 | |||
251 | conf = xzalloc(sizeof(struct modprobe_conf)); | ||
252 | conf->options = options; | ||
253 | filename2modname(argv[i], conf->probename); | ||
254 | read_config(conf, "/etc/modprobe.conf"); | ||
255 | read_config(conf, "/etc/modprobe.d"); | ||
256 | if (ENABLE_FEATURE_MODUTILS_SYMBOLS && | ||
257 | conf->aliases == NULL && strncmp(argv[i], "symbol:", 7) == 0) | ||
258 | read_config(conf, "modules.symbols"); | ||
259 | |||
260 | if (ENABLE_FEATURE_MODUTILS_ALIAS && conf->aliases == NULL) | ||
261 | read_config(conf, "modules.alias"); | ||
262 | |||
263 | if (conf->aliases == NULL) { | ||
264 | /* Try if module by literal name is found; literal | ||
265 | * names are blacklist only if '-b' is given. */ | ||
266 | if (!(option_mask32 & MODPROBE_OPT_BLACKLIST) || | ||
267 | check_blacklist(conf, conf->probename)) { | ||
268 | rc = do_modprobe(conf, conf->probename); | ||
269 | if (rc < 0 && !(option_mask32 & INSMOD_OPT_SILENT)) | ||
270 | bb_error_msg("Module %s not found.", argv[i]); | ||
271 | } | ||
272 | } else { | ||
273 | /* Probe all aliases */ | ||
274 | while (conf->aliases != NULL) { | ||
275 | char *realname = llist_pop(&conf->aliases); | ||
276 | if (check_blacklist(conf, realname)) | ||
277 | do_modprobe(conf, realname); | ||
278 | if (ENABLE_FEATURE_CLEAN_UP) | ||
279 | free(realname); | ||
280 | } | ||
281 | } | ||
282 | } | ||
950 | 283 | ||
951 | return rc; | 284 | return EXIT_SUCCESS; |
952 | } | 285 | } |