diff options
Diffstat (limited to 'meta-initramfs/recipes-devtools/klibc/klibc-2.0/modprobe.patch')
-rw-r--r-- | meta-initramfs/recipes-devtools/klibc/klibc-2.0/modprobe.patch | 1984 |
1 files changed, 1984 insertions, 0 deletions
diff --git a/meta-initramfs/recipes-devtools/klibc/klibc-2.0/modprobe.patch b/meta-initramfs/recipes-devtools/klibc/klibc-2.0/modprobe.patch new file mode 100644 index 000000000..eb2fb39e6 --- /dev/null +++ b/meta-initramfs/recipes-devtools/klibc/klibc-2.0/modprobe.patch | |||
@@ -0,0 +1,1984 @@ | |||
1 | Patch was imported from the OpenEmbedded git server | ||
2 | (git://git.openembedded.org/openembedded) | ||
3 | as of commit id 70ae69edb02e0174db0841ae501299159c888514 | ||
4 | Signed-off-by: Thomas Kunze <thommycheck@gmx.de> | ||
5 | Minor adjustments tracking upstream changes | ||
6 | Signed-off-by: Andrea Adami <andrea.adami@gmail.com> | ||
7 | |||
8 | diff --git a/usr/utils/Kbuild b/usr/utils/Kbuild | ||
9 | --- a/usr/utils/Kbuild | ||
10 | +++ b/usr/utils/Kbuild | ||
11 | @@ -4,7 +4,7 @@ | ||
12 | |||
13 | progs := chroot dd mkdir mkfifo mknod mount pivot_root umount | ||
14 | progs += true false sleep ln mv nuke minips cat ls losetup | ||
15 | -progs += uname halt kill readlink cpio sync dmesg | ||
16 | +progs += uname halt kill readlink cpio sync dmesg modprobe | ||
17 | |||
18 | static-y := $(addprefix static/, $(progs)) | ||
19 | shared-y := $(addprefix shared/, $(progs)) | ||
20 | @@ -58,6 +58,8 @@ static/sync-y := sync.o | ||
21 | shared/sync-y := sync.o | ||
22 | static/losetup-y := losetup.o | ||
23 | shared/losetup-y := losetup.o | ||
24 | +static/modprobe-y := modprobe.o | ||
25 | +shared/modprobe-y := modprobe.o | ||
26 | |||
27 | # Additionally linked targets | ||
28 | always := static/reboot static/poweroff shared/reboot shared/poweroff | ||
29 | diff --git a/usr/utils/list.h b/usr/utils/list.h | ||
30 | --- /dev/null | ||
31 | +++ b/usr/utils/list.h | ||
32 | @@ -0,0 +1,238 @@ | ||
33 | +/* Stolen from Linux Kernel Source's list.h -- GPL. */ | ||
34 | +#ifndef _MODINITTOOLS_LIST_H | ||
35 | +#define _MODINITTOOLS_LIST_H | ||
36 | + | ||
37 | +#undef offsetof | ||
38 | +#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) | ||
39 | + | ||
40 | +/** | ||
41 | + * container_of - cast a member of a structure out to the containing structure | ||
42 | + * | ||
43 | + * @ptr: the pointer to the member. | ||
44 | + * @type: the type of the container struct this is embedded in. | ||
45 | + * @member: the name of the member within the struct. | ||
46 | + * | ||
47 | + */ | ||
48 | +#define container_of(ptr, type, member) ({ \ | ||
49 | + const typeof( ((type *)0)->member ) *__mptr = (ptr); \ | ||
50 | + (type *)( (char *)__mptr - offsetof(type,member) );}) | ||
51 | + | ||
52 | +/* | ||
53 | + * Simple doubly linked list implementation. | ||
54 | + * | ||
55 | + * Some of the internal functions ("__xxx") are useful when | ||
56 | + * manipulating whole lists rather than single entries, as | ||
57 | + * sometimes we already know the next/prev entries and we can | ||
58 | + * generate better code by using them directly rather than | ||
59 | + * using the generic single-entry routines. | ||
60 | + */ | ||
61 | + | ||
62 | +struct list_head { | ||
63 | + struct list_head *next, *prev; | ||
64 | +}; | ||
65 | + | ||
66 | +#define LIST_HEAD_INIT(name) { &(name), &(name) } | ||
67 | + | ||
68 | +#define LIST_HEAD(name) \ | ||
69 | + struct list_head name = LIST_HEAD_INIT(name) | ||
70 | + | ||
71 | +#define INIT_LIST_HEAD(ptr) do { \ | ||
72 | + (ptr)->next = (ptr); (ptr)->prev = (ptr); \ | ||
73 | +} while (0) | ||
74 | + | ||
75 | +/* | ||
76 | + * Insert a new entry between two known consecutive entries. | ||
77 | + * | ||
78 | + * This is only for internal list manipulation where we know | ||
79 | + * the prev/next entries already! | ||
80 | + */ | ||
81 | +static inline void __list_add(struct list_head *new, | ||
82 | + struct list_head *prev, | ||
83 | + struct list_head *next) | ||
84 | +{ | ||
85 | + next->prev = new; | ||
86 | + new->next = next; | ||
87 | + new->prev = prev; | ||
88 | + prev->next = new; | ||
89 | +} | ||
90 | + | ||
91 | +/** | ||
92 | + * list_add - add a new entry | ||
93 | + * @new: new entry to be added | ||
94 | + * @head: list head to add it after | ||
95 | + * | ||
96 | + * Insert a new entry after the specified head. | ||
97 | + * This is good for implementing stacks. | ||
98 | + */ | ||
99 | +static inline void list_add(struct list_head *new, struct list_head *head) | ||
100 | +{ | ||
101 | + __list_add(new, head, head->next); | ||
102 | +} | ||
103 | + | ||
104 | +/** | ||
105 | + * list_add_tail - add a new entry | ||
106 | + * @new: new entry to be added | ||
107 | + * @head: list head to add it before | ||
108 | + * | ||
109 | + * Insert a new entry before the specified head. | ||
110 | + * This is useful for implementing queues. | ||
111 | + */ | ||
112 | +static inline void list_add_tail(struct list_head *new, struct list_head *head) | ||
113 | +{ | ||
114 | + __list_add(new, head->prev, head); | ||
115 | +} | ||
116 | + | ||
117 | +/* | ||
118 | + * Delete a list entry by making the prev/next entries | ||
119 | + * point to each other. | ||
120 | + * | ||
121 | + * This is only for internal list manipulation where we know | ||
122 | + * the prev/next entries already! | ||
123 | + */ | ||
124 | +static inline void __list_del(struct list_head * prev, struct list_head * next) | ||
125 | +{ | ||
126 | + next->prev = prev; | ||
127 | + prev->next = next; | ||
128 | +} | ||
129 | + | ||
130 | +/** | ||
131 | + * list_del - deletes entry from list. | ||
132 | + * @entry: the element to delete from the list. | ||
133 | + * Note: list_empty on entry does not return true after this, the entry is | ||
134 | + * in an undefined state. | ||
135 | + */ | ||
136 | +static inline void list_del(struct list_head *entry) | ||
137 | +{ | ||
138 | + __list_del(entry->prev, entry->next); | ||
139 | +} | ||
140 | + | ||
141 | +/** | ||
142 | + * list_del_init - deletes entry from list and reinitialize it. | ||
143 | + * @entry: the element to delete from the list. | ||
144 | + */ | ||
145 | +static inline void list_del_init(struct list_head *entry) | ||
146 | +{ | ||
147 | + __list_del(entry->prev, entry->next); | ||
148 | + INIT_LIST_HEAD(entry); | ||
149 | +} | ||
150 | + | ||
151 | +/** | ||
152 | + * list_move - delete from one list and add as another's head | ||
153 | + * @list: the entry to move | ||
154 | + * @head: the head that will precede our entry | ||
155 | + */ | ||
156 | +static inline void list_move(struct list_head *list, struct list_head *head) | ||
157 | +{ | ||
158 | + __list_del(list->prev, list->next); | ||
159 | + list_add(list, head); | ||
160 | +} | ||
161 | + | ||
162 | +/** | ||
163 | + * list_move_tail - delete from one list and add as another's tail | ||
164 | + * @list: the entry to move | ||
165 | + * @head: the head that will follow our entry | ||
166 | + */ | ||
167 | +static inline void list_move_tail(struct list_head *list, | ||
168 | + struct list_head *head) | ||
169 | +{ | ||
170 | + __list_del(list->prev, list->next); | ||
171 | + list_add_tail(list, head); | ||
172 | +} | ||
173 | + | ||
174 | +/** | ||
175 | + * list_empty - tests whether a list is empty | ||
176 | + * @head: the list to test. | ||
177 | + */ | ||
178 | +static inline int list_empty(struct list_head *head) | ||
179 | +{ | ||
180 | + return head->next == head; | ||
181 | +} | ||
182 | + | ||
183 | +static inline void __list_splice(struct list_head *list, | ||
184 | + struct list_head *head) | ||
185 | +{ | ||
186 | + struct list_head *first = list->next; | ||
187 | + struct list_head *last = list->prev; | ||
188 | + struct list_head *at = head->next; | ||
189 | + | ||
190 | + first->prev = head; | ||
191 | + head->next = first; | ||
192 | + | ||
193 | + last->next = at; | ||
194 | + at->prev = last; | ||
195 | +} | ||
196 | + | ||
197 | +/** | ||
198 | + * list_splice - join two lists | ||
199 | + * @list: the new list to add. | ||
200 | + * @head: the place to add it in the first list. | ||
201 | + */ | ||
202 | +static inline void list_splice(struct list_head *list, struct list_head *head) | ||
203 | +{ | ||
204 | + if (!list_empty(list)) | ||
205 | + __list_splice(list, head); | ||
206 | +} | ||
207 | + | ||
208 | +/** | ||
209 | + * list_splice_init - join two lists and reinitialise the emptied list. | ||
210 | + * @list: the new list to add. | ||
211 | + * @head: the place to add it in the first list. | ||
212 | + * | ||
213 | + * The list at @list is reinitialised | ||
214 | + */ | ||
215 | +static inline void list_splice_init(struct list_head *list, | ||
216 | + struct list_head *head) | ||
217 | +{ | ||
218 | + if (!list_empty(list)) { | ||
219 | + __list_splice(list, head); | ||
220 | + INIT_LIST_HEAD(list); | ||
221 | + } | ||
222 | +} | ||
223 | + | ||
224 | +/** | ||
225 | + * list_entry - get the struct for this entry | ||
226 | + * @ptr: the &struct list_head pointer. | ||
227 | + * @type: the type of the struct this is embedded in. | ||
228 | + * @member: the name of the list_struct within the struct. | ||
229 | + */ | ||
230 | +#define list_entry(ptr, type, member) \ | ||
231 | + container_of(ptr, type, member) | ||
232 | + | ||
233 | +/** | ||
234 | + * list_for_each - iterate over a list | ||
235 | + * @pos: the &struct list_head to use as a loop counter. | ||
236 | + * @head: the head for your list. | ||
237 | + */ | ||
238 | +#define list_for_each(pos, head) \ | ||
239 | + for (pos = (head)->next; pos != (head); pos = pos->next) | ||
240 | + | ||
241 | +/** | ||
242 | + * list_for_each_prev - iterate over a list backwards | ||
243 | + * @pos: the &struct list_head to use as a loop counter. | ||
244 | + * @head: the head for your list. | ||
245 | + */ | ||
246 | +#define list_for_each_prev(pos, head) \ | ||
247 | + for (pos = (head)->prev; pos != (head); pos = pos->prev) | ||
248 | + | ||
249 | +/** | ||
250 | + * list_for_each_safe - iterate over a list safe against removal of list entry | ||
251 | + * @pos: the &struct list_head to use as a loop counter. | ||
252 | + * @n: another &struct list_head to use as temporary storage | ||
253 | + * @head: the head for your list. | ||
254 | + */ | ||
255 | +#define list_for_each_safe(pos, n, head) \ | ||
256 | + for (pos = (head)->next, n = pos->next; pos != (head); \ | ||
257 | + pos = n, n = pos->next) | ||
258 | + | ||
259 | +/** | ||
260 | + * list_for_each_entry - iterate over list of given type | ||
261 | + * @pos: the type * to use as a loop counter. | ||
262 | + * @head: the head for your list. | ||
263 | + * @member: the name of the list_struct within the struct. | ||
264 | + */ | ||
265 | +#define list_for_each_entry(pos, head, member) \ | ||
266 | + for (pos = list_entry((head)->next, typeof(*pos), member); \ | ||
267 | + &pos->member != (head); \ | ||
268 | + pos = list_entry(pos->member.next, typeof(*pos), member)) | ||
269 | + | ||
270 | +#endif | ||
271 | diff --git a/usr/utils/modprobe.c b/usr/utils/modprobe.c | ||
272 | --- /dev/null | ||
273 | +++ b/usr/utils/modprobe.c | ||
274 | @@ -0,0 +1,1710 @@ | ||
275 | +/* modprobe.c: insert a module into the kernel, intelligently. | ||
276 | + Copyright (C) 2001 Rusty Russell. | ||
277 | + Copyright (C) 2002, 2003 Rusty Russell, IBM Corporation. | ||
278 | + | ||
279 | + This program is free software; you can redistribute it and/or modify | ||
280 | + it under the terms of the GNU General Public License as published by | ||
281 | + the Free Software Foundation; either version 2 of the License, or | ||
282 | + (at your option) any later version. | ||
283 | + | ||
284 | + This program is distributed in the hope that it will be useful, | ||
285 | + but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
286 | + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
287 | + GNU General Public License for more details. | ||
288 | + | ||
289 | + You should have received a copy of the GNU General Public License | ||
290 | + along with this program; if not, write to the Free Software | ||
291 | + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
292 | +*/ | ||
293 | +#define _GNU_SOURCE /* asprintf */ | ||
294 | + | ||
295 | +#include <sys/utsname.h> | ||
296 | +#include <sys/types.h> | ||
297 | +#include <sys/stat.h> | ||
298 | +#include <sys/mman.h> | ||
299 | +#include <fcntl.h> | ||
300 | +#include <stdarg.h> | ||
301 | +#include <stdio.h> | ||
302 | +#include <stdlib.h> | ||
303 | +#include <ctype.h> | ||
304 | +#include <string.h> | ||
305 | +#include <errno.h> | ||
306 | +#include <unistd.h> | ||
307 | +#include <dirent.h> | ||
308 | +#include <limits.h> | ||
309 | +#include <elf.h> | ||
310 | +#include <getopt.h> | ||
311 | +#include <fnmatch.h> | ||
312 | +#include <asm/unistd.h> | ||
313 | +#include <sys/wait.h> | ||
314 | +#include <syslog.h> | ||
315 | +#include <zlib.h> | ||
316 | + | ||
317 | +#define streq(a,b) (strcmp((a),(b)) == 0) | ||
318 | +#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) | ||
319 | + | ||
320 | +#include "list.h" | ||
321 | +static inline void try_old_version(const char *progname, char *argv[]) | ||
322 | +{ | ||
323 | +} | ||
324 | +extern long init_module(void *, unsigned long, const char *); | ||
325 | +extern long delete_module(const char *, unsigned int); | ||
326 | + | ||
327 | +struct module { | ||
328 | + struct list_head list; | ||
329 | + char *modname; | ||
330 | + char filename[0]; | ||
331 | +}; | ||
332 | + | ||
333 | +#ifndef MODULE_DIR | ||
334 | +#define MODULE_DIR "/lib/modules" | ||
335 | +#endif | ||
336 | + | ||
337 | +typedef void (*errfn_t)(const char *fmt, ...); | ||
338 | + | ||
339 | +/* Do we use syslog or stderr for messages? */ | ||
340 | +static int log; | ||
341 | + | ||
342 | +static void message(const char *prefix, const char *fmt, va_list *arglist) | ||
343 | +{ | ||
344 | + char *buf, *buf2; | ||
345 | + | ||
346 | + vasprintf(&buf, fmt, *arglist); | ||
347 | + asprintf(&buf2, "%s%s", prefix, buf); | ||
348 | + | ||
349 | + if (log) | ||
350 | + syslog(LOG_NOTICE, "%s", buf2); | ||
351 | + else | ||
352 | + fprintf(stderr, "%s", buf2); | ||
353 | + free(buf2); | ||
354 | + free(buf); | ||
355 | +} | ||
356 | +void *grab_contents(gzFile *gzfd, unsigned long *size) | ||
357 | +{ | ||
358 | + unsigned int max = 16384; | ||
359 | + void *buffer = malloc(max); | ||
360 | + int ret; | ||
361 | + | ||
362 | + if (!buffer) | ||
363 | + return NULL; | ||
364 | + | ||
365 | + *size = 0; | ||
366 | + while ((ret = gzread(gzfd, buffer + *size, max - *size)) > 0) { | ||
367 | + *size += ret; | ||
368 | + if (*size == max) { | ||
369 | + buffer = realloc(buffer, max *= 2); | ||
370 | + if (!buffer) | ||
371 | + return NULL; | ||
372 | + } | ||
373 | + } | ||
374 | + if (ret < 0) { | ||
375 | + free(buffer); | ||
376 | + buffer = NULL; | ||
377 | + } | ||
378 | + return buffer; | ||
379 | +} | ||
380 | + | ||
381 | +void *grab_fd(int fd, unsigned long *size) | ||
382 | +{ | ||
383 | + gzFile gzfd; | ||
384 | + | ||
385 | + gzfd = gzdopen(fd, "rb"); | ||
386 | + if (!gzfd) | ||
387 | + return NULL; | ||
388 | + | ||
389 | + /* gzclose(gzfd) would close fd, which would drop locks. | ||
390 | + Don't blame zlib: POSIX locking semantics are so horribly | ||
391 | + broken that they should be ripped out. */ | ||
392 | + return grab_contents(gzfd, size); | ||
393 | +} | ||
394 | +void release_file(void *data, unsigned long size) | ||
395 | +{ | ||
396 | + free(data); | ||
397 | +} | ||
398 | + | ||
399 | + | ||
400 | +static int warned = 0; | ||
401 | +static void warn(const char *fmt, ...) | ||
402 | +{ | ||
403 | + va_list arglist; | ||
404 | + warned++; | ||
405 | + va_start(arglist, fmt); | ||
406 | + message("WARNING: ", fmt, &arglist); | ||
407 | + va_end(arglist); | ||
408 | +} | ||
409 | + | ||
410 | +static void fatal(const char *fmt, ...) | ||
411 | +{ | ||
412 | + va_list arglist; | ||
413 | + va_start(arglist, fmt); | ||
414 | + message("FATAL: ", fmt, &arglist); | ||
415 | + va_end(arglist); | ||
416 | + exit(1); | ||
417 | +} | ||
418 | + | ||
419 | + | ||
420 | +static void grammar(const char *cmd, const char *filename, unsigned int line) | ||
421 | +{ | ||
422 | + warn("%s line %u: ignoring bad line starting with '%s'\n", | ||
423 | + filename, line, cmd); | ||
424 | +} | ||
425 | + | ||
426 | +static void *do_nofail(void *ptr, const char *file, int line, const char *expr) | ||
427 | +{ | ||
428 | + if (!ptr) { | ||
429 | + fatal("Memory allocation failure %s line %d: %s.\n", | ||
430 | + file, line, expr); | ||
431 | + } | ||
432 | + return ptr; | ||
433 | +} | ||
434 | + | ||
435 | +#define NOFAIL(ptr) do_nofail((ptr), __FILE__, __LINE__, #ptr) | ||
436 | + | ||
437 | +static void print_usage(const char *progname) | ||
438 | +{ | ||
439 | + fprintf(stderr, | ||
440 | + "Usage: %s [-v] [-V] [-C config-file] [-n] [-i] [-q] [-b] [-o <modname>] <modname> [parameters...]\n" | ||
441 | + "%s -r [-n] [-i] [-v] <modulename> ...\n" | ||
442 | + "%s -l -t <dirname> [ -a <modulename> ...]\n", | ||
443 | + progname, progname, progname); | ||
444 | + exit(1); | ||
445 | +} | ||
446 | + | ||
447 | +static int fgetc_wrapped(FILE *file, unsigned int *linenum) | ||
448 | +{ | ||
449 | + for (;;) { | ||
450 | + int ch = fgetc(file); | ||
451 | + if (ch != '\\') | ||
452 | + return ch; | ||
453 | + ch = fgetc(file); | ||
454 | + if (ch != '\n') | ||
455 | + return ch; | ||
456 | + if (linenum) | ||
457 | + (*linenum)++; | ||
458 | + } | ||
459 | +} | ||
460 | + | ||
461 | +static char *getline_wrapped(FILE *file, unsigned int *linenum) | ||
462 | +{ | ||
463 | + int size = 1024; | ||
464 | + int i = 0; | ||
465 | + char *buf = NOFAIL(malloc(size)); | ||
466 | + for(;;) { | ||
467 | + int ch = fgetc_wrapped(file, linenum); | ||
468 | + if (i == size) { | ||
469 | + size *= 2; | ||
470 | + buf = NOFAIL(realloc(buf, size)); | ||
471 | + } | ||
472 | + if (ch < 0 && i == 0) { | ||
473 | + free(buf); | ||
474 | + return NULL; | ||
475 | + } | ||
476 | + if (ch < 0 || ch == '\n') { | ||
477 | + if (linenum) | ||
478 | + (*linenum)++; | ||
479 | + buf[i] = '\0'; | ||
480 | + return NOFAIL(realloc(buf, i+1)); | ||
481 | + } | ||
482 | + buf[i++] = ch; | ||
483 | + } | ||
484 | +} | ||
485 | + | ||
486 | +static struct module *find_module(const char *filename, struct list_head *list) | ||
487 | +{ | ||
488 | + struct module *i; | ||
489 | + | ||
490 | + list_for_each_entry(i, list, list) { | ||
491 | + if (strcmp(i->filename, filename) == 0) | ||
492 | + return i; | ||
493 | + } | ||
494 | + return NULL; | ||
495 | +} | ||
496 | + | ||
497 | +/* Convert filename to the module name. Works if filename == modname, too. */ | ||
498 | +static void filename2modname(char *modname, const char *filename) | ||
499 | +{ | ||
500 | + const char *afterslash; | ||
501 | + unsigned int i; | ||
502 | + | ||
503 | + afterslash = strrchr(filename, '/'); | ||
504 | + if (!afterslash) | ||
505 | + afterslash = filename; | ||
506 | + else | ||
507 | + afterslash++; | ||
508 | + | ||
509 | + /* Convert to underscores, stop at first . */ | ||
510 | + for (i = 0; afterslash[i] && afterslash[i] != '.'; i++) { | ||
511 | + if (afterslash[i] == '-') | ||
512 | + modname[i] = '_'; | ||
513 | + else | ||
514 | + modname[i] = afterslash[i]; | ||
515 | + } | ||
516 | + modname[i] = '\0'; | ||
517 | +} | ||
518 | + | ||
519 | +static int lock_file(const char *filename) | ||
520 | +{ | ||
521 | + int fd = open(filename, O_RDWR, 0); | ||
522 | + | ||
523 | + if (fd >= 0) { | ||
524 | + struct flock lock; | ||
525 | + lock.l_type = F_WRLCK; | ||
526 | + lock.l_whence = SEEK_SET; | ||
527 | + lock.l_start = 0; | ||
528 | + lock.l_len = 1; | ||
529 | + fcntl(fd, F_SETLKW, &lock); | ||
530 | + } else | ||
531 | + /* Read-only filesystem? There goes locking... */ | ||
532 | + fd = open(filename, O_RDONLY, 0); | ||
533 | + return fd; | ||
534 | +} | ||
535 | + | ||
536 | +static void unlock_file(int fd) | ||
537 | +{ | ||
538 | + /* Valgrind is picky... */ | ||
539 | + close(fd); | ||
540 | +} | ||
541 | + | ||
542 | +static void add_module(char *filename, int namelen, struct list_head *list) | ||
543 | +{ | ||
544 | + struct module *mod; | ||
545 | + | ||
546 | + /* If it's a duplicate: move it to the end, so it gets | ||
547 | + inserted where it is *first* required. */ | ||
548 | + mod = find_module(filename, list); | ||
549 | + if (mod) | ||
550 | + list_del(&mod->list); | ||
551 | + else { | ||
552 | + /* No match. Create a new module. */ | ||
553 | + mod = NOFAIL(malloc(sizeof(struct module) + namelen + 1)); | ||
554 | + memcpy(mod->filename, filename, namelen); | ||
555 | + mod->filename[namelen] = '\0'; | ||
556 | + mod->modname = NOFAIL(malloc(namelen + 1)); | ||
557 | + filename2modname(mod->modname, mod->filename); | ||
558 | + } | ||
559 | + | ||
560 | + list_add_tail(&mod->list, list); | ||
561 | +} | ||
562 | + | ||
563 | +/* Compare len chars of a to b, with _ and - equivalent. */ | ||
564 | +static int modname_equal(const char *a, const char *b, unsigned int len) | ||
565 | +{ | ||
566 | + unsigned int i; | ||
567 | + | ||
568 | + if (strlen(b) != len) | ||
569 | + return 0; | ||
570 | + | ||
571 | + for (i = 0; i < len; i++) { | ||
572 | + if ((a[i] == '_' || a[i] == '-') | ||
573 | + && (b[i] == '_' || b[i] == '-')) | ||
574 | + continue; | ||
575 | + if (a[i] != b[i]) | ||
576 | + return 0; | ||
577 | + } | ||
578 | + return 1; | ||
579 | +} | ||
580 | + | ||
581 | +/* Fills in list of modules if this is the line we want. */ | ||
582 | +static int add_modules_dep_line(char *line, | ||
583 | + const char *name, | ||
584 | + struct list_head *list) | ||
585 | +{ | ||
586 | + char *ptr; | ||
587 | + int len; | ||
588 | + char *modname; | ||
589 | + | ||
590 | + /* Ignore lines without : or which start with a # */ | ||
591 | + ptr = strchr(line, ':'); | ||
592 | + if (ptr == NULL || line[strspn(line, "\t ")] == '#') | ||
593 | + return 0; | ||
594 | + | ||
595 | + /* Is this the module we are looking for? */ | ||
596 | + *ptr = '\0'; | ||
597 | + if (strrchr(line, '/')) | ||
598 | + modname = strrchr(line, '/') + 1; | ||
599 | + else | ||
600 | + modname = line; | ||
601 | + | ||
602 | + len = strlen(modname); | ||
603 | + if (strchr(modname, '.')) | ||
604 | + len = strchr(modname, '.') - modname; | ||
605 | + if (!modname_equal(modname, name, len)) | ||
606 | + return 0; | ||
607 | + | ||
608 | + /* Create the list. */ | ||
609 | + add_module(line, ptr - line, list); | ||
610 | + | ||
611 | + ptr++; | ||
612 | + for(;;) { | ||
613 | + char *dep_start; | ||
614 | + ptr += strspn(ptr, " \t"); | ||
615 | + if (*ptr == '\0') | ||
616 | + break; | ||
617 | + dep_start = ptr; | ||
618 | + ptr += strcspn(ptr, " \t"); | ||
619 | + add_module(dep_start, ptr - dep_start, list); | ||
620 | + } | ||
621 | + return 1; | ||
622 | +} | ||
623 | + | ||
624 | +static void read_depends(const char *dirname, | ||
625 | + const char *start_name, | ||
626 | + struct list_head *list) | ||
627 | +{ | ||
628 | + char *modules_dep_name; | ||
629 | + char *line; | ||
630 | + FILE *modules_dep; | ||
631 | + int done = 0; | ||
632 | + | ||
633 | + asprintf(&modules_dep_name, "%s/%s", dirname, "modules.dep"); | ||
634 | + modules_dep = fopen(modules_dep_name, "r"); | ||
635 | + if (!modules_dep) | ||
636 | + fatal("Could not load %s: %s\n", | ||
637 | + modules_dep_name, strerror(errno)); | ||
638 | + | ||
639 | + /* Stop at first line, as we can have duplicates (eg. symlinks | ||
640 | + from boot/ */ | ||
641 | + while (!done && (line = getline_wrapped(modules_dep, NULL)) != NULL) { | ||
642 | + done = add_modules_dep_line(line, start_name, list); | ||
643 | + free(line); | ||
644 | + } | ||
645 | + fclose(modules_dep); | ||
646 | + free(modules_dep_name); | ||
647 | +} | ||
648 | + | ||
649 | +/* We use error numbers in a loose translation... */ | ||
650 | +static const char *insert_moderror(int err) | ||
651 | +{ | ||
652 | + switch (err) { | ||
653 | + case ENOEXEC: | ||
654 | + return "Invalid module format"; | ||
655 | + case ENOENT: | ||
656 | + return "Unknown symbol in module, or unknown parameter (see dmesg)"; | ||
657 | + case ENOSYS: | ||
658 | + return "Kernel does not have module support"; | ||
659 | + default: | ||
660 | + return strerror(err); | ||
661 | + } | ||
662 | +} | ||
663 | + | ||
664 | +static const char *remove_moderror(int err) | ||
665 | +{ | ||
666 | + switch (err) { | ||
667 | + case ENOENT: | ||
668 | + return "No such module"; | ||
669 | + case ENOSYS: | ||
670 | + return "Kernel does not have module unloading support"; | ||
671 | + default: | ||
672 | + return strerror(err); | ||
673 | + } | ||
674 | +} | ||
675 | + | ||
676 | +/* Is module in /proc/modules? If so, fill in usecount if not NULL. | ||
677 | + 0 means no, 1 means yes, -1 means unknown. | ||
678 | + */ | ||
679 | +static int module_in_kernel(const char *modname, unsigned int *usecount) | ||
680 | +{ | ||
681 | + FILE *proc_modules; | ||
682 | + char *line; | ||
683 | + | ||
684 | +again: | ||
685 | + /* Might not be mounted yet. Don't fail. */ | ||
686 | + proc_modules = fopen("/proc/modules", "r"); | ||
687 | + if (!proc_modules) | ||
688 | + return -1; | ||
689 | + | ||
690 | + while ((line = getline_wrapped(proc_modules, NULL)) != NULL) { | ||
691 | + char *entry = strtok(line, " \n"); | ||
692 | + | ||
693 | + if (entry && streq(entry, modname)) { | ||
694 | + /* If it exists, usecount is the third entry. */ | ||
695 | + if (!strtok(NULL, " \n")) | ||
696 | + goto out; | ||
697 | + | ||
698 | + if (!(entry = strtok(NULL, " \n"))) /* usecount */ | ||
699 | + goto out; | ||
700 | + else | ||
701 | + if (usecount) | ||
702 | + *usecount = atoi(entry); | ||
703 | + | ||
704 | + /* Followed by - then status. */ | ||
705 | + if (strtok(NULL, " \n") | ||
706 | + && (entry = strtok(NULL, " \n")) != NULL) { | ||
707 | + /* Locking will fail on ro fs, we might hit | ||
708 | + * cases where module is in flux. Spin. */ | ||
709 | + if (streq(entry, "Loading") | ||
710 | + || streq(entry, "Unloading")) { | ||
711 | + usleep(100000); | ||
712 | + free(line); | ||
713 | + fclose(proc_modules); | ||
714 | + goto again; | ||
715 | + } | ||
716 | + } | ||
717 | + | ||
718 | + out: | ||
719 | + free(line); | ||
720 | + fclose(proc_modules); | ||
721 | + return 1; | ||
722 | + } | ||
723 | + free(line); | ||
724 | + } | ||
725 | + fclose(proc_modules); | ||
726 | + return 0; | ||
727 | +} | ||
728 | + | ||
729 | +static void replace_modname(struct module *module, | ||
730 | + void *mem, unsigned long len, | ||
731 | + const char *oldname, const char *newname) | ||
732 | +{ | ||
733 | + char *p; | ||
734 | + | ||
735 | + /* 64 - sizeof(unsigned long) - 1 */ | ||
736 | + if (strlen(newname) > 55) | ||
737 | + fatal("New name %s is too long\n", newname); | ||
738 | + | ||
739 | + /* Find where it is in the module structure. Don't assume layout! */ | ||
740 | + for (p = mem; p < (char *)mem + len - strlen(oldname); p++) { | ||
741 | + if (memcmp(p, oldname, strlen(oldname)) == 0) { | ||
742 | + strcpy(p, newname); | ||
743 | + return; | ||
744 | + } | ||
745 | + } | ||
746 | + | ||
747 | + warn("Could not find old name in %s to replace!\n", module->filename); | ||
748 | +} | ||
749 | + | ||
750 | +static void *get_section32(void *file, | ||
751 | + unsigned long size, | ||
752 | + const char *name, | ||
753 | + unsigned long *secsize) | ||
754 | +{ | ||
755 | + Elf32_Ehdr *hdr = file; | ||
756 | + Elf32_Shdr *sechdrs = file + hdr->e_shoff; | ||
757 | + const char *secnames; | ||
758 | + unsigned int i; | ||
759 | + | ||
760 | + /* Too short? */ | ||
761 | + if (size < sizeof(*hdr)) | ||
762 | + return NULL; | ||
763 | + if (size < hdr->e_shoff + hdr->e_shnum * sizeof(sechdrs[0])) | ||
764 | + return NULL; | ||
765 | + if (size < sechdrs[hdr->e_shstrndx].sh_offset) | ||
766 | + return NULL; | ||
767 | + | ||
768 | + secnames = file + sechdrs[hdr->e_shstrndx].sh_offset; | ||
769 | + for (i = 1; i < hdr->e_shnum; i++) | ||
770 | + if (strcmp(secnames + sechdrs[i].sh_name, name) == 0) { | ||
771 | + *secsize = sechdrs[i].sh_size; | ||
772 | + return file + sechdrs[i].sh_offset; | ||
773 | + } | ||
774 | + return NULL; | ||
775 | +} | ||
776 | + | ||
777 | +static void *get_section64(void *file, | ||
778 | + unsigned long size, | ||
779 | + const char *name, | ||
780 | + unsigned long *secsize) | ||
781 | +{ | ||
782 | + Elf64_Ehdr *hdr = file; | ||
783 | + Elf64_Shdr *sechdrs = file + hdr->e_shoff; | ||
784 | + const char *secnames; | ||
785 | + unsigned int i; | ||
786 | + | ||
787 | + /* Too short? */ | ||
788 | + if (size < sizeof(*hdr)) | ||
789 | + return NULL; | ||
790 | + if (size < hdr->e_shoff + hdr->e_shnum * sizeof(sechdrs[0])) | ||
791 | + return NULL; | ||
792 | + if (size < sechdrs[hdr->e_shstrndx].sh_offset) | ||
793 | + return NULL; | ||
794 | + | ||
795 | + secnames = file + sechdrs[hdr->e_shstrndx].sh_offset; | ||
796 | + for (i = 1; i < hdr->e_shnum; i++) | ||
797 | + if (strcmp(secnames + sechdrs[i].sh_name, name) == 0) { | ||
798 | + *secsize = sechdrs[i].sh_size; | ||
799 | + return file + sechdrs[i].sh_offset; | ||
800 | + } | ||
801 | + return NULL; | ||
802 | +} | ||
803 | + | ||
804 | +static int elf_ident(void *mod, unsigned long size) | ||
805 | +{ | ||
806 | + /* "\177ELF" <byte> where byte = 001 for 32-bit, 002 for 64 */ | ||
807 | + char *ident = mod; | ||
808 | + | ||
809 | + if (size < EI_CLASS || memcmp(mod, ELFMAG, SELFMAG) != 0) | ||
810 | + return ELFCLASSNONE; | ||
811 | + return ident[EI_CLASS]; | ||
812 | +} | ||
813 | + | ||
814 | +static void *get_section(void *file, | ||
815 | + unsigned long size, | ||
816 | + const char *name, | ||
817 | + unsigned long *secsize) | ||
818 | +{ | ||
819 | + switch (elf_ident(file, size)) { | ||
820 | + case ELFCLASS32: | ||
821 | + return get_section32(file, size, name, secsize); | ||
822 | + case ELFCLASS64: | ||
823 | + return get_section64(file, size, name, secsize); | ||
824 | + default: | ||
825 | + return NULL; | ||
826 | + } | ||
827 | +} | ||
828 | + | ||
829 | +static void rename_module(struct module *module, | ||
830 | + void *mod, | ||
831 | + unsigned long len, | ||
832 | + const char *newname) | ||
833 | +{ | ||
834 | + void *modstruct; | ||
835 | + unsigned long modstruct_len; | ||
836 | + | ||
837 | + /* Old-style */ | ||
838 | + modstruct = get_section(mod, len, ".gnu.linkonce.this_module", | ||
839 | + &modstruct_len); | ||
840 | + /* New-style */ | ||
841 | + if (!modstruct) | ||
842 | + modstruct = get_section(mod, len, "__module", &modstruct_len); | ||
843 | + if (!modstruct) | ||
844 | + warn("Could not find module name to change in %s\n", | ||
845 | + module->filename); | ||
846 | + else | ||
847 | + replace_modname(module, modstruct, modstruct_len, | ||
848 | + module->modname, newname); | ||
849 | +} | ||
850 | + | ||
851 | +/* Kernel told to ignore these sections if SHF_ALLOC not set. */ | ||
852 | +static void invalidate_section32(void *mod, const char *secname) | ||
853 | +{ | ||
854 | + Elf32_Ehdr *hdr = mod; | ||
855 | + Elf32_Shdr *sechdrs = mod + hdr->e_shoff; | ||
856 | + const char *secnames = mod + sechdrs[hdr->e_shstrndx].sh_offset; | ||
857 | + unsigned int i; | ||
858 | + | ||
859 | + for (i = 1; i < hdr->e_shnum; i++) | ||
860 | + if (strcmp(secnames+sechdrs[i].sh_name, secname) == 0) | ||
861 | + sechdrs[i].sh_flags &= ~SHF_ALLOC; | ||
862 | +} | ||
863 | + | ||
864 | +static void invalidate_section64(void *mod, const char *secname) | ||
865 | +{ | ||
866 | + Elf64_Ehdr *hdr = mod; | ||
867 | + Elf64_Shdr *sechdrs = mod + hdr->e_shoff; | ||
868 | + const char *secnames = mod + sechdrs[hdr->e_shstrndx].sh_offset; | ||
869 | + unsigned int i; | ||
870 | + | ||
871 | + for (i = 1; i < hdr->e_shnum; i++) | ||
872 | + if (strcmp(secnames+sechdrs[i].sh_name, secname) == 0) | ||
873 | + sechdrs[i].sh_flags &= ~(unsigned long long)SHF_ALLOC; | ||
874 | +} | ||
875 | + | ||
876 | +static void strip_section(struct module *module, | ||
877 | + void *mod, | ||
878 | + unsigned long len, | ||
879 | + const char *secname) | ||
880 | +{ | ||
881 | + switch (elf_ident(mod, len)) { | ||
882 | + case ELFCLASS32: | ||
883 | + invalidate_section32(mod, secname); | ||
884 | + break; | ||
885 | + case ELFCLASS64: | ||
886 | + invalidate_section64(mod, secname); | ||
887 | + break; | ||
888 | + default: | ||
889 | + warn("Unknown module format in %s: not forcing version\n", | ||
890 | + module->filename); | ||
891 | + } | ||
892 | +} | ||
893 | + | ||
894 | +static const char *next_string(const char *string, unsigned long *secsize) | ||
895 | +{ | ||
896 | + /* Skip non-zero chars */ | ||
897 | + while (string[0]) { | ||
898 | + string++; | ||
899 | + if ((*secsize)-- <= 1) | ||
900 | + return NULL; | ||
901 | + } | ||
902 | + | ||
903 | + /* Skip any zero padding. */ | ||
904 | + while (!string[0]) { | ||
905 | + string++; | ||
906 | + if ((*secsize)-- <= 1) | ||
907 | + return NULL; | ||
908 | + } | ||
909 | + return string; | ||
910 | +} | ||
911 | + | ||
912 | +static void clear_magic(struct module *module, void *mod, unsigned long len) | ||
913 | +{ | ||
914 | + const char *p; | ||
915 | + unsigned long modlen; | ||
916 | + | ||
917 | + /* Old-style: __vermagic section */ | ||
918 | + strip_section(module, mod, len, "__vermagic"); | ||
919 | + | ||
920 | + /* New-style: in .modinfo section */ | ||
921 | + for (p = get_section(mod, len, ".modinfo", &modlen); | ||
922 | + p; | ||
923 | + p = next_string(p, &modlen)) { | ||
924 | + if (strncmp(p, "vermagic=", strlen("vermagic=")) == 0) { | ||
925 | + memset((char *)p, 0, strlen(p)); | ||
926 | + return; | ||
927 | + } | ||
928 | + } | ||
929 | +} | ||
930 | + | ||
931 | +struct module_options | ||
932 | +{ | ||
933 | + struct module_options *next; | ||
934 | + char *modulename; | ||
935 | + char *options; | ||
936 | +}; | ||
937 | + | ||
938 | +struct module_command | ||
939 | +{ | ||
940 | + struct module_command *next; | ||
941 | + char *modulename; | ||
942 | + char *command; | ||
943 | +}; | ||
944 | + | ||
945 | +struct module_alias | ||
946 | +{ | ||
947 | + struct module_alias *next; | ||
948 | + char *module; | ||
949 | +}; | ||
950 | + | ||
951 | +struct module_blacklist | ||
952 | +{ | ||
953 | + struct module_blacklist *next; | ||
954 | + char *modulename; | ||
955 | +}; | ||
956 | + | ||
957 | +/* Link in a new option line from the config file. */ | ||
958 | +static struct module_options * | ||
959 | +add_options(const char *modname, | ||
960 | + const char *option, | ||
961 | + struct module_options *options) | ||
962 | +{ | ||
963 | + struct module_options *new; | ||
964 | + char *tab; | ||
965 | + | ||
966 | + new = NOFAIL(malloc(sizeof(*new))); | ||
967 | + new->modulename = NOFAIL(strdup(modname)); | ||
968 | + new->options = NOFAIL(strdup(option)); | ||
969 | + /* We can handle tabs, kernel can't. */ | ||
970 | + for (tab = strchr(new->options, '\t'); tab; tab = strchr(tab, '\t')) | ||
971 | + *tab = ' '; | ||
972 | + new->next = options; | ||
973 | + return new; | ||
974 | +} | ||
975 | + | ||
976 | +/* Link in a new install line from the config file. */ | ||
977 | +static struct module_command * | ||
978 | +add_command(const char *modname, | ||
979 | + const char *command, | ||
980 | + struct module_command *commands) | ||
981 | +{ | ||
982 | + struct module_command *new; | ||
983 | + | ||
984 | + new = NOFAIL(malloc(sizeof(*new))); | ||
985 | + new->modulename = NOFAIL(strdup(modname)); | ||
986 | + new->command = NOFAIL(strdup(command)); | ||
987 | + new->next = commands; | ||
988 | + return new; | ||
989 | +} | ||
990 | + | ||
991 | +/* Link in a new alias line from the config file. */ | ||
992 | +static struct module_alias * | ||
993 | +add_alias(const char *modname, struct module_alias *aliases) | ||
994 | +{ | ||
995 | + struct module_alias *new; | ||
996 | + | ||
997 | + new = NOFAIL(malloc(sizeof(*new))); | ||
998 | + new->module = NOFAIL(strdup(modname)); | ||
999 | + new->next = aliases; | ||
1000 | + return new; | ||
1001 | +} | ||
1002 | + | ||
1003 | +/* Link in a new blacklist line from the config file. */ | ||
1004 | +static struct module_blacklist * | ||
1005 | +add_blacklist(const char *modname, struct module_blacklist *blacklist) | ||
1006 | +{ | ||
1007 | + struct module_blacklist *new; | ||
1008 | + | ||
1009 | + new = NOFAIL(malloc(sizeof(*new))); | ||
1010 | + new->modulename = NOFAIL(strdup(modname)); | ||
1011 | + new->next = blacklist; | ||
1012 | + return new; | ||
1013 | +} | ||
1014 | + | ||
1015 | +/* Find blacklist commands if any. */ | ||
1016 | +static int | ||
1017 | +find_blacklist(const char *modname, const struct module_blacklist *blacklist) | ||
1018 | +{ | ||
1019 | + while (blacklist) { | ||
1020 | + if (strcmp(blacklist->modulename, modname) == 0) | ||
1021 | + return 1; | ||
1022 | + blacklist = blacklist->next; | ||
1023 | + } | ||
1024 | + return 0; | ||
1025 | +} | ||
1026 | + | ||
1027 | +/* return a new alias list, with backlisted elems filtered out */ | ||
1028 | +static struct module_alias * | ||
1029 | +apply_blacklist(const struct module_alias *aliases, | ||
1030 | + const struct module_blacklist *blacklist) | ||
1031 | +{ | ||
1032 | + struct module_alias *result = NULL; | ||
1033 | + while (aliases) { | ||
1034 | + char *modname = aliases->module; | ||
1035 | + if (!find_blacklist(modname, blacklist)) | ||
1036 | + result = add_alias(modname, result); | ||
1037 | + aliases = aliases->next; | ||
1038 | + } | ||
1039 | + return result; | ||
1040 | +} | ||
1041 | + | ||
1042 | +/* Find install commands if any. */ | ||
1043 | +static const char *find_command(const char *modname, | ||
1044 | + const struct module_command *commands) | ||
1045 | +{ | ||
1046 | + while (commands) { | ||
1047 | + if (fnmatch(commands->modulename, modname, 0) == 0) | ||
1048 | + return commands->command; | ||
1049 | + commands = commands->next; | ||
1050 | + } | ||
1051 | + return NULL; | ||
1052 | +} | ||
1053 | + | ||
1054 | +static char *append_option(char *options, const char *newoption) | ||
1055 | +{ | ||
1056 | + options = NOFAIL(realloc(options, strlen(options) + 1 | ||
1057 | + + strlen(newoption) + 1)); | ||
1058 | + if (strlen(options)) strcat(options, " "); | ||
1059 | + strcat(options, newoption); | ||
1060 | + return options; | ||
1061 | +} | ||
1062 | + | ||
1063 | +/* Add to options */ | ||
1064 | +static char *add_extra_options(const char *modname, | ||
1065 | + char *optstring, | ||
1066 | + const struct module_options *options) | ||
1067 | +{ | ||
1068 | + while (options) { | ||
1069 | + if (strcmp(options->modulename, modname) == 0) | ||
1070 | + optstring = append_option(optstring, options->options); | ||
1071 | + options = options->next; | ||
1072 | + } | ||
1073 | + return optstring; | ||
1074 | +} | ||
1075 | + | ||
1076 | +/* If we don't flush, then child processes print before we do */ | ||
1077 | +static void verbose_printf(int verbose, const char *fmt, ...) | ||
1078 | +{ | ||
1079 | + va_list arglist; | ||
1080 | + | ||
1081 | + if (verbose) { | ||
1082 | + va_start(arglist, fmt); | ||
1083 | + vprintf(fmt, arglist); | ||
1084 | + fflush(stdout); | ||
1085 | + va_end(arglist); | ||
1086 | + } | ||
1087 | +} | ||
1088 | + | ||
1089 | +/* Do an install/remove command: replace $CMDLINE_OPTS if it's specified. */ | ||
1090 | +static void do_command(const char *modname, | ||
1091 | + const char *command, | ||
1092 | + int verbose, int dry_run, | ||
1093 | + errfn_t error, | ||
1094 | + const char *type, | ||
1095 | + const char *cmdline_opts) | ||
1096 | +{ | ||
1097 | + int ret; | ||
1098 | + char *p, *replaced_cmd = NOFAIL(strdup(command)); | ||
1099 | + | ||
1100 | + while ((p = strstr(replaced_cmd, "$CMDLINE_OPTS")) != NULL) { | ||
1101 | + char *new; | ||
1102 | + asprintf(&new, "%.*s%s%s", | ||
1103 | + p - replaced_cmd, replaced_cmd, cmdline_opts, | ||
1104 | + p + strlen("$CMDLINE_OPTS")); | ||
1105 | + NOFAIL(new); | ||
1106 | + free(replaced_cmd); | ||
1107 | + replaced_cmd = new; | ||
1108 | + } | ||
1109 | + | ||
1110 | + verbose_printf(verbose, "%s %s\n", type, replaced_cmd); | ||
1111 | + if (dry_run) | ||
1112 | + return; | ||
1113 | + | ||
1114 | + setenv("MODPROBE_MODULE", modname, 1); | ||
1115 | + ret = system(replaced_cmd); | ||
1116 | + if (ret == -1 || WEXITSTATUS(ret)) | ||
1117 | + error("Error running %s command for %s\n", type, modname); | ||
1118 | + free(replaced_cmd); | ||
1119 | +} | ||
1120 | + | ||
1121 | +/* Actually do the insert. Frees second arg. */ | ||
1122 | +static void insmod(struct list_head *list, | ||
1123 | + char *optstring, | ||
1124 | + const char *newname, | ||
1125 | + int first_time, | ||
1126 | + errfn_t error, | ||
1127 | + int dry_run, | ||
1128 | + int verbose, | ||
1129 | + const struct module_options *options, | ||
1130 | + const struct module_command *commands, | ||
1131 | + int ignore_commands, | ||
1132 | + int ignore_proc, | ||
1133 | + int strip_vermagic, | ||
1134 | + int strip_modversion, | ||
1135 | + const char *cmdline_opts) | ||
1136 | +{ | ||
1137 | + int ret, fd; | ||
1138 | + unsigned long len; | ||
1139 | + void *map; | ||
1140 | + const char *command; | ||
1141 | + struct module *mod = list_entry(list->next, struct module, list); | ||
1142 | + | ||
1143 | + /* Take us off the list. */ | ||
1144 | + list_del(&mod->list); | ||
1145 | + | ||
1146 | + /* Do things we (or parent) depend on first, but don't die if | ||
1147 | + * they fail. */ | ||
1148 | + if (!list_empty(list)) { | ||
1149 | + insmod(list, NOFAIL(strdup("")), NULL, 0, warn, | ||
1150 | + dry_run, verbose, options, commands, 0, ignore_proc, | ||
1151 | + strip_vermagic, strip_modversion, cmdline_opts); | ||
1152 | + } | ||
1153 | + | ||
1154 | + /* Lock before we look, in case it's initializing. */ | ||
1155 | + fd = lock_file(mod->filename); | ||
1156 | + if (fd < 0) { | ||
1157 | + error("Could not open '%s': %s\n", | ||
1158 | + mod->filename, strerror(errno)); | ||
1159 | + goto out_optstring; | ||
1160 | + } | ||
1161 | + | ||
1162 | + /* Don't do ANYTHING if already in kernel. */ | ||
1163 | + if (!ignore_proc | ||
1164 | + && module_in_kernel(newname ?: mod->modname, NULL) == 1) { | ||
1165 | + if (first_time) | ||
1166 | + error("Module %s already in kernel.\n", | ||
1167 | + newname ?: mod->modname); | ||
1168 | + goto out_unlock; | ||
1169 | + } | ||
1170 | + | ||
1171 | + command = find_command(mod->modname, commands); | ||
1172 | + if (command && !ignore_commands) { | ||
1173 | + /* It might recurse: unlock. */ | ||
1174 | + unlock_file(fd); | ||
1175 | + do_command(mod->modname, command, verbose, dry_run, error, | ||
1176 | + "install", cmdline_opts); | ||
1177 | + goto out_optstring; | ||
1178 | + } | ||
1179 | + | ||
1180 | + map = grab_fd(fd, &len); | ||
1181 | + if (!map) { | ||
1182 | + error("Could not read '%s': %s\n", | ||
1183 | + mod->filename, strerror(errno)); | ||
1184 | + goto out_unlock; | ||
1185 | + } | ||
1186 | + | ||
1187 | + /* Rename it? */ | ||
1188 | + if (newname) | ||
1189 | + rename_module(mod, map, len, newname); | ||
1190 | + | ||
1191 | + if (strip_modversion) | ||
1192 | + strip_section(mod, map, len, "__versions"); | ||
1193 | + if (strip_vermagic) | ||
1194 | + clear_magic(mod, map, len); | ||
1195 | + | ||
1196 | + /* Config file might have given more options */ | ||
1197 | + optstring = add_extra_options(mod->modname, optstring, options); | ||
1198 | + | ||
1199 | + verbose_printf(verbose, "insmod %s %s\n", mod->filename, optstring); | ||
1200 | + | ||
1201 | + if (dry_run) | ||
1202 | + goto out; | ||
1203 | + | ||
1204 | + ret = init_module(map, len, optstring); | ||
1205 | + if (ret != 0) { | ||
1206 | + if (errno == EEXIST) { | ||
1207 | + if (first_time) | ||
1208 | + error("Module %s already in kernel.\n", | ||
1209 | + newname ?: mod->modname); | ||
1210 | + goto out_unlock; | ||
1211 | + } | ||
1212 | + error("Error inserting %s (%s): %s\n", | ||
1213 | + mod->modname, mod->filename, insert_moderror(errno)); | ||
1214 | + } | ||
1215 | + out: | ||
1216 | + release_file(map, len); | ||
1217 | + out_unlock: | ||
1218 | + unlock_file(fd); | ||
1219 | + out_optstring: | ||
1220 | + free(optstring); | ||
1221 | + return; | ||
1222 | +} | ||
1223 | + | ||
1224 | +/* Do recursive removal. */ | ||
1225 | +static void rmmod(struct list_head *list, | ||
1226 | + const char *name, | ||
1227 | + int first_time, | ||
1228 | + errfn_t error, | ||
1229 | + int dry_run, | ||
1230 | + int verbose, | ||
1231 | + struct module_command *commands, | ||
1232 | + int ignore_commands, | ||
1233 | + int ignore_inuse, | ||
1234 | + const char *cmdline_opts) | ||
1235 | +{ | ||
1236 | + const char *command; | ||
1237 | + unsigned int usecount = 0; | ||
1238 | + int lock; | ||
1239 | + struct module *mod = list_entry(list->next, struct module, list); | ||
1240 | + | ||
1241 | + /* Take first one off the list. */ | ||
1242 | + list_del(&mod->list); | ||
1243 | + | ||
1244 | + /* Ignore failure; it's best effort here. */ | ||
1245 | + lock = lock_file(mod->filename); | ||
1246 | + | ||
1247 | + if (!name) | ||
1248 | + name = mod->modname; | ||
1249 | + | ||
1250 | + /* Even if renamed, find commands to orig. name. */ | ||
1251 | + command = find_command(mod->modname, commands); | ||
1252 | + if (command && !ignore_commands) { | ||
1253 | + /* It might recurse: unlock. */ | ||
1254 | + unlock_file(lock); | ||
1255 | + do_command(mod->modname, command, verbose, dry_run, error, | ||
1256 | + "remove", cmdline_opts); | ||
1257 | + goto remove_rest_no_unlock; | ||
1258 | + } | ||
1259 | + | ||
1260 | + if (module_in_kernel(name, &usecount) == 0) | ||
1261 | + goto nonexistent_module; | ||
1262 | + | ||
1263 | + if (usecount != 0) { | ||
1264 | + if (!ignore_inuse) | ||
1265 | + error("Module %s is in use.\n", name); | ||
1266 | + goto remove_rest; | ||
1267 | + } | ||
1268 | + | ||
1269 | + verbose_printf(verbose, "rmmod %s\n", mod->filename); | ||
1270 | + | ||
1271 | + if (dry_run) | ||
1272 | + goto remove_rest; | ||
1273 | + | ||
1274 | + if (delete_module(name, O_EXCL) != 0) { | ||
1275 | + if (errno == ENOENT) | ||
1276 | + goto nonexistent_module; | ||
1277 | + error("Error removing %s (%s): %s\n", | ||
1278 | + name, mod->filename, | ||
1279 | + remove_moderror(errno)); | ||
1280 | + } | ||
1281 | + | ||
1282 | + remove_rest: | ||
1283 | + unlock_file(lock); | ||
1284 | + remove_rest_no_unlock: | ||
1285 | + /* Now do things we depend. */ | ||
1286 | + if (!list_empty(list)) | ||
1287 | + rmmod(list, NULL, 0, warn, dry_run, verbose, commands, | ||
1288 | + 0, 1, cmdline_opts); | ||
1289 | + return; | ||
1290 | + | ||
1291 | +nonexistent_module: | ||
1292 | + if (first_time) | ||
1293 | + fatal("Module %s is not in kernel.\n", mod->modname); | ||
1294 | + goto remove_rest; | ||
1295 | +} | ||
1296 | + | ||
1297 | +/* Does path contain directory(s) subpath? */ | ||
1298 | +static int type_matches(const char *path, const char *subpath) | ||
1299 | +{ | ||
1300 | + char *subpath_with_slashes; | ||
1301 | + int ret; | ||
1302 | + | ||
1303 | + asprintf(&subpath_with_slashes, "/%s/", subpath); | ||
1304 | + NOFAIL(subpath_with_slashes); | ||
1305 | + | ||
1306 | + ret = (strstr(path, subpath_with_slashes) != NULL); | ||
1307 | + free(subpath_with_slashes); | ||
1308 | + return ret; | ||
1309 | +} | ||
1310 | + | ||
1311 | +static char *underscores(char *string) | ||
1312 | +{ | ||
1313 | + if (string) { | ||
1314 | + unsigned int i; | ||
1315 | + for (i = 0; string[i]; i++) | ||
1316 | + if (string[i] == '-') | ||
1317 | + string[i] = '_'; | ||
1318 | + } | ||
1319 | + return string; | ||
1320 | +} | ||
1321 | + | ||
1322 | +static int do_wildcard(const char *dirname, | ||
1323 | + const char *type, | ||
1324 | + const char *wildcard) | ||
1325 | +{ | ||
1326 | + char modules_dep_name[strlen(dirname) + sizeof("modules.dep") + 1]; | ||
1327 | + char *line, *wcard; | ||
1328 | + FILE *modules_dep; | ||
1329 | + | ||
1330 | + /* Canonicalize wildcard */ | ||
1331 | + wcard = strdup(wildcard); | ||
1332 | + underscores(wcard); | ||
1333 | + | ||
1334 | + sprintf(modules_dep_name, "%s/%s", dirname, "modules.dep"); | ||
1335 | + modules_dep = fopen(modules_dep_name, "r"); | ||
1336 | + if (!modules_dep) | ||
1337 | + fatal("Could not load %s: %s\n", | ||
1338 | + modules_dep_name, strerror(errno)); | ||
1339 | + | ||
1340 | + while ((line = getline_wrapped(modules_dep, NULL)) != NULL) { | ||
1341 | + char *ptr; | ||
1342 | + | ||
1343 | + /* Ignore lines without : or which start with a # */ | ||
1344 | + ptr = strchr(line, ':'); | ||
1345 | + if (ptr == NULL || line[strspn(line, "\t ")] == '#') | ||
1346 | + goto next; | ||
1347 | + *ptr = '\0'; | ||
1348 | + | ||
1349 | + /* "type" must match complete directory component(s). */ | ||
1350 | + if (!type || type_matches(line, type)) { | ||
1351 | + char modname[strlen(line)+1]; | ||
1352 | + | ||
1353 | + filename2modname(modname, line); | ||
1354 | + if (fnmatch(wcard, modname, 0) == 0) | ||
1355 | + printf("%s\n", line); | ||
1356 | + } | ||
1357 | + next: | ||
1358 | + free(line); | ||
1359 | + } | ||
1360 | + | ||
1361 | + free(wcard); | ||
1362 | + return 0; | ||
1363 | +} | ||
1364 | + | ||
1365 | +static char *strsep_skipspace(char **string, char *delim) | ||
1366 | +{ | ||
1367 | + if (!*string) | ||
1368 | + return NULL; | ||
1369 | + *string += strspn(*string, delim); | ||
1370 | + return strsep(string, delim); | ||
1371 | +} | ||
1372 | + | ||
1373 | +/* Recursion */ | ||
1374 | +static int read_config(const char *filename, | ||
1375 | + const char *name, | ||
1376 | + int dump_only, | ||
1377 | + int removing, | ||
1378 | + struct module_options **options, | ||
1379 | + struct module_command **commands, | ||
1380 | + struct module_alias **alias, | ||
1381 | + struct module_blacklist **blacklist); | ||
1382 | + | ||
1383 | +/* FIXME: Maybe should be extended to "alias a b [and|or c]...". --RR */ | ||
1384 | +static int read_config_file(const char *filename, | ||
1385 | + const char *name, | ||
1386 | + int dump_only, | ||
1387 | + int removing, | ||
1388 | + struct module_options **options, | ||
1389 | + struct module_command **commands, | ||
1390 | + struct module_alias **aliases, | ||
1391 | + struct module_blacklist **blacklist) | ||
1392 | +{ | ||
1393 | + char *line; | ||
1394 | + unsigned int linenum = 0; | ||
1395 | + FILE *cfile; | ||
1396 | + | ||
1397 | + cfile = fopen(filename, "r"); | ||
1398 | + if (!cfile) | ||
1399 | + return 0; | ||
1400 | + | ||
1401 | + while ((line = getline_wrapped(cfile, &linenum)) != NULL) { | ||
1402 | + char *ptr = line; | ||
1403 | + char *cmd, *modname; | ||
1404 | + | ||
1405 | + if (dump_only) | ||
1406 | + printf("%s\n", line); | ||
1407 | + | ||
1408 | + cmd = strsep_skipspace(&ptr, "\t "); | ||
1409 | + if (cmd == NULL || cmd[0] == '#' || cmd[0] == '\0') | ||
1410 | + continue; | ||
1411 | + | ||
1412 | + if (strcmp(cmd, "alias") == 0) { | ||
1413 | + char *wildcard | ||
1414 | + = underscores(strsep_skipspace(&ptr, "\t ")); | ||
1415 | + char *realname | ||
1416 | + = underscores(strsep_skipspace(&ptr, "\t ")); | ||
1417 | + | ||
1418 | + if (!wildcard || !realname) | ||
1419 | + grammar(cmd, filename, linenum); | ||
1420 | + else if (fnmatch(wildcard,name,0) == 0) | ||
1421 | + *aliases = add_alias(realname, *aliases); | ||
1422 | + } else if (strcmp(cmd, "include") == 0) { | ||
1423 | + struct module_alias *newalias = NULL; | ||
1424 | + char *newfilename; | ||
1425 | + | ||
1426 | + newfilename = strsep_skipspace(&ptr, "\t "); | ||
1427 | + if (!newfilename) | ||
1428 | + grammar(cmd, filename, linenum); | ||
1429 | + else { | ||
1430 | + if (!read_config(newfilename, name, | ||
1431 | + dump_only, removing, | ||
1432 | + options, commands, &newalias, | ||
1433 | + blacklist)) | ||
1434 | + warn("Failed to open included" | ||
1435 | + " config file %s: %s\n", | ||
1436 | + newfilename, strerror(errno)); | ||
1437 | + | ||
1438 | + /* Files included override aliases, | ||
1439 | + etc that was already set ... */ | ||
1440 | + if (newalias) | ||
1441 | + *aliases = newalias; | ||
1442 | + } | ||
1443 | + } else if (strcmp(cmd, "options") == 0) { | ||
1444 | + modname = strsep_skipspace(&ptr, "\t "); | ||
1445 | + if (!modname || !ptr) | ||
1446 | + grammar(cmd, filename, linenum); | ||
1447 | + else { | ||
1448 | + ptr += strspn(ptr, "\t "); | ||
1449 | + *options = add_options(underscores(modname), | ||
1450 | + ptr, *options); | ||
1451 | + } | ||
1452 | + } else if (strcmp(cmd, "install") == 0) { | ||
1453 | + modname = strsep_skipspace(&ptr, "\t "); | ||
1454 | + if (!modname || !ptr) | ||
1455 | + grammar(cmd, filename, linenum); | ||
1456 | + else if (!removing) { | ||
1457 | + ptr += strspn(ptr, "\t "); | ||
1458 | + *commands = add_command(underscores(modname), | ||
1459 | + ptr, *commands); | ||
1460 | + } | ||
1461 | + } else if (strcmp(cmd, "blacklist") == 0) { | ||
1462 | + modname = strsep_skipspace(&ptr, "\t "); | ||
1463 | + if (!modname) | ||
1464 | + grammar(cmd, filename, linenum); | ||
1465 | + else if (!removing) { | ||
1466 | + *blacklist = add_blacklist(underscores(modname), | ||
1467 | + *blacklist); | ||
1468 | + } | ||
1469 | + } else if (strcmp(cmd, "remove") == 0) { | ||
1470 | + modname = strsep_skipspace(&ptr, "\t "); | ||
1471 | + if (!modname || !ptr) | ||
1472 | + grammar(cmd, filename, linenum); | ||
1473 | + else if (removing) { | ||
1474 | + ptr += strspn(ptr, "\t "); | ||
1475 | + *commands = add_command(underscores(modname), | ||
1476 | + ptr, *commands); | ||
1477 | + } | ||
1478 | + } else | ||
1479 | + grammar(cmd, filename, linenum); | ||
1480 | + | ||
1481 | + free(line); | ||
1482 | + } | ||
1483 | + fclose(cfile); | ||
1484 | + return 1; | ||
1485 | +} | ||
1486 | + | ||
1487 | +/* Simple format, ignore lines starting with #, one command per line. | ||
1488 | + Returns true or false. */ | ||
1489 | +static int read_config(const char *filename, | ||
1490 | + const char *name, | ||
1491 | + int dump_only, | ||
1492 | + int removing, | ||
1493 | + struct module_options **options, | ||
1494 | + struct module_command **commands, | ||
1495 | + struct module_alias **aliases, | ||
1496 | + struct module_blacklist **blacklist) | ||
1497 | +{ | ||
1498 | + DIR *dir; | ||
1499 | + int ret = 0; | ||
1500 | + | ||
1501 | + /* ignore everything in this directory */ | ||
1502 | + if (streq(filename, "/etc/modprobe.d/arch")) | ||
1503 | + return 1; | ||
1504 | + | ||
1505 | + /* Reiser4 has file/directory duality: treat it as both. */ | ||
1506 | + dir = opendir(filename); | ||
1507 | + if (dir) { | ||
1508 | + struct dirent *i; | ||
1509 | + while ((i = readdir(dir)) != NULL) { | ||
1510 | + if (!streq(i->d_name,".") && !streq(i->d_name,"..")) { | ||
1511 | + char sub[strlen(filename) + 1 | ||
1512 | + + strlen(i->d_name) + 1]; | ||
1513 | + | ||
1514 | + sprintf(sub, "%s/%s", filename, i->d_name); | ||
1515 | + if (!read_config(sub, name, | ||
1516 | + dump_only, removing, options, | ||
1517 | + commands, aliases, blacklist)) | ||
1518 | + warn("Failed to open" | ||
1519 | + " config file %s: %s\n", | ||
1520 | + sub, strerror(errno)); | ||
1521 | + } | ||
1522 | + } | ||
1523 | + closedir(dir); | ||
1524 | + ret = 1; | ||
1525 | + } | ||
1526 | + | ||
1527 | + if (read_config_file(filename, name, dump_only, removing, | ||
1528 | + options, commands, aliases, blacklist)) | ||
1529 | + ret = 1; | ||
1530 | + | ||
1531 | + return ret; | ||
1532 | +} | ||
1533 | + | ||
1534 | +static const char *default_configs[] = | ||
1535 | +{ | ||
1536 | + "/etc/modprobe.conf", | ||
1537 | + "/etc/modprobe.d", | ||
1538 | +}; | ||
1539 | + | ||
1540 | +static void read_toplevel_config(const char *filename, | ||
1541 | + const char *name, | ||
1542 | + int dump_only, | ||
1543 | + int removing, | ||
1544 | + struct module_options **options, | ||
1545 | + struct module_command **commands, | ||
1546 | + struct module_alias **aliases, | ||
1547 | + struct module_blacklist **blacklist) | ||
1548 | +{ | ||
1549 | + unsigned int i; | ||
1550 | + | ||
1551 | + if (filename) { | ||
1552 | + if (!read_config(filename, name, dump_only, removing, | ||
1553 | + options, commands, aliases, blacklist)) | ||
1554 | + fatal("Failed to open config file %s: %s\n", | ||
1555 | + filename, strerror(errno)); | ||
1556 | + return; | ||
1557 | + } | ||
1558 | + | ||
1559 | + /* Try defaults. */ | ||
1560 | + for (i = 0; i < ARRAY_SIZE(default_configs); i++) { | ||
1561 | + if (read_config(default_configs[i], name, dump_only, removing, | ||
1562 | + options, commands, aliases, blacklist)) | ||
1563 | + return; | ||
1564 | + } | ||
1565 | +} | ||
1566 | + | ||
1567 | +static void add_to_env_var(const char *option) | ||
1568 | +{ | ||
1569 | + const char *oldenv; | ||
1570 | + | ||
1571 | + if ((oldenv = getenv("MODPROBE_OPTIONS")) != NULL) { | ||
1572 | + char *newenv; | ||
1573 | + asprintf(&newenv, "%s %s", oldenv, option); | ||
1574 | + setenv("MODPROBE_OPTIONS", newenv, 1); | ||
1575 | + } else | ||
1576 | + setenv("MODPROBE_OPTIONS", option, 1); | ||
1577 | +} | ||
1578 | + | ||
1579 | +/* Prepend options from environment. */ | ||
1580 | +static char **merge_args(char *args, char *argv[], int *argc) | ||
1581 | +{ | ||
1582 | + char *arg, *argstring; | ||
1583 | + char **newargs = NULL; | ||
1584 | + unsigned int i, num_env = 0; | ||
1585 | + | ||
1586 | + if (!args) | ||
1587 | + return argv; | ||
1588 | + | ||
1589 | + argstring = NOFAIL(strdup(args)); | ||
1590 | + for (arg = strtok(argstring, " "); arg; arg = strtok(NULL, " ")) { | ||
1591 | + num_env++; | ||
1592 | + newargs = NOFAIL(realloc(newargs, | ||
1593 | + sizeof(newargs[0]) | ||
1594 | + * (num_env + *argc + 1))); | ||
1595 | + newargs[num_env] = arg; | ||
1596 | + } | ||
1597 | + | ||
1598 | + /* Append commandline args */ | ||
1599 | + newargs[0] = argv[0]; | ||
1600 | + for (i = 1; i <= *argc; i++) | ||
1601 | + newargs[num_env+i] = argv[i]; | ||
1602 | + | ||
1603 | + *argc += num_env; | ||
1604 | + return newargs; | ||
1605 | +} | ||
1606 | + | ||
1607 | +static char *gather_options(char *argv[]) | ||
1608 | +{ | ||
1609 | + char *optstring = NOFAIL(strdup("")); | ||
1610 | + | ||
1611 | + /* Rest is module options */ | ||
1612 | + while (*argv) { | ||
1613 | + /* Quote value if it contains spaces. */ | ||
1614 | + unsigned int eq = strcspn(*argv, "="); | ||
1615 | + | ||
1616 | + if (strchr(*argv+eq, ' ') && !strchr(*argv, '"')) { | ||
1617 | + char quoted[strlen(*argv) + 3]; | ||
1618 | + (*argv)[eq] = '\0'; | ||
1619 | + sprintf(quoted, "%s=\"%s\"", *argv, *argv+eq+1); | ||
1620 | + optstring = append_option(optstring, quoted); | ||
1621 | + } else | ||
1622 | + optstring = append_option(optstring, *argv); | ||
1623 | + argv++; | ||
1624 | + } | ||
1625 | + return optstring; | ||
1626 | +} | ||
1627 | + | ||
1628 | +static void handle_module(const char *modname, | ||
1629 | + struct list_head *todo_list, | ||
1630 | + const char *newname, | ||
1631 | + int remove, | ||
1632 | + char *options, | ||
1633 | + int first_time, | ||
1634 | + errfn_t error, | ||
1635 | + int dry_run, | ||
1636 | + int verbose, | ||
1637 | + struct module_options *modoptions, | ||
1638 | + struct module_command *commands, | ||
1639 | + int ignore_commands, | ||
1640 | + int ignore_proc, | ||
1641 | + int strip_vermagic, | ||
1642 | + int strip_modversion, | ||
1643 | + int unknown_silent, | ||
1644 | + const char *cmdline_opts) | ||
1645 | +{ | ||
1646 | + if (list_empty(todo_list)) { | ||
1647 | + const char *command; | ||
1648 | + | ||
1649 | + /* The dependencies have to be real modules, but | ||
1650 | + handle case where the first is completely bogus. */ | ||
1651 | + command = find_command(modname, commands); | ||
1652 | + if (command && !ignore_commands) { | ||
1653 | + do_command(modname, command, verbose, dry_run, error, | ||
1654 | + remove ? "remove":"install", cmdline_opts); | ||
1655 | + return; | ||
1656 | + } | ||
1657 | + | ||
1658 | + if (unknown_silent) | ||
1659 | + exit(1); | ||
1660 | + error("Module %s not found.\n", modname); | ||
1661 | + return; | ||
1662 | + } | ||
1663 | + | ||
1664 | + if (remove) | ||
1665 | + rmmod(todo_list, newname, first_time, error, dry_run, verbose, | ||
1666 | + commands, ignore_commands, 0, cmdline_opts); | ||
1667 | + else | ||
1668 | + insmod(todo_list, NOFAIL(strdup(options)), newname, | ||
1669 | + first_time, error, dry_run, verbose, modoptions, | ||
1670 | + commands, ignore_commands, ignore_proc, strip_vermagic, | ||
1671 | + strip_modversion, cmdline_opts); | ||
1672 | +} | ||
1673 | + | ||
1674 | +static struct option options[] = { { "verbose", 0, NULL, 'v' }, | ||
1675 | + { "version", 0, NULL, 'V' }, | ||
1676 | + { "config", 1, NULL, 'C' }, | ||
1677 | + { "name", 1, NULL, 'o' }, | ||
1678 | + { "remove", 0, NULL, 'r' }, | ||
1679 | + { "showconfig", 0, NULL, 'c' }, | ||
1680 | + { "autoclean", 0, NULL, 'k' }, | ||
1681 | + { "quiet", 0, NULL, 'q' }, | ||
1682 | + { "show", 0, NULL, 'n' }, | ||
1683 | + { "dry-run", 0, NULL, 'n' }, | ||
1684 | + { "syslog", 0, NULL, 's' }, | ||
1685 | + { "type", 1, NULL, 't' }, | ||
1686 | + { "list", 0, NULL, 'l' }, | ||
1687 | + { "all", 0, NULL, 'a' }, | ||
1688 | + { "ignore-install", 0, NULL, 'i' }, | ||
1689 | + { "ignore-remove", 0, NULL, 'i' }, | ||
1690 | + { "force", 0, NULL, 'f' }, | ||
1691 | + { "force-vermagic", 0, NULL, 1 }, | ||
1692 | + { "force-modversion", 0, NULL, 2 }, | ||
1693 | + { "set-version", 1, NULL, 'S' }, | ||
1694 | + { "show-depends", 0, NULL, 'D' }, | ||
1695 | + { "first-time", 0, NULL, 3 }, | ||
1696 | + { "use-blacklist", 0, NULL, 'b' }, | ||
1697 | + { NULL, 0, NULL, 0 } }; | ||
1698 | + | ||
1699 | +#define MODPROBE_DEVFSD_CONF "/etc/modprobe.devfs" | ||
1700 | + | ||
1701 | +/* This is a horrible hack to allow devfsd, which calls modprobe with | ||
1702 | + -C /etc/modules.conf or /etc/modules.devfs, to work. FIXME. */ | ||
1703 | +/* Modern devfsd or variants should use -q explicitly in 2.6. */ | ||
1704 | +static int is_devfs_call(char *argv[]) | ||
1705 | +{ | ||
1706 | + unsigned int i; | ||
1707 | + | ||
1708 | + /* Look for "/dev" arg */ | ||
1709 | + for (i = 1; argv[i]; i++) { | ||
1710 | + if (strncmp(argv[i], "/dev/", 5) == 0) | ||
1711 | + return 1; | ||
1712 | + } | ||
1713 | + return 0; | ||
1714 | +} | ||
1715 | + | ||
1716 | +int main(int argc, char *argv[]) | ||
1717 | +{ | ||
1718 | + struct utsname buf; | ||
1719 | + struct stat statbuf; | ||
1720 | + int opt; | ||
1721 | + int dump_only = 0; | ||
1722 | + int dry_run = 0; | ||
1723 | + int remove = 0; | ||
1724 | + int verbose = 0; | ||
1725 | + int unknown_silent = 0; | ||
1726 | + int list_only = 0; | ||
1727 | + int all = 0; | ||
1728 | + int ignore_commands = 0; | ||
1729 | + int strip_vermagic = 0; | ||
1730 | + int strip_modversion = 0; | ||
1731 | + int ignore_proc = 0; | ||
1732 | + int first_time = 0; | ||
1733 | + int use_blacklist = 0; | ||
1734 | + unsigned int i, num_modules; | ||
1735 | + char *type = NULL; | ||
1736 | + const char *config = NULL; | ||
1737 | + char *dirname, *optstring; | ||
1738 | + char *newname = NULL; | ||
1739 | + char *aliasfilename, *symfilename; | ||
1740 | + errfn_t error = fatal; | ||
1741 | + | ||
1742 | + /* Prepend options from environment. */ | ||
1743 | + argv = merge_args(getenv("MODPROBE_OPTIONS"), argv, &argc); | ||
1744 | + | ||
1745 | + /* --set-version overrides version, and disables backwards compat. */ | ||
1746 | + for (opt = 1; opt < argc; opt++) | ||
1747 | + if (strncmp(argv[opt],"--set-version",strlen("--set-version")) | ||
1748 | + == 0) | ||
1749 | + break; | ||
1750 | + | ||
1751 | + if (opt == argc) | ||
1752 | + try_old_version("modprobe", argv); | ||
1753 | + | ||
1754 | + uname(&buf); | ||
1755 | + while ((opt = getopt_long(argc, argv, "vVC:o:rknqQsclt:aifb", options, NULL)) != -1){ | ||
1756 | + switch (opt) { | ||
1757 | + case 'v': | ||
1758 | + add_to_env_var("-v"); | ||
1759 | + verbose = 1; | ||
1760 | + break; | ||
1761 | + case 'V': | ||
1762 | + puts("module-init-tools version 3.2.2"); | ||
1763 | + exit(0); | ||
1764 | + case 'S': | ||
1765 | + strncpy(buf.release, optarg, sizeof(buf.release)); | ||
1766 | + buf.release[sizeof(buf.release)-1] = '\0'; | ||
1767 | + break; | ||
1768 | + case 'C': | ||
1769 | + if (is_devfs_call(argv)) { | ||
1770 | + if (streq("/etc/modules.devfs", optarg)) { | ||
1771 | + config = MODPROBE_DEVFSD_CONF; | ||
1772 | + add_to_env_var("-C"); | ||
1773 | + add_to_env_var(config); | ||
1774 | + /* Fall thru to -q */ | ||
1775 | + } else if (streq("/etc/modules.conf", optarg)) | ||
1776 | + /* Ignore config, fall thru to -q */ | ||
1777 | + ; | ||
1778 | + else { | ||
1779 | + /* False alarm. Treat as normal. */ | ||
1780 | + config = optarg; | ||
1781 | + add_to_env_var("-C"); | ||
1782 | + add_to_env_var(config); | ||
1783 | + break; | ||
1784 | + } | ||
1785 | + } else { | ||
1786 | + config = optarg; | ||
1787 | + add_to_env_var("-C"); | ||
1788 | + add_to_env_var(config); | ||
1789 | + break; | ||
1790 | + } | ||
1791 | + case 'q': | ||
1792 | + unknown_silent = 1; | ||
1793 | + add_to_env_var("-q"); | ||
1794 | + break; | ||
1795 | + case 'D': | ||
1796 | + dry_run = 1; | ||
1797 | + ignore_proc = 1; | ||
1798 | + verbose = 1; | ||
1799 | + add_to_env_var("-D"); | ||
1800 | + break; | ||
1801 | + case 'o': | ||
1802 | + newname = optarg; | ||
1803 | + break; | ||
1804 | + case 'r': | ||
1805 | + remove = 1; | ||
1806 | + break; | ||
1807 | + case 'c': | ||
1808 | + dump_only = 1; | ||
1809 | + break; | ||
1810 | + case 't': | ||
1811 | + type = optarg; | ||
1812 | + break; | ||
1813 | + case 'l': | ||
1814 | + list_only = 1; | ||
1815 | + break; | ||
1816 | + case 'a': | ||
1817 | + all = 1; | ||
1818 | + error = warn; | ||
1819 | + break; | ||
1820 | + case 'k': | ||
1821 | + /* FIXME: This should actually do something */ | ||
1822 | + break; | ||
1823 | + case 'n': | ||
1824 | + dry_run = 1; | ||
1825 | + break; | ||
1826 | + case 's': | ||
1827 | + add_to_env_var("-s"); | ||
1828 | + log = 1; | ||
1829 | + break; | ||
1830 | + case 'i': | ||
1831 | + ignore_commands = 1; | ||
1832 | + break; | ||
1833 | + case 'f': | ||
1834 | + strip_vermagic = 1; | ||
1835 | + strip_modversion = 1; | ||
1836 | + break; | ||
1837 | + case 'b': | ||
1838 | + use_blacklist = 1; | ||
1839 | + break; | ||
1840 | + case 1: | ||
1841 | + strip_vermagic = 1; | ||
1842 | + break; | ||
1843 | + case 2: | ||
1844 | + strip_modversion = 1; | ||
1845 | + break; | ||
1846 | + case 3: | ||
1847 | + first_time = 1; | ||
1848 | + break; | ||
1849 | + default: | ||
1850 | + print_usage(argv[0]); | ||
1851 | + } | ||
1852 | + } | ||
1853 | + | ||
1854 | + /* If stderr not open, go to syslog */ | ||
1855 | + if (log || fstat(STDERR_FILENO, &statbuf) != 0) { | ||
1856 | + openlog("modprobe", LOG_CONS, LOG_DAEMON); | ||
1857 | + log = 1; | ||
1858 | + } | ||
1859 | + | ||
1860 | + if (argc < optind + 1 && !dump_only && !list_only && !remove) | ||
1861 | + print_usage(argv[0]); | ||
1862 | + | ||
1863 | + dirname = NOFAIL(malloc(strlen(buf.release) + sizeof(MODULE_DIR) + 1)); | ||
1864 | + sprintf(dirname, "%s/%s", MODULE_DIR, buf.release); | ||
1865 | + aliasfilename = NOFAIL(malloc(strlen(dirname) | ||
1866 | + + sizeof("/modules.alias"))); | ||
1867 | + sprintf(aliasfilename, "%s/modules.alias", dirname); | ||
1868 | + symfilename = NOFAIL(malloc(strlen(dirname) | ||
1869 | + + sizeof("/modules.symbols"))); | ||
1870 | + sprintf(symfilename, "%s/modules.symbols", dirname); | ||
1871 | + | ||
1872 | + /* Old-style -t xxx wildcard? Only with -l. */ | ||
1873 | + if (list_only) { | ||
1874 | + if (optind+1 < argc) | ||
1875 | + fatal("Can't have multiple wildcards\n"); | ||
1876 | + /* fprintf(stderr, "man find\n"); return 1; */ | ||
1877 | + return do_wildcard(dirname, type, argv[optind]?:"*"); | ||
1878 | + } | ||
1879 | + if (type) | ||
1880 | + fatal("-t only supported with -l"); | ||
1881 | + | ||
1882 | + if (dump_only) { | ||
1883 | + struct module_command *commands = NULL; | ||
1884 | + struct module_options *modoptions = NULL; | ||
1885 | + struct module_alias *aliases = NULL; | ||
1886 | + struct module_blacklist *blacklist = NULL; | ||
1887 | + | ||
1888 | + read_toplevel_config(config, "", 1, 0, | ||
1889 | + &modoptions, &commands, &aliases, &blacklist); | ||
1890 | + read_config(aliasfilename, "", 1, 0,&modoptions, &commands, | ||
1891 | + &aliases, &blacklist); | ||
1892 | + read_config(symfilename, "", 1, 0, &modoptions, &commands, | ||
1893 | + &aliases, &blacklist); | ||
1894 | + exit(0); | ||
1895 | + } | ||
1896 | + | ||
1897 | + if (remove || all) { | ||
1898 | + num_modules = argc - optind; | ||
1899 | + optstring = NOFAIL(strdup("")); | ||
1900 | + } else { | ||
1901 | + num_modules = 1; | ||
1902 | + optstring = gather_options(argv+optind+1); | ||
1903 | + } | ||
1904 | + | ||
1905 | + /* num_modules is always 1 except for -r or -a. */ | ||
1906 | + for (i = 0; i < num_modules; i++) { | ||
1907 | + struct module_command *commands = NULL; | ||
1908 | + struct module_options *modoptions = NULL; | ||
1909 | + struct module_alias *aliases = NULL; | ||
1910 | + struct module_blacklist *blacklist = NULL; | ||
1911 | + LIST_HEAD(list); | ||
1912 | + char *modulearg = argv[optind + i]; | ||
1913 | + | ||
1914 | + /* Convert name we are looking for */ | ||
1915 | + underscores(modulearg); | ||
1916 | + | ||
1917 | + /* Returns the resolved alias, options */ | ||
1918 | + read_toplevel_config(config, modulearg, 0, | ||
1919 | + remove, &modoptions, &commands, &aliases, &blacklist); | ||
1920 | + | ||
1921 | + /* No luck? Try symbol names, if starts with symbol:. */ | ||
1922 | + if (!aliases | ||
1923 | + && strncmp(modulearg, "symbol:", strlen("symbol:")) == 0) | ||
1924 | + read_config(symfilename, modulearg, 0, | ||
1925 | + remove, &modoptions, &commands, | ||
1926 | + &aliases, &blacklist); | ||
1927 | + | ||
1928 | + if (!aliases) { | ||
1929 | + /* We only use canned aliases as last resort. */ | ||
1930 | + read_depends(dirname, modulearg, &list); | ||
1931 | + | ||
1932 | + if (list_empty(&list) | ||
1933 | + && !find_command(modulearg, commands)) | ||
1934 | + { | ||
1935 | + read_config(aliasfilename, modulearg, 0, | ||
1936 | + remove, &modoptions, &commands, | ||
1937 | + &aliases, &blacklist); | ||
1938 | + aliases = apply_blacklist(aliases, blacklist); | ||
1939 | + } | ||
1940 | + } | ||
1941 | + | ||
1942 | + if (aliases) { | ||
1943 | + errfn_t err = error; | ||
1944 | + | ||
1945 | + /* More than one alias? Don't bail out on failure. */ | ||
1946 | + if (aliases->next) | ||
1947 | + err = warn; | ||
1948 | + while (aliases) { | ||
1949 | + /* Add the options for this alias. */ | ||
1950 | + char *opts = NOFAIL(strdup(optstring)); | ||
1951 | + opts = add_extra_options(modulearg, | ||
1952 | + opts, modoptions); | ||
1953 | + | ||
1954 | + read_depends(dirname, aliases->module, &list); | ||
1955 | + handle_module(aliases->module, &list, newname, | ||
1956 | + remove, opts, first_time, err, | ||
1957 | + dry_run, verbose, modoptions, | ||
1958 | + commands, ignore_commands, | ||
1959 | + ignore_proc, strip_vermagic, | ||
1960 | + strip_modversion, | ||
1961 | + unknown_silent, | ||
1962 | + optstring); | ||
1963 | + | ||
1964 | + aliases = aliases->next; | ||
1965 | + INIT_LIST_HEAD(&list); | ||
1966 | + } | ||
1967 | + } else { | ||
1968 | + if (use_blacklist | ||
1969 | + && find_blacklist(modulearg, blacklist)) | ||
1970 | + continue; | ||
1971 | + | ||
1972 | + handle_module(modulearg, &list, newname, remove, | ||
1973 | + optstring, first_time, error, dry_run, | ||
1974 | + verbose, modoptions, commands, | ||
1975 | + ignore_commands, ignore_proc, | ||
1976 | + strip_vermagic, strip_modversion, | ||
1977 | + unknown_silent, optstring); | ||
1978 | + } | ||
1979 | + } | ||
1980 | + if (log) | ||
1981 | + closelog(); | ||
1982 | + | ||
1983 | + return 0; | ||
1984 | +} | ||