Contents of /tags/mkinitrd-6_1_0/busybox/modutils/modprobe.c
Parent Directory | Revision Log
Revision 820 -
(show annotations)
(download)
Fri Apr 24 19:09:57 2009 UTC (15 years, 5 months ago) by niro
File MIME type: text/plain
File size: 7846 byte(s)
Fri Apr 24 19:09:57 2009 UTC (15 years, 5 months ago) by niro
File MIME type: text/plain
File size: 7846 byte(s)
tagged 'mkinitrd-6_1_0'
1 | /* vi: set sw=4 ts=4: */ |
2 | /* |
3 | * Modprobe written from scratch for BusyBox |
4 | * |
5 | * Copyright (c) 2008 Timo Teras <timo.teras@iki.fi> |
6 | * Copyright (c) 2008 Vladimir Dronnikov |
7 | * |
8 | * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. |
9 | */ |
10 | |
11 | #include "libbb.h" |
12 | #include "modutils.h" |
13 | #include <sys/utsname.h> |
14 | #include <fnmatch.h> |
15 | |
16 | struct modprobe_option { |
17 | char *module; |
18 | char *option; |
19 | }; |
20 | |
21 | struct modprobe_conf { |
22 | char probename[MODULE_NAME_LEN]; |
23 | llist_t *options; |
24 | llist_t *aliases; |
25 | #if ENABLE_FEATURE_MODPROBE_BLACKLIST |
26 | #define add_to_blacklist(conf, name) llist_add_to(&conf->blacklist, name) |
27 | #define check_blacklist(conf, name) (llist_find(conf->blacklist, name) == NULL) |
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 |
33 | }; |
34 | |
35 | #define MODPROBE_OPTS "acdlnrt:VC:" USE_FEATURE_MODPROBE_BLACKLIST("b") |
36 | enum { |
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, |
47 | }; |
48 | |
49 | static llist_t *loaded; |
50 | |
51 | static int read_config(struct modprobe_conf *conf, const char *path); |
52 | |
53 | static void add_option(llist_t **all_opts, const char *module, const char *opts) |
54 | { |
55 | struct modprobe_option *o; |
56 | |
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); |
62 | } |
63 | |
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) |
68 | { |
69 | struct modprobe_conf *conf = (struct modprobe_conf *) userdata; |
70 | RESERVE_CONFIG_BUFFER(modname, MODULE_NAME_LEN); |
71 | char *tokens[3]; |
72 | parser_t *p; |
73 | int rc = TRUE; |
74 | |
75 | if (bb_basename(filename)[0] == '.') |
76 | goto error; |
77 | |
78 | p = config_open2(filename, fopen_for_read); |
79 | if (p == NULL) { |
80 | rc = FALSE; |
81 | goto error; |
82 | } |
83 | |
84 | while (config_read(p, tokens, 3, 2, "# \t", PARSE_NORMAL)) { |
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]); |
96 | } else if (ENABLE_FEATURE_MODPROBE_BLACKLIST && |
97 | strcmp(tokens[0], "blacklist") == 0) { |
98 | add_to_blacklist(conf, xstrdup(tokens[1])); |
99 | } |
100 | } |
101 | config_close(p); |
102 | error: |
103 | if (ENABLE_FEATURE_CLEAN_UP) |
104 | RELEASE_CONFIG_BUFFER(modname); |
105 | return rc; |
106 | } |
107 | |
108 | static int read_config(struct modprobe_conf *conf, const char *path) |
109 | { |
110 | return recursive_action(path, ACTION_RECURSE | ACTION_QUIET, |
111 | config_file_action, NULL, conf, 1); |
112 | } |
113 | |
114 | static char *gather_options(llist_t *first, const char *module, int usecmdline) |
115 | { |
116 | struct modprobe_option *opt; |
117 | llist_t *n; |
118 | char *opts = xstrdup(""); |
119 | int optlen = 0; |
120 | |
121 | for (n = first; n != NULL; n = n->link) { |
122 | opt = (struct modprobe_option *) n->data; |
123 | |
124 | if (opt->module == NULL && !usecmdline) |
125 | continue; |
126 | if (opt->module != NULL && strcmp(opt->module, module) != 0) |
127 | continue; |
128 | |
129 | opts = xrealloc(opts, optlen + strlen(opt->option) + 2); |
130 | optlen += sprintf(opts + optlen, "%s ", opt->option); |
131 | } |
132 | return opts; |
133 | } |
134 | |
135 | static int do_modprobe(struct modprobe_conf *conf, const char *module) |
136 | { |
137 | RESERVE_CONFIG_BUFFER(modname, MODULE_NAME_LEN); |
138 | llist_t *deps = NULL; |
139 | char *fn, *options, *colon = NULL, *tokens[2]; |
140 | parser_t *p; |
141 | int rc = -1; |
142 | |
143 | p = config_open2(CONFIG_DEFAULT_DEPMOD_FILE, fopen_for_read); |
144 | if (p == NULL) |
145 | goto error; |
146 | |
147 | while (config_read(p, tokens, 2, 1, "# \t", PARSE_NORMAL)) { |
148 | colon = last_char_is(tokens[0], ':'); |
149 | if (colon == NULL) |
150 | continue; |
151 | |
152 | filename2modname(tokens[0], modname); |
153 | if (strcmp(modname, module) == 0) |
154 | break; |
155 | |
156 | colon = NULL; |
157 | } |
158 | if (colon == NULL) |
159 | goto error_not_found; |
160 | |
161 | colon[0] = '\0'; |
162 | llist_add_to(&deps, xstrdup(tokens[0])); |
163 | if (tokens[1]) |
164 | string_to_llist(tokens[1], &deps, " "); |
165 | |
166 | if (!(option_mask32 & MODPROBE_OPT_REMOVE)) |
167 | deps = llist_rev(deps); |
168 | |
169 | rc = 0; |
170 | while (deps && rc == 0) { |
171 | fn = llist_pop(&deps); |
172 | filename2modname(fn, modname); |
173 | if (option_mask32 & MODPROBE_OPT_REMOVE) { |
174 | if (bb_delete_module(modname, O_EXCL) != 0) |
175 | rc = errno; |
176 | } else if (llist_find(loaded, modname) == NULL) { |
177 | options = gather_options(conf->options, modname, |
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); |
184 | } |
185 | |
186 | if (ENABLE_FEATURE_CLEAN_UP) |
187 | free(fn); |
188 | } |
189 | |
190 | error_not_found: |
191 | config_close(p); |
192 | error: |
193 | if (ENABLE_FEATURE_CLEAN_UP) |
194 | RELEASE_CONFIG_BUFFER(modname); |
195 | if (rc > 0 && !(option_mask32 & INSMOD_OPT_SILENT)) |
196 | bb_error_msg("Failed to %sload module %s: %s.", |
197 | (option_mask32 & MODPROBE_OPT_REMOVE) ? "un" : "", |
198 | module, moderror(rc)); |
199 | return rc; |
200 | } |
201 | |
202 | int modprobe_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
203 | int modprobe_main(int argc UNUSED_PARAM, char **argv) |
204 | { |
205 | struct utsname uts; |
206 | int rc; |
207 | unsigned opt; |
208 | llist_t *options = NULL; |
209 | |
210 | opt_complementary = "q-v:v-q"; |
211 | opt = getopt32(argv, INSMOD_OPTS MODPROBE_OPTS INSMOD_ARGS, |
212 | NULL, NULL); |
213 | argv += optind; |
214 | |
215 | if (opt & (MODPROBE_OPT_DUMP_ONLY | MODPROBE_OPT_LIST_ONLY | |
216 | MODPROBE_OPT_SHOW_ONLY)) |
217 | bb_error_msg_and_die("not supported"); |
218 | |
219 | /* goto modules location */ |
220 | xchdir(CONFIG_DEFAULT_MODULES_DIR); |
221 | uname(&uts); |
222 | xchdir(uts.release); |
223 | |
224 | if (!argv[0]) { |
225 | if (opt & MODPROBE_OPT_REMOVE) { |
226 | if (bb_delete_module(NULL, O_NONBLOCK|O_EXCL) != 0) |
227 | bb_perror_msg_and_die("rmmod"); |
228 | } |
229 | return EXIT_SUCCESS; |
230 | } |
231 | if (!(opt & MODPROBE_OPT_INSERT_ALL)) { |
232 | /* If not -a, we have only one module name, |
233 | * the rest of parameters are options */ |
234 | add_option(&options, NULL, parse_cmdline_module_options(argv)); |
235 | argv[1] = NULL; |
236 | } |
237 | |
238 | /* cache modules */ |
239 | { |
240 | char *s; |
241 | parser_t *parser = config_open2("/proc/modules", fopen_for_read); |
242 | while (config_read(parser, &s, 1, 1, "# \t", PARSE_NORMAL & ~PARSE_GREEDY)) |
243 | llist_add_to(&loaded, xstrdup(s)); |
244 | config_close(parser); |
245 | } |
246 | |
247 | while (*argv) { |
248 | const char *arg = *argv; |
249 | struct modprobe_conf *conf; |
250 | |
251 | conf = xzalloc(sizeof(*conf)); |
252 | conf->options = options; |
253 | filename2modname(arg, 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 |
258 | && strncmp(arg, "symbol:", 7) == 0 |
259 | ) { |
260 | read_config(conf, "modules.symbols"); |
261 | } |
262 | |
263 | if (ENABLE_FEATURE_MODUTILS_ALIAS && conf->aliases == NULL) |
264 | read_config(conf, "modules.alias"); |
265 | |
266 | if (conf->aliases == NULL) { |
267 | /* Try if module by literal name is found; literal |
268 | * names are blacklist only if '-b' is given. */ |
269 | if (!(opt & MODPROBE_OPT_BLACKLIST) || |
270 | check_blacklist(conf, conf->probename)) { |
271 | rc = do_modprobe(conf, conf->probename); |
272 | if (rc < 0 && !(opt & INSMOD_OPT_SILENT)) |
273 | bb_error_msg("Module %s not found.", arg); |
274 | } |
275 | } else { |
276 | /* Probe all aliases */ |
277 | while (conf->aliases != NULL) { |
278 | char *realname = llist_pop(&conf->aliases); |
279 | if (check_blacklist(conf, realname)) |
280 | do_modprobe(conf, realname); |
281 | if (ENABLE_FEATURE_CLEAN_UP) |
282 | free(realname); |
283 | } |
284 | } |
285 | argv++; |
286 | } |
287 | |
288 | return EXIT_SUCCESS; |
289 | } |