8 |
* Licensed under GPLv2 or later, see file LICENSE in this tarball for details. |
* Licensed under GPLv2 or later, see file LICENSE in this tarball for details. |
9 |
*/ |
*/ |
10 |
|
|
11 |
|
/* Note that unlike older versions of modules.dep/depmod (busybox and m-i-t), |
12 |
|
* we expect the full dependency list to be specified in modules.dep. |
13 |
|
* Older versions would only export the direct dependency list. |
14 |
|
*/ |
15 |
#include "libbb.h" |
#include "libbb.h" |
16 |
#include "modutils.h" |
#include "modutils.h" |
17 |
#include <sys/utsname.h> |
#include <sys/utsname.h> |
18 |
#include <fnmatch.h> |
#include <fnmatch.h> |
19 |
|
|
20 |
struct modprobe_option { |
//#define DBG(fmt, ...) bb_error_msg("%s: " fmt, __func__, ## __VA_ARGS__) |
21 |
char *module; |
#define DBG(...) ((void)0) |
|
char *option; |
|
|
}; |
|
22 |
|
|
23 |
struct modprobe_conf { |
#define MODULE_FLAG_LOADED 0x0001 |
24 |
char probename[MODULE_NAME_LEN]; |
#define MODULE_FLAG_NEED_DEPS 0x0002 |
25 |
llist_t *options; |
/* "was seen in modules.dep": */ |
26 |
llist_t *aliases; |
#define MODULE_FLAG_FOUND_IN_MODDEP 0x0004 |
27 |
#if ENABLE_FEATURE_MODPROBE_BLACKLIST |
#define MODULE_FLAG_BLACKLISTED 0x0008 |
28 |
#define add_to_blacklist(conf, name) llist_add_to(&conf->blacklist, name) |
|
29 |
#define check_blacklist(conf, name) (llist_find(conf->blacklist, name) == NULL) |
struct module_entry { /* I'll call it ME. */ |
30 |
llist_t *blacklist; |
unsigned flags; |
31 |
#else |
char *modname; /* stripped of /path/, .ext and s/-/_/g */ |
32 |
#define add_to_blacklist(conf, name) do {} while (0) |
const char *probed_name; /* verbatim as seen on cmdline */ |
33 |
#define check_blacklist(conf, name) (1) |
char *options; /* options from config files */ |
34 |
#endif |
llist_t *realnames; /* strings. if this module is an alias, */ |
35 |
|
/* real module name is one of these. */ |
36 |
|
//Can there really be more than one? Example from real kernel? |
37 |
|
llist_t *deps; /* strings. modules we depend on */ |
38 |
}; |
}; |
39 |
|
|
40 |
#define MODPROBE_OPTS "acdlnrt:VC:" USE_FEATURE_MODPROBE_BLACKLIST("b") |
/* NB: INSMOD_OPT_SILENT bit suppresses ONLY non-existent modules, |
41 |
|
* not deleted ones (those are still listed in modules.dep). |
42 |
|
* module-init-tools version 3.4: |
43 |
|
* # modprobe bogus |
44 |
|
* FATAL: Module bogus not found. [exitcode 1] |
45 |
|
* # modprobe -q bogus [silent, exitcode still 1] |
46 |
|
* but: |
47 |
|
* # rm kernel/drivers/net/dummy.ko |
48 |
|
* # modprobe -q dummy |
49 |
|
* FATAL: Could not open '/lib/modules/xxx/kernel/drivers/net/dummy.ko': No such file or directory |
50 |
|
* [exitcode 1] |
51 |
|
*/ |
52 |
|
#define MODPROBE_OPTS "acdlnrt:VC:" IF_FEATURE_MODPROBE_BLACKLIST("b") |
53 |
enum { |
enum { |
54 |
MODPROBE_OPT_INSERT_ALL = (INSMOD_OPT_UNUSED << 0), /* a */ |
MODPROBE_OPT_INSERT_ALL = (INSMOD_OPT_UNUSED << 0), /* a */ |
55 |
MODPROBE_OPT_DUMP_ONLY = (INSMOD_OPT_UNUSED << 1), /* c */ |
MODPROBE_OPT_DUMP_ONLY = (INSMOD_OPT_UNUSED << 1), /* c */ |
56 |
MODPROBE_OPT_D = (INSMOD_OPT_UNUSED << 2), /* d */ |
MODPROBE_OPT_D = (INSMOD_OPT_UNUSED << 2), /* d */ |
57 |
MODPROBE_OPT_LIST_ONLY = (INSMOD_OPT_UNUSED << 3), /* l */ |
MODPROBE_OPT_LIST_ONLY = (INSMOD_OPT_UNUSED << 3), /* l */ |
58 |
MODPROBE_OPT_SHOW_ONLY = (INSMOD_OPT_UNUSED << 4), /* n */ |
MODPROBE_OPT_SHOW_ONLY = (INSMOD_OPT_UNUSED << 4), /* n */ |
59 |
MODPROBE_OPT_REMOVE = (INSMOD_OPT_UNUSED << 5), /* r */ |
MODPROBE_OPT_REMOVE = (INSMOD_OPT_UNUSED << 5), /* r */ |
60 |
MODPROBE_OPT_RESTRICT = (INSMOD_OPT_UNUSED << 6), /* t */ |
MODPROBE_OPT_RESTRICT = (INSMOD_OPT_UNUSED << 6), /* t */ |
61 |
MODPROBE_OPT_VERONLY = (INSMOD_OPT_UNUSED << 7), /* V */ |
MODPROBE_OPT_VERONLY = (INSMOD_OPT_UNUSED << 7), /* V */ |
62 |
MODPROBE_OPT_CONFIGFILE = (INSMOD_OPT_UNUSED << 8), /* C */ |
MODPROBE_OPT_CONFIGFILE = (INSMOD_OPT_UNUSED << 8), /* C */ |
63 |
MODPROBE_OPT_BLACKLIST = (INSMOD_OPT_UNUSED << 9) * ENABLE_FEATURE_MODPROBE_BLACKLIST, |
MODPROBE_OPT_BLACKLIST = (INSMOD_OPT_UNUSED << 9) * ENABLE_FEATURE_MODPROBE_BLACKLIST, |
64 |
}; |
}; |
65 |
|
|
66 |
static llist_t *loaded; |
struct globals { |
67 |
|
llist_t *db; /* MEs of all modules ever seen (caching for speed) */ |
68 |
|
llist_t *probes; /* MEs of module(s) requested on cmdline */ |
69 |
|
char *cmdline_mopts; /* module options from cmdline */ |
70 |
|
int num_unresolved_deps; |
71 |
|
/* bool. "Did we have 'symbol:FOO' requested on cmdline?" */ |
72 |
|
smallint need_symbols; |
73 |
|
}; |
74 |
|
#define G (*(struct globals*)&bb_common_bufsiz1) |
75 |
|
#define INIT_G() do { } while (0) |
76 |
|
|
77 |
|
|
78 |
static int read_config(struct modprobe_conf *conf, const char *path); |
static int read_config(const char *path); |
79 |
|
|
80 |
static void add_option(llist_t **all_opts, const char *module, const char *opts) |
static char *gather_options_str(char *opts, const char *append) |
81 |
{ |
{ |
82 |
struct modprobe_option *o; |
/* Speed-optimized. We call gather_options_str many times. */ |
83 |
|
if (append) { |
84 |
|
if (opts == NULL) { |
85 |
|
opts = xstrdup(append); |
86 |
|
} else { |
87 |
|
int optlen = strlen(opts); |
88 |
|
opts = xrealloc(opts, optlen + strlen(append) + 2); |
89 |
|
sprintf(opts + optlen, " %s", append); |
90 |
|
} |
91 |
|
} |
92 |
|
return opts; |
93 |
|
} |
94 |
|
|
95 |
o = xzalloc(sizeof(struct modprobe_option)); |
static struct module_entry *helper_get_module(const char *module, int create) |
96 |
if (module) |
{ |
97 |
o->module = filename2modname(module, NULL); |
char modname[MODULE_NAME_LEN]; |
98 |
o->option = xstrdup(opts); |
struct module_entry *e; |
99 |
llist_add_to(all_opts, o); |
llist_t *l; |
100 |
|
|
101 |
|
filename2modname(module, modname); |
102 |
|
for (l = G.db; l != NULL; l = l->link) { |
103 |
|
e = (struct module_entry *) l->data; |
104 |
|
if (strcmp(e->modname, modname) == 0) |
105 |
|
return e; |
106 |
|
} |
107 |
|
if (!create) |
108 |
|
return NULL; |
109 |
|
|
110 |
|
e = xzalloc(sizeof(*e)); |
111 |
|
e->modname = xstrdup(modname); |
112 |
|
llist_add_to(&G.db, e); |
113 |
|
|
114 |
|
return e; |
115 |
|
} |
116 |
|
static struct module_entry *get_or_add_modentry(const char *module) |
117 |
|
{ |
118 |
|
return helper_get_module(module, 1); |
119 |
|
} |
120 |
|
static struct module_entry *get_modentry(const char *module) |
121 |
|
{ |
122 |
|
return helper_get_module(module, 0); |
123 |
|
} |
124 |
|
|
125 |
|
static void add_probe(const char *name) |
126 |
|
{ |
127 |
|
struct module_entry *m; |
128 |
|
|
129 |
|
m = get_or_add_modentry(name); |
130 |
|
if (!(option_mask32 & MODPROBE_OPT_REMOVE) |
131 |
|
&& (m->flags & MODULE_FLAG_LOADED) |
132 |
|
) { |
133 |
|
DBG("skipping %s, it is already loaded", name); |
134 |
|
return; |
135 |
|
} |
136 |
|
|
137 |
|
DBG("queuing %s", name); |
138 |
|
m->probed_name = name; |
139 |
|
m->flags |= MODULE_FLAG_NEED_DEPS; |
140 |
|
llist_add_to_end(&G.probes, m); |
141 |
|
G.num_unresolved_deps++; |
142 |
|
if (ENABLE_FEATURE_MODUTILS_SYMBOLS |
143 |
|
&& strncmp(m->modname, "symbol:", 7) == 0 |
144 |
|
) { |
145 |
|
G.need_symbols = 1; |
146 |
|
} |
147 |
} |
} |
148 |
|
|
149 |
static int FAST_FUNC config_file_action(const char *filename, |
static int FAST_FUNC config_file_action(const char *filename, |
150 |
struct stat *statbuf UNUSED_PARAM, |
struct stat *statbuf UNUSED_PARAM, |
151 |
void *userdata, |
void *userdata UNUSED_PARAM, |
152 |
int depth UNUSED_PARAM) |
int depth UNUSED_PARAM) |
153 |
{ |
{ |
|
struct modprobe_conf *conf = (struct modprobe_conf *) userdata; |
|
|
RESERVE_CONFIG_BUFFER(modname, MODULE_NAME_LEN); |
|
154 |
char *tokens[3]; |
char *tokens[3]; |
155 |
parser_t *p; |
parser_t *p; |
156 |
|
struct module_entry *m; |
157 |
int rc = TRUE; |
int rc = TRUE; |
158 |
|
|
159 |
if (bb_basename(filename)[0] == '.') |
if (bb_basename(filename)[0] == '.') |
166 |
} |
} |
167 |
|
|
168 |
while (config_read(p, tokens, 3, 2, "# \t", PARSE_NORMAL)) { |
while (config_read(p, tokens, 3, 2, "# \t", PARSE_NORMAL)) { |
169 |
|
//Use index_in_strings? |
170 |
if (strcmp(tokens[0], "alias") == 0) { |
if (strcmp(tokens[0], "alias") == 0) { |
171 |
filename2modname(tokens[1], modname); |
/* alias <wildcard> <modulename> */ |
172 |
if (tokens[2] && |
llist_t *l; |
173 |
fnmatch(modname, conf->probename, 0) == 0) |
char wildcard[MODULE_NAME_LEN]; |
174 |
llist_add_to(&conf->aliases, |
char *rmod; |
175 |
filename2modname(tokens[2], NULL)); |
|
176 |
|
if (tokens[2] == NULL) |
177 |
|
continue; |
178 |
|
filename2modname(tokens[1], wildcard); |
179 |
|
|
180 |
|
for (l = G.probes; l != NULL; l = l->link) { |
181 |
|
m = (struct module_entry *) l->data; |
182 |
|
if (fnmatch(wildcard, m->modname, 0) != 0) |
183 |
|
continue; |
184 |
|
rmod = filename2modname(tokens[2], NULL); |
185 |
|
llist_add_to(&m->realnames, rmod); |
186 |
|
|
187 |
|
if (m->flags & MODULE_FLAG_NEED_DEPS) { |
188 |
|
m->flags &= ~MODULE_FLAG_NEED_DEPS; |
189 |
|
G.num_unresolved_deps--; |
190 |
|
} |
191 |
|
|
192 |
|
m = get_or_add_modentry(rmod); |
193 |
|
if (!(m->flags & MODULE_FLAG_NEED_DEPS)) { |
194 |
|
m->flags |= MODULE_FLAG_NEED_DEPS; |
195 |
|
G.num_unresolved_deps++; |
196 |
|
} |
197 |
|
} |
198 |
} else if (strcmp(tokens[0], "options") == 0) { |
} else if (strcmp(tokens[0], "options") == 0) { |
199 |
if (tokens[2]) |
/* options <modulename> <option...> */ |
200 |
add_option(&conf->options, tokens[1], tokens[2]); |
if (tokens[2] == NULL) |
201 |
|
continue; |
202 |
|
m = get_or_add_modentry(tokens[1]); |
203 |
|
m->options = gather_options_str(m->options, tokens[2]); |
204 |
} else if (strcmp(tokens[0], "include") == 0) { |
} else if (strcmp(tokens[0], "include") == 0) { |
205 |
read_config(conf, tokens[1]); |
/* include <filename> */ |
206 |
} else if (ENABLE_FEATURE_MODPROBE_BLACKLIST && |
read_config(tokens[1]); |
207 |
strcmp(tokens[0], "blacklist") == 0) { |
} else if (ENABLE_FEATURE_MODPROBE_BLACKLIST |
208 |
add_to_blacklist(conf, xstrdup(tokens[1])); |
&& strcmp(tokens[0], "blacklist") == 0 |
209 |
|
) { |
210 |
|
/* blacklist <modulename> */ |
211 |
|
get_or_add_modentry(tokens[1])->flags |= MODULE_FLAG_BLACKLISTED; |
212 |
} |
} |
213 |
} |
} |
214 |
config_close(p); |
config_close(p); |
215 |
error: |
error: |
|
if (ENABLE_FEATURE_CLEAN_UP) |
|
|
RELEASE_CONFIG_BUFFER(modname); |
|
216 |
return rc; |
return rc; |
217 |
} |
} |
218 |
|
|
219 |
static int read_config(struct modprobe_conf *conf, const char *path) |
static int read_config(const char *path) |
220 |
{ |
{ |
221 |
return recursive_action(path, ACTION_RECURSE | ACTION_QUIET, |
return recursive_action(path, ACTION_RECURSE | ACTION_QUIET, |
222 |
config_file_action, NULL, conf, 1); |
config_file_action, NULL, NULL, 1); |
223 |
} |
} |
224 |
|
|
225 |
static char *gather_options(llist_t *first, const char *module, int usecmdline) |
static const char *humanly_readable_name(struct module_entry *m) |
226 |
|
{ |
227 |
|
/* probed_name may be NULL. modname always exists. */ |
228 |
|
return m->probed_name ? m->probed_name : m->modname; |
229 |
|
} |
230 |
|
|
231 |
|
/* Return: similar to bb_init_module: |
232 |
|
* 0 on success, |
233 |
|
* -errno on open/read error, |
234 |
|
* errno on init_module() error |
235 |
|
*/ |
236 |
|
static int do_modprobe(struct module_entry *m) |
237 |
{ |
{ |
238 |
struct modprobe_option *opt; |
struct module_entry *m2 = m2; /* for compiler */ |
239 |
llist_t *n; |
char *fn, *options; |
240 |
char *opts = xstrdup(""); |
int rc, first; |
241 |
int optlen = 0; |
llist_t *l; |
242 |
|
|
243 |
|
if (!(m->flags & MODULE_FLAG_FOUND_IN_MODDEP)) { |
244 |
|
if (!(option_mask32 & INSMOD_OPT_SILENT)) |
245 |
|
bb_error_msg("module %s not found in modules.dep", |
246 |
|
humanly_readable_name(m)); |
247 |
|
return -ENOENT; |
248 |
|
} |
249 |
|
DBG("do_modprob'ing %s", m->modname); |
250 |
|
|
251 |
|
if (!(option_mask32 & MODPROBE_OPT_REMOVE)) |
252 |
|
m->deps = llist_rev(m->deps); |
253 |
|
|
254 |
for (n = first; n != NULL; n = n->link) { |
for (l = m->deps; l != NULL; l = l->link) |
255 |
opt = (struct modprobe_option *) n->data; |
DBG("dep: %s", l->data); |
256 |
|
|
257 |
|
first = 1; |
258 |
|
rc = 0; |
259 |
|
while (m->deps) { |
260 |
|
rc = 0; |
261 |
|
fn = llist_pop(&m->deps); /* we leak it */ |
262 |
|
m2 = get_or_add_modentry(fn); |
263 |
|
|
264 |
if (opt->module == NULL && !usecmdline) |
if (option_mask32 & MODPROBE_OPT_REMOVE) { |
265 |
|
/* modprobe -r */ |
266 |
|
if (m2->flags & MODULE_FLAG_LOADED) { |
267 |
|
rc = bb_delete_module(m2->modname, O_EXCL); |
268 |
|
if (rc) { |
269 |
|
if (first) { |
270 |
|
bb_error_msg("failed to unload module %s: %s", |
271 |
|
humanly_readable_name(m2), |
272 |
|
moderror(rc)); |
273 |
|
break; |
274 |
|
} |
275 |
|
} else { |
276 |
|
m2->flags &= ~MODULE_FLAG_LOADED; |
277 |
|
} |
278 |
|
} |
279 |
|
/* do not error out if *deps* fail to unload */ |
280 |
|
first = 0; |
281 |
continue; |
continue; |
282 |
if (opt->module != NULL && strcmp(opt->module, module) != 0) |
} |
283 |
|
|
284 |
|
if (m2->flags & MODULE_FLAG_LOADED) { |
285 |
|
DBG("%s is already loaded, skipping", fn); |
286 |
continue; |
continue; |
287 |
|
} |
288 |
|
|
289 |
opts = xrealloc(opts, optlen + strlen(opt->option) + 2); |
options = m2->options; |
290 |
optlen += sprintf(opts + optlen, "%s ", opt->option); |
m2->options = NULL; |
291 |
|
if (m == m2) |
292 |
|
options = gather_options_str(options, G.cmdline_mopts); |
293 |
|
rc = bb_init_module(fn, options); |
294 |
|
DBG("loaded %s '%s', rc:%d", fn, options, rc); |
295 |
|
if (rc == EEXIST) |
296 |
|
rc = 0; |
297 |
|
free(options); |
298 |
|
if (rc) { |
299 |
|
bb_error_msg("failed to load module %s (%s): %s", |
300 |
|
humanly_readable_name(m2), |
301 |
|
fn, |
302 |
|
moderror(rc) |
303 |
|
); |
304 |
|
break; |
305 |
|
} |
306 |
|
m2->flags |= MODULE_FLAG_LOADED; |
307 |
} |
} |
308 |
return opts; |
|
309 |
|
return rc; |
310 |
} |
} |
311 |
|
|
312 |
static int do_modprobe(struct modprobe_conf *conf, const char *module) |
static void load_modules_dep(void) |
313 |
{ |
{ |
314 |
RESERVE_CONFIG_BUFFER(modname, MODULE_NAME_LEN); |
struct module_entry *m; |
315 |
llist_t *deps = NULL; |
char *colon, *tokens[2]; |
|
char *fn, *options, *colon = NULL, *tokens[2]; |
|
316 |
parser_t *p; |
parser_t *p; |
|
int rc = -1; |
|
317 |
|
|
318 |
p = config_open2(CONFIG_DEFAULT_DEPMOD_FILE, fopen_for_read); |
/* Modprobe does not work at all without modules.dep, |
319 |
if (p == NULL) |
* even if the full module name is given. Returning error here |
320 |
goto error; |
* was making us later confuse user with this message: |
321 |
|
* "module /full/path/to/existing/file/module.ko not found". |
322 |
while (config_read(p, tokens, 2, 1, "# \t", PARSE_NORMAL)) { |
* It's better to die immediately, with good message. |
323 |
|
* xfopen_for_read provides that. */ |
324 |
|
p = config_open2(CONFIG_DEFAULT_DEPMOD_FILE, xfopen_for_read); |
325 |
|
|
326 |
|
while (G.num_unresolved_deps |
327 |
|
&& config_read(p, tokens, 2, 1, "# \t", PARSE_NORMAL) |
328 |
|
) { |
329 |
colon = last_char_is(tokens[0], ':'); |
colon = last_char_is(tokens[0], ':'); |
330 |
if (colon == NULL) |
if (colon == NULL) |
331 |
continue; |
continue; |
332 |
|
*colon = 0; |
333 |
|
|
334 |
filename2modname(tokens[0], modname); |
m = get_modentry(tokens[0]); |
335 |
if (strcmp(modname, module) == 0) |
if (m == NULL) |
336 |
break; |
continue; |
|
|
|
|
colon = NULL; |
|
|
} |
|
|
if (colon == NULL) |
|
|
goto error_not_found; |
|
|
|
|
|
colon[0] = '\0'; |
|
|
llist_add_to(&deps, xstrdup(tokens[0])); |
|
|
if (tokens[1]) |
|
|
string_to_llist(tokens[1], &deps, " "); |
|
|
|
|
|
if (!(option_mask32 & MODPROBE_OPT_REMOVE)) |
|
|
deps = llist_rev(deps); |
|
337 |
|
|
338 |
rc = 0; |
/* Optimization... */ |
339 |
while (deps && rc == 0) { |
if ((m->flags & MODULE_FLAG_LOADED) |
340 |
fn = llist_pop(&deps); |
&& !(option_mask32 & MODPROBE_OPT_REMOVE) |
341 |
filename2modname(fn, modname); |
) { |
342 |
if (option_mask32 & MODPROBE_OPT_REMOVE) { |
DBG("skip deps of %s, it's already loaded", tokens[0]); |
343 |
if (bb_delete_module(modname, O_EXCL) != 0) |
continue; |
|
rc = errno; |
|
|
} else if (llist_find(loaded, modname) == NULL) { |
|
|
options = gather_options(conf->options, modname, |
|
|
strcmp(modname, module) == 0); |
|
|
rc = bb_init_module(fn, options); |
|
|
if (rc == 0) |
|
|
llist_add_to(&loaded, xstrdup(modname)); |
|
|
if (ENABLE_FEATURE_CLEAN_UP) |
|
|
free(options); |
|
344 |
} |
} |
345 |
|
|
346 |
if (ENABLE_FEATURE_CLEAN_UP) |
m->flags |= MODULE_FLAG_FOUND_IN_MODDEP; |
347 |
free(fn); |
if ((m->flags & MODULE_FLAG_NEED_DEPS) && (m->deps == NULL)) { |
348 |
|
G.num_unresolved_deps--; |
349 |
|
llist_add_to(&m->deps, xstrdup(tokens[0])); |
350 |
|
if (tokens[1]) |
351 |
|
string_to_llist(tokens[1], &m->deps, " \t"); |
352 |
|
} else |
353 |
|
DBG("skipping dep line"); |
354 |
} |
} |
|
|
|
|
error_not_found: |
|
355 |
config_close(p); |
config_close(p); |
|
error: |
|
|
if (ENABLE_FEATURE_CLEAN_UP) |
|
|
RELEASE_CONFIG_BUFFER(modname); |
|
|
if (rc > 0 && !(option_mask32 & INSMOD_OPT_SILENT)) |
|
|
bb_error_msg("Failed to %sload module %s: %s.", |
|
|
(option_mask32 & MODPROBE_OPT_REMOVE) ? "un" : "", |
|
|
module, moderror(rc)); |
|
|
return rc; |
|
356 |
} |
} |
357 |
|
|
358 |
int modprobe_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
int modprobe_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
361 |
struct utsname uts; |
struct utsname uts; |
362 |
int rc; |
int rc; |
363 |
unsigned opt; |
unsigned opt; |
364 |
llist_t *options = NULL; |
struct module_entry *me; |
365 |
|
|
366 |
opt_complementary = "q-v:v-q"; |
opt_complementary = "q-v:v-q"; |
367 |
opt = getopt32(argv, INSMOD_OPTS MODPROBE_OPTS INSMOD_ARGS, |
opt = getopt32(argv, INSMOD_OPTS MODPROBE_OPTS INSMOD_ARGS, NULL, NULL); |
|
NULL, NULL); |
|
368 |
argv += optind; |
argv += optind; |
369 |
|
|
370 |
if (opt & (MODPROBE_OPT_DUMP_ONLY | MODPROBE_OPT_LIST_ONLY | |
if (opt & (MODPROBE_OPT_DUMP_ONLY | MODPROBE_OPT_LIST_ONLY | |
371 |
MODPROBE_OPT_SHOW_ONLY)) |
MODPROBE_OPT_SHOW_ONLY)) |
372 |
bb_error_msg_and_die("not supported"); |
bb_error_msg_and_die("not supported"); |
373 |
|
|
|
/* goto modules location */ |
|
|
xchdir(CONFIG_DEFAULT_MODULES_DIR); |
|
|
uname(&uts); |
|
|
xchdir(uts.release); |
|
|
|
|
374 |
if (!argv[0]) { |
if (!argv[0]) { |
375 |
if (opt & MODPROBE_OPT_REMOVE) { |
if (opt & MODPROBE_OPT_REMOVE) { |
376 |
if (bb_delete_module(NULL, O_NONBLOCK|O_EXCL) != 0) |
/* "modprobe -r" (w/o params). |
377 |
|
* "If name is NULL, all unused modules marked |
378 |
|
* autoclean will be removed". |
379 |
|
*/ |
380 |
|
if (bb_delete_module(NULL, O_NONBLOCK | O_EXCL) != 0) |
381 |
bb_perror_msg_and_die("rmmod"); |
bb_perror_msg_and_die("rmmod"); |
382 |
} |
} |
383 |
return EXIT_SUCCESS; |
return EXIT_SUCCESS; |
384 |
} |
} |
|
if (!(opt & MODPROBE_OPT_INSERT_ALL)) { |
|
|
/* If not -a, we have only one module name, |
|
|
* the rest of parameters are options */ |
|
|
add_option(&options, NULL, parse_cmdline_module_options(argv)); |
|
|
argv[1] = NULL; |
|
|
} |
|
385 |
|
|
386 |
/* cache modules */ |
/* Goto modules location */ |
387 |
|
xchdir(CONFIG_DEFAULT_MODULES_DIR); |
388 |
|
uname(&uts); |
389 |
|
xchdir(uts.release); |
390 |
|
|
391 |
|
/* Retrieve module names of already loaded modules */ |
392 |
{ |
{ |
393 |
char *s; |
char *s; |
394 |
parser_t *parser = config_open2("/proc/modules", fopen_for_read); |
parser_t *parser = config_open2("/proc/modules", fopen_for_read); |
395 |
while (config_read(parser, &s, 1, 1, "# \t", PARSE_NORMAL & ~PARSE_GREEDY)) |
while (config_read(parser, &s, 1, 1, "# \t", PARSE_NORMAL & ~PARSE_GREEDY)) |
396 |
llist_add_to(&loaded, xstrdup(s)); |
get_or_add_modentry(s)->flags |= MODULE_FLAG_LOADED; |
397 |
config_close(parser); |
config_close(parser); |
398 |
} |
} |
399 |
|
|
400 |
while (*argv) { |
if (opt & (MODPROBE_OPT_INSERT_ALL | MODPROBE_OPT_REMOVE)) { |
401 |
const char *arg = *argv; |
/* Each argument is a module name */ |
402 |
struct modprobe_conf *conf; |
do { |
403 |
|
DBG("adding module %s", *argv); |
404 |
conf = xzalloc(sizeof(*conf)); |
add_probe(*argv++); |
405 |
conf->options = options; |
} while (*argv); |
406 |
filename2modname(arg, conf->probename); |
} else { |
407 |
read_config(conf, "/etc/modprobe.conf"); |
/* First argument is module name, rest are parameters */ |
408 |
read_config(conf, "/etc/modprobe.d"); |
DBG("probing just module %s", *argv); |
409 |
if (ENABLE_FEATURE_MODUTILS_SYMBOLS |
add_probe(argv[0]); |
410 |
&& conf->aliases == NULL |
G.cmdline_mopts = parse_cmdline_module_options(argv); |
411 |
&& strncmp(arg, "symbol:", 7) == 0 |
} |
|
) { |
|
|
read_config(conf, "modules.symbols"); |
|
|
} |
|
412 |
|
|
413 |
if (ENABLE_FEATURE_MODUTILS_ALIAS && conf->aliases == NULL) |
/* Happens if all requested modules are already loaded */ |
414 |
read_config(conf, "modules.alias"); |
if (G.probes == NULL) |
415 |
|
return EXIT_SUCCESS; |
416 |
|
|
417 |
if (conf->aliases == NULL) { |
read_config("/etc/modprobe.conf"); |
418 |
/* Try if module by literal name is found; literal |
read_config("/etc/modprobe.d"); |
419 |
* names are blacklist only if '-b' is given. */ |
if (ENABLE_FEATURE_MODUTILS_SYMBOLS && G.need_symbols) |
420 |
if (!(opt & MODPROBE_OPT_BLACKLIST) || |
read_config("modules.symbols"); |
421 |
check_blacklist(conf, conf->probename)) { |
load_modules_dep(); |
422 |
rc = do_modprobe(conf, conf->probename); |
if (ENABLE_FEATURE_MODUTILS_ALIAS && G.num_unresolved_deps) { |
423 |
if (rc < 0 && !(opt & INSMOD_OPT_SILENT)) |
read_config("modules.alias"); |
424 |
bb_error_msg("Module %s not found.", arg); |
load_modules_dep(); |
425 |
} |
} |
426 |
} else { |
|
427 |
/* Probe all aliases */ |
rc = 0; |
428 |
while (conf->aliases != NULL) { |
while ((me = llist_pop(&G.probes)) != NULL) { |
429 |
char *realname = llist_pop(&conf->aliases); |
if (me->realnames == NULL) { |
430 |
if (check_blacklist(conf, realname)) |
DBG("probing by module name"); |
431 |
do_modprobe(conf, realname); |
/* This is not an alias. Literal names are blacklisted |
432 |
if (ENABLE_FEATURE_CLEAN_UP) |
* only if '-b' is given. |
433 |
free(realname); |
*/ |
434 |
|
if (!(opt & MODPROBE_OPT_BLACKLIST) |
435 |
|
|| !(me->flags & MODULE_FLAG_BLACKLISTED) |
436 |
|
) { |
437 |
|
rc |= do_modprobe(me); |
438 |
} |
} |
439 |
|
continue; |
440 |
} |
} |
441 |
argv++; |
|
442 |
|
/* Probe all real names for the alias */ |
443 |
|
do { |
444 |
|
char *realname = llist_pop(&me->realnames); |
445 |
|
struct module_entry *m2; |
446 |
|
|
447 |
|
DBG("probing alias %s by realname %s", me->modname, realname); |
448 |
|
m2 = get_or_add_modentry(realname); |
449 |
|
if (!(m2->flags & MODULE_FLAG_BLACKLISTED) |
450 |
|
&& (!(m2->flags & MODULE_FLAG_LOADED) |
451 |
|
|| (opt & MODPROBE_OPT_REMOVE)) |
452 |
|
) { |
453 |
|
//TODO: we can pass "me" as 2nd param to do_modprobe, |
454 |
|
//and make do_modprobe emit more meaningful error messages |
455 |
|
//with alias name included, not just module name alias resolves to. |
456 |
|
rc |= do_modprobe(m2); |
457 |
|
} |
458 |
|
free(realname); |
459 |
|
} while (me->realnames != NULL); |
460 |
} |
} |
461 |
|
|
462 |
return EXIT_SUCCESS; |
return (rc != 0); |
463 |
} |
} |