*: tweak error messages
[oweals/busybox.git] / modutils / modprobe.c
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 }