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