]>
Commit | Line | Data |
---|---|---|
81dadce5 | 1 | /* |
aa29418a | 2 | * load kernel modules |
81dadce5 KS |
3 | * |
4 | * Copyright (C) 2011 Kay Sievers <kay.sievers@vrfy.org> | |
b2ddca8a | 5 | * Copyright (C) 2011 ProFUSION embedded systems |
81dadce5 KS |
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> | |
06316d9f | 29 | #include <sys/wait.h> |
b45ce692 | 30 | #include <libkmod.h> |
81dadce5 KS |
31 | |
32 | #include "udev.h" | |
33 | ||
b45ce692 KS |
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 | ||
e216e514 | 319 | static int builtin_kmod(struct udev_device *dev, int argc, char *argv[], bool test) |
81dadce5 | 320 | { |
e216e514 | 321 | struct udev *udev = udev_device_get_udev(dev); |
b45ce692 KS |
322 | int i; |
323 | ||
324 | if (!ctx) | |
325 | return EXIT_FAILURE; | |
e216e514 KS |
326 | |
327 | if (argc < 3) { | |
328 | err(udev, "missing command + argument\n"); | |
329 | return EXIT_FAILURE; | |
330 | } | |
331 | ||
b45ce692 KS |
332 | for (i = 2; argv[i]; i++) { |
333 | info(udev, "%s '%s'\n", argv[1], argv[i]); | |
334 | insmod(ctx, argv[i], NULL); | |
06316d9f KS |
335 | } |
336 | ||
81dadce5 KS |
337 | return EXIT_SUCCESS; |
338 | } | |
339 | ||
aa29418a KS |
340 | static int builtin_kmod_load(struct udev *udev) |
341 | { | |
b45ce692 KS |
342 | kmod_unref(ctx); |
343 | ctx = kmod_new(NULL, NULL); | |
344 | if (!ctx) | |
345 | return -ENOMEM; | |
346 | ||
e216e514 | 347 | info(udev, "load module index\n"); |
aa29418a KS |
348 | return 0; |
349 | } | |
350 | ||
351 | static int builtin_kmod_unload(struct udev *udev) | |
352 | { | |
f775ccfc | 353 | ctx = kmod_unref(ctx); |
e216e514 | 354 | info(udev, "unload module index\n"); |
aa29418a KS |
355 | return 0; |
356 | } | |
357 | ||
81dadce5 KS |
358 | const struct udev_builtin udev_builtin_kmod = { |
359 | .name = "kmod", | |
360 | .cmd = builtin_kmod, | |
aa29418a KS |
361 | .load = builtin_kmod_load, |
362 | .unload = builtin_kmod_unload, | |
81dadce5 KS |
363 | .help = "kernel module loader", |
364 | .run_once = false, | |
365 | }; |