diff options
-rw-r--r-- | modutils/Config.in | 14 | ||||
-rw-r--r-- | modutils/depmod.c | 128 |
2 files changed, 121 insertions, 21 deletions
diff --git a/modutils/Config.in b/modutils/Config.in index c69e1b376..fb7fb2266 100644 --- a/modutils/Config.in +++ b/modutils/Config.in | |||
@@ -11,6 +11,20 @@ config DEPMOD | |||
11 | help | 11 | help |
12 | depmod generates modules.dep (FIXME: elaborate) | 12 | depmod generates modules.dep (FIXME: elaborate) |
13 | 13 | ||
14 | config FEATURE_DEPMOD_PRUNE_FANCY | ||
15 | bool "fancy dependency pruning" | ||
16 | default n | ||
17 | depends on DEPMOD | ||
18 | help | ||
19 | By default modules.dep contains all dependencies as listed by | ||
20 | the modules. | ||
21 | If you enable this option then we remove implied modules from | ||
22 | the dependencies. | ||
23 | This makes depmod somewhat bigger but generates a smaller | ||
24 | modules.dep file. | ||
25 | |||
26 | If unsure, say N. | ||
27 | |||
14 | config INSMOD | 28 | config INSMOD |
15 | bool "insmod" | 29 | bool "insmod" |
16 | default n | 30 | default n |
diff --git a/modutils/depmod.c b/modutils/depmod.c index e061501f6..b0b09c235 100644 --- a/modutils/depmod.c +++ b/modutils/depmod.c | |||
@@ -10,15 +10,27 @@ | |||
10 | #define _GNU_SOURCE | 10 | #define _GNU_SOURCE |
11 | #include <libbb.h> | 11 | #include <libbb.h> |
12 | #include <sys/utsname.h> /* uname() */ | 12 | #include <sys/utsname.h> /* uname() */ |
13 | 13 | #if ENABLE_DEBUG | |
14 | #include <assert.h> | ||
15 | #define dbg_assert assert | ||
16 | #else | ||
17 | #define dbg_assert(stuff) do {} while (0) | ||
18 | #endif | ||
14 | /* | 19 | /* |
15 | * Theory of operation: | 20 | * Theory of operation: |
16 | * - iterate over all modules and record their full path | 21 | * - iterate over all modules and record their full path |
17 | * - iterate over all modules looking for "depends=" entries | 22 | * - iterate over all modules looking for "depends=" entries |
18 | * for each depends, look through our list of full paths and emit if found | 23 | * for each depends, look through our list of full paths and emit if found |
19 | */ | 24 | */ |
25 | |||
26 | typedef struct dep_lst_t { | ||
27 | char *name; | ||
28 | llist_t *dependencies; | ||
29 | struct dep_lst_t *next; | ||
30 | } dep_lst_t; | ||
31 | |||
20 | struct globals { | 32 | struct globals { |
21 | llist_t *lst; /* modules without their corresponding extension */ | 33 | dep_lst_t *lst; /* modules without their corresponding extension */ |
22 | size_t moddir_base_len; /* length of the "-b basedir" */ | 34 | size_t moddir_base_len; /* length of the "-b basedir" */ |
23 | }; | 35 | }; |
24 | #define G (*(struct globals*)&bb_common_bufsiz1) | 36 | #define G (*(struct globals*)&bb_common_bufsiz1) |
@@ -28,22 +40,28 @@ struct globals { | |||
28 | static int fill_lst(const char *modulename, struct stat ATTRIBUTE_UNUSED *sb, | 40 | static int fill_lst(const char *modulename, struct stat ATTRIBUTE_UNUSED *sb, |
29 | void ATTRIBUTE_UNUSED *data, int ATTRIBUTE_UNUSED depth) | 41 | void ATTRIBUTE_UNUSED *data, int ATTRIBUTE_UNUSED depth) |
30 | { | 42 | { |
43 | |||
31 | /* We get a file here. If the file does not have ".ko" but an | 44 | /* We get a file here. If the file does not have ".ko" but an |
32 | * intermittent dentry has, it's just their fault. | 45 | * intermittent dentry has, it's just their fault. |
33 | */ | 46 | */ |
34 | if (strrstr(modulename, ".ko") != NULL) | 47 | if (strrstr(modulename, ".ko") != NULL) { |
35 | llist_add_to(&G.lst, xstrdup(modulename + G.moddir_base_len)); | 48 | dep_lst_t *new = xzalloc(sizeof(dep_lst_t)); |
49 | new->name = xstrdup(modulename + G.moddir_base_len); | ||
50 | new->next = G.lst; | ||
51 | G.lst = new; | ||
52 | } | ||
36 | return TRUE; | 53 | return TRUE; |
37 | } | 54 | } |
38 | 55 | ||
39 | static int fileAction(const char *fname, struct stat *sb, | 56 | static int fileAction(const char *fname, struct stat *sb, |
40 | void *data, int ATTRIBUTE_UNUSED depth) | 57 | void ATTRIBUTE_UNUSED *data, int ATTRIBUTE_UNUSED depth) |
41 | { | 58 | { |
42 | size_t len = sb->st_size; | 59 | size_t len = sb->st_size; |
43 | void *the_module; | 60 | void *the_module; |
44 | char *ptr; | 61 | char *ptr; |
45 | int fd; | 62 | int fd; |
46 | char *depends, *deps; | 63 | char *depends, *deps; |
64 | dep_lst_t *this; | ||
47 | 65 | ||
48 | if (strrstr(fname, ".ko") == NULL) /* not a module */ | 66 | if (strrstr(fname, ".ko") == NULL) /* not a module */ |
49 | goto skip; | 67 | goto skip; |
@@ -61,40 +79,44 @@ static int fileAction(const char *fname, struct stat *sb, | |||
61 | close(fd); | 79 | close(fd); |
62 | if (the_module == MAP_FAILED) | 80 | if (the_module == MAP_FAILED) |
63 | bb_perror_msg_and_die("mmap"); | 81 | bb_perror_msg_and_die("mmap"); |
64 | |||
65 | ptr = the_module; | 82 | ptr = the_module; |
66 | 83 | this = G.lst; | |
67 | fprintf((FILE*)data, "%s:", fname + G.moddir_base_len); | 84 | do { |
85 | if (!strcmp(fname + G.moddir_base_len, this->name)) | ||
86 | break; | ||
87 | this = this->next; | ||
88 | } while (this); | ||
89 | dbg_assert (this); | ||
68 | //bb_info_msg("fname='%s'", fname + G.moddir_base_len); | 90 | //bb_info_msg("fname='%s'", fname + G.moddir_base_len); |
69 | do { | 91 | do { |
70 | /* search for a 'd' */ | 92 | /* search for a 'd' */ |
71 | ptr = memchr(ptr, 'd', len - (ptr - (char*)the_module)); | 93 | ptr = memchr(ptr, 'd', len - (ptr - (char*)the_module)); |
72 | if (ptr == NULL) /* no d left, done */ | 94 | if (ptr == NULL) /* no d left, done */ |
73 | goto none; | 95 | goto none; |
74 | if (strncmp(ptr, "depends=", sizeof("depends=")-1) == 0) | 96 | if (!strncmp(ptr, "depends=", sizeof("depends=")-1)) |
75 | break; | 97 | break; |
76 | ++ptr; | 98 | ++ptr; |
77 | } while (1); | 99 | } while (1); |
78 | deps = depends = xstrdup (ptr + sizeof("depends=")-1); | 100 | deps = depends = xstrdup (ptr + sizeof("depends=")-1); |
79 | //bb_info_msg(" depends='%s'", depends); | 101 | //bb_info_msg(" depends='%s'", depends); |
80 | while (deps) { | 102 | while (deps) { |
81 | llist_t * _lst = G.lst; | 103 | dep_lst_t *all = G.lst; |
82 | 104 | ||
83 | ptr = strsep(&deps, ","); | 105 | ptr = strsep(&deps, ","); |
84 | while (_lst) { | 106 | while (all) { |
85 | /* Compare the recorded filenames ignoring ".ko*" at the end. */ | 107 | /* Compare the recorded filenames ignoring ".ko*" at the end. */ |
86 | char *tmp = bb_get_last_path_component_nostrip(_lst->data); | 108 | char *tmp = bb_get_last_path_component_nostrip(all->name); |
87 | if (strncmp(ptr, tmp, strrstr(tmp, ".ko") - tmp) == 0) | 109 | if (!strncmp(ptr, tmp, MAX(strlen(ptr),strrstr(tmp, ".ko") - tmp))) |
88 | break; /* found it */ | 110 | break; /* found it */ |
89 | _lst = _lst->link; | 111 | all = all->next; |
90 | } | 112 | } |
91 | if (_lst) { | 113 | if (all) { |
92 | //bb_info_msg("[%s] -> '%s'", (char*)ptr, _lst->data); | 114 | dbg_assert(all->name); /* this cannot be empty */ |
93 | fprintf((FILE*)data, " %s", _lst->data); | 115 | //bb_info_msg("[%s] -> '%s'", (char*)ptr, all->name); |
116 | llist_add_to_end(&this->dependencies, all->name); | ||
94 | } | 117 | } |
95 | } | 118 | } |
96 | free(depends); | 119 | free(depends); |
97 | fprintf((FILE*)data, "\n"); | ||
98 | none: | 120 | none: |
99 | munmap(the_module, sb->st_size); | 121 | munmap(the_module, sb->st_size); |
100 | skip: | 122 | skip: |
@@ -115,11 +137,14 @@ int depmod_main(int ATTRIBUTE_UNUSED argc, char **argv) | |||
115 | ARG_F = (1<<4), /* System.map that contains the symbols */ | 137 | ARG_F = (1<<4), /* System.map that contains the symbols */ |
116 | ARG_n = (1<<5) /* dry-run, print to stdout only */ | 138 | ARG_n = (1<<5) /* dry-run, print to stdout only */ |
117 | }; | 139 | }; |
140 | INIT_G(); | ||
118 | 141 | ||
119 | getopt32(argv, "aAb:eF:n", &moddir_base, &system_map); | 142 | getopt32(argv, "aAb:eF:n", &moddir_base, &system_map); |
120 | argv += optind; | 143 | argv += optind; |
121 | 144 | ||
122 | /* got a version to use? */ | 145 | /* If a version is provided, then that kernel version’s module directory |
146 | * is used, rather than the current kernel version (as returned by | ||
147 | * "uname -r"). */ | ||
123 | if (*argv && (sscanf(*argv, "%d.%d.%d", &ret, &ret, &ret) == 3)) { | 148 | if (*argv && (sscanf(*argv, "%d.%d.%d", &ret, &ret, &ret) == 3)) { |
124 | moddir = concat_path_file(CONFIG_DEFAULT_MODULES_DIR, *argv++); | 149 | moddir = concat_path_file(CONFIG_DEFAULT_MODULES_DIR, *argv++); |
125 | } else { | 150 | } else { |
@@ -160,6 +185,9 @@ int depmod_main(int ATTRIBUTE_UNUSED argc, char **argv) | |||
160 | else | 185 | else |
161 | return EXIT_FAILURE; | 186 | return EXIT_FAILURE; |
162 | } | 187 | } |
188 | #if ENABLE_FEATURE_CLEAN_UP | ||
189 | else | ||
190 | #endif | ||
163 | do { | 191 | do { |
164 | chp = option_mask32 & ARG_a ? moddir : *argv++; | 192 | chp = option_mask32 & ARG_a ? moddir : *argv++; |
165 | 193 | ||
@@ -167,16 +195,74 @@ int depmod_main(int ATTRIBUTE_UNUSED argc, char **argv) | |||
167 | ACTION_RECURSE, /* flags */ | 195 | ACTION_RECURSE, /* flags */ |
168 | fileAction, /* file action */ | 196 | fileAction, /* file action */ |
169 | NULL, /* dir action */ | 197 | NULL, /* dir action */ |
170 | (void*)filedes, /* user data */ | 198 | NULL, /* user data */ |
171 | 0)) { /* depth */ | 199 | 0)) { /* depth */ |
172 | ret = EXIT_FAILURE; | 200 | ret = EXIT_FAILURE; |
173 | } | 201 | } |
174 | } while (!(option_mask32 & ARG_a) && *argv); | 202 | } while (!(option_mask32 & ARG_a) && *argv); |
175 | 203 | ||
204 | /* modprobe allegedly wants dependencies without duplicates, i.e. | ||
205 | * mod1: mod2 mod3 | ||
206 | * mod2: mod3 | ||
207 | * mod3: | ||
208 | * implies that mod1 directly depends on mod2 and _not_ mod3 as mod3 is | ||
209 | * already implicitely pulled in via mod2. This leaves us with: | ||
210 | * mod1: mod2 | ||
211 | * mod2: mod3 | ||
212 | * mod3: | ||
213 | */ | ||
214 | { | ||
215 | dep_lst_t *mods = G.lst; | ||
216 | #if ENABLE_FEATURE_DEPMOD_PRUNE_FANCY | ||
217 | while (mods) { | ||
218 | llist_t *deps = mods->dependencies; | ||
219 | while (deps) { | ||
220 | dep_lst_t *all = G.lst; | ||
221 | while (all) { | ||
222 | if (!strcmp(all->name, deps->data)) { | ||
223 | llist_t *implied = all->dependencies; | ||
224 | while (implied) { | ||
225 | /* erm, nicer would be to just | ||
226 | * llist_unlink(&mods->dependencies, implied) */ | ||
227 | llist_t *prune = mods->dependencies; | ||
228 | while (prune) { | ||
229 | if (!strcmp(implied->data, prune->data)) | ||
230 | break; | ||
231 | prune = prune->link; | ||
232 | } | ||
233 | //if (prune) bb_info_msg("[%s] '%s' implies '%s', removing", mods->name, all->name, implied->data); | ||
234 | llist_unlink(&mods->dependencies, prune); | ||
235 | implied = implied->link; | ||
236 | } | ||
237 | } | ||
238 | all = all->next; | ||
239 | } | ||
240 | deps = deps->link; | ||
241 | } | ||
242 | mods = mods->next; | ||
243 | } | ||
244 | |||
245 | mods = G.lst; | ||
246 | #endif | ||
247 | /* Finally print them. */ | ||
248 | while (mods) { | ||
249 | fprintf(filedes, "%s:", mods->name); | ||
250 | while (mods->dependencies) | ||
251 | fprintf(filedes, " %s", (char*)llist_pop(&mods->dependencies)); | ||
252 | fprintf(filedes, "\n"); | ||
253 | mods = mods->next; | ||
254 | } | ||
255 | } | ||
256 | |||
176 | if (ENABLE_FEATURE_CLEAN_UP) { | 257 | if (ENABLE_FEATURE_CLEAN_UP) { |
177 | fclose_if_not_stdin(filedes); | 258 | fclose_if_not_stdin(filedes); |
178 | llist_free(G.lst, free); | ||
179 | free(moddir); | 259 | free(moddir); |
260 | while (G.lst) { | ||
261 | dep_lst_t *old = G.lst; | ||
262 | G.lst = G.lst->next; | ||
263 | free(old->name); | ||
264 | free(old); | ||
265 | } | ||
180 | } | 266 | } |
181 | return ret; | 267 | return ret; |
182 | } | 268 | } |