chiark / gitweb /
7f2faee261995fd1ee74769d8964960028afb2e3
[elogind.git] / udev / udev-builtin-kmod.c
1 /*
2  * load kernel modules
3  *
4  * Copyright (C) 2011 Kay Sievers <kay.sievers@vrfy.org>
5  * Copyright (C) 2011 ProFUSION embedded systems
6  *
7  * This program is free software: you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation, either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
19  */
20
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <stdarg.h>
24 #include <unistd.h>
25 #include <string.h>
26 #include <errno.h>
27 #include <fcntl.h>
28 #include <sys/stat.h>
29 #include <sys/wait.h>
30 #include <libkmod.h>
31
32 #include "udev.h"
33
34 static struct kmod_ctx *ctx;
35
36 static int command_do(struct kmod_module *module, const char *type, const char *command, const char *cmdline_opts)
37 {
38         const char *modname = kmod_module_get_name(module);
39         char *p, *cmd = NULL;
40         size_t cmdlen, cmdline_opts_len, varlen;
41         int ret = 0;
42
43         if (cmdline_opts == NULL)
44                 cmdline_opts = "";
45         cmdline_opts_len = strlen(cmdline_opts);
46
47         cmd = strdup(command);
48         if (cmd == NULL)
49                 return -ENOMEM;
50         cmdlen = strlen(cmd);
51         varlen = sizeof("$CMDLINE_OPTS") - 1;
52         while ((p = strstr(cmd, "$CMDLINE_OPTS")) != NULL) {
53                 size_t prefixlen = p - cmd;
54                 size_t suffixlen = cmdlen - prefixlen - varlen;
55                 size_t slen = cmdlen - varlen + cmdline_opts_len;
56                 char *suffix = p + varlen;
57                 char *s = malloc(slen + 1);
58                 if (s == NULL) {
59                         free(cmd);
60                         return -ENOMEM;
61                 }
62                 memcpy(s, cmd, p - cmd);
63                 memcpy(s + prefixlen, cmdline_opts, cmdline_opts_len);
64                 memcpy(s + prefixlen + cmdline_opts_len, suffix, suffixlen);
65                 s[slen] = '\0';
66
67                 free(cmd);
68                 cmd = s;
69                 cmdlen = slen;
70         }
71
72         setenv("MODPROBE_MODULE", modname, 1);
73         ret = system(cmd);
74         unsetenv("MODPROBE_MODULE");
75         if (ret == -1 || WEXITSTATUS(ret)) {
76                 //LOG("Error running %s command for %s\n", type, modname);
77                 if (ret != -1)
78                         ret = -WEXITSTATUS(ret);
79         }
80
81 end:
82         free(cmd);
83         return ret;
84 }
85
86 static int insmod_do_dependencies(struct kmod_module *parent);
87 static int insmod_do_soft_dependencies(struct kmod_module *mod, struct kmod_list *deps);
88
89 static int insmod_do_deps_list(struct kmod_module *parent, struct kmod_list *deps, unsigned stop_on_errors)
90 {
91         struct kmod_list *d;
92         int err = 0;
93
94         kmod_list_foreach(d, deps) {
95                 struct kmod_module *dm = kmod_module_get_module(d);
96                 struct kmod_list *pre = NULL, *post = NULL;
97                 const char *cmd, *opts, *dmname = kmod_module_get_name(dm);
98                 int state;
99                 int r;
100
101                 r = insmod_do_dependencies(dm);
102                 if (r < 0) {
103                         //WRN("could not insert dependencies of '%s': %s\n", dmname, strerror(-r));
104                         goto dep_error;
105                 }
106
107                 r = kmod_module_get_softdeps(dm, &pre, &post);
108                 if (r < 0) {
109                         //WRN("could not get softdeps of '%s': %s\n", dmname, strerror(-r));
110                         goto dep_done;
111                 }
112
113                 r = insmod_do_soft_dependencies(dm, pre);
114                 if (r < 0) {
115                         //WRN("could not insert pre softdeps of '%s': %s\n", dmname, strerror(-r));
116                         goto dep_error;
117                 }
118
119                 state = kmod_module_get_initstate(dm);
120                 if (state == KMOD_MODULE_LIVE ||
121                                 state == KMOD_MODULE_COMING ||
122                                 state == KMOD_MODULE_BUILTIN)
123                         goto dep_done;
124
125                 cmd = kmod_module_get_install_commands(dm);
126                 if (cmd) {
127                         r = command_do(dm, "install", cmd, NULL);
128                         if (r < 0) {
129                                 //WRN("failed to execute install command of '%s':" " %s\n", dmname, strerror(-r));
130                                 goto dep_error;
131                         } else
132                                 goto dep_done;
133                 }
134
135                 opts = kmod_module_get_options(dm);
136
137                 r = kmod_module_insert_module(dm, 0, opts);
138                 if (r < 0) {
139                         //WRN("could not insert '%s': %s\n", dmname, strerror(-r));
140                         goto dep_error;
141                 }
142
143         dep_done:
144                 r = insmod_do_soft_dependencies(dm, post);
145                 if (r < 0) {
146                         //WRN("could not insert post softdeps of '%s': %s\n", dmname, strerror(-r));
147                         goto dep_error;
148                 }
149
150                 kmod_module_unref_list(pre);
151                 kmod_module_unref_list(post);
152                 kmod_module_unref(dm);
153                 continue;
154
155         dep_error:
156                 err = r;
157                 kmod_module_unref_list(pre);
158                 kmod_module_unref_list(post);
159                 kmod_module_unref(dm);
160                 if (stop_on_errors)
161                         break;
162                 else
163                         continue;
164         }
165
166         return err;
167 }
168
169 static int insmod_do_soft_dependencies(struct kmod_module *mod, struct kmod_list *deps)
170 {
171         return insmod_do_deps_list(mod, deps, 0);
172 }
173
174 static int insmod_do_dependencies(struct kmod_module *parent)
175 {
176         struct kmod_list *deps = kmod_module_get_dependencies(parent);
177         int err = insmod_do_deps_list(parent, deps, 1);
178         kmod_module_unref_list(deps);
179         return err;
180 }
181
182 static int insmod_do(struct kmod_module *mod, const char *extra_opts)
183 {
184         const char *modname = kmod_module_get_name(mod);
185         const char *conf_opts = kmod_module_get_options(mod);
186         struct kmod_list *pre = NULL, *post = NULL;
187         char *opts = NULL;
188         const char *cmd;
189         int state;
190         int err;
191
192         err = kmod_module_get_softdeps(mod, &pre, &post);
193         if (err < 0) {
194                 //WRN("could not get softdeps of '%s': %s\n", modname, strerror(-err));
195                 return err;
196         }
197
198         err = insmod_do_soft_dependencies(mod, pre);
199         if (err < 0) {
200                 //WRN("could not insert pre softdeps of '%s': %s\n", modname, strerror(-err));
201                 goto error;
202         }
203
204         cmd = kmod_module_get_install_commands(mod);
205         if (cmd != NULL) {
206                 err = command_do(mod, "install", cmd, extra_opts);
207                 goto done;
208         }
209
210         state = kmod_module_get_initstate(mod);
211         if (state == KMOD_MODULE_BUILTIN || state == KMOD_MODULE_LIVE)
212                 return 0;
213
214         /*
215          * At this point it's not possible to be a install/remove command
216          * anymore. So if we can't get module's path, it's because it was
217          * really intended to be a module and it doesn't exist
218          */
219         if (kmod_module_get_path(mod) == NULL) {
220                 //LOG("Module %s not found.\n", modname);
221                 return -ENOENT;
222         }
223
224         err = insmod_do_dependencies(mod);
225         if (err < 0)
226                 return err;
227
228         if (conf_opts || extra_opts) {
229                 if (conf_opts == NULL)
230                         opts = strdup(extra_opts);
231                 else if (extra_opts == NULL)
232                         opts = strdup(conf_opts);
233                 else if (asprintf(&opts, "%s %s", conf_opts, extra_opts) < 0)
234                         opts = NULL;
235
236                 if (opts == NULL) {
237                         err = -ENOMEM;
238                         goto error;
239                 }
240         }
241
242         err = kmod_module_insert_module(mod, 0, opts);
243         if (err == -EEXIST)
244                 err = 0;
245
246 done:
247         err = insmod_do_soft_dependencies(mod, post);
248         if (err < 0) {
249                 //WRN("could not insert post softdeps of '%s': %s\n", modname, strerror(-err));
250                 goto error;
251         }
252
253 error:
254         kmod_module_unref_list(pre);
255         kmod_module_unref_list(post);
256         free(opts);
257         return err;
258 }
259
260 static int insmod_path(struct kmod_ctx *ctx, const char *path, const char *extra_options)
261 {
262         struct kmod_module *mod;
263         int err;
264
265         err = kmod_module_new_from_path(ctx, path, &mod);
266         if (err < 0) {
267                 //LOG("Module %s not found.\n", path);
268                 return err;
269         }
270         err = insmod_do(mod, extra_options);
271         kmod_module_unref(mod);
272         return err;
273 }
274
275 static int insmod_alias(struct kmod_ctx *ctx, const char *alias, const char *extra_options)
276 {
277         struct kmod_list *l, *list = NULL;
278         struct kmod_list *filtered = NULL;
279         int err;
280
281         err = kmod_module_new_from_lookup(ctx, alias, &list);
282         if (err < 0)
283                 return err;
284
285         if (list == NULL) {
286                 //LOG("Module %s not found.\n", alias);
287                 return err;
288         }
289
290         err = kmod_module_get_filtered_blacklist(ctx, list, &filtered);
291         kmod_module_unref_list(list);
292         if (err < 0) {
293                 //LOG("Could not filter alias list!\n");
294                 return err;
295         }
296         list = filtered;
297
298         kmod_list_foreach(l, list) {
299                 struct kmod_module *mod = kmod_module_get_module(l);
300                 err = insmod_do(mod, extra_options);
301                 kmod_module_unref(mod);
302                 if (err < 0)
303                         break;
304         }
305
306         kmod_module_unref_list(list);
307         return err;
308 }
309
310 static int insmod(struct kmod_ctx *ctx, const char *name, const char *extra_options)
311 {
312         struct stat st;
313         if (stat(name, &st) == 0)
314                 return insmod_path(ctx, name, extra_options);
315         else
316                 return insmod_alias(ctx, name, extra_options);
317 }
318
319 static void udev_kmod_log(void *data, int priority, const char *file, int line,
320                           const char *fn, const char *format, va_list args)
321 {
322         udev_main_log(data, priority, file, line, fn, format, args);
323 }
324
325 /* needs to re-instantiate the context after a reload */
326 static int builtin_kmod(struct udev_device *dev, int argc, char *argv[], bool test)
327 {
328         struct udev *udev = udev_device_get_udev(dev);
329         int i;
330
331         if (!ctx) {
332                 ctx = kmod_new(NULL, NULL);
333                 if (!ctx)
334                         return -ENOMEM;
335
336                 info(udev, "load module index\n");
337                 kmod_set_log_fn(ctx, udev_kmod_log, udev);
338                 kmod_load_resources(ctx);
339         }
340
341         if (argc < 3 || strcmp(argv[1], "load")) {
342                 err(udev, "expect: %s load <module>\n", argv[0]);
343                 return EXIT_FAILURE;
344         }
345
346         for (i = 2; argv[i]; i++) {
347                 info(udev, "execute '%s' '%s'\n", argv[1], argv[i]);
348                 insmod(ctx, argv[i], NULL);
349         }
350
351         return EXIT_SUCCESS;
352 }
353
354 /* called at udev startup */
355 static int builtin_kmod_init(struct udev *udev)
356 {
357         if (ctx)
358                 return 0;
359
360         ctx = kmod_new(NULL, NULL);
361         if (!ctx)
362                 return -ENOMEM;
363
364         info(udev, "load module index\n");
365         kmod_set_log_fn(ctx, udev_kmod_log, udev);
366         kmod_load_resources(ctx);
367         return 0;
368 }
369
370 /* called on udev shutdown and reload request */
371 static void builtin_kmod_exit(struct udev *udev)
372 {
373         ctx = kmod_unref(ctx);
374         info(udev, "unload module index\n");
375 }
376
377 /* called every couple of seconds during event activity; 'true' if config has changed */
378 static bool builtin_kmod_validate(struct udev *udev)
379 {
380         info(udev, "validate module index\n");
381         return false;
382 }
383
384 const struct udev_builtin udev_builtin_kmod = {
385         .name = "kmod",
386         .cmd = builtin_kmod,
387         .init = builtin_kmod_init,
388         .exit = builtin_kmod_exit,
389         .validate = builtin_kmod_validate,
390         .help = "kernel module loader",
391         .run_once = false,
392 };