File src/libkmod-config.c added (mode: 100644) (index 0000000..d4f53d6) |
|
1 |
|
/* |
|
2 |
|
* libkmod - interface to kernel module operations |
|
3 |
|
* |
|
4 |
|
* Copyright (C) 2011-2013 ProFUSION embedded systems |
|
5 |
|
* Copyright (C) 2013 Intel Corporation. All rights reserved. |
|
6 |
|
* |
|
7 |
|
* This library is free software; you can redistribute it and/or |
|
8 |
|
* modify it under the terms of the GNU Lesser General Public |
|
9 |
|
* License as published by the Free Software Foundation; either |
|
10 |
|
* version 2.1 of the License, or (at your option) any later version. |
|
11 |
|
* |
|
12 |
|
* This library 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 GNU |
|
15 |
|
* Lesser General Public License for more details. |
|
16 |
|
* |
|
17 |
|
* You should have received a copy of the GNU Lesser General Public |
|
18 |
|
* License along with this library; if not, see <http://www.gnu.org/licenses/>. |
|
19 |
|
*/ |
|
20 |
|
|
|
21 |
|
#include <ctype.h> |
|
22 |
|
#include <dirent.h> |
|
23 |
|
#include <errno.h> |
|
24 |
|
#include <stdarg.h> |
|
25 |
|
#include <stddef.h> |
|
26 |
|
#include <stdio.h> |
|
27 |
|
#include <stdlib.h> |
|
28 |
|
#include <string.h> |
|
29 |
|
#include <unistd.h> |
|
30 |
|
#include <inttypes.h> |
|
31 |
|
#include <limits.h> |
|
32 |
|
#include <stdbool.h> |
|
33 |
|
#include <syslog.h> |
|
34 |
|
#include <sys/stat.h> |
|
35 |
|
#include <sys/types.h> |
|
36 |
|
|
|
37 |
|
#include <libgen.h> |
|
38 |
|
|
|
39 |
|
#include "config.h" |
|
40 |
|
|
|
41 |
|
#include "libkmod.h" |
|
42 |
|
#include "libkmod-namespace.h" |
|
43 |
|
#include "libkmod-paths.h" |
|
44 |
|
#include "shared/macro.h" |
|
45 |
|
#include "shared/util.h" |
|
46 |
|
#define LIBKMOD_CONFIG_C |
|
47 |
|
#include "libkmod-internal.h" |
|
48 |
|
#undef LIBKMOD_CONFIG_C |
|
49 |
|
|
|
50 |
|
struct kmod_alias { |
|
51 |
|
char *name; |
|
52 |
|
char modname[]; |
|
53 |
|
}; |
|
54 |
|
|
|
55 |
|
struct kmod_options { |
|
56 |
|
char *options; |
|
57 |
|
char modname[]; |
|
58 |
|
}; |
|
59 |
|
|
|
60 |
|
struct kmod_command { |
|
61 |
|
char *command; |
|
62 |
|
char modname[]; |
|
63 |
|
}; |
|
64 |
|
|
|
65 |
|
struct kmod_softdep { |
|
66 |
|
char *name; |
|
67 |
|
const char **pre; |
|
68 |
|
const char **post; |
|
69 |
|
unsigned int n_pre; |
|
70 |
|
unsigned int n_post; |
|
71 |
|
}; |
|
72 |
|
|
|
73 |
|
const char *kmod_blacklist_get_modname(const struct kmod_list *l) |
|
74 |
|
{ |
|
75 |
|
return l->data; |
|
76 |
|
} |
|
77 |
|
|
|
78 |
|
const char *kmod_alias_get_name(const struct kmod_list *l) { |
|
79 |
|
const struct kmod_alias *alias = l->data; |
|
80 |
|
return alias->name; |
|
81 |
|
} |
|
82 |
|
|
|
83 |
|
const char *kmod_alias_get_modname(const struct kmod_list *l) { |
|
84 |
|
const struct kmod_alias *alias = l->data; |
|
85 |
|
return alias->modname; |
|
86 |
|
} |
|
87 |
|
|
|
88 |
|
const char *kmod_option_get_options(const struct kmod_list *l) { |
|
89 |
|
const struct kmod_options *alias = l->data; |
|
90 |
|
return alias->options; |
|
91 |
|
} |
|
92 |
|
|
|
93 |
|
const char *kmod_option_get_modname(const struct kmod_list *l) { |
|
94 |
|
const struct kmod_options *alias = l->data; |
|
95 |
|
return alias->modname; |
|
96 |
|
} |
|
97 |
|
|
|
98 |
|
const char *kmod_command_get_command(const struct kmod_list *l) { |
|
99 |
|
const struct kmod_command *alias = l->data; |
|
100 |
|
return alias->command; |
|
101 |
|
} |
|
102 |
|
|
|
103 |
|
const char *kmod_command_get_modname(const struct kmod_list *l) { |
|
104 |
|
const struct kmod_command *alias = l->data; |
|
105 |
|
return alias->modname; |
|
106 |
|
} |
|
107 |
|
|
|
108 |
|
const char *kmod_softdep_get_name(const struct kmod_list *l) { |
|
109 |
|
const struct kmod_softdep *dep = l->data; |
|
110 |
|
return dep->name; |
|
111 |
|
} |
|
112 |
|
|
|
113 |
|
const char * const *kmod_softdep_get_pre(const struct kmod_list *l, unsigned int *count) { |
|
114 |
|
const struct kmod_softdep *dep = l->data; |
|
115 |
|
*count = dep->n_pre; |
|
116 |
|
return dep->pre; |
|
117 |
|
} |
|
118 |
|
|
|
119 |
|
const char * const *kmod_softdep_get_post(const struct kmod_list *l, unsigned int *count) { |
|
120 |
|
const struct kmod_softdep *dep = l->data; |
|
121 |
|
*count = dep->n_post; |
|
122 |
|
return dep->post; |
|
123 |
|
} |
|
124 |
|
|
|
125 |
|
static int kmod_config_add_command(struct kmod_config *config, |
|
126 |
|
const char *modname, |
|
127 |
|
const char *command, |
|
128 |
|
const char *command_name, |
|
129 |
|
struct kmod_list **list) |
|
130 |
|
{ |
|
131 |
|
struct kmod_command *cmd = NULL; |
|
132 |
|
struct kmod_list *l; |
|
133 |
|
size_t modnamelen = strlen(modname) + 1; |
|
134 |
|
size_t commandlen = strlen(command) + 1; |
|
135 |
|
|
|
136 |
|
DBG(config->ctx, "modname='%s' cmd='%s %s'\n", modname, command_name, |
|
137 |
|
command); |
|
138 |
|
|
|
139 |
|
cmd = malloc(sizeof(*cmd) + modnamelen + commandlen); |
|
140 |
|
if (!cmd) |
|
141 |
|
return -ENOMEM; |
|
142 |
|
|
|
143 |
|
cmd->command = sizeof(*cmd) + modnamelen + (char *)cmd; |
|
144 |
|
memcpy(cmd->modname, modname, modnamelen); |
|
145 |
|
memcpy(cmd->command, command, commandlen); |
|
146 |
|
|
|
147 |
|
l = __kmod_list_append(*list, cmd); |
|
148 |
|
if (!l) { |
|
149 |
|
if (cmd) |
|
150 |
|
free(cmd); |
|
151 |
|
return -ENOMEM; |
|
152 |
|
} |
|
153 |
|
|
|
154 |
|
*list = l; |
|
155 |
|
return 0; |
|
156 |
|
} |
|
157 |
|
|
|
158 |
|
static void kmod_config_free_command(struct kmod_config *config, |
|
159 |
|
struct kmod_list *l, |
|
160 |
|
struct kmod_list **list) |
|
161 |
|
{ |
|
162 |
|
struct kmod_command *cmd = l->data; |
|
163 |
|
|
|
164 |
|
free(cmd); |
|
165 |
|
*list = __kmod_list_remove(l); |
|
166 |
|
} |
|
167 |
|
|
|
168 |
|
static int kmod_config_add_options(struct kmod_config *config, |
|
169 |
|
const char *modname, const char *options) |
|
170 |
|
{ |
|
171 |
|
struct kmod_options *opt = NULL; |
|
172 |
|
struct kmod_list *list; |
|
173 |
|
size_t modnamelen = strlen(modname) + 1; |
|
174 |
|
size_t optionslen = strlen(options) + 1; |
|
175 |
|
|
|
176 |
|
DBG(config->ctx, "modname='%s' options='%s'\n", modname, options); |
|
177 |
|
|
|
178 |
|
opt = malloc(sizeof(*opt) + modnamelen + optionslen); |
|
179 |
|
if (!opt) |
|
180 |
|
return -ENOMEM; |
|
181 |
|
|
|
182 |
|
opt->options = sizeof(*opt) + modnamelen + (char *)opt; |
|
183 |
|
|
|
184 |
|
memcpy(opt->modname, modname, modnamelen); |
|
185 |
|
memcpy(opt->options, options, optionslen); |
|
186 |
|
strchr_replace(opt->options, '\t', ' '); |
|
187 |
|
|
|
188 |
|
list = __kmod_list_append(config->options, opt); |
|
189 |
|
if (!list) |
|
190 |
|
return -ENOMEM; |
|
191 |
|
|
|
192 |
|
opt = NULL; |
|
193 |
|
config->options = list; |
|
194 |
|
return 0; |
|
195 |
|
} |
|
196 |
|
|
|
197 |
|
static void kmod_config_free_options(struct kmod_config *config, |
|
198 |
|
struct kmod_list *l) |
|
199 |
|
{ |
|
200 |
|
struct kmod_options *opt = l->data; |
|
201 |
|
|
|
202 |
|
free(opt); |
|
203 |
|
|
|
204 |
|
config->options = __kmod_list_remove(l); |
|
205 |
|
} |
|
206 |
|
|
|
207 |
|
static int kmod_config_add_alias(struct kmod_config *config, |
|
208 |
|
const char *name, const char *modname) |
|
209 |
|
{ |
|
210 |
|
struct kmod_alias *alias; |
|
211 |
|
struct kmod_list *list; |
|
212 |
|
size_t namelen = strlen(name) + 1, modnamelen = strlen(modname) + 1; |
|
213 |
|
|
|
214 |
|
DBG(config->ctx, "name=%s modname=%s\n", name, modname); |
|
215 |
|
|
|
216 |
|
alias = malloc(sizeof(*alias) + namelen + modnamelen); |
|
217 |
|
if (!alias) |
|
218 |
|
return -ENOMEM; |
|
219 |
|
|
|
220 |
|
alias->name = sizeof(*alias) + modnamelen + (char *)alias; |
|
221 |
|
|
|
222 |
|
memcpy(alias->modname, modname, modnamelen); |
|
223 |
|
memcpy(alias->name, name, namelen); |
|
224 |
|
|
|
225 |
|
list = __kmod_list_append(config->aliases, alias); |
|
226 |
|
if (!list) { |
|
227 |
|
if (alias) |
|
228 |
|
free(alias); |
|
229 |
|
return -ENOMEM; |
|
230 |
|
} |
|
231 |
|
|
|
232 |
|
config->aliases = list; |
|
233 |
|
return 0; |
|
234 |
|
} |
|
235 |
|
|
|
236 |
|
static void kmod_config_free_alias(struct kmod_config *config, |
|
237 |
|
struct kmod_list *l) |
|
238 |
|
{ |
|
239 |
|
struct kmod_alias *alias = l->data; |
|
240 |
|
|
|
241 |
|
free(alias); |
|
242 |
|
|
|
243 |
|
config->aliases = __kmod_list_remove(l); |
|
244 |
|
} |
|
245 |
|
|
|
246 |
|
static int kmod_config_add_blacklist(struct kmod_config *config, |
|
247 |
|
const char *modname) |
|
248 |
|
{ |
|
249 |
|
char *p = NULL; |
|
250 |
|
struct kmod_list *list; |
|
251 |
|
|
|
252 |
|
DBG(config->ctx, "modname=%s\n", modname); |
|
253 |
|
|
|
254 |
|
p = strdup(modname); |
|
255 |
|
if (!p) |
|
256 |
|
return -ENOMEM; |
|
257 |
|
|
|
258 |
|
list = __kmod_list_append(config->blacklists, p); |
|
259 |
|
if (!list) { |
|
260 |
|
if (p) |
|
261 |
|
free(p); |
|
262 |
|
return -ENOMEM; |
|
263 |
|
} |
|
264 |
|
|
|
265 |
|
config->blacklists = list; |
|
266 |
|
return 0; |
|
267 |
|
} |
|
268 |
|
|
|
269 |
|
static void kmod_config_free_blacklist(struct kmod_config *config, |
|
270 |
|
struct kmod_list *l) |
|
271 |
|
{ |
|
272 |
|
free(l->data); |
|
273 |
|
config->blacklists = __kmod_list_remove(l); |
|
274 |
|
} |
|
275 |
|
|
|
276 |
|
static int kmod_config_add_softdep(struct kmod_config *config, |
|
277 |
|
const char *modname, |
|
278 |
|
const char *line) |
|
279 |
|
{ |
|
280 |
|
struct kmod_list *list; |
|
281 |
|
struct kmod_softdep *dep; |
|
282 |
|
const char *s, *p; |
|
283 |
|
char *itr; |
|
284 |
|
unsigned int n_pre = 0, n_post = 0; |
|
285 |
|
size_t modnamelen = strlen(modname) + 1; |
|
286 |
|
size_t buflen = 0; |
|
287 |
|
bool was_space = false; |
|
288 |
|
enum { S_NONE, S_PRE, S_POST } mode = S_NONE; |
|
289 |
|
|
|
290 |
|
DBG(config->ctx, "modname=%s\n", modname); |
|
291 |
|
|
|
292 |
|
/* analyze and count */ |
|
293 |
|
for (p = s = line; ; s++) { |
|
294 |
|
size_t plen; |
|
295 |
|
|
|
296 |
|
if (*s != '\0') { |
|
297 |
|
if (!isspace(*s)) { |
|
298 |
|
was_space = false; |
|
299 |
|
continue; |
|
300 |
|
} |
|
301 |
|
|
|
302 |
|
if (was_space) { |
|
303 |
|
p = s + 1; |
|
304 |
|
continue; |
|
305 |
|
} |
|
306 |
|
was_space = true; |
|
307 |
|
|
|
308 |
|
if (p >= s) |
|
309 |
|
continue; |
|
310 |
|
} |
|
311 |
|
plen = s - p; |
|
312 |
|
|
|
313 |
|
if (plen == sizeof("pre:") - 1 && |
|
314 |
|
memcmp(p, "pre:", sizeof("pre:") - 1) == 0) |
|
315 |
|
mode = S_PRE; |
|
316 |
|
else if (plen == sizeof("post:") - 1 && |
|
317 |
|
memcmp(p, "post:", sizeof("post:") - 1) == 0) |
|
318 |
|
mode = S_POST; |
|
319 |
|
else if (*s != '\0' || (*s == '\0' && !was_space)) { |
|
320 |
|
if (mode == S_PRE) { |
|
321 |
|
buflen += plen + 1; |
|
322 |
|
n_pre++; |
|
323 |
|
} else if (mode == S_POST) { |
|
324 |
|
buflen += plen + 1; |
|
325 |
|
n_post++; |
|
326 |
|
} |
|
327 |
|
} |
|
328 |
|
p = s + 1; |
|
329 |
|
if (*s == '\0') |
|
330 |
|
break; |
|
331 |
|
} |
|
332 |
|
|
|
333 |
|
DBG(config->ctx, "%u pre, %u post\n", n_pre, n_post); |
|
334 |
|
|
|
335 |
|
dep = malloc(sizeof(struct kmod_softdep) + modnamelen + |
|
336 |
|
n_pre * sizeof(const char *) + |
|
337 |
|
n_post * sizeof(const char *) + |
|
338 |
|
buflen); |
|
339 |
|
if (dep == NULL) { |
|
340 |
|
ERR(config->ctx, "out-of-memory modname=%s\n", modname); |
|
341 |
|
return -ENOMEM; |
|
342 |
|
} |
|
343 |
|
dep->n_pre = n_pre; |
|
344 |
|
dep->n_post = n_post; |
|
345 |
|
dep->pre = (const char **)((char *)dep + sizeof(struct kmod_softdep)); |
|
346 |
|
dep->post = dep->pre + n_pre; |
|
347 |
|
dep->name = (char *)(dep->post + n_post); |
|
348 |
|
|
|
349 |
|
memcpy(dep->name, modname, modnamelen); |
|
350 |
|
|
|
351 |
|
/* copy strings */ |
|
352 |
|
itr = dep->name + modnamelen; |
|
353 |
|
n_pre = 0; |
|
354 |
|
n_post = 0; |
|
355 |
|
mode = S_NONE; |
|
356 |
|
for (p = s = line; ; s++) { |
|
357 |
|
size_t plen; |
|
358 |
|
|
|
359 |
|
if (*s != '\0') { |
|
360 |
|
if (!isspace(*s)) { |
|
361 |
|
was_space = false; |
|
362 |
|
continue; |
|
363 |
|
} |
|
364 |
|
|
|
365 |
|
if (was_space) { |
|
366 |
|
p = s + 1; |
|
367 |
|
continue; |
|
368 |
|
} |
|
369 |
|
was_space = true; |
|
370 |
|
|
|
371 |
|
if (p >= s) |
|
372 |
|
continue; |
|
373 |
|
} |
|
374 |
|
plen = s - p; |
|
375 |
|
|
|
376 |
|
if (plen == sizeof("pre:") - 1 && |
|
377 |
|
memcmp(p, "pre:", sizeof("pre:") - 1) == 0) |
|
378 |
|
mode = S_PRE; |
|
379 |
|
else if (plen == sizeof("post:") - 1 && |
|
380 |
|
memcmp(p, "post:", sizeof("post:") - 1) == 0) |
|
381 |
|
mode = S_POST; |
|
382 |
|
else if (*s != '\0' || (*s == '\0' && !was_space)) { |
|
383 |
|
if (mode == S_PRE) { |
|
384 |
|
dep->pre[n_pre] = itr; |
|
385 |
|
memcpy(itr, p, plen); |
|
386 |
|
itr[plen] = '\0'; |
|
387 |
|
itr += plen + 1; |
|
388 |
|
n_pre++; |
|
389 |
|
} else if (mode == S_POST) { |
|
390 |
|
dep->post[n_post] = itr; |
|
391 |
|
memcpy(itr, p, plen); |
|
392 |
|
itr[plen] = '\0'; |
|
393 |
|
itr += plen + 1; |
|
394 |
|
n_post++; |
|
395 |
|
} |
|
396 |
|
} |
|
397 |
|
p = s + 1; |
|
398 |
|
if (*s == '\0') |
|
399 |
|
break; |
|
400 |
|
} |
|
401 |
|
|
|
402 |
|
list = __kmod_list_append(config->softdeps, dep); |
|
403 |
|
if (list == NULL) { |
|
404 |
|
free(dep); |
|
405 |
|
return -ENOMEM; |
|
406 |
|
} |
|
407 |
|
config->softdeps = list; |
|
408 |
|
|
|
409 |
|
return 0; |
|
410 |
|
} |
|
411 |
|
|
|
412 |
|
static char *softdep_to_char(struct kmod_softdep *dep) { |
|
413 |
|
const size_t sz_preprefix = sizeof("pre: ") - 1; |
|
414 |
|
const size_t sz_postprefix = sizeof("post: ") - 1; |
|
415 |
|
size_t sz = 1; /* at least '\0' */ |
|
416 |
|
size_t sz_pre, sz_post; |
|
417 |
|
const char *start, *end; |
|
418 |
|
char *s, *itr; |
|
419 |
|
|
|
420 |
|
/* |
|
421 |
|
* Rely on the fact that dep->pre[] and dep->post[] are strv's that |
|
422 |
|
* point to a contiguous buffer |
|
423 |
|
*/ |
|
424 |
|
if (dep->n_pre > 0) { |
|
425 |
|
start = dep->pre[0]; |
|
426 |
|
end = dep->pre[dep->n_pre - 1] |
|
427 |
|
+ strlen(dep->pre[dep->n_pre - 1]); |
|
428 |
|
sz_pre = end - start; |
|
429 |
|
sz += sz_pre + sz_preprefix; |
|
430 |
|
} else |
|
431 |
|
sz_pre = 0; |
|
432 |
|
|
|
433 |
|
if (dep->n_post > 0) { |
|
434 |
|
start = dep->post[0]; |
|
435 |
|
end = dep->post[dep->n_post - 1] |
|
436 |
|
+ strlen(dep->post[dep->n_post - 1]); |
|
437 |
|
sz_post = end - start; |
|
438 |
|
sz += sz_post + sz_postprefix; |
|
439 |
|
} else |
|
440 |
|
sz_post = 0; |
|
441 |
|
|
|
442 |
|
itr = s = malloc(sz); |
|
443 |
|
if (s == NULL) |
|
444 |
|
return NULL; |
|
445 |
|
|
|
446 |
|
if (sz_pre) { |
|
447 |
|
char *p; |
|
448 |
|
|
|
449 |
|
memcpy(itr, "pre: ", sz_preprefix); |
|
450 |
|
itr += sz_preprefix; |
|
451 |
|
|
|
452 |
|
/* include last '\0' */ |
|
453 |
|
memcpy(itr, dep->pre[0], sz_pre + 1); |
|
454 |
|
for (p = itr; p < itr + sz_pre; p++) { |
|
455 |
|
if (*p == '\0') |
|
456 |
|
*p = ' '; |
|
457 |
|
} |
|
458 |
|
itr = p; |
|
459 |
|
} |
|
460 |
|
|
|
461 |
|
if (sz_post) { |
|
462 |
|
char *p; |
|
463 |
|
|
|
464 |
|
memcpy(itr, "post: ", sz_postprefix); |
|
465 |
|
itr += sz_postprefix; |
|
466 |
|
|
|
467 |
|
/* include last '\0' */ |
|
468 |
|
memcpy(itr, dep->post[0], sz_post + 1); |
|
469 |
|
for (p = itr; p < itr + sz_post; p++) { |
|
470 |
|
if (*p == '\0') |
|
471 |
|
*p = ' '; |
|
472 |
|
} |
|
473 |
|
itr = p; |
|
474 |
|
} |
|
475 |
|
|
|
476 |
|
*itr = '\0'; |
|
477 |
|
|
|
478 |
|
return s; |
|
479 |
|
} |
|
480 |
|
|
|
481 |
|
static void kmod_config_free_softdep(struct kmod_config *config, |
|
482 |
|
struct kmod_list *l) |
|
483 |
|
{ |
|
484 |
|
free(l->data); |
|
485 |
|
config->softdeps = __kmod_list_remove(l); |
|
486 |
|
} |
|
487 |
|
|
|
488 |
|
static void kcmdline_parse_result(struct kmod_config *config, char *modname, |
|
489 |
|
char *param, char *value) |
|
490 |
|
{ |
|
491 |
|
if (modname == NULL || param == NULL) |
|
492 |
|
return; |
|
493 |
|
|
|
494 |
|
DBG(config->ctx, "%s %s\n", modname, param); |
|
495 |
|
|
|
496 |
|
if (streq(modname, "modprobe") && !strncmp(param, "blacklist=", 10)) { |
|
497 |
|
for (;;) { |
|
498 |
|
char *t = strsep(&value, ","); |
|
499 |
|
if (t == NULL) |
|
500 |
|
break; |
|
501 |
|
|
|
502 |
|
kmod_config_add_blacklist(config, t); |
|
503 |
|
} |
|
504 |
|
} else { |
|
505 |
|
if (underscores(modname) < 0) { |
|
506 |
|
ERR(config->ctx, "Ignoring bad option on kernel command line while parsing module name: '%s'\n", |
|
507 |
|
modname); |
|
508 |
|
} |
|
509 |
|
kmod_config_add_options(config, modname, param); |
|
510 |
|
} |
|
511 |
|
} |
|
512 |
|
|
|
513 |
|
static int kmod_config_parse_kcmdline(struct kmod_config *config) |
|
514 |
|
{ |
|
515 |
|
char buf[KCMD_LINE_SIZE]; |
|
516 |
|
int fd, err; |
|
517 |
|
char *p, *modname, *param = NULL, *value = NULL; |
|
518 |
|
bool is_quoted = false, is_module = true; |
|
519 |
|
|
|
520 |
|
fd = open("/proc/cmdline", O_RDONLY|O_CLOEXEC); |
|
521 |
|
if (fd < 0) { |
|
522 |
|
err = -errno; |
|
523 |
|
DBG(config->ctx, "could not open '/proc/cmdline' for reading: %m\n"); |
|
524 |
|
return err; |
|
525 |
|
} |
|
526 |
|
|
|
527 |
|
err = read_str_safe(fd, buf, sizeof(buf)); |
|
528 |
|
close(fd); |
|
529 |
|
if (err < 0) { |
|
530 |
|
ERR(config->ctx, "could not read from '/proc/cmdline': %s\n", |
|
531 |
|
strerror(-err)); |
|
532 |
|
return err; |
|
533 |
|
} |
|
534 |
|
|
|
535 |
|
for (p = buf, modname = buf; *p != '\0' && *p != '\n'; p++) { |
|
536 |
|
if (*p == '"') { |
|
537 |
|
is_quoted = !is_quoted; |
|
538 |
|
|
|
539 |
|
if (is_quoted) { |
|
540 |
|
/* don't consider a module until closing quotes */ |
|
541 |
|
is_module = false; |
|
542 |
|
} else if (param != NULL && value != NULL) { |
|
543 |
|
/* |
|
544 |
|
* If we are indeed expecting a value and |
|
545 |
|
* closing quotes, then this can be considered |
|
546 |
|
* a valid option for a module |
|
547 |
|
*/ |
|
548 |
|
is_module = true; |
|
549 |
|
} |
|
550 |
|
|
|
551 |
|
continue; |
|
552 |
|
} |
|
553 |
|
if (is_quoted) |
|
554 |
|
continue; |
|
555 |
|
|
|
556 |
|
switch (*p) { |
|
557 |
|
case ' ': |
|
558 |
|
*p = '\0'; |
|
559 |
|
if (is_module) |
|
560 |
|
kcmdline_parse_result(config, modname, param, value); |
|
561 |
|
param = value = NULL; |
|
562 |
|
modname = p + 1; |
|
563 |
|
is_module = true; |
|
564 |
|
break; |
|
565 |
|
case '.': |
|
566 |
|
if (param == NULL) { |
|
567 |
|
*p = '\0'; |
|
568 |
|
param = p + 1; |
|
569 |
|
} |
|
570 |
|
break; |
|
571 |
|
case '=': |
|
572 |
|
if (param != NULL) |
|
573 |
|
value = p + 1; |
|
574 |
|
else |
|
575 |
|
is_module = false; |
|
576 |
|
break; |
|
577 |
|
} |
|
578 |
|
} |
|
579 |
|
|
|
580 |
|
*p = '\0'; |
|
581 |
|
if (is_module) |
|
582 |
|
kcmdline_parse_result(config, modname, param, value); |
|
583 |
|
|
|
584 |
|
return 0; |
|
585 |
|
} |
|
586 |
|
|
|
587 |
|
/* |
|
588 |
|
* Take an fd and own it. It will be closed on return. filename is used only |
|
589 |
|
* for debug messages |
|
590 |
|
*/ |
|
591 |
|
static int kmod_config_parse(struct kmod_config *config, int fd, |
|
592 |
|
const char *filename) |
|
593 |
|
{ |
|
594 |
|
struct kmod_ctx *ctx = config->ctx; |
|
595 |
|
char *line; |
|
596 |
|
FILE *fp; |
|
597 |
|
unsigned int linenum = 0; |
|
598 |
|
int err; |
|
599 |
|
|
|
600 |
|
fp = fdopen(fd, "r"); |
|
601 |
|
if (fp == NULL) { |
|
602 |
|
err = -errno; |
|
603 |
|
ERR(config->ctx, "fd %d: %m\n", fd); |
|
604 |
|
close(fd); |
|
605 |
|
return err; |
|
606 |
|
} |
|
607 |
|
|
|
608 |
|
while ((line = freadline_wrapped(fp, &linenum)) != NULL) { |
|
609 |
|
char *cmd, *saveptr; |
|
610 |
|
|
|
611 |
|
if (line[0] == '\0' || line[0] == '#') |
|
612 |
|
goto done_next; |
|
613 |
|
|
|
614 |
|
cmd = strtok_r(line, "\t ", &saveptr); |
|
615 |
|
if (cmd == NULL) |
|
616 |
|
goto done_next; |
|
617 |
|
|
|
618 |
|
if (streq(cmd, "alias")) { |
|
619 |
|
char *alias = strtok_r(NULL, "\t ", &saveptr); |
|
620 |
|
char *modname = strtok_r(NULL, "\t ", &saveptr); |
|
621 |
|
|
|
622 |
|
if (underscores(alias) < 0 || underscores(modname) < 0) |
|
623 |
|
goto syntax_error; |
|
624 |
|
|
|
625 |
|
kmod_config_add_alias(config, alias, modname); |
|
626 |
|
} else if (streq(cmd, "blacklist")) { |
|
627 |
|
char *modname = strtok_r(NULL, "\t ", &saveptr); |
|
628 |
|
|
|
629 |
|
if (underscores(modname) < 0) |
|
630 |
|
goto syntax_error; |
|
631 |
|
|
|
632 |
|
kmod_config_add_blacklist(config, modname); |
|
633 |
|
} else if (streq(cmd, "options")) { |
|
634 |
|
char *modname = strtok_r(NULL, "\t ", &saveptr); |
|
635 |
|
char *options = strtok_r(NULL, "\0", &saveptr); |
|
636 |
|
|
|
637 |
|
if (underscores(modname) < 0 || options == NULL) |
|
638 |
|
goto syntax_error; |
|
639 |
|
|
|
640 |
|
kmod_config_add_options(config, modname, options); |
|
641 |
|
} else if (streq(cmd, "install")) { |
|
642 |
|
char *modname = strtok_r(NULL, "\t ", &saveptr); |
|
643 |
|
char *installcmd = strtok_r(NULL, "\0", &saveptr); |
|
644 |
|
|
|
645 |
|
if (underscores(modname) < 0 || installcmd == NULL) |
|
646 |
|
goto syntax_error; |
|
647 |
|
|
|
648 |
|
kmod_config_add_command(config, modname, installcmd, |
|
649 |
|
cmd, &config->install_commands); |
|
650 |
|
} else if (streq(cmd, "remove")) { |
|
651 |
|
char *modname = strtok_r(NULL, "\t ", &saveptr); |
|
652 |
|
char *removecmd = strtok_r(NULL, "\0", &saveptr); |
|
653 |
|
|
|
654 |
|
if (underscores(modname) < 0 || removecmd == NULL) |
|
655 |
|
goto syntax_error; |
|
656 |
|
|
|
657 |
|
kmod_config_add_command(config, modname, removecmd, |
|
658 |
|
cmd, &config->remove_commands); |
|
659 |
|
} else if (streq(cmd, "softdep")) { |
|
660 |
|
char *modname = strtok_r(NULL, "\t ", &saveptr); |
|
661 |
|
char *softdeps = strtok_r(NULL, "\0", &saveptr); |
|
662 |
|
|
|
663 |
|
if (underscores(modname) < 0 || softdeps == NULL) |
|
664 |
|
goto syntax_error; |
|
665 |
|
|
|
666 |
|
kmod_config_add_softdep(config, modname, softdeps); |
|
667 |
|
} else if (streq(cmd, "include") |
|
668 |
|
|| streq(cmd, "config")) { |
|
669 |
|
ERR(ctx, "%s: command %s is deprecated and not parsed anymore\n", |
|
670 |
|
filename, cmd); |
|
671 |
|
} else { |
|
672 |
|
syntax_error: |
|
673 |
|
ERR(ctx, "%s line %u: ignoring bad line starting with '%s'\n", |
|
674 |
|
filename, linenum, cmd); |
|
675 |
|
} |
|
676 |
|
|
|
677 |
|
done_next: |
|
678 |
|
free(line); |
|
679 |
|
} |
|
680 |
|
|
|
681 |
|
fclose(fp); |
|
682 |
|
|
|
683 |
|
return 0; |
|
684 |
|
} |
|
685 |
|
|
|
686 |
|
void kmod_config_free(struct kmod_config *config) |
|
687 |
|
{ |
|
688 |
|
while (config->aliases) |
|
689 |
|
kmod_config_free_alias(config, config->aliases); |
|
690 |
|
|
|
691 |
|
while (config->blacklists) |
|
692 |
|
kmod_config_free_blacklist(config, config->blacklists); |
|
693 |
|
|
|
694 |
|
while (config->options) |
|
695 |
|
kmod_config_free_options(config, config->options); |
|
696 |
|
|
|
697 |
|
while (config->install_commands) { |
|
698 |
|
kmod_config_free_command(config, config->install_commands, |
|
699 |
|
&config->install_commands); |
|
700 |
|
} |
|
701 |
|
|
|
702 |
|
while (config->remove_commands) { |
|
703 |
|
kmod_config_free_command(config, config->remove_commands, |
|
704 |
|
&config->remove_commands); |
|
705 |
|
} |
|
706 |
|
|
|
707 |
|
while (config->softdeps) |
|
708 |
|
kmod_config_free_softdep(config, config->softdeps); |
|
709 |
|
|
|
710 |
|
for (; config->paths != NULL; |
|
711 |
|
config->paths = __kmod_list_remove(config->paths)) |
|
712 |
|
free(config->paths->data); |
|
713 |
|
|
|
714 |
|
free(config); |
|
715 |
|
} |
|
716 |
|
|
|
717 |
|
static bool conf_files_filter_out(struct kmod_ctx *ctx, DIR *d, |
|
718 |
|
const char *path, const char *fn) |
|
719 |
|
{ |
|
720 |
|
size_t len = strlen(fn); |
|
721 |
|
struct stat st; |
|
722 |
|
|
|
723 |
|
if (fn[0] == '.') |
|
724 |
|
return true; |
|
725 |
|
|
|
726 |
|
if (len < 6 || (!streq(&fn[len - 5], ".conf") |
|
727 |
|
&& !streq(&fn[len - 6], ".alias"))) |
|
728 |
|
return true; |
|
729 |
|
|
|
730 |
|
fstatat(dirfd(d), fn, &st, 0); |
|
731 |
|
|
|
732 |
|
if (S_ISDIR(st.st_mode)) { |
|
733 |
|
ERR(ctx, "Directories inside directories are not supported: " |
|
734 |
|
"%s/%s\n", path, fn); |
|
735 |
|
return true; |
|
736 |
|
} |
|
737 |
|
|
|
738 |
|
return false; |
|
739 |
|
} |
|
740 |
|
|
|
741 |
|
struct conf_file { |
|
742 |
|
const char *path; |
|
743 |
|
bool is_single; |
|
744 |
|
char name[]; |
|
745 |
|
}; |
|
746 |
|
|
|
747 |
|
static int conf_files_insert_sorted(struct kmod_ctx *ctx, |
|
748 |
|
struct kmod_list **list, |
|
749 |
|
const char *path, const char *name) |
|
750 |
|
{ |
|
751 |
|
struct kmod_list *lpos, *tmp; |
|
752 |
|
struct conf_file *cf; |
|
753 |
|
size_t namelen; |
|
754 |
|
int cmp = -1; |
|
755 |
|
bool is_single = false; |
|
756 |
|
char *path_cpy=strdup(path); |
|
757 |
|
|
|
758 |
|
if (!path_cpy) |
|
759 |
|
return -ENOMEM; |
|
760 |
|
|
|
761 |
|
if (name == NULL) { |
|
762 |
|
name = basename(path_cpy); |
|
763 |
|
is_single = true; |
|
764 |
|
} |
|
765 |
|
|
|
766 |
|
kmod_list_foreach(lpos, *list) { |
|
767 |
|
cf = lpos->data; |
|
768 |
|
|
|
769 |
|
if ((cmp = strcmp(name, cf->name)) <= 0) |
|
770 |
|
break; |
|
771 |
|
} |
|
772 |
|
|
|
773 |
|
if (cmp == 0) { |
|
774 |
|
DBG(ctx, "Ignoring duplicate config file: %s/%s\n", path, |
|
775 |
|
name); |
|
776 |
|
free(path_cpy); |
|
777 |
|
return -EEXIST; |
|
778 |
|
} |
|
779 |
|
|
|
780 |
|
namelen = strlen(name); |
|
781 |
|
cf = malloc(sizeof(*cf) + namelen + 1); |
|
782 |
|
if (cf == NULL) { |
|
783 |
|
free(path_cpy); |
|
784 |
|
return -ENOMEM; |
|
785 |
|
} |
|
786 |
|
|
|
787 |
|
memcpy(cf->name, name, namelen + 1); |
|
788 |
|
cf->path = path; |
|
789 |
|
cf->is_single = is_single; |
|
790 |
|
|
|
791 |
|
if (lpos == NULL) |
|
792 |
|
tmp = __kmod_list_append(*list, cf); |
|
793 |
|
else if (lpos == *list) |
|
794 |
|
tmp = kmod_list_prepend(*list, cf); |
|
795 |
|
else |
|
796 |
|
tmp = kmod_list_insert_before(lpos, cf); |
|
797 |
|
|
|
798 |
|
if (tmp == NULL) { |
|
799 |
|
free(cf); |
|
800 |
|
free(path_cpy); |
|
801 |
|
return -ENOMEM; |
|
802 |
|
} |
|
803 |
|
|
|
804 |
|
if (lpos == NULL || lpos == *list) |
|
805 |
|
*list = tmp; |
|
806 |
|
free(path_cpy); |
|
807 |
|
return 0; |
|
808 |
|
} |
|
809 |
|
|
|
810 |
|
/* |
|
811 |
|
* Insert configuration files in @list, ignoring duplicates |
|
812 |
|
*/ |
|
813 |
|
static int conf_files_list(struct kmod_ctx *ctx, struct kmod_list **list, |
|
814 |
|
const char *path, |
|
815 |
|
unsigned long long *path_stamp) |
|
816 |
|
{ |
|
817 |
|
DIR *d; |
|
818 |
|
int err; |
|
819 |
|
struct stat st; |
|
820 |
|
struct dirent *dent; |
|
821 |
|
|
|
822 |
|
if (stat(path, &st) != 0) { |
|
823 |
|
err = -errno; |
|
824 |
|
DBG(ctx, "could not stat '%s': %m\n", path); |
|
825 |
|
return err; |
|
826 |
|
} |
|
827 |
|
|
|
828 |
|
*path_stamp = stat_mstamp(&st); |
|
829 |
|
|
|
830 |
|
if (!S_ISDIR(st.st_mode)) { |
|
831 |
|
conf_files_insert_sorted(ctx, list, path, NULL); |
|
832 |
|
return 0; |
|
833 |
|
} |
|
834 |
|
|
|
835 |
|
d = opendir(path); |
|
836 |
|
if (d == NULL) { |
|
837 |
|
ERR(ctx, "opendir(%s): %m\n", path); |
|
838 |
|
return -EINVAL; |
|
839 |
|
} |
|
840 |
|
|
|
841 |
|
for (dent = readdir(d); dent != NULL; dent = readdir(d)) { |
|
842 |
|
if (conf_files_filter_out(ctx, d, path, dent->d_name)) |
|
843 |
|
continue; |
|
844 |
|
|
|
845 |
|
conf_files_insert_sorted(ctx, list, path, dent->d_name); |
|
846 |
|
} |
|
847 |
|
|
|
848 |
|
closedir(d); |
|
849 |
|
return 0; |
|
850 |
|
} |
|
851 |
|
|
|
852 |
|
int kmod_config_new(struct kmod_ctx *ctx, struct kmod_config **p_config, |
|
853 |
|
const char * const *config_paths) |
|
854 |
|
{ |
|
855 |
|
struct kmod_config *config; |
|
856 |
|
struct kmod_list *list = NULL; |
|
857 |
|
struct kmod_list *path_list = NULL; |
|
858 |
|
size_t i; |
|
859 |
|
|
|
860 |
|
conf_files_insert_sorted(ctx, &list, kmod_get_dirname(ctx), "modules.softdep"); |
|
861 |
|
|
|
862 |
|
for (i = 0; config_paths[i] != NULL; i++) { |
|
863 |
|
const char *path = config_paths[i]; |
|
864 |
|
unsigned long long path_stamp = 0; |
|
865 |
|
size_t pathlen; |
|
866 |
|
struct kmod_list *tmp; |
|
867 |
|
struct kmod_config_path *cf; |
|
868 |
|
|
|
869 |
|
if (conf_files_list(ctx, &list, path, &path_stamp) < 0) |
|
870 |
|
continue; |
|
871 |
|
|
|
872 |
|
pathlen = strlen(path) + 1; |
|
873 |
|
cf = malloc(sizeof(*cf) + pathlen); |
|
874 |
|
if (cf == NULL) |
|
875 |
|
goto oom; |
|
876 |
|
|
|
877 |
|
cf->stamp = path_stamp; |
|
878 |
|
memcpy(cf->path, path, pathlen); |
|
879 |
|
|
|
880 |
|
tmp = __kmod_list_append(path_list, cf); |
|
881 |
|
if (tmp == NULL) |
|
882 |
|
goto oom; |
|
883 |
|
path_list = tmp; |
|
884 |
|
} |
|
885 |
|
|
|
886 |
|
*p_config = config = calloc(1, sizeof(struct kmod_config)); |
|
887 |
|
if (config == NULL) |
|
888 |
|
goto oom; |
|
889 |
|
|
|
890 |
|
config->paths = path_list; |
|
891 |
|
config->ctx = ctx; |
|
892 |
|
|
|
893 |
|
for (; list != NULL; list = __kmod_list_remove(list)) { |
|
894 |
|
char buf[PATH_MAX]; |
|
895 |
|
const char *fn = buf; |
|
896 |
|
struct conf_file *cf = list->data; |
|
897 |
|
int fd; |
|
898 |
|
|
|
899 |
|
if (cf->is_single) { |
|
900 |
|
fn = cf->path; |
|
901 |
|
} else if (snprintf(buf, sizeof(buf), "%s/%s", |
|
902 |
|
cf->path, cf->name) >= (int)sizeof(buf)) { |
|
903 |
|
ERR(ctx, "Error parsing %s/%s: path too long\n", |
|
904 |
|
cf->path, cf->name); |
|
905 |
|
free(cf); |
|
906 |
|
continue; |
|
907 |
|
} |
|
908 |
|
|
|
909 |
|
fd = open(fn, O_RDONLY|O_CLOEXEC); |
|
910 |
|
DBG(ctx, "parsing file '%s' fd=%d\n", fn, fd); |
|
911 |
|
|
|
912 |
|
if (fd >= 0) |
|
913 |
|
kmod_config_parse(config, fd, fn); |
|
914 |
|
|
|
915 |
|
free(cf); |
|
916 |
|
} |
|
917 |
|
|
|
918 |
|
kmod_config_parse_kcmdline(config); |
|
919 |
|
|
|
920 |
|
return 0; |
|
921 |
|
|
|
922 |
|
oom: |
|
923 |
|
for (; list != NULL; list = __kmod_list_remove(list)) |
|
924 |
|
free(list->data); |
|
925 |
|
|
|
926 |
|
for (; path_list != NULL; path_list = __kmod_list_remove(path_list)) |
|
927 |
|
free(path_list->data); |
|
928 |
|
|
|
929 |
|
return -ENOMEM; |
|
930 |
|
} |
|
931 |
|
|
|
932 |
|
/********************************************************************** |
|
933 |
|
* struct kmod_config_iter functions |
|
934 |
|
**********************************************************************/ |
|
935 |
|
|
|
936 |
|
enum config_type { |
|
937 |
|
CONFIG_TYPE_BLACKLIST = 0, |
|
938 |
|
CONFIG_TYPE_INSTALL, |
|
939 |
|
CONFIG_TYPE_REMOVE, |
|
940 |
|
CONFIG_TYPE_ALIAS, |
|
941 |
|
CONFIG_TYPE_OPTION, |
|
942 |
|
CONFIG_TYPE_SOFTDEP, |
|
943 |
|
}; |
|
944 |
|
|
|
945 |
|
struct kmod_config_iter { |
|
946 |
|
enum config_type type; |
|
947 |
|
bool intermediate; |
|
948 |
|
const struct kmod_list *list; |
|
949 |
|
const struct kmod_list *curr; |
|
950 |
|
void *data; |
|
951 |
|
const char *(*get_key)(const struct kmod_list *l); |
|
952 |
|
const char *(*get_value)(const struct kmod_list *l); |
|
953 |
|
}; |
|
954 |
|
|
|
955 |
|
static const char *softdep_get_plain_softdep(const struct kmod_list *l) |
|
956 |
|
{ |
|
957 |
|
char *s = softdep_to_char(l->data); |
|
958 |
|
return s; |
|
959 |
|
} |
|
960 |
|
|
|
961 |
|
static struct kmod_config_iter *kmod_config_iter_new(const struct kmod_ctx* ctx, |
|
962 |
|
enum config_type type) |
|
963 |
|
{ |
|
964 |
|
struct kmod_config_iter *iter = calloc(1, sizeof(*iter)); |
|
965 |
|
const struct kmod_config *config = kmod_get_config(ctx); |
|
966 |
|
|
|
967 |
|
if (iter == NULL) |
|
968 |
|
return NULL; |
|
969 |
|
|
|
970 |
|
iter->type = type; |
|
971 |
|
|
|
972 |
|
switch (type) { |
|
973 |
|
case CONFIG_TYPE_BLACKLIST: |
|
974 |
|
iter->list = config->blacklists; |
|
975 |
|
iter->get_key = kmod_blacklist_get_modname; |
|
976 |
|
break; |
|
977 |
|
case CONFIG_TYPE_INSTALL: |
|
978 |
|
iter->list = config->install_commands; |
|
979 |
|
iter->get_key = kmod_command_get_modname; |
|
980 |
|
iter->get_value = kmod_command_get_command; |
|
981 |
|
break; |
|
982 |
|
case CONFIG_TYPE_REMOVE: |
|
983 |
|
iter->list = config->remove_commands; |
|
984 |
|
iter->get_key = kmod_command_get_modname; |
|
985 |
|
iter->get_value = kmod_command_get_command; |
|
986 |
|
break; |
|
987 |
|
case CONFIG_TYPE_ALIAS: |
|
988 |
|
iter->list = config->aliases; |
|
989 |
|
iter->get_key = kmod_alias_get_name; |
|
990 |
|
iter->get_value = kmod_alias_get_modname; |
|
991 |
|
break; |
|
992 |
|
case CONFIG_TYPE_OPTION: |
|
993 |
|
iter->list = config->options; |
|
994 |
|
iter->get_key = kmod_option_get_modname; |
|
995 |
|
iter->get_value = kmod_option_get_options; |
|
996 |
|
break; |
|
997 |
|
case CONFIG_TYPE_SOFTDEP: |
|
998 |
|
iter->list = config->softdeps; |
|
999 |
|
iter->get_key = kmod_softdep_get_name; |
|
1000 |
|
iter->get_value = softdep_get_plain_softdep; |
|
1001 |
|
iter->intermediate = true; |
|
1002 |
|
break; |
|
1003 |
|
} |
|
1004 |
|
|
|
1005 |
|
return iter; |
|
1006 |
|
} |
|
1007 |
|
|
|
1008 |
|
/** |
|
1009 |
|
* SECTION:libkmod-config |
|
1010 |
|
* @short_description: retrieve current libkmod configuration |
|
1011 |
|
*/ |
|
1012 |
|
|
|
1013 |
|
/** |
|
1014 |
|
* kmod_config_get_blacklists: |
|
1015 |
|
* @ctx: kmod library context |
|
1016 |
|
* |
|
1017 |
|
* Retrieve an iterator to deal with the blacklist maintained inside the |
|
1018 |
|
* library. See kmod_config_iter_get_key(), kmod_config_iter_get_value() and |
|
1019 |
|
* kmod_config_iter_next(). At least one call to kmod_config_iter_next() must |
|
1020 |
|
* be made to initialize the iterator and check if it's valid. |
|
1021 |
|
* |
|
1022 |
|
* Returns: a new iterator over the blacklists or NULL on failure. Free it |
|
1023 |
|
* with kmod_config_iter_free_iter(). |
|
1024 |
|
*/ |
|
1025 |
|
struct kmod_config_iter *kmod_config_get_blacklists(const struct kmod_ctx *ctx) |
|
1026 |
|
{ |
|
1027 |
|
if (ctx == NULL) |
|
1028 |
|
return NULL;; |
|
1029 |
|
|
|
1030 |
|
return kmod_config_iter_new(ctx, CONFIG_TYPE_BLACKLIST); |
|
1031 |
|
} |
|
1032 |
|
|
|
1033 |
|
/** |
|
1034 |
|
* kmod_config_get_install_commands: |
|
1035 |
|
* @ctx: kmod library context |
|
1036 |
|
* |
|
1037 |
|
* Retrieve an iterator to deal with the install commands maintained inside the |
|
1038 |
|
* library. See kmod_config_iter_get_key(), kmod_config_iter_get_value() and |
|
1039 |
|
* kmod_config_iter_next(). At least one call to kmod_config_iter_next() must |
|
1040 |
|
* be made to initialize the iterator and check if it's valid. |
|
1041 |
|
* |
|
1042 |
|
* Returns: a new iterator over the install commands or NULL on failure. Free |
|
1043 |
|
* it with kmod_config_iter_free_iter(). |
|
1044 |
|
*/ |
|
1045 |
|
struct kmod_config_iter *kmod_config_get_install_commands(const struct kmod_ctx *ctx) |
|
1046 |
|
{ |
|
1047 |
|
if (ctx == NULL) |
|
1048 |
|
return NULL;; |
|
1049 |
|
|
|
1050 |
|
return kmod_config_iter_new(ctx, CONFIG_TYPE_INSTALL); |
|
1051 |
|
} |
|
1052 |
|
|
|
1053 |
|
/** |
|
1054 |
|
* kmod_config_get_remove_commands: |
|
1055 |
|
* @ctx: kmod library context |
|
1056 |
|
* |
|
1057 |
|
* Retrieve an iterator to deal with the remove commands maintained inside the |
|
1058 |
|
* library. See kmod_config_iter_get_key(), kmod_config_iter_get_value() and |
|
1059 |
|
* kmod_config_iter_next(). At least one call to kmod_config_iter_next() must |
|
1060 |
|
* be made to initialize the iterator and check if it's valid. |
|
1061 |
|
* |
|
1062 |
|
* Returns: a new iterator over the remove commands or NULL on failure. Free |
|
1063 |
|
* it with kmod_config_iter_free_iter(). |
|
1064 |
|
*/ |
|
1065 |
|
struct kmod_config_iter *kmod_config_get_remove_commands(const struct kmod_ctx *ctx) |
|
1066 |
|
{ |
|
1067 |
|
if (ctx == NULL) |
|
1068 |
|
return NULL;; |
|
1069 |
|
|
|
1070 |
|
return kmod_config_iter_new(ctx, CONFIG_TYPE_REMOVE); |
|
1071 |
|
} |
|
1072 |
|
|
|
1073 |
|
/** |
|
1074 |
|
* kmod_config_get_aliases: |
|
1075 |
|
* @ctx: kmod library context |
|
1076 |
|
* |
|
1077 |
|
* Retrieve an iterator to deal with the aliases maintained inside the |
|
1078 |
|
* library. See kmod_config_iter_get_key(), kmod_config_iter_get_value() and |
|
1079 |
|
* kmod_config_iter_next(). At least one call to kmod_config_iter_next() must |
|
1080 |
|
* be made to initialize the iterator and check if it's valid. |
|
1081 |
|
* |
|
1082 |
|
* Returns: a new iterator over the aliases or NULL on failure. Free it with |
|
1083 |
|
* kmod_config_iter_free_iter(). |
|
1084 |
|
*/ |
|
1085 |
|
struct kmod_config_iter *kmod_config_get_aliases(const struct kmod_ctx *ctx) |
|
1086 |
|
{ |
|
1087 |
|
if (ctx == NULL) |
|
1088 |
|
return NULL;; |
|
1089 |
|
|
|
1090 |
|
return kmod_config_iter_new(ctx, CONFIG_TYPE_ALIAS); |
|
1091 |
|
} |
|
1092 |
|
|
|
1093 |
|
/** |
|
1094 |
|
* kmod_config_get_options: |
|
1095 |
|
* @ctx: kmod library context |
|
1096 |
|
* |
|
1097 |
|
* Retrieve an iterator to deal with the options maintained inside the |
|
1098 |
|
* library. See kmod_config_iter_get_key(), kmod_config_iter_get_value() and |
|
1099 |
|
* kmod_config_iter_next(). At least one call to kmod_config_iter_next() must |
|
1100 |
|
* be made to initialize the iterator and check if it's valid. |
|
1101 |
|
* |
|
1102 |
|
* Returns: a new iterator over the options or NULL on failure. Free it with |
|
1103 |
|
* kmod_config_iter_free_iter(). |
|
1104 |
|
*/ |
|
1105 |
|
struct kmod_config_iter *kmod_config_get_options(const struct kmod_ctx *ctx) |
|
1106 |
|
{ |
|
1107 |
|
if (ctx == NULL) |
|
1108 |
|
return NULL;; |
|
1109 |
|
|
|
1110 |
|
return kmod_config_iter_new(ctx, CONFIG_TYPE_OPTION); |
|
1111 |
|
} |
|
1112 |
|
|
|
1113 |
|
/** |
|
1114 |
|
* kmod_config_get_softdeps: |
|
1115 |
|
* @ctx: kmod library context |
|
1116 |
|
* |
|
1117 |
|
* Retrieve an iterator to deal with the softdeps maintained inside the |
|
1118 |
|
* library. See kmod_config_iter_get_key(), kmod_config_iter_get_value() and |
|
1119 |
|
* kmod_config_iter_next(). At least one call to kmod_config_iter_next() must |
|
1120 |
|
* be made to initialize the iterator and check if it's valid. |
|
1121 |
|
* |
|
1122 |
|
* Returns: a new iterator over the softdeps or NULL on failure. Free it with |
|
1123 |
|
* kmod_config_iter_free_iter(). |
|
1124 |
|
*/ |
|
1125 |
|
struct kmod_config_iter *kmod_config_get_softdeps(const struct kmod_ctx *ctx) |
|
1126 |
|
{ |
|
1127 |
|
if (ctx == NULL) |
|
1128 |
|
return NULL;; |
|
1129 |
|
|
|
1130 |
|
return kmod_config_iter_new(ctx, CONFIG_TYPE_SOFTDEP); |
|
1131 |
|
} |
|
1132 |
|
|
|
1133 |
|
/** |
|
1134 |
|
* kmod_config_iter_get_key: |
|
1135 |
|
* @iter: iterator over a certain configuration |
|
1136 |
|
* |
|
1137 |
|
* When using a new allocated iterator, user must perform a call to |
|
1138 |
|
* kmod_config_iter_next() to initialize iterator's position and check if it's |
|
1139 |
|
* valid. |
|
1140 |
|
* |
|
1141 |
|
* Returns: the key of the current configuration pointed by @iter. |
|
1142 |
|
*/ |
|
1143 |
|
const char *kmod_config_iter_get_key(const struct kmod_config_iter *iter) |
|
1144 |
|
{ |
|
1145 |
|
if (iter == NULL || iter->curr == NULL) |
|
1146 |
|
return NULL; |
|
1147 |
|
|
|
1148 |
|
return iter->get_key(iter->curr); |
|
1149 |
|
} |
|
1150 |
|
|
|
1151 |
|
/** |
|
1152 |
|
* kmod_config_iter_get_value: |
|
1153 |
|
* @iter: iterator over a certain configuration |
|
1154 |
|
* |
|
1155 |
|
* When using a new allocated iterator, user must perform a call to |
|
1156 |
|
* kmod_config_iter_next() to initialize iterator's position and check if it's |
|
1157 |
|
* valid. |
|
1158 |
|
* |
|
1159 |
|
* Returns: the value of the current configuration pointed by @iter. |
|
1160 |
|
*/ |
|
1161 |
|
const char *kmod_config_iter_get_value(const struct kmod_config_iter *iter) |
|
1162 |
|
{ |
|
1163 |
|
const char *s; |
|
1164 |
|
|
|
1165 |
|
if (iter == NULL || iter->curr == NULL) |
|
1166 |
|
return NULL; |
|
1167 |
|
|
|
1168 |
|
if (iter->get_value == NULL) |
|
1169 |
|
return NULL; |
|
1170 |
|
|
|
1171 |
|
if (iter->intermediate) { |
|
1172 |
|
struct kmod_config_iter *i = (struct kmod_config_iter *)iter; |
|
1173 |
|
|
|
1174 |
|
free(i->data); |
|
1175 |
|
s = i->data = (void *) iter->get_value(iter->curr); |
|
1176 |
|
} else |
|
1177 |
|
s = iter->get_value(iter->curr); |
|
1178 |
|
|
|
1179 |
|
return s; |
|
1180 |
|
} |
|
1181 |
|
|
|
1182 |
|
/** |
|
1183 |
|
* kmod_config_iter_next: |
|
1184 |
|
* @iter: iterator over a certain configuration |
|
1185 |
|
* |
|
1186 |
|
* Make @iter point to the next item of a certain configuration. It's an |
|
1187 |
|
* automatically recycling iterator. When it reaches the end, false is |
|
1188 |
|
* returned; then if user wants to iterate again, it's sufficient to call this |
|
1189 |
|
* function once more. |
|
1190 |
|
* |
|
1191 |
|
* Returns: true if next position of @iter is valid or false if its end is |
|
1192 |
|
* reached. |
|
1193 |
|
*/ |
|
1194 |
|
bool kmod_config_iter_next(struct kmod_config_iter *iter) |
|
1195 |
|
{ |
|
1196 |
|
if (iter == NULL) |
|
1197 |
|
return false; |
|
1198 |
|
|
|
1199 |
|
if (iter->curr == NULL) { |
|
1200 |
|
iter->curr = iter->list; |
|
1201 |
|
return iter->curr != NULL; |
|
1202 |
|
} |
|
1203 |
|
|
|
1204 |
|
iter->curr = kmod_list_next(iter->list, iter->curr); |
|
1205 |
|
|
|
1206 |
|
return iter->curr != NULL; |
|
1207 |
|
} |
|
1208 |
|
|
|
1209 |
|
/** |
|
1210 |
|
* kmod_config_iter_free_iter: |
|
1211 |
|
* @iter: iterator over a certain configuration |
|
1212 |
|
* |
|
1213 |
|
* Free resources used by the iterator. |
|
1214 |
|
*/ |
|
1215 |
|
void kmod_config_iter_free_iter(struct kmod_config_iter *iter) |
|
1216 |
|
{ |
|
1217 |
|
free(iter->data); |
|
1218 |
|
free(iter); |
|
1219 |
|
} |
File src/libkmod-elf.c added (mode: 100644) (index 0000000..5aec464) |
|
1 |
|
/* |
|
2 |
|
* libkmod - interface to kernel module operations |
|
3 |
|
* |
|
4 |
|
* Copyright (C) 2011-2013 ProFUSION embedded systems |
|
5 |
|
* |
|
6 |
|
* This library is free software; you can redistribute it and/or |
|
7 |
|
* modify it under the terms of the GNU Lesser General Public |
|
8 |
|
* License as published by the Free Software Foundation; either |
|
9 |
|
* version 2.1 of the License, or (at your option) any later version. |
|
10 |
|
* |
|
11 |
|
* This library is distributed in the hope that it will be useful, |
|
12 |
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
13 |
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
|
14 |
|
* Lesser General Public License for more details. |
|
15 |
|
* |
|
16 |
|
* You should have received a copy of the GNU Lesser General Public |
|
17 |
|
* License along with this library; if not, see <http://www.gnu.org/licenses/>. |
|
18 |
|
*/ |
|
19 |
|
|
|
20 |
|
#include <assert.h> |
|
21 |
|
#include <elf.h> |
|
22 |
|
#include <errno.h> |
|
23 |
|
#include <stdlib.h> |
|
24 |
|
#include <string.h> |
|
25 |
|
#include <limits.h> |
|
26 |
|
#include <stddef.h> |
|
27 |
|
#include <stdio.h> |
|
28 |
|
|
|
29 |
|
#include "config.h" |
|
30 |
|
|
|
31 |
|
#include "libkmod.h" |
|
32 |
|
#include "libkmod-namespace.h" |
|
33 |
|
#include "libkmod-paths.h" |
|
34 |
|
#include "shared/macro.h" |
|
35 |
|
#include "shared/util.h" |
|
36 |
|
#define LIBKMOD_ELF_C |
|
37 |
|
#include "libkmod-internal.h" |
|
38 |
|
#undef LIBKMOD_ELF_C |
|
39 |
|
|
|
40 |
|
enum kmod_elf_class { |
|
41 |
|
KMOD_ELF_32 = (1 << 1), |
|
42 |
|
KMOD_ELF_64 = (1 << 2), |
|
43 |
|
KMOD_ELF_LSB = (1 << 3), |
|
44 |
|
KMOD_ELF_MSB = (1 << 4) |
|
45 |
|
}; |
|
46 |
|
|
|
47 |
|
/* as defined in module-init-tools */ |
|
48 |
|
struct kmod_modversion32 { |
|
49 |
|
uint32_t crc; |
|
50 |
|
char name[64 - sizeof(uint32_t)]; |
|
51 |
|
}; |
|
52 |
|
|
|
53 |
|
struct kmod_modversion64 { |
|
54 |
|
uint64_t crc; |
|
55 |
|
char name[64 - sizeof(uint64_t)]; |
|
56 |
|
}; |
|
57 |
|
|
|
58 |
|
struct kmod_elf { |
|
59 |
|
const uint8_t *memory; |
|
60 |
|
uint8_t *changed; |
|
61 |
|
uint64_t size; |
|
62 |
|
enum kmod_elf_class class; |
|
63 |
|
struct kmod_elf_header { |
|
64 |
|
struct { |
|
65 |
|
uint64_t offset; |
|
66 |
|
uint16_t count; |
|
67 |
|
uint16_t entry_size; |
|
68 |
|
} section; |
|
69 |
|
struct { |
|
70 |
|
uint16_t section; /* index of the strings section */ |
|
71 |
|
uint64_t size; |
|
72 |
|
uint64_t offset; |
|
73 |
|
uint32_t nameoff; /* offset in strings itself */ |
|
74 |
|
} strings; |
|
75 |
|
uint16_t machine; |
|
76 |
|
} header; |
|
77 |
|
}; |
|
78 |
|
|
|
79 |
|
//#define ENABLE_ELFDBG 1 |
|
80 |
|
|
|
81 |
|
#if defined(ENABLE_LOGGING) && defined(ENABLE_ELFDBG) |
|
82 |
|
#define ELFDBG(elf, ...) \ |
|
83 |
|
_elf_dbg(elf, __FILE__, __LINE__, __func__, __VA_ARGS__); |
|
84 |
|
|
|
85 |
|
static inline void _elf_dbg(const struct kmod_elf *elf, const char *fname, unsigned line, const char *func, const char *fmt, ...) |
|
86 |
|
{ |
|
87 |
|
va_list args; |
|
88 |
|
|
|
89 |
|
fprintf(stderr, "ELFDBG-%d%c: %s:%u %s() ", |
|
90 |
|
(elf->class & KMOD_ELF_32) ? 32 : 64, |
|
91 |
|
(elf->class & KMOD_ELF_MSB) ? 'M' : 'L', |
|
92 |
|
fname, line, func); |
|
93 |
|
va_start(args, fmt); |
|
94 |
|
vfprintf(stderr, fmt, args); |
|
95 |
|
va_end(args); |
|
96 |
|
} |
|
97 |
|
#else |
|
98 |
|
#define ELFDBG(elf, ...) |
|
99 |
|
#endif |
|
100 |
|
|
|
101 |
|
|
|
102 |
|
static int elf_identify(const void *memory, uint64_t size) |
|
103 |
|
{ |
|
104 |
|
const uint8_t *p = memory; |
|
105 |
|
int class = 0; |
|
106 |
|
|
|
107 |
|
if (size <= EI_NIDENT || memcmp(p, ELFMAG, SELFMAG) != 0) |
|
108 |
|
return -ENOEXEC; |
|
109 |
|
|
|
110 |
|
switch (p[EI_CLASS]) { |
|
111 |
|
case ELFCLASS32: |
|
112 |
|
if (size <= sizeof(Elf32_Ehdr)) |
|
113 |
|
return -EINVAL; |
|
114 |
|
class |= KMOD_ELF_32; |
|
115 |
|
break; |
|
116 |
|
case ELFCLASS64: |
|
117 |
|
if (size <= sizeof(Elf64_Ehdr)) |
|
118 |
|
return -EINVAL; |
|
119 |
|
class |= KMOD_ELF_64; |
|
120 |
|
break; |
|
121 |
|
default: |
|
122 |
|
return -EINVAL; |
|
123 |
|
} |
|
124 |
|
|
|
125 |
|
switch (p[EI_DATA]) { |
|
126 |
|
case ELFDATA2LSB: |
|
127 |
|
class |= KMOD_ELF_LSB; |
|
128 |
|
break; |
|
129 |
|
case ELFDATA2MSB: |
|
130 |
|
class |= KMOD_ELF_MSB; |
|
131 |
|
break; |
|
132 |
|
default: |
|
133 |
|
return -EINVAL; |
|
134 |
|
} |
|
135 |
|
|
|
136 |
|
return class; |
|
137 |
|
} |
|
138 |
|
|
|
139 |
|
static inline uint64_t elf_get_uint(const struct kmod_elf *elf, uint64_t offset, uint16_t size) |
|
140 |
|
{ |
|
141 |
|
const uint8_t *p; |
|
142 |
|
uint64_t ret = 0; |
|
143 |
|
size_t i; |
|
144 |
|
|
|
145 |
|
assert(size <= sizeof(uint64_t)); |
|
146 |
|
assert(offset + size <= elf->size); |
|
147 |
|
if (offset + size > elf->size) { |
|
148 |
|
ELFDBG(elf, "out of bounds: %"PRIu64" + %"PRIu16" = %"PRIu64"> %"PRIu64" (ELF size)\n", |
|
149 |
|
offset, size, offset + size, elf->size); |
|
150 |
|
return (uint64_t)-1; |
|
151 |
|
} |
|
152 |
|
|
|
153 |
|
p = elf->memory + offset; |
|
154 |
|
if (elf->class & KMOD_ELF_MSB) { |
|
155 |
|
for (i = 0; i < size; i++) |
|
156 |
|
ret = (ret << 8) | p[i]; |
|
157 |
|
} else { |
|
158 |
|
for (i = 1; i <= size; i++) |
|
159 |
|
ret = (ret << 8) | p[size - i]; |
|
160 |
|
} |
|
161 |
|
|
|
162 |
|
ELFDBG(elf, "size=%"PRIu16" offset=%"PRIu64" value=%"PRIu64"\n", |
|
163 |
|
size, offset, ret); |
|
164 |
|
|
|
165 |
|
return ret; |
|
166 |
|
} |
|
167 |
|
|
|
168 |
|
static inline int elf_set_uint(struct kmod_elf *elf, uint64_t offset, uint64_t size, uint64_t value) |
|
169 |
|
{ |
|
170 |
|
uint8_t *p; |
|
171 |
|
size_t i; |
|
172 |
|
|
|
173 |
|
ELFDBG(elf, "size=%"PRIu16" offset=%"PRIu64" value=%"PRIu64" write memory=%p\n", |
|
174 |
|
size, offset, value, elf->changed); |
|
175 |
|
|
|
176 |
|
assert(size <= sizeof(uint64_t)); |
|
177 |
|
assert(offset + size <= elf->size); |
|
178 |
|
if (offset + size > elf->size) { |
|
179 |
|
ELFDBG(elf, "out of bounds: %"PRIu64" + %"PRIu16" = %"PRIu64"> %"PRIu64" (ELF size)\n", |
|
180 |
|
offset, size, offset + size, elf->size); |
|
181 |
|
return -1; |
|
182 |
|
} |
|
183 |
|
|
|
184 |
|
if (elf->changed == NULL) { |
|
185 |
|
elf->changed = malloc(elf->size); |
|
186 |
|
if (elf->changed == NULL) |
|
187 |
|
return -errno; |
|
188 |
|
memcpy(elf->changed, elf->memory, elf->size); |
|
189 |
|
elf->memory = elf->changed; |
|
190 |
|
ELFDBG(elf, "copied memory to allow writing.\n"); |
|
191 |
|
} |
|
192 |
|
|
|
193 |
|
p = elf->changed + offset; |
|
194 |
|
if (elf->class & KMOD_ELF_MSB) { |
|
195 |
|
for (i = 1; i <= size; i++) { |
|
196 |
|
p[size - i] = value & 0xff; |
|
197 |
|
value = (value & 0xffffffffffffff00) >> 8; |
|
198 |
|
} |
|
199 |
|
} else { |
|
200 |
|
for (i = 0; i < size; i++) { |
|
201 |
|
p[i] = value & 0xff; |
|
202 |
|
value = (value & 0xffffffffffffff00) >> 8; |
|
203 |
|
} |
|
204 |
|
} |
|
205 |
|
|
|
206 |
|
return 0; |
|
207 |
|
} |
|
208 |
|
|
|
209 |
|
static inline const void *elf_get_mem(const struct kmod_elf *elf, uint64_t offset) |
|
210 |
|
{ |
|
211 |
|
assert(offset < elf->size); |
|
212 |
|
if (offset >= elf->size) { |
|
213 |
|
ELFDBG(elf, "out-of-bounds: %"PRIu64" >= %"PRIu64" (ELF size)\n", |
|
214 |
|
offset, elf->size); |
|
215 |
|
return NULL; |
|
216 |
|
} |
|
217 |
|
return elf->memory + offset; |
|
218 |
|
} |
|
219 |
|
|
|
220 |
|
static inline const void *elf_get_section_header(const struct kmod_elf *elf, uint16_t idx) |
|
221 |
|
{ |
|
222 |
|
assert(idx != SHN_UNDEF); |
|
223 |
|
assert(idx < elf->header.section.count); |
|
224 |
|
if (idx == SHN_UNDEF || idx >= elf->header.section.count) { |
|
225 |
|
ELFDBG(elf, "invalid section number: %"PRIu16", last=%"PRIu16"\n", |
|
226 |
|
idx, elf->header.section.count); |
|
227 |
|
return NULL; |
|
228 |
|
} |
|
229 |
|
return elf_get_mem(elf, elf->header.section.offset + |
|
230 |
|
(uint64_t)(idx * elf->header.section.entry_size)); |
|
231 |
|
} |
|
232 |
|
|
|
233 |
|
static inline int elf_get_section_info(const struct kmod_elf *elf, uint16_t idx, uint64_t *offset, uint64_t *size, uint32_t *nameoff) |
|
234 |
|
{ |
|
235 |
|
const uint8_t *p = elf_get_section_header(elf, idx); |
|
236 |
|
uint64_t min_size, off = p - elf->memory; |
|
237 |
|
|
|
238 |
|
if (p == NULL) { |
|
239 |
|
ELFDBG(elf, "no section at %"PRIu16"\n", idx); |
|
240 |
|
*offset = 0; |
|
241 |
|
*size = 0; |
|
242 |
|
*nameoff = 0; |
|
243 |
|
return -EINVAL; |
|
244 |
|
} |
|
245 |
|
|
|
246 |
|
#define READV(field) \ |
|
247 |
|
elf_get_uint(elf, off + offsetof(typeof(*hdr), field), sizeof(hdr->field)) |
|
248 |
|
|
|
249 |
|
if (elf->class & KMOD_ELF_32) { |
|
250 |
|
const Elf32_Shdr *hdr _unused_ = (const Elf32_Shdr *)p; |
|
251 |
|
*size = READV(sh_size); |
|
252 |
|
*offset = READV(sh_offset); |
|
253 |
|
*nameoff = READV(sh_name); |
|
254 |
|
} else { |
|
255 |
|
const Elf64_Shdr *hdr _unused_ = (const Elf64_Shdr *)p; |
|
256 |
|
*size = READV(sh_size); |
|
257 |
|
*offset = READV(sh_offset); |
|
258 |
|
*nameoff = READV(sh_name); |
|
259 |
|
} |
|
260 |
|
#undef READV |
|
261 |
|
|
|
262 |
|
if (addu64_overflow(*offset, *size, &min_size) |
|
263 |
|
|| min_size > elf->size) { |
|
264 |
|
ELFDBG(elf, "out-of-bounds: %"PRIu64" >= %"PRIu64" (ELF size)\n", |
|
265 |
|
min_size, elf->size); |
|
266 |
|
return -EINVAL; |
|
267 |
|
} |
|
268 |
|
|
|
269 |
|
ELFDBG(elf, "section=%"PRIu16" is: offset=%"PRIu64" size=%"PRIu64" nameoff=%"PRIu32"\n", |
|
270 |
|
idx, *offset, *size, *nameoff); |
|
271 |
|
|
|
272 |
|
return 0; |
|
273 |
|
} |
|
274 |
|
|
|
275 |
|
static const char *elf_get_strings_section(const struct kmod_elf *elf, uint64_t *size) |
|
276 |
|
{ |
|
277 |
|
*size = elf->header.strings.size; |
|
278 |
|
return elf_get_mem(elf, elf->header.strings.offset); |
|
279 |
|
} |
|
280 |
|
|
|
281 |
|
struct kmod_elf *kmod_elf_new(const void *memory, off_t size) |
|
282 |
|
{ |
|
283 |
|
struct kmod_elf *elf; |
|
284 |
|
uint64_t min_size; |
|
285 |
|
size_t shdrs_size, shdr_size; |
|
286 |
|
int class; |
|
287 |
|
|
|
288 |
|
class = elf_identify(memory, size); |
|
289 |
|
if (class < 0) { |
|
290 |
|
errno = -class; |
|
291 |
|
return NULL; |
|
292 |
|
} |
|
293 |
|
|
|
294 |
|
elf = malloc(sizeof(struct kmod_elf)); |
|
295 |
|
if (elf == NULL) { |
|
296 |
|
return NULL; |
|
297 |
|
} |
|
298 |
|
|
|
299 |
|
elf->memory = memory; |
|
300 |
|
elf->changed = NULL; |
|
301 |
|
elf->size = size; |
|
302 |
|
elf->class = class; |
|
303 |
|
|
|
304 |
|
#define READV(field) \ |
|
305 |
|
elf_get_uint(elf, offsetof(typeof(*hdr), field), sizeof(hdr->field)) |
|
306 |
|
|
|
307 |
|
#define LOAD_HEADER \ |
|
308 |
|
elf->header.section.offset = READV(e_shoff); \ |
|
309 |
|
elf->header.section.count = READV(e_shnum); \ |
|
310 |
|
elf->header.section.entry_size = READV(e_shentsize); \ |
|
311 |
|
elf->header.strings.section = READV(e_shstrndx); \ |
|
312 |
|
elf->header.machine = READV(e_machine) |
|
313 |
|
if (elf->class & KMOD_ELF_32) { |
|
314 |
|
const Elf32_Ehdr *hdr _unused_ = elf_get_mem(elf, 0); |
|
315 |
|
LOAD_HEADER; |
|
316 |
|
shdr_size = sizeof(Elf32_Shdr); |
|
317 |
|
} else { |
|
318 |
|
const Elf64_Ehdr *hdr _unused_ = elf_get_mem(elf, 0); |
|
319 |
|
LOAD_HEADER; |
|
320 |
|
shdr_size = sizeof(Elf64_Shdr); |
|
321 |
|
} |
|
322 |
|
#undef LOAD_HEADER |
|
323 |
|
#undef READV |
|
324 |
|
|
|
325 |
|
ELFDBG(elf, "section: offset=%"PRIu64" count=%"PRIu16" entry_size=%"PRIu16" strings index=%"PRIu16"\n", |
|
326 |
|
elf->header.section.offset, |
|
327 |
|
elf->header.section.count, |
|
328 |
|
elf->header.section.entry_size, |
|
329 |
|
elf->header.strings.section); |
|
330 |
|
|
|
331 |
|
if (elf->header.section.entry_size != shdr_size) { |
|
332 |
|
ELFDBG(elf, "unexpected section entry size: %"PRIu16", expected %"PRIu16"\n", |
|
333 |
|
elf->header.section.entry_size, shdr_size); |
|
334 |
|
goto invalid; |
|
335 |
|
} |
|
336 |
|
shdrs_size = shdr_size * elf->header.section.count; |
|
337 |
|
if (addu64_overflow(shdrs_size, elf->header.section.offset, &min_size) |
|
338 |
|
|| min_size > elf->size) { |
|
339 |
|
ELFDBG(elf, "file is too short to hold sections\n"); |
|
340 |
|
goto invalid; |
|
341 |
|
} |
|
342 |
|
|
|
343 |
|
if (elf_get_section_info(elf, elf->header.strings.section, |
|
344 |
|
&elf->header.strings.offset, |
|
345 |
|
&elf->header.strings.size, |
|
346 |
|
&elf->header.strings.nameoff) < 0) { |
|
347 |
|
ELFDBG(elf, "could not get strings section\n"); |
|
348 |
|
goto invalid; |
|
349 |
|
} else { |
|
350 |
|
uint64_t slen; |
|
351 |
|
const char *s = elf_get_strings_section(elf, &slen); |
|
352 |
|
if (slen == 0 || s[slen - 1] != '\0') { |
|
353 |
|
ELFDBG(elf, "strings section does not ends with \\0\n"); |
|
354 |
|
goto invalid; |
|
355 |
|
} |
|
356 |
|
} |
|
357 |
|
|
|
358 |
|
return elf; |
|
359 |
|
|
|
360 |
|
invalid: |
|
361 |
|
free(elf); |
|
362 |
|
errno = EINVAL; |
|
363 |
|
return NULL; |
|
364 |
|
} |
|
365 |
|
|
|
366 |
|
void kmod_elf_unref(struct kmod_elf *elf) |
|
367 |
|
{ |
|
368 |
|
free(elf->changed); |
|
369 |
|
free(elf); |
|
370 |
|
} |
|
371 |
|
|
|
372 |
|
const void *kmod_elf_get_memory(const struct kmod_elf *elf) |
|
373 |
|
{ |
|
374 |
|
return elf->memory; |
|
375 |
|
} |
|
376 |
|
|
|
377 |
|
static int elf_find_section(const struct kmod_elf *elf, const char *section) |
|
378 |
|
{ |
|
379 |
|
uint64_t nameslen; |
|
380 |
|
const char *names = elf_get_strings_section(elf, &nameslen); |
|
381 |
|
uint16_t i; |
|
382 |
|
|
|
383 |
|
for (i = 1; i < elf->header.section.count; i++) { |
|
384 |
|
uint64_t off, size; |
|
385 |
|
uint32_t nameoff; |
|
386 |
|
const char *n; |
|
387 |
|
int err = elf_get_section_info(elf, i, &off, &size, &nameoff); |
|
388 |
|
if (err < 0) |
|
389 |
|
continue; |
|
390 |
|
if (nameoff >= nameslen) |
|
391 |
|
continue; |
|
392 |
|
n = names + nameoff; |
|
393 |
|
if (!streq(section, n)) |
|
394 |
|
continue; |
|
395 |
|
|
|
396 |
|
return i; |
|
397 |
|
} |
|
398 |
|
|
|
399 |
|
return -ENOENT; |
|
400 |
|
} |
|
401 |
|
|
|
402 |
|
int kmod_elf_get_section(const struct kmod_elf *elf, const char *section, const void **buf, uint64_t *buf_size) |
|
403 |
|
{ |
|
404 |
|
uint64_t nameslen; |
|
405 |
|
const char *names = elf_get_strings_section(elf, &nameslen); |
|
406 |
|
uint16_t i; |
|
407 |
|
|
|
408 |
|
*buf = NULL; |
|
409 |
|
*buf_size = 0; |
|
410 |
|
|
|
411 |
|
for (i = 1; i < elf->header.section.count; i++) { |
|
412 |
|
uint64_t off, size; |
|
413 |
|
uint32_t nameoff; |
|
414 |
|
const char *n; |
|
415 |
|
int err = elf_get_section_info(elf, i, &off, &size, &nameoff); |
|
416 |
|
if (err < 0) |
|
417 |
|
continue; |
|
418 |
|
if (nameoff >= nameslen) |
|
419 |
|
continue; |
|
420 |
|
n = names + nameoff; |
|
421 |
|
if (!streq(section, n)) |
|
422 |
|
continue; |
|
423 |
|
|
|
424 |
|
*buf = elf_get_mem(elf, off); |
|
425 |
|
*buf_size = size; |
|
426 |
|
return 0; |
|
427 |
|
} |
|
428 |
|
|
|
429 |
|
return -ENOENT; |
|
430 |
|
} |
|
431 |
|
|
|
432 |
|
/* array will be allocated with strings in a single malloc, just free *array */ |
|
433 |
|
int kmod_elf_get_strings(const struct kmod_elf *elf, const char *section, char ***array) |
|
434 |
|
{ |
|
435 |
|
size_t i, j, count; |
|
436 |
|
uint64_t size; |
|
437 |
|
const void *buf; |
|
438 |
|
const char *strings; |
|
439 |
|
char *s, **a; |
|
440 |
|
int err; |
|
441 |
|
|
|
442 |
|
*array = NULL; |
|
443 |
|
|
|
444 |
|
err = kmod_elf_get_section(elf, section, &buf, &size); |
|
445 |
|
if (err < 0) |
|
446 |
|
return err; |
|
447 |
|
|
|
448 |
|
strings = buf; |
|
449 |
|
if (strings == NULL || size == 0) |
|
450 |
|
return 0; |
|
451 |
|
|
|
452 |
|
/* skip zero padding */ |
|
453 |
|
while (strings[0] == '\0' && size > 1) { |
|
454 |
|
strings++; |
|
455 |
|
size--; |
|
456 |
|
} |
|
457 |
|
|
|
458 |
|
if (size <= 1) |
|
459 |
|
return 0; |
|
460 |
|
|
|
461 |
|
for (i = 0, count = 0; i < size; ) { |
|
462 |
|
if (strings[i] != '\0') { |
|
463 |
|
i++; |
|
464 |
|
continue; |
|
465 |
|
} |
|
466 |
|
|
|
467 |
|
while (strings[i] == '\0' && i < size) |
|
468 |
|
i++; |
|
469 |
|
|
|
470 |
|
count++; |
|
471 |
|
} |
|
472 |
|
|
|
473 |
|
if (strings[i - 1] != '\0') |
|
474 |
|
count++; |
|
475 |
|
|
|
476 |
|
*array = a = malloc(size + 1 + sizeof(char *) * (count + 1)); |
|
477 |
|
if (*array == NULL) |
|
478 |
|
return -errno; |
|
479 |
|
|
|
480 |
|
s = (char *)(a + count + 1); |
|
481 |
|
memcpy(s, strings, size); |
|
482 |
|
|
|
483 |
|
/* make sure the last string is NULL-terminated */ |
|
484 |
|
s[size] = '\0'; |
|
485 |
|
a[count] = NULL; |
|
486 |
|
a[0] = s; |
|
487 |
|
|
|
488 |
|
for (i = 0, j = 1; j < count && i < size; ) { |
|
489 |
|
if (s[i] != '\0') { |
|
490 |
|
i++; |
|
491 |
|
continue; |
|
492 |
|
} |
|
493 |
|
|
|
494 |
|
while (strings[i] == '\0' && i < size) |
|
495 |
|
i++; |
|
496 |
|
|
|
497 |
|
a[j] = &s[i]; |
|
498 |
|
j++; |
|
499 |
|
} |
|
500 |
|
|
|
501 |
|
return count; |
|
502 |
|
} |
|
503 |
|
|
|
504 |
|
/* array will be allocated with strings in a single malloc, just free *array */ |
|
505 |
|
int kmod_elf_get_modversions(const struct kmod_elf *elf, struct kmod_modversion **array) |
|
506 |
|
{ |
|
507 |
|
size_t off, offcrc, slen; |
|
508 |
|
uint64_t size; |
|
509 |
|
struct kmod_modversion *a; |
|
510 |
|
const void *buf; |
|
511 |
|
char *itr; |
|
512 |
|
int i, count, err; |
|
513 |
|
#define MODVERSION_SEC_SIZE (sizeof(struct kmod_modversion64)) |
|
514 |
|
|
|
515 |
|
if (elf->class & KMOD_ELF_32) |
|
516 |
|
offcrc = sizeof(uint32_t); |
|
517 |
|
else |
|
518 |
|
offcrc = sizeof(uint64_t); |
|
519 |
|
|
|
520 |
|
*array = NULL; |
|
521 |
|
|
|
522 |
|
err = kmod_elf_get_section(elf, "__versions", &buf, &size); |
|
523 |
|
if (err < 0) |
|
524 |
|
return err; |
|
525 |
|
|
|
526 |
|
if (buf == NULL || size == 0) |
|
527 |
|
return 0; |
|
528 |
|
|
|
529 |
|
if (size % MODVERSION_SEC_SIZE != 0) |
|
530 |
|
return -EINVAL; |
|
531 |
|
|
|
532 |
|
count = size / MODVERSION_SEC_SIZE; |
|
533 |
|
|
|
534 |
|
off = (const uint8_t *)buf - elf->memory; |
|
535 |
|
slen = 0; |
|
536 |
|
|
|
537 |
|
for (i = 0; i < count; i++, off += MODVERSION_SEC_SIZE) { |
|
538 |
|
const char *symbol = elf_get_mem(elf, off + offcrc); |
|
539 |
|
|
|
540 |
|
if (symbol[0] == '.') |
|
541 |
|
symbol++; |
|
542 |
|
|
|
543 |
|
slen += strlen(symbol) + 1; |
|
544 |
|
} |
|
545 |
|
|
|
546 |
|
*array = a = malloc(sizeof(struct kmod_modversion) * count + slen); |
|
547 |
|
if (*array == NULL) |
|
548 |
|
return -errno; |
|
549 |
|
|
|
550 |
|
itr = (char *)(a + count); |
|
551 |
|
off = (const uint8_t *)buf - elf->memory; |
|
552 |
|
|
|
553 |
|
for (i = 0; i < count; i++, off += MODVERSION_SEC_SIZE) { |
|
554 |
|
uint64_t crc = elf_get_uint(elf, off, offcrc); |
|
555 |
|
const char *symbol = elf_get_mem(elf, off + offcrc); |
|
556 |
|
size_t symbollen; |
|
557 |
|
|
|
558 |
|
if (symbol[0] == '.') |
|
559 |
|
symbol++; |
|
560 |
|
|
|
561 |
|
a[i].crc = crc; |
|
562 |
|
a[i].bind = KMOD_SYMBOL_UNDEF; |
|
563 |
|
a[i].symbol = itr; |
|
564 |
|
symbollen = strlen(symbol) + 1; |
|
565 |
|
memcpy(itr, symbol, symbollen); |
|
566 |
|
itr += symbollen; |
|
567 |
|
} |
|
568 |
|
|
|
569 |
|
return count; |
|
570 |
|
} |
|
571 |
|
|
|
572 |
|
int kmod_elf_strip_section(struct kmod_elf *elf, const char *section) |
|
573 |
|
{ |
|
574 |
|
uint64_t off, size; |
|
575 |
|
const void *buf; |
|
576 |
|
int idx = elf_find_section(elf, section); |
|
577 |
|
uint64_t val; |
|
578 |
|
|
|
579 |
|
if (idx < 0) |
|
580 |
|
return idx; |
|
581 |
|
|
|
582 |
|
buf = elf_get_section_header(elf, idx); |
|
583 |
|
off = (const uint8_t *)buf - elf->memory; |
|
584 |
|
|
|
585 |
|
if (elf->class & KMOD_ELF_32) { |
|
586 |
|
off += offsetof(Elf32_Shdr, sh_flags); |
|
587 |
|
size = sizeof(((Elf32_Shdr *)buf)->sh_flags); |
|
588 |
|
} else { |
|
589 |
|
off += offsetof(Elf64_Shdr, sh_flags); |
|
590 |
|
size = sizeof(((Elf64_Shdr *)buf)->sh_flags); |
|
591 |
|
} |
|
592 |
|
|
|
593 |
|
val = elf_get_uint(elf, off, size); |
|
594 |
|
val &= ~(uint64_t)SHF_ALLOC; |
|
595 |
|
|
|
596 |
|
return elf_set_uint(elf, off, size, val); |
|
597 |
|
} |
|
598 |
|
|
|
599 |
|
int kmod_elf_strip_vermagic(struct kmod_elf *elf) |
|
600 |
|
{ |
|
601 |
|
uint64_t i, size; |
|
602 |
|
const void *buf; |
|
603 |
|
const char *strings; |
|
604 |
|
int err; |
|
605 |
|
|
|
606 |
|
err = kmod_elf_get_section(elf, ".modinfo", &buf, &size); |
|
607 |
|
if (err < 0) |
|
608 |
|
return err; |
|
609 |
|
strings = buf; |
|
610 |
|
if (strings == NULL || size == 0) |
|
611 |
|
return 0; |
|
612 |
|
|
|
613 |
|
/* skip zero padding */ |
|
614 |
|
while (strings[0] == '\0' && size > 1) { |
|
615 |
|
strings++; |
|
616 |
|
size--; |
|
617 |
|
} |
|
618 |
|
if (size <= 1) |
|
619 |
|
return 0; |
|
620 |
|
|
|
621 |
|
for (i = 0; i < size; i++) { |
|
622 |
|
const char *s; |
|
623 |
|
size_t off, len; |
|
624 |
|
|
|
625 |
|
if (strings[i] == '\0') |
|
626 |
|
continue; |
|
627 |
|
if (i + 1 >= size) |
|
628 |
|
continue; |
|
629 |
|
|
|
630 |
|
s = strings + i; |
|
631 |
|
len = sizeof("vermagic=") - 1; |
|
632 |
|
if (i + len >= size) |
|
633 |
|
continue; |
|
634 |
|
if (strncmp(s, "vermagic=", len) != 0) { |
|
635 |
|
i += strlen(s); |
|
636 |
|
continue; |
|
637 |
|
} |
|
638 |
|
off = (const uint8_t *)s - elf->memory; |
|
639 |
|
|
|
640 |
|
if (elf->changed == NULL) { |
|
641 |
|
elf->changed = malloc(elf->size); |
|
642 |
|
if (elf->changed == NULL) |
|
643 |
|
return -errno; |
|
644 |
|
memcpy(elf->changed, elf->memory, elf->size); |
|
645 |
|
elf->memory = elf->changed; |
|
646 |
|
ELFDBG(elf, "copied memory to allow writing.\n"); |
|
647 |
|
} |
|
648 |
|
|
|
649 |
|
len = strlen(s); |
|
650 |
|
ELFDBG(elf, "clear .modinfo vermagic \"%s\" (%zd bytes)\n", |
|
651 |
|
s, len); |
|
652 |
|
memset(elf->changed + off, '\0', len); |
|
653 |
|
return 0; |
|
654 |
|
} |
|
655 |
|
|
|
656 |
|
ELFDBG(elf, "no vermagic found in .modinfo\n"); |
|
657 |
|
return -ENOENT; |
|
658 |
|
} |
|
659 |
|
|
|
660 |
|
|
|
661 |
|
static int kmod_elf_get_symbols_symtab(const struct kmod_elf *elf, struct kmod_modversion **array) |
|
662 |
|
{ |
|
663 |
|
uint64_t i, last, size; |
|
664 |
|
const void *buf; |
|
665 |
|
const char *strings; |
|
666 |
|
char *itr; |
|
667 |
|
struct kmod_modversion *a; |
|
668 |
|
int count, err; |
|
669 |
|
|
|
670 |
|
*array = NULL; |
|
671 |
|
|
|
672 |
|
err = kmod_elf_get_section(elf, "__ksymtab_strings", &buf, &size); |
|
673 |
|
if (err < 0) |
|
674 |
|
return err; |
|
675 |
|
strings = buf; |
|
676 |
|
if (strings == NULL || size == 0) |
|
677 |
|
return 0; |
|
678 |
|
|
|
679 |
|
/* skip zero padding */ |
|
680 |
|
while (strings[0] == '\0' && size > 1) { |
|
681 |
|
strings++; |
|
682 |
|
size--; |
|
683 |
|
} |
|
684 |
|
if (size <= 1) |
|
685 |
|
return 0; |
|
686 |
|
|
|
687 |
|
last = 0; |
|
688 |
|
for (i = 0, count = 0; i < size; i++) { |
|
689 |
|
if (strings[i] == '\0') { |
|
690 |
|
if (last == i) { |
|
691 |
|
last = i + 1; |
|
692 |
|
continue; |
|
693 |
|
} |
|
694 |
|
count++; |
|
695 |
|
last = i + 1; |
|
696 |
|
} |
|
697 |
|
} |
|
698 |
|
if (strings[i - 1] != '\0') |
|
699 |
|
count++; |
|
700 |
|
|
|
701 |
|
*array = a = malloc(size + 1 + sizeof(struct kmod_modversion) * count); |
|
702 |
|
if (*array == NULL) |
|
703 |
|
return -errno; |
|
704 |
|
|
|
705 |
|
itr = (char *)(a + count); |
|
706 |
|
last = 0; |
|
707 |
|
for (i = 0, count = 0; i < size; i++) { |
|
708 |
|
if (strings[i] == '\0') { |
|
709 |
|
size_t slen = i - last; |
|
710 |
|
if (last == i) { |
|
711 |
|
last = i + 1; |
|
712 |
|
continue; |
|
713 |
|
} |
|
714 |
|
a[count].crc = 0; |
|
715 |
|
a[count].bind = KMOD_SYMBOL_GLOBAL; |
|
716 |
|
a[count].symbol = itr; |
|
717 |
|
memcpy(itr, strings + last, slen); |
|
718 |
|
itr[slen] = '\0'; |
|
719 |
|
itr += slen + 1; |
|
720 |
|
count++; |
|
721 |
|
last = i + 1; |
|
722 |
|
} |
|
723 |
|
} |
|
724 |
|
if (strings[i - 1] != '\0') { |
|
725 |
|
size_t slen = i - last; |
|
726 |
|
a[count].crc = 0; |
|
727 |
|
a[count].bind = KMOD_SYMBOL_GLOBAL; |
|
728 |
|
a[count].symbol = itr; |
|
729 |
|
memcpy(itr, strings + last, slen); |
|
730 |
|
itr[slen] = '\0'; |
|
731 |
|
count++; |
|
732 |
|
} |
|
733 |
|
|
|
734 |
|
return count; |
|
735 |
|
} |
|
736 |
|
|
|
737 |
|
static inline uint8_t kmod_symbol_bind_from_elf(uint8_t elf_value) |
|
738 |
|
{ |
|
739 |
|
switch (elf_value) { |
|
740 |
|
case STB_LOCAL: |
|
741 |
|
return KMOD_SYMBOL_LOCAL; |
|
742 |
|
case STB_GLOBAL: |
|
743 |
|
return KMOD_SYMBOL_GLOBAL; |
|
744 |
|
case STB_WEAK: |
|
745 |
|
return KMOD_SYMBOL_WEAK; |
|
746 |
|
default: |
|
747 |
|
return KMOD_SYMBOL_NONE; |
|
748 |
|
} |
|
749 |
|
} |
|
750 |
|
|
|
751 |
|
/* array will be allocated with strings in a single malloc, just free *array */ |
|
752 |
|
int kmod_elf_get_symbols(const struct kmod_elf *elf, struct kmod_modversion **array) |
|
753 |
|
{ |
|
754 |
|
static const char crc_str[] = "__crc_"; |
|
755 |
|
static const size_t crc_strlen = sizeof(crc_str) - 1; |
|
756 |
|
uint64_t strtablen, symtablen, str_off, sym_off; |
|
757 |
|
const void *strtab, *symtab; |
|
758 |
|
struct kmod_modversion *a; |
|
759 |
|
char *itr; |
|
760 |
|
size_t slen, symlen; |
|
761 |
|
int i, count, symcount, err; |
|
762 |
|
|
|
763 |
|
err = kmod_elf_get_section(elf, ".strtab", &strtab, &strtablen); |
|
764 |
|
if (err < 0) { |
|
765 |
|
ELFDBG(elf, "no .strtab found.\n"); |
|
766 |
|
goto fallback; |
|
767 |
|
} |
|
768 |
|
|
|
769 |
|
err = kmod_elf_get_section(elf, ".symtab", &symtab, &symtablen); |
|
770 |
|
if (err < 0) { |
|
771 |
|
ELFDBG(elf, "no .symtab found.\n"); |
|
772 |
|
goto fallback; |
|
773 |
|
} |
|
774 |
|
|
|
775 |
|
if (elf->class & KMOD_ELF_32) |
|
776 |
|
symlen = sizeof(Elf32_Sym); |
|
777 |
|
else |
|
778 |
|
symlen = sizeof(Elf64_Sym); |
|
779 |
|
|
|
780 |
|
if (symtablen % symlen != 0) { |
|
781 |
|
ELFDBG(elf, "unexpected .symtab of length %"PRIu64", not multiple of %"PRIu64" as expected.\n", symtablen, symlen); |
|
782 |
|
goto fallback; |
|
783 |
|
} |
|
784 |
|
|
|
785 |
|
symcount = symtablen / symlen; |
|
786 |
|
count = 0; |
|
787 |
|
slen = 0; |
|
788 |
|
str_off = (const uint8_t *)strtab - elf->memory; |
|
789 |
|
sym_off = (const uint8_t *)symtab - elf->memory + symlen; |
|
790 |
|
for (i = 1; i < symcount; i++, sym_off += symlen) { |
|
791 |
|
const char *name; |
|
792 |
|
uint32_t name_off; |
|
793 |
|
|
|
794 |
|
#define READV(field) \ |
|
795 |
|
elf_get_uint(elf, sym_off + offsetof(typeof(*s), field),\ |
|
796 |
|
sizeof(s->field)) |
|
797 |
|
if (elf->class & KMOD_ELF_32) { |
|
798 |
|
Elf32_Sym *s; |
|
799 |
|
name_off = READV(st_name); |
|
800 |
|
} else { |
|
801 |
|
Elf64_Sym *s; |
|
802 |
|
name_off = READV(st_name); |
|
803 |
|
} |
|
804 |
|
#undef READV |
|
805 |
|
if (name_off >= strtablen) { |
|
806 |
|
ELFDBG(elf, ".strtab is %"PRIu64" bytes, but .symtab entry %d wants to access offset %"PRIu32".\n", strtablen, i, name_off); |
|
807 |
|
goto fallback; |
|
808 |
|
} |
|
809 |
|
|
|
810 |
|
name = elf_get_mem(elf, str_off + name_off); |
|
811 |
|
|
|
812 |
|
if (strncmp(name, crc_str, crc_strlen) != 0) |
|
813 |
|
continue; |
|
814 |
|
slen += strlen(name + crc_strlen) + 1; |
|
815 |
|
count++; |
|
816 |
|
} |
|
817 |
|
|
|
818 |
|
if (count == 0) |
|
819 |
|
goto fallback; |
|
820 |
|
|
|
821 |
|
*array = a = malloc(sizeof(struct kmod_modversion) * count + slen); |
|
822 |
|
if (*array == NULL) |
|
823 |
|
return -errno; |
|
824 |
|
|
|
825 |
|
itr = (char *)(a + count); |
|
826 |
|
count = 0; |
|
827 |
|
str_off = (const uint8_t *)strtab - elf->memory; |
|
828 |
|
sym_off = (const uint8_t *)symtab - elf->memory + symlen; |
|
829 |
|
for (i = 1; i < symcount; i++, sym_off += symlen) { |
|
830 |
|
const char *name; |
|
831 |
|
uint32_t name_off; |
|
832 |
|
uint64_t crc; |
|
833 |
|
uint8_t info, bind; |
|
834 |
|
|
|
835 |
|
#define READV(field) \ |
|
836 |
|
elf_get_uint(elf, sym_off + offsetof(typeof(*s), field),\ |
|
837 |
|
sizeof(s->field)) |
|
838 |
|
if (elf->class & KMOD_ELF_32) { |
|
839 |
|
Elf32_Sym *s; |
|
840 |
|
name_off = READV(st_name); |
|
841 |
|
crc = READV(st_value); |
|
842 |
|
info = READV(st_info); |
|
843 |
|
} else { |
|
844 |
|
Elf64_Sym *s; |
|
845 |
|
name_off = READV(st_name); |
|
846 |
|
crc = READV(st_value); |
|
847 |
|
info = READV(st_info); |
|
848 |
|
} |
|
849 |
|
#undef READV |
|
850 |
|
name = elf_get_mem(elf, str_off + name_off); |
|
851 |
|
if (strncmp(name, crc_str, crc_strlen) != 0) |
|
852 |
|
continue; |
|
853 |
|
name += crc_strlen; |
|
854 |
|
|
|
855 |
|
if (elf->class & KMOD_ELF_32) |
|
856 |
|
bind = ELF32_ST_BIND(info); |
|
857 |
|
else |
|
858 |
|
bind = ELF64_ST_BIND(info); |
|
859 |
|
|
|
860 |
|
a[count].crc = crc; |
|
861 |
|
a[count].bind = kmod_symbol_bind_from_elf(bind); |
|
862 |
|
a[count].symbol = itr; |
|
863 |
|
slen = strlen(name); |
|
864 |
|
memcpy(itr, name, slen); |
|
865 |
|
itr[slen] = '\0'; |
|
866 |
|
itr += slen + 1; |
|
867 |
|
count++; |
|
868 |
|
} |
|
869 |
|
return count; |
|
870 |
|
|
|
871 |
|
fallback: |
|
872 |
|
ELFDBG(elf, "Falling back to __ksymtab_strings!\n"); |
|
873 |
|
return kmod_elf_get_symbols_symtab(elf, array); |
|
874 |
|
} |
|
875 |
|
|
|
876 |
|
static int kmod_elf_crc_find(const struct kmod_elf *elf, const void *versions, uint64_t versionslen, const char *name, uint64_t *crc) |
|
877 |
|
{ |
|
878 |
|
size_t verlen, crclen, off; |
|
879 |
|
uint64_t i; |
|
880 |
|
|
|
881 |
|
if (elf->class & KMOD_ELF_32) { |
|
882 |
|
struct kmod_modversion32 *mv; |
|
883 |
|
verlen = sizeof(*mv); |
|
884 |
|
crclen = sizeof(mv->crc); |
|
885 |
|
} else { |
|
886 |
|
struct kmod_modversion64 *mv; |
|
887 |
|
verlen = sizeof(*mv); |
|
888 |
|
crclen = sizeof(mv->crc); |
|
889 |
|
} |
|
890 |
|
|
|
891 |
|
off = (const uint8_t *)versions - elf->memory; |
|
892 |
|
for (i = 0; i < versionslen; i += verlen) { |
|
893 |
|
const char *symbol = elf_get_mem(elf, off + i + crclen); |
|
894 |
|
if (!streq(name, symbol)) |
|
895 |
|
continue; |
|
896 |
|
*crc = elf_get_uint(elf, off + i, crclen); |
|
897 |
|
return i / verlen; |
|
898 |
|
} |
|
899 |
|
|
|
900 |
|
ELFDBG(elf, "could not find crc for symbol '%s'\n", name); |
|
901 |
|
*crc = 0; |
|
902 |
|
return -1; |
|
903 |
|
} |
|
904 |
|
|
|
905 |
|
/* from module-init-tools:elfops_core.c */ |
|
906 |
|
#ifndef STT_REGISTER |
|
907 |
|
#define STT_REGISTER 13 /* Global register reserved to app. */ |
|
908 |
|
#endif |
|
909 |
|
|
|
910 |
|
/* array will be allocated with strings in a single malloc, just free *array */ |
|
911 |
|
int kmod_elf_get_dependency_symbols(const struct kmod_elf *elf, struct kmod_modversion **array) |
|
912 |
|
{ |
|
913 |
|
uint64_t versionslen, strtablen, symtablen, str_off, sym_off, ver_off; |
|
914 |
|
const void *versions, *strtab, *symtab; |
|
915 |
|
struct kmod_modversion *a; |
|
916 |
|
char *itr; |
|
917 |
|
size_t slen, verlen, symlen, crclen; |
|
918 |
|
int i, count, symcount, vercount, err; |
|
919 |
|
bool handle_register_symbols; |
|
920 |
|
uint8_t *visited_versions; |
|
921 |
|
uint64_t *symcrcs; |
|
922 |
|
|
|
923 |
|
err = kmod_elf_get_section(elf, "__versions", &versions, &versionslen); |
|
924 |
|
if (err < 0) { |
|
925 |
|
versions = NULL; |
|
926 |
|
versionslen = 0; |
|
927 |
|
verlen = 0; |
|
928 |
|
crclen = 0; |
|
929 |
|
} else { |
|
930 |
|
if (elf->class & KMOD_ELF_32) { |
|
931 |
|
struct kmod_modversion32 *mv; |
|
932 |
|
verlen = sizeof(*mv); |
|
933 |
|
crclen = sizeof(mv->crc); |
|
934 |
|
} else { |
|
935 |
|
struct kmod_modversion64 *mv; |
|
936 |
|
verlen = sizeof(*mv); |
|
937 |
|
crclen = sizeof(mv->crc); |
|
938 |
|
} |
|
939 |
|
if (versionslen % verlen != 0) { |
|
940 |
|
ELFDBG(elf, "unexpected __versions of length %"PRIu64", not multiple of %zd as expected.\n", versionslen, verlen); |
|
941 |
|
versions = NULL; |
|
942 |
|
versionslen = 0; |
|
943 |
|
} |
|
944 |
|
} |
|
945 |
|
|
|
946 |
|
err = kmod_elf_get_section(elf, ".strtab", &strtab, &strtablen); |
|
947 |
|
if (err < 0) { |
|
948 |
|
ELFDBG(elf, "no .strtab found.\n"); |
|
949 |
|
return -EINVAL; |
|
950 |
|
} |
|
951 |
|
|
|
952 |
|
err = kmod_elf_get_section(elf, ".symtab", &symtab, &symtablen); |
|
953 |
|
if (err < 0) { |
|
954 |
|
ELFDBG(elf, "no .symtab found.\n"); |
|
955 |
|
return -EINVAL; |
|
956 |
|
} |
|
957 |
|
|
|
958 |
|
if (elf->class & KMOD_ELF_32) |
|
959 |
|
symlen = sizeof(Elf32_Sym); |
|
960 |
|
else |
|
961 |
|
symlen = sizeof(Elf64_Sym); |
|
962 |
|
|
|
963 |
|
if (symtablen % symlen != 0) { |
|
964 |
|
ELFDBG(elf, "unexpected .symtab of length %"PRIu64", not multiple of %"PRIu64" as expected.\n", symtablen, symlen); |
|
965 |
|
return -EINVAL; |
|
966 |
|
} |
|
967 |
|
|
|
968 |
|
if (versionslen == 0) { |
|
969 |
|
vercount = 0; |
|
970 |
|
visited_versions = NULL; |
|
971 |
|
} else { |
|
972 |
|
vercount = versionslen / verlen; |
|
973 |
|
visited_versions = calloc(vercount, sizeof(uint8_t)); |
|
974 |
|
if (visited_versions == NULL) |
|
975 |
|
return -ENOMEM; |
|
976 |
|
} |
|
977 |
|
|
|
978 |
|
handle_register_symbols = (elf->header.machine == EM_SPARC || |
|
979 |
|
elf->header.machine == EM_SPARCV9); |
|
980 |
|
|
|
981 |
|
symcount = symtablen / symlen; |
|
982 |
|
count = 0; |
|
983 |
|
slen = 0; |
|
984 |
|
str_off = (const uint8_t *)strtab - elf->memory; |
|
985 |
|
sym_off = (const uint8_t *)symtab - elf->memory + symlen; |
|
986 |
|
|
|
987 |
|
symcrcs = calloc(symcount, sizeof(uint64_t)); |
|
988 |
|
if (symcrcs == NULL) { |
|
989 |
|
free(visited_versions); |
|
990 |
|
return -ENOMEM; |
|
991 |
|
} |
|
992 |
|
|
|
993 |
|
for (i = 1; i < symcount; i++, sym_off += symlen) { |
|
994 |
|
const char *name; |
|
995 |
|
uint64_t crc; |
|
996 |
|
uint32_t name_off; |
|
997 |
|
uint16_t secidx; |
|
998 |
|
uint8_t info; |
|
999 |
|
int idx; |
|
1000 |
|
|
|
1001 |
|
#define READV(field) \ |
|
1002 |
|
elf_get_uint(elf, sym_off + offsetof(typeof(*s), field),\ |
|
1003 |
|
sizeof(s->field)) |
|
1004 |
|
if (elf->class & KMOD_ELF_32) { |
|
1005 |
|
Elf32_Sym *s; |
|
1006 |
|
name_off = READV(st_name); |
|
1007 |
|
secidx = READV(st_shndx); |
|
1008 |
|
info = READV(st_info); |
|
1009 |
|
} else { |
|
1010 |
|
Elf64_Sym *s; |
|
1011 |
|
name_off = READV(st_name); |
|
1012 |
|
secidx = READV(st_shndx); |
|
1013 |
|
info = READV(st_info); |
|
1014 |
|
} |
|
1015 |
|
#undef READV |
|
1016 |
|
if (secidx != SHN_UNDEF) |
|
1017 |
|
continue; |
|
1018 |
|
|
|
1019 |
|
if (handle_register_symbols) { |
|
1020 |
|
uint8_t type; |
|
1021 |
|
if (elf->class & KMOD_ELF_32) |
|
1022 |
|
type = ELF32_ST_TYPE(info); |
|
1023 |
|
else |
|
1024 |
|
type = ELF64_ST_TYPE(info); |
|
1025 |
|
|
|
1026 |
|
/* Not really undefined: sparc gcc 3.3 creates |
|
1027 |
|
* U references when you have global asm |
|
1028 |
|
* variables, to avoid anyone else misusing |
|
1029 |
|
* them. |
|
1030 |
|
*/ |
|
1031 |
|
if (type == STT_REGISTER) |
|
1032 |
|
continue; |
|
1033 |
|
} |
|
1034 |
|
|
|
1035 |
|
if (name_off >= strtablen) { |
|
1036 |
|
ELFDBG(elf, ".strtab is %"PRIu64" bytes, but .symtab entry %d wants to access offset %"PRIu32".\n", strtablen, i, name_off); |
|
1037 |
|
free(visited_versions); |
|
1038 |
|
free(symcrcs); |
|
1039 |
|
return -EINVAL; |
|
1040 |
|
} |
|
1041 |
|
|
|
1042 |
|
name = elf_get_mem(elf, str_off + name_off); |
|
1043 |
|
if (name[0] == '\0') { |
|
1044 |
|
ELFDBG(elf, "empty symbol name at index %"PRIu64"\n", i); |
|
1045 |
|
continue; |
|
1046 |
|
} |
|
1047 |
|
|
|
1048 |
|
slen += strlen(name) + 1; |
|
1049 |
|
count++; |
|
1050 |
|
|
|
1051 |
|
idx = kmod_elf_crc_find(elf, versions, versionslen, name, &crc); |
|
1052 |
|
if (idx >= 0 && visited_versions != NULL) |
|
1053 |
|
visited_versions[idx] = 1; |
|
1054 |
|
symcrcs[i] = crc; |
|
1055 |
|
} |
|
1056 |
|
|
|
1057 |
|
if (visited_versions != NULL) { |
|
1058 |
|
/* module_layout/struct_module are not visited, but needed */ |
|
1059 |
|
ver_off = (const uint8_t *)versions - elf->memory; |
|
1060 |
|
for (i = 0; i < vercount; i++) { |
|
1061 |
|
if (visited_versions[i] == 0) { |
|
1062 |
|
const char *name; |
|
1063 |
|
name = elf_get_mem(elf, ver_off + i * verlen + crclen); |
|
1064 |
|
slen += strlen(name) + 1; |
|
1065 |
|
|
|
1066 |
|
count++; |
|
1067 |
|
} |
|
1068 |
|
} |
|
1069 |
|
} |
|
1070 |
|
|
|
1071 |
|
if (count == 0) { |
|
1072 |
|
free(visited_versions); |
|
1073 |
|
free(symcrcs); |
|
1074 |
|
*array = NULL; |
|
1075 |
|
return 0; |
|
1076 |
|
} |
|
1077 |
|
|
|
1078 |
|
*array = a = malloc(sizeof(struct kmod_modversion) * count + slen); |
|
1079 |
|
if (*array == NULL) { |
|
1080 |
|
free(visited_versions); |
|
1081 |
|
free(symcrcs); |
|
1082 |
|
return -errno; |
|
1083 |
|
} |
|
1084 |
|
|
|
1085 |
|
itr = (char *)(a + count); |
|
1086 |
|
count = 0; |
|
1087 |
|
str_off = (const uint8_t *)strtab - elf->memory; |
|
1088 |
|
sym_off = (const uint8_t *)symtab - elf->memory + symlen; |
|
1089 |
|
for (i = 1; i < symcount; i++, sym_off += symlen) { |
|
1090 |
|
const char *name; |
|
1091 |
|
uint64_t crc; |
|
1092 |
|
uint32_t name_off; |
|
1093 |
|
uint16_t secidx; |
|
1094 |
|
uint8_t info, bind; |
|
1095 |
|
|
|
1096 |
|
#define READV(field) \ |
|
1097 |
|
elf_get_uint(elf, sym_off + offsetof(typeof(*s), field),\ |
|
1098 |
|
sizeof(s->field)) |
|
1099 |
|
if (elf->class & KMOD_ELF_32) { |
|
1100 |
|
Elf32_Sym *s; |
|
1101 |
|
name_off = READV(st_name); |
|
1102 |
|
secidx = READV(st_shndx); |
|
1103 |
|
info = READV(st_info); |
|
1104 |
|
} else { |
|
1105 |
|
Elf64_Sym *s; |
|
1106 |
|
name_off = READV(st_name); |
|
1107 |
|
secidx = READV(st_shndx); |
|
1108 |
|
info = READV(st_info); |
|
1109 |
|
} |
|
1110 |
|
#undef READV |
|
1111 |
|
if (secidx != SHN_UNDEF) |
|
1112 |
|
continue; |
|
1113 |
|
|
|
1114 |
|
if (handle_register_symbols) { |
|
1115 |
|
uint8_t type; |
|
1116 |
|
if (elf->class & KMOD_ELF_32) |
|
1117 |
|
type = ELF32_ST_TYPE(info); |
|
1118 |
|
else |
|
1119 |
|
type = ELF64_ST_TYPE(info); |
|
1120 |
|
|
|
1121 |
|
/* Not really undefined: sparc gcc 3.3 creates |
|
1122 |
|
* U references when you have global asm |
|
1123 |
|
* variables, to avoid anyone else misusing |
|
1124 |
|
* them. |
|
1125 |
|
*/ |
|
1126 |
|
if (type == STT_REGISTER) |
|
1127 |
|
continue; |
|
1128 |
|
} |
|
1129 |
|
|
|
1130 |
|
name = elf_get_mem(elf, str_off + name_off); |
|
1131 |
|
if (name[0] == '\0') { |
|
1132 |
|
ELFDBG(elf, "empty symbol name at index %"PRIu64"\n", i); |
|
1133 |
|
continue; |
|
1134 |
|
} |
|
1135 |
|
|
|
1136 |
|
if (elf->class & KMOD_ELF_32) |
|
1137 |
|
bind = ELF32_ST_BIND(info); |
|
1138 |
|
else |
|
1139 |
|
bind = ELF64_ST_BIND(info); |
|
1140 |
|
if (bind == STB_WEAK) |
|
1141 |
|
bind = KMOD_SYMBOL_WEAK; |
|
1142 |
|
else |
|
1143 |
|
bind = KMOD_SYMBOL_UNDEF; |
|
1144 |
|
|
|
1145 |
|
slen = strlen(name); |
|
1146 |
|
crc = symcrcs[i]; |
|
1147 |
|
|
|
1148 |
|
a[count].crc = crc; |
|
1149 |
|
a[count].bind = bind; |
|
1150 |
|
a[count].symbol = itr; |
|
1151 |
|
memcpy(itr, name, slen); |
|
1152 |
|
itr[slen] = '\0'; |
|
1153 |
|
itr += slen + 1; |
|
1154 |
|
|
|
1155 |
|
count++; |
|
1156 |
|
} |
|
1157 |
|
|
|
1158 |
|
free(symcrcs); |
|
1159 |
|
|
|
1160 |
|
if (visited_versions == NULL) |
|
1161 |
|
return count; |
|
1162 |
|
|
|
1163 |
|
/* add unvisited (module_layout/struct_module) */ |
|
1164 |
|
ver_off = (const uint8_t *)versions - elf->memory; |
|
1165 |
|
for (i = 0; i < vercount; i++) { |
|
1166 |
|
const char *name; |
|
1167 |
|
uint64_t crc; |
|
1168 |
|
|
|
1169 |
|
if (visited_versions[i] != 0) |
|
1170 |
|
continue; |
|
1171 |
|
|
|
1172 |
|
name = elf_get_mem(elf, ver_off + i * verlen + crclen); |
|
1173 |
|
slen = strlen(name); |
|
1174 |
|
crc = elf_get_uint(elf, ver_off + i * verlen, crclen); |
|
1175 |
|
|
|
1176 |
|
a[count].crc = crc; |
|
1177 |
|
a[count].bind = KMOD_SYMBOL_UNDEF; |
|
1178 |
|
a[count].symbol = itr; |
|
1179 |
|
memcpy(itr, name, slen); |
|
1180 |
|
itr[slen] = '\0'; |
|
1181 |
|
itr += slen + 1; |
|
1182 |
|
|
|
1183 |
|
count++; |
|
1184 |
|
} |
|
1185 |
|
free(visited_versions); |
|
1186 |
|
return count; |
|
1187 |
|
} |
File src/libkmod-index.c added (mode: 100644) (index 0000000..7516e62) |
|
1 |
|
/* |
|
2 |
|
* libkmod - interface to kernel module operations |
|
3 |
|
* |
|
4 |
|
* Copyright (C) 2011-2013 ProFUSION embedded systems |
|
5 |
|
* |
|
6 |
|
* This library is free software; you can redistribute it and/or |
|
7 |
|
* modify it under the terms of the GNU Lesser General Public |
|
8 |
|
* License as published by the Free Software Foundation; either |
|
9 |
|
* version 2.1 of the License, or (at your option) any later version. |
|
10 |
|
* |
|
11 |
|
* This library is distributed in the hope that it will be useful, |
|
12 |
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
13 |
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
|
14 |
|
* Lesser General Public License for more details. |
|
15 |
|
* |
|
16 |
|
* You should have received a copy of the GNU Lesser General Public |
|
17 |
|
* License along with this library; if not, see <http://www.gnu.org/licenses/>. |
|
18 |
|
*/ |
|
19 |
|
|
|
20 |
|
#include <arpa/inet.h> |
|
21 |
|
#include <assert.h> |
|
22 |
|
#include <errno.h> |
|
23 |
|
#include <fnmatch.h> |
|
24 |
|
#include <inttypes.h> |
|
25 |
|
#include <stdio.h> |
|
26 |
|
#include <stdlib.h> |
|
27 |
|
#include <string.h> |
|
28 |
|
#include <limits.h> |
|
29 |
|
#include <syslog.h> |
|
30 |
|
#include <sys/stat.h> |
|
31 |
|
|
|
32 |
|
#include "config.h" |
|
33 |
|
|
|
34 |
|
#include "libkmod.h" |
|
35 |
|
#include "libkmod-namespace.h" |
|
36 |
|
#include "libkmod-paths.h" |
|
37 |
|
#include "shared/macro.h" |
|
38 |
|
#include "shared/strbuf.h" |
|
39 |
|
#include "shared/util.h" |
|
40 |
|
#include "libkmod-internal.h" |
|
41 |
|
#define LIBKMOD_INDEX_C |
|
42 |
|
#include "libkmod-index.h" |
|
43 |
|
#undef LIBKMOD_INDEX_C |
|
44 |
|
|
|
45 |
|
/* libkmod-index.c: module index file implementation |
|
46 |
|
* |
|
47 |
|
* Integers are stored as 32 bit unsigned in "network" order, i.e. MSB first. |
|
48 |
|
* All files start with a magic number. |
|
49 |
|
* |
|
50 |
|
* Magic spells "BOOTFAST". Second one used on newer versioned binary files. |
|
51 |
|
* #define INDEX_MAGIC_OLD 0xB007FA57 |
|
52 |
|
* |
|
53 |
|
* We use a version string to keep track of changes to the binary format |
|
54 |
|
* This is stored in the form: INDEX_MAJOR (hi) INDEX_MINOR (lo) just in |
|
55 |
|
* case we ever decide to have minor changes that are not incompatible. |
|
56 |
|
*/ |
|
57 |
|
#define INDEX_MAGIC 0xB007F457 |
|
58 |
|
#define INDEX_VERSION_MAJOR 0x0002 |
|
59 |
|
#define INDEX_VERSION_MINOR 0x0001 |
|
60 |
|
#define INDEX_VERSION ((INDEX_VERSION_MAJOR<<16)|INDEX_VERSION_MINOR) |
|
61 |
|
|
|
62 |
|
/* The index file maps keys to values. Both keys and values are ASCII strings. |
|
63 |
|
* Each key can have multiple values. Values are sorted by an integer priority. |
|
64 |
|
* |
|
65 |
|
* The reader also implements a wildcard search (including range expressions) |
|
66 |
|
* where the keys in the index are treated as patterns. |
|
67 |
|
* This feature is required for module aliases. |
|
68 |
|
*/ |
|
69 |
|
#define INDEX_CHILDMAX 128 |
|
70 |
|
|
|
71 |
|
/* Disk format: |
|
72 |
|
* |
|
73 |
|
* uint32_t magic = INDEX_MAGIC; |
|
74 |
|
* uint32_t version = INDEX_VERSION; |
|
75 |
|
* uint32_t root_offset; |
|
76 |
|
* |
|
77 |
|
* (node_offset & INDEX_NODE_MASK) specifies the file offset of nodes: |
|
78 |
|
* |
|
79 |
|
* char[] prefix; // nul terminated |
|
80 |
|
* |
|
81 |
|
* char first; |
|
82 |
|
* char last; |
|
83 |
|
* uint32_t children[last - first + 1]; |
|
84 |
|
* |
|
85 |
|
* uint32_t value_count; |
|
86 |
|
* struct { |
|
87 |
|
* uint32_t priority; |
|
88 |
|
* char[] value; // nul terminated |
|
89 |
|
* } values[value_count]; |
|
90 |
|
* |
|
91 |
|
* (node_offset & INDEX_NODE_FLAGS) indicates which fields are present. |
|
92 |
|
* Empty prefixes are omitted, leaf nodes omit the three child-related fields. |
|
93 |
|
* |
|
94 |
|
* This could be optimised further by adding a sparse child format |
|
95 |
|
* (indicated using a new flag). |
|
96 |
|
* |
|
97 |
|
* |
|
98 |
|
* Implementation is based on a radix tree, or "trie". |
|
99 |
|
* Each arc from parent to child is labelled with a character. |
|
100 |
|
* Each path from the root represents a string. |
|
101 |
|
* |
|
102 |
|
* == Example strings == |
|
103 |
|
* |
|
104 |
|
* ask |
|
105 |
|
* ate |
|
106 |
|
* on |
|
107 |
|
* once |
|
108 |
|
* one |
|
109 |
|
* |
|
110 |
|
* == Key == |
|
111 |
|
* + Normal node |
|
112 |
|
* * Marked node, representing a key and it's values. |
|
113 |
|
* |
|
114 |
|
* + |
|
115 |
|
* |-a-+-s-+-k-* |
|
116 |
|
* | | |
|
117 |
|
* | `-t-+-e-* |
|
118 |
|
* | |
|
119 |
|
* `-o-+-n-*-c-+-e-* |
|
120 |
|
* | |
|
121 |
|
* `-e-* |
|
122 |
|
* |
|
123 |
|
* Naive implementations tend to be very space inefficient; child pointers |
|
124 |
|
* are stored in arrays indexed by character, but most child pointers are null. |
|
125 |
|
* |
|
126 |
|
* Our implementation uses a scheme described by Wikipedia as a Patrica trie, |
|
127 |
|
* |
|
128 |
|
* "easiest to understand as a space-optimized trie where |
|
129 |
|
* each node with only one child is merged with its child" |
|
130 |
|
* |
|
131 |
|
* + |
|
132 |
|
* |-a-+-sk-* |
|
133 |
|
* | | |
|
134 |
|
* | `-te-* |
|
135 |
|
* | |
|
136 |
|
* `-on-*-ce-* |
|
137 |
|
* | |
|
138 |
|
* `-e-* |
|
139 |
|
* |
|
140 |
|
* We still use arrays of child pointers indexed by a single character; |
|
141 |
|
* the remaining characters of the label are stored as a "prefix" in the child. |
|
142 |
|
* |
|
143 |
|
* The paper describing the original Patrica trie works on individiual bits - |
|
144 |
|
* each node has a maximum of two children, which increases space efficiency. |
|
145 |
|
* However for this application it is simpler to use the ASCII character set. |
|
146 |
|
* Since the index file is read-only, it can be compressed by omitting null |
|
147 |
|
* child pointers at the start and end of arrays. |
|
148 |
|
*/ |
|
149 |
|
|
|
150 |
|
/* Format of node offsets within index file */ |
|
151 |
|
enum node_offset { |
|
152 |
|
INDEX_NODE_FLAGS = 0xF0000000, /* Flags in high nibble */ |
|
153 |
|
INDEX_NODE_PREFIX = 0x80000000, |
|
154 |
|
INDEX_NODE_VALUES = 0x40000000, |
|
155 |
|
INDEX_NODE_CHILDS = 0x20000000, |
|
156 |
|
|
|
157 |
|
INDEX_NODE_MASK = 0x0FFFFFFF, /* Offset value */ |
|
158 |
|
}; |
|
159 |
|
|
|
160 |
|
void index_values_free(struct index_value *values) |
|
161 |
|
{ |
|
162 |
|
while (values) { |
|
163 |
|
struct index_value *value = values; |
|
164 |
|
|
|
165 |
|
values = value->next; |
|
166 |
|
free(value); |
|
167 |
|
} |
|
168 |
|
} |
|
169 |
|
|
|
170 |
|
static int add_value(struct index_value **values, |
|
171 |
|
const char *value, unsigned len, unsigned int priority) |
|
172 |
|
{ |
|
173 |
|
struct index_value *v; |
|
174 |
|
|
|
175 |
|
/* find position to insert value */ |
|
176 |
|
while (*values && (*values)->priority < priority) |
|
177 |
|
values = &(*values)->next; |
|
178 |
|
|
|
179 |
|
v = malloc(sizeof(struct index_value) + len + 1); |
|
180 |
|
if (!v) |
|
181 |
|
return -1; |
|
182 |
|
v->next = *values; |
|
183 |
|
v->priority = priority; |
|
184 |
|
v->len = len; |
|
185 |
|
memcpy(v->value, value, len); |
|
186 |
|
v->value[len] = '\0'; |
|
187 |
|
*values = v; |
|
188 |
|
|
|
189 |
|
return 0; |
|
190 |
|
} |
|
191 |
|
|
|
192 |
|
static void read_error(void) |
|
193 |
|
{ |
|
194 |
|
fatal("Module index: unexpected error: %s\n" |
|
195 |
|
"Try re-running depmod\n", errno ? strerror(errno) : "EOF"); |
|
196 |
|
} |
|
197 |
|
|
|
198 |
|
static int read_char(FILE *in) |
|
199 |
|
{ |
|
200 |
|
int ch; |
|
201 |
|
|
|
202 |
|
errno = 0; |
|
203 |
|
ch = getc_unlocked(in); |
|
204 |
|
if (ch == EOF) |
|
205 |
|
read_error(); |
|
206 |
|
return ch; |
|
207 |
|
} |
|
208 |
|
|
|
209 |
|
static uint32_t read_long(FILE *in) |
|
210 |
|
{ |
|
211 |
|
uint32_t l; |
|
212 |
|
|
|
213 |
|
errno = 0; |
|
214 |
|
if (fread(&l, sizeof(uint32_t), 1, in) != sizeof(uint32_t)) |
|
215 |
|
read_error(); |
|
216 |
|
return ntohl(l); |
|
217 |
|
} |
|
218 |
|
|
|
219 |
|
static unsigned buf_freadchars(struct strbuf *buf, FILE *in) |
|
220 |
|
{ |
|
221 |
|
unsigned i = 0; |
|
222 |
|
int ch; |
|
223 |
|
|
|
224 |
|
while ((ch = read_char(in))) { |
|
225 |
|
if (!strbuf_pushchar(buf, ch)) |
|
226 |
|
break; |
|
227 |
|
i++; |
|
228 |
|
} |
|
229 |
|
|
|
230 |
|
return i; |
|
231 |
|
} |
|
232 |
|
|
|
233 |
|
/* |
|
234 |
|
* Index file searching |
|
235 |
|
*/ |
|
236 |
|
struct index_node_f { |
|
237 |
|
FILE *file; |
|
238 |
|
char *prefix; /* path compression */ |
|
239 |
|
struct index_value *values; |
|
240 |
|
unsigned char first; /* range of child nodes */ |
|
241 |
|
unsigned char last; |
|
242 |
|
uint32_t children[0]; |
|
243 |
|
}; |
|
244 |
|
|
|
245 |
|
static struct index_node_f *index_read(FILE *in, uint32_t offset) |
|
246 |
|
{ |
|
247 |
|
struct index_node_f *node; |
|
248 |
|
char *prefix; |
|
249 |
|
int i, child_count = 0; |
|
250 |
|
|
|
251 |
|
if ((offset & INDEX_NODE_MASK) == 0) |
|
252 |
|
return NULL; |
|
253 |
|
|
|
254 |
|
if (fseek(in, offset & INDEX_NODE_MASK, SEEK_SET) < 0) |
|
255 |
|
return NULL; |
|
256 |
|
|
|
257 |
|
if (offset & INDEX_NODE_PREFIX) { |
|
258 |
|
struct strbuf buf; |
|
259 |
|
strbuf_init(&buf); |
|
260 |
|
buf_freadchars(&buf, in); |
|
261 |
|
prefix = strbuf_steal(&buf); |
|
262 |
|
} else |
|
263 |
|
prefix = NOFAIL(strdup("")); |
|
264 |
|
|
|
265 |
|
if (offset & INDEX_NODE_CHILDS) { |
|
266 |
|
char first = read_char(in); |
|
267 |
|
char last = read_char(in); |
|
268 |
|
child_count = last - first + 1; |
|
269 |
|
|
|
270 |
|
node = NOFAIL(malloc(sizeof(struct index_node_f) + |
|
271 |
|
sizeof(uint32_t) * child_count)); |
|
272 |
|
|
|
273 |
|
node->first = first; |
|
274 |
|
node->last = last; |
|
275 |
|
|
|
276 |
|
for (i = 0; i < child_count; i++) |
|
277 |
|
node->children[i] = read_long(in); |
|
278 |
|
} else { |
|
279 |
|
node = NOFAIL(malloc(sizeof(struct index_node_f))); |
|
280 |
|
node->first = INDEX_CHILDMAX; |
|
281 |
|
node->last = 0; |
|
282 |
|
} |
|
283 |
|
|
|
284 |
|
node->values = NULL; |
|
285 |
|
if (offset & INDEX_NODE_VALUES) { |
|
286 |
|
int value_count; |
|
287 |
|
struct strbuf buf; |
|
288 |
|
const char *value; |
|
289 |
|
unsigned int priority; |
|
290 |
|
|
|
291 |
|
value_count = read_long(in); |
|
292 |
|
|
|
293 |
|
strbuf_init(&buf); |
|
294 |
|
while (value_count--) { |
|
295 |
|
priority = read_long(in); |
|
296 |
|
buf_freadchars(&buf, in); |
|
297 |
|
value = strbuf_str(&buf); |
|
298 |
|
add_value(&node->values, value, buf.used, priority); |
|
299 |
|
strbuf_clear(&buf); |
|
300 |
|
} |
|
301 |
|
strbuf_release(&buf); |
|
302 |
|
} |
|
303 |
|
|
|
304 |
|
node->prefix = prefix; |
|
305 |
|
node->file = in; |
|
306 |
|
return node; |
|
307 |
|
} |
|
308 |
|
|
|
309 |
|
static void index_close(struct index_node_f *node) |
|
310 |
|
{ |
|
311 |
|
free(node->prefix); |
|
312 |
|
index_values_free(node->values); |
|
313 |
|
free(node); |
|
314 |
|
} |
|
315 |
|
|
|
316 |
|
struct index_file { |
|
317 |
|
FILE *file; |
|
318 |
|
uint32_t root_offset; |
|
319 |
|
}; |
|
320 |
|
|
|
321 |
|
struct index_file *index_file_open(const char *filename) |
|
322 |
|
{ |
|
323 |
|
FILE *file; |
|
324 |
|
uint32_t magic, version; |
|
325 |
|
struct index_file *new; |
|
326 |
|
|
|
327 |
|
file = fopen(filename, "re"); |
|
328 |
|
if (!file) |
|
329 |
|
return NULL; |
|
330 |
|
errno = EINVAL; |
|
331 |
|
|
|
332 |
|
magic = read_long(file); |
|
333 |
|
if (magic != INDEX_MAGIC) { |
|
334 |
|
fclose(file); |
|
335 |
|
return NULL; |
|
336 |
|
} |
|
337 |
|
|
|
338 |
|
version = read_long(file); |
|
339 |
|
if (version >> 16 != INDEX_VERSION_MAJOR) { |
|
340 |
|
fclose(file); |
|
341 |
|
return NULL; |
|
342 |
|
} |
|
343 |
|
|
|
344 |
|
new = NOFAIL(malloc(sizeof(struct index_file))); |
|
345 |
|
new->file = file; |
|
346 |
|
new->root_offset = read_long(new->file); |
|
347 |
|
|
|
348 |
|
errno = 0; |
|
349 |
|
return new; |
|
350 |
|
} |
|
351 |
|
|
|
352 |
|
void index_file_close(struct index_file *idx) |
|
353 |
|
{ |
|
354 |
|
fclose(idx->file); |
|
355 |
|
free(idx); |
|
356 |
|
} |
|
357 |
|
|
|
358 |
|
static struct index_node_f *index_readroot(struct index_file *in) |
|
359 |
|
{ |
|
360 |
|
return index_read(in->file, in->root_offset); |
|
361 |
|
} |
|
362 |
|
|
|
363 |
|
static struct index_node_f *index_readchild(const struct index_node_f *parent, |
|
364 |
|
int ch) |
|
365 |
|
{ |
|
366 |
|
if (parent->first <= ch && ch <= parent->last) { |
|
367 |
|
return index_read(parent->file, |
|
368 |
|
parent->children[ch - parent->first]); |
|
369 |
|
} |
|
370 |
|
|
|
371 |
|
return NULL; |
|
372 |
|
} |
|
373 |
|
|
|
374 |
|
static void index_dump_node(struct index_node_f *node, struct strbuf *buf, |
|
375 |
|
int fd) |
|
376 |
|
{ |
|
377 |
|
struct index_value *v; |
|
378 |
|
int ch, pushed; |
|
379 |
|
|
|
380 |
|
pushed = strbuf_pushchars(buf, node->prefix); |
|
381 |
|
|
|
382 |
|
for (v = node->values; v != NULL; v = v->next) { |
|
383 |
|
write_str_safe(fd, buf->bytes, buf->used); |
|
384 |
|
write_str_safe(fd, " ", 1); |
|
385 |
|
write_str_safe(fd, v->value, strlen(v->value)); |
|
386 |
|
write_str_safe(fd, "\n", 1); |
|
387 |
|
} |
|
388 |
|
|
|
389 |
|
for (ch = node->first; ch <= node->last; ch++) { |
|
390 |
|
struct index_node_f *child = index_readchild(node, ch); |
|
391 |
|
|
|
392 |
|
if (!child) |
|
393 |
|
continue; |
|
394 |
|
|
|
395 |
|
strbuf_pushchar(buf, ch); |
|
396 |
|
index_dump_node(child, buf, fd); |
|
397 |
|
strbuf_popchar(buf); |
|
398 |
|
} |
|
399 |
|
|
|
400 |
|
strbuf_popchars(buf, pushed); |
|
401 |
|
index_close(node); |
|
402 |
|
} |
|
403 |
|
|
|
404 |
|
void index_dump(struct index_file *in, int fd, const char *prefix) |
|
405 |
|
{ |
|
406 |
|
struct index_node_f *root; |
|
407 |
|
struct strbuf buf; |
|
408 |
|
|
|
409 |
|
root = index_readroot(in); |
|
410 |
|
if (root == NULL) |
|
411 |
|
return; |
|
412 |
|
|
|
413 |
|
strbuf_init(&buf); |
|
414 |
|
strbuf_pushchars(&buf, prefix); |
|
415 |
|
index_dump_node(root, &buf, fd); |
|
416 |
|
strbuf_release(&buf); |
|
417 |
|
} |
|
418 |
|
|
|
419 |
|
static char *index_search__node(struct index_node_f *node, const char *key, int i) |
|
420 |
|
{ |
|
421 |
|
char *value; |
|
422 |
|
struct index_node_f *child; |
|
423 |
|
int ch; |
|
424 |
|
int j; |
|
425 |
|
|
|
426 |
|
while(node) { |
|
427 |
|
for (j = 0; node->prefix[j]; j++) { |
|
428 |
|
ch = node->prefix[j]; |
|
429 |
|
|
|
430 |
|
if (ch != key[i+j]) { |
|
431 |
|
index_close(node); |
|
432 |
|
return NULL; |
|
433 |
|
} |
|
434 |
|
} |
|
435 |
|
|
|
436 |
|
i += j; |
|
437 |
|
|
|
438 |
|
if (key[i] == '\0') { |
|
439 |
|
value = node->values != NULL |
|
440 |
|
? strdup(node->values[0].value) |
|
441 |
|
: NULL; |
|
442 |
|
|
|
443 |
|
index_close(node); |
|
444 |
|
return value; |
|
445 |
|
} |
|
446 |
|
|
|
447 |
|
child = index_readchild(node, key[i]); |
|
448 |
|
index_close(node); |
|
449 |
|
node = child; |
|
450 |
|
i++; |
|
451 |
|
} |
|
452 |
|
|
|
453 |
|
return NULL; |
|
454 |
|
} |
|
455 |
|
|
|
456 |
|
/* |
|
457 |
|
* Search the index for a key |
|
458 |
|
* |
|
459 |
|
* Returns the value of the first match |
|
460 |
|
* |
|
461 |
|
* The recursive functions free their node argument (using index_close). |
|
462 |
|
*/ |
|
463 |
|
char *index_search(struct index_file *in, const char *key) |
|
464 |
|
{ |
|
465 |
|
// FIXME: return value by reference instead of strdup |
|
466 |
|
struct index_node_f *root; |
|
467 |
|
char *value; |
|
468 |
|
|
|
469 |
|
root = index_readroot(in); |
|
470 |
|
value = index_search__node(root, key, 0); |
|
471 |
|
|
|
472 |
|
return value; |
|
473 |
|
} |
|
474 |
|
|
|
475 |
|
|
|
476 |
|
|
|
477 |
|
/* Level 4: add all the values from a matching node */ |
|
478 |
|
static void index_searchwild__allvalues(struct index_node_f *node, |
|
479 |
|
struct index_value **out) |
|
480 |
|
{ |
|
481 |
|
struct index_value *v; |
|
482 |
|
|
|
483 |
|
for (v = node->values; v != NULL; v = v->next) |
|
484 |
|
add_value(out, v->value, v->len, v->priority); |
|
485 |
|
|
|
486 |
|
index_close(node); |
|
487 |
|
} |
|
488 |
|
|
|
489 |
|
/* |
|
490 |
|
* Level 3: traverse a sub-keyspace which starts with a wildcard, |
|
491 |
|
* looking for matches. |
|
492 |
|
*/ |
|
493 |
|
static void index_searchwild__all(struct index_node_f *node, int j, |
|
494 |
|
struct strbuf *buf, |
|
495 |
|
const char *subkey, |
|
496 |
|
struct index_value **out) |
|
497 |
|
{ |
|
498 |
|
int pushed = 0; |
|
499 |
|
int ch; |
|
500 |
|
|
|
501 |
|
while (node->prefix[j]) { |
|
502 |
|
ch = node->prefix[j]; |
|
503 |
|
|
|
504 |
|
strbuf_pushchar(buf, ch); |
|
505 |
|
pushed++; |
|
506 |
|
j++; |
|
507 |
|
} |
|
508 |
|
|
|
509 |
|
for (ch = node->first; ch <= node->last; ch++) { |
|
510 |
|
struct index_node_f *child = index_readchild(node, ch); |
|
511 |
|
|
|
512 |
|
if (!child) |
|
513 |
|
continue; |
|
514 |
|
|
|
515 |
|
strbuf_pushchar(buf, ch); |
|
516 |
|
index_searchwild__all(child, 0, buf, subkey, out); |
|
517 |
|
strbuf_popchar(buf); |
|
518 |
|
} |
|
519 |
|
|
|
520 |
|
if (node->values) { |
|
521 |
|
if (fnmatch(strbuf_str(buf), subkey, 0) == 0) |
|
522 |
|
index_searchwild__allvalues(node, out); |
|
523 |
|
else |
|
524 |
|
index_close(node); |
|
525 |
|
} else { |
|
526 |
|
index_close(node); |
|
527 |
|
} |
|
528 |
|
|
|
529 |
|
strbuf_popchars(buf, pushed); |
|
530 |
|
} |
|
531 |
|
|
|
532 |
|
/* Level 2: descend the tree (until we hit a wildcard) */ |
|
533 |
|
static void index_searchwild__node(struct index_node_f *node, |
|
534 |
|
struct strbuf *buf, |
|
535 |
|
const char *key, int i, |
|
536 |
|
struct index_value **out) |
|
537 |
|
{ |
|
538 |
|
struct index_node_f *child; |
|
539 |
|
int j; |
|
540 |
|
int ch; |
|
541 |
|
|
|
542 |
|
while(node) { |
|
543 |
|
for (j = 0; node->prefix[j]; j++) { |
|
544 |
|
ch = node->prefix[j]; |
|
545 |
|
|
|
546 |
|
if (ch == '*' || ch == '?' || ch == '[') { |
|
547 |
|
index_searchwild__all(node, j, buf, |
|
548 |
|
&key[i+j], out); |
|
549 |
|
return; |
|
550 |
|
} |
|
551 |
|
|
|
552 |
|
if (ch != key[i+j]) { |
|
553 |
|
index_close(node); |
|
554 |
|
return; |
|
555 |
|
} |
|
556 |
|
} |
|
557 |
|
|
|
558 |
|
i += j; |
|
559 |
|
|
|
560 |
|
child = index_readchild(node, '*'); |
|
561 |
|
if (child) { |
|
562 |
|
strbuf_pushchar(buf, '*'); |
|
563 |
|
index_searchwild__all(child, 0, buf, &key[i], out); |
|
564 |
|
strbuf_popchar(buf); |
|
565 |
|
} |
|
566 |
|
|
|
567 |
|
child = index_readchild(node, '?'); |
|
568 |
|
if (child) { |
|
569 |
|
strbuf_pushchar(buf, '?'); |
|
570 |
|
index_searchwild__all(child, 0, buf, &key[i], out); |
|
571 |
|
strbuf_popchar(buf); |
|
572 |
|
} |
|
573 |
|
|
|
574 |
|
child = index_readchild(node, '['); |
|
575 |
|
if (child) { |
|
576 |
|
strbuf_pushchar(buf, '['); |
|
577 |
|
index_searchwild__all(child, 0, buf, &key[i], out); |
|
578 |
|
strbuf_popchar(buf); |
|
579 |
|
} |
|
580 |
|
|
|
581 |
|
if (key[i] == '\0') { |
|
582 |
|
index_searchwild__allvalues(node, out); |
|
583 |
|
|
|
584 |
|
return; |
|
585 |
|
} |
|
586 |
|
|
|
587 |
|
child = index_readchild(node, key[i]); |
|
588 |
|
index_close(node); |
|
589 |
|
node = child; |
|
590 |
|
i++; |
|
591 |
|
} |
|
592 |
|
} |
|
593 |
|
|
|
594 |
|
/* |
|
595 |
|
* Search the index for a key. The index may contain wildcards. |
|
596 |
|
* |
|
597 |
|
* Returns a list of all the values of matching keys. |
|
598 |
|
*/ |
|
599 |
|
struct index_value *index_searchwild(struct index_file *in, const char *key) |
|
600 |
|
{ |
|
601 |
|
struct index_node_f *root = index_readroot(in); |
|
602 |
|
struct strbuf buf; |
|
603 |
|
struct index_value *out = NULL; |
|
604 |
|
|
|
605 |
|
strbuf_init(&buf); |
|
606 |
|
index_searchwild__node(root, &buf, key, 0, &out); |
|
607 |
|
strbuf_release(&buf); |
|
608 |
|
return out; |
|
609 |
|
} |
|
610 |
|
|
|
611 |
|
/**************************************************************************/ |
|
612 |
|
/* |
|
613 |
|
* Alternative implementation, using mmap to map all the file to memory when |
|
614 |
|
* starting |
|
615 |
|
*/ |
|
616 |
|
#include <sys/mman.h> |
|
617 |
|
#include <sys/stat.h> |
|
618 |
|
#include <unistd.h> |
|
619 |
|
|
|
620 |
|
static const char _idx_empty_str[] = ""; |
|
621 |
|
|
|
622 |
|
struct index_mm { |
|
623 |
|
struct kmod_ctx *ctx; |
|
624 |
|
void *mm; |
|
625 |
|
uint32_t root_offset; |
|
626 |
|
size_t size; |
|
627 |
|
}; |
|
628 |
|
|
|
629 |
|
struct index_mm_value { |
|
630 |
|
unsigned int priority; |
|
631 |
|
unsigned int len; |
|
632 |
|
const char *value; |
|
633 |
|
}; |
|
634 |
|
|
|
635 |
|
struct index_mm_value_array { |
|
636 |
|
struct index_mm_value *values; |
|
637 |
|
unsigned int len; |
|
638 |
|
}; |
|
639 |
|
|
|
640 |
|
struct index_mm_node { |
|
641 |
|
struct index_mm *idx; |
|
642 |
|
const char *prefix; /* mmape'd value */ |
|
643 |
|
struct index_mm_value_array values; |
|
644 |
|
unsigned char first; |
|
645 |
|
unsigned char last; |
|
646 |
|
uint32_t children[]; |
|
647 |
|
}; |
|
648 |
|
|
|
649 |
|
static inline uint32_t read_long_mm(void **p) |
|
650 |
|
{ |
|
651 |
|
uint8_t *addr = *(uint8_t **)p; |
|
652 |
|
uint32_t v; |
|
653 |
|
|
|
654 |
|
v = *(uint32_t *)addr; |
|
655 |
|
|
|
656 |
|
*p = addr + sizeof(uint32_t); |
|
657 |
|
return ntohl(v); |
|
658 |
|
} |
|
659 |
|
|
|
660 |
|
static inline uint8_t read_char_mm(void **p) |
|
661 |
|
{ |
|
662 |
|
uint8_t *addr = *(uint8_t **)p; |
|
663 |
|
uint8_t v = *addr; |
|
664 |
|
*p = addr + sizeof(uint8_t); |
|
665 |
|
return v; |
|
666 |
|
} |
|
667 |
|
|
|
668 |
|
static inline char *read_chars_mm(void **p, unsigned *rlen) |
|
669 |
|
{ |
|
670 |
|
char *addr = *(char **)p; |
|
671 |
|
size_t len = *rlen = strlen(addr); |
|
672 |
|
*p = addr + len + 1; |
|
673 |
|
return addr; |
|
674 |
|
} |
|
675 |
|
|
|
676 |
|
static struct index_mm_node *index_mm_read_node(struct index_mm *idx, |
|
677 |
|
uint32_t offset) { |
|
678 |
|
void *p = idx->mm; |
|
679 |
|
struct index_mm_node *node; |
|
680 |
|
const char *prefix; |
|
681 |
|
int i, child_count, value_count, children_padding; |
|
682 |
|
uint32_t children[INDEX_CHILDMAX]; |
|
683 |
|
char first, last; |
|
684 |
|
|
|
685 |
|
if ((offset & INDEX_NODE_MASK) == 0) |
|
686 |
|
return NULL; |
|
687 |
|
|
|
688 |
|
p = (char *)p + (offset & INDEX_NODE_MASK); |
|
689 |
|
|
|
690 |
|
if (offset & INDEX_NODE_PREFIX) { |
|
691 |
|
unsigned len; |
|
692 |
|
prefix = read_chars_mm(&p, &len); |
|
693 |
|
} else |
|
694 |
|
prefix = _idx_empty_str; |
|
695 |
|
|
|
696 |
|
if (offset & INDEX_NODE_CHILDS) { |
|
697 |
|
first = read_char_mm(&p); |
|
698 |
|
last = read_char_mm(&p); |
|
699 |
|
child_count = last - first + 1; |
|
700 |
|
for (i = 0; i < child_count; i++) |
|
701 |
|
children[i] = read_long_mm(&p); |
|
702 |
|
} else { |
|
703 |
|
first = INDEX_CHILDMAX; |
|
704 |
|
last = 0; |
|
705 |
|
child_count = 0; |
|
706 |
|
} |
|
707 |
|
|
|
708 |
|
children_padding = (sizeof(struct index_mm_node) + |
|
709 |
|
(sizeof(uint32_t) * child_count)) % sizeof(void *); |
|
710 |
|
|
|
711 |
|
if (offset & INDEX_NODE_VALUES) |
|
712 |
|
value_count = read_long_mm(&p); |
|
713 |
|
else |
|
714 |
|
value_count = 0; |
|
715 |
|
|
|
716 |
|
node = malloc(sizeof(struct index_mm_node) |
|
717 |
|
+ sizeof(uint32_t) * child_count + children_padding |
|
718 |
|
+ sizeof(struct index_mm_value) * value_count); |
|
719 |
|
if (node == NULL) |
|
720 |
|
return NULL; |
|
721 |
|
|
|
722 |
|
node->idx = idx; |
|
723 |
|
node->prefix = prefix; |
|
724 |
|
if (value_count == 0) |
|
725 |
|
node->values.values = NULL; |
|
726 |
|
else { |
|
727 |
|
node->values.values = (struct index_mm_value *) |
|
728 |
|
((char *)node + sizeof(struct index_mm_node) + |
|
729 |
|
sizeof(uint32_t) * child_count + children_padding); |
|
730 |
|
} |
|
731 |
|
node->values.len = value_count; |
|
732 |
|
node->first = first; |
|
733 |
|
node->last = last; |
|
734 |
|
memcpy(node->children, children, sizeof(uint32_t) * child_count); |
|
735 |
|
|
|
736 |
|
for (i = 0; i < value_count; i++) { |
|
737 |
|
struct index_mm_value *v = node->values.values + i; |
|
738 |
|
v->priority = read_long_mm(&p); |
|
739 |
|
v->value = read_chars_mm(&p, &v->len); |
|
740 |
|
} |
|
741 |
|
|
|
742 |
|
return node; |
|
743 |
|
} |
|
744 |
|
|
|
745 |
|
static void index_mm_free_node(struct index_mm_node *node) |
|
746 |
|
{ |
|
747 |
|
free(node); |
|
748 |
|
} |
|
749 |
|
|
|
750 |
|
struct index_mm *index_mm_open(struct kmod_ctx *ctx, const char *filename, |
|
751 |
|
unsigned long long *stamp) |
|
752 |
|
{ |
|
753 |
|
int fd; |
|
754 |
|
struct stat st; |
|
755 |
|
struct index_mm *idx; |
|
756 |
|
struct { |
|
757 |
|
uint32_t magic; |
|
758 |
|
uint32_t version; |
|
759 |
|
uint32_t root_offset; |
|
760 |
|
} hdr; |
|
761 |
|
void *p; |
|
762 |
|
|
|
763 |
|
DBG(ctx, "file=%s\n", filename); |
|
764 |
|
|
|
765 |
|
idx = malloc(sizeof(*idx)); |
|
766 |
|
if (idx == NULL) { |
|
767 |
|
ERR(ctx, "malloc: %m\n"); |
|
768 |
|
return NULL; |
|
769 |
|
} |
|
770 |
|
|
|
771 |
|
if ((fd = open(filename, O_RDONLY|O_CLOEXEC)) < 0) { |
|
772 |
|
DBG(ctx, "open(%s, O_RDONLY|O_CLOEXEC): %m\n", filename); |
|
773 |
|
goto fail_open; |
|
774 |
|
} |
|
775 |
|
|
|
776 |
|
if (fstat(fd, &st) < 0) |
|
777 |
|
goto fail_nommap; |
|
778 |
|
if ((size_t) st.st_size < sizeof(hdr)) |
|
779 |
|
goto fail_nommap; |
|
780 |
|
|
|
781 |
|
if ((idx->mm = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0)) |
|
782 |
|
== MAP_FAILED) { |
|
783 |
|
ERR(ctx, "mmap(NULL, %"PRIu64", PROT_READ, %d, MAP_PRIVATE, 0): %m\n", |
|
784 |
|
st.st_size, fd); |
|
785 |
|
goto fail_nommap; |
|
786 |
|
} |
|
787 |
|
|
|
788 |
|
p = idx->mm; |
|
789 |
|
hdr.magic = read_long_mm(&p); |
|
790 |
|
hdr.version = read_long_mm(&p); |
|
791 |
|
hdr.root_offset = read_long_mm(&p); |
|
792 |
|
|
|
793 |
|
if (hdr.magic != INDEX_MAGIC) { |
|
794 |
|
ERR(ctx, "magic check fail: %x instead of %x\n", hdr.magic, |
|
795 |
|
INDEX_MAGIC); |
|
796 |
|
goto fail; |
|
797 |
|
} |
|
798 |
|
|
|
799 |
|
if (hdr.version >> 16 != INDEX_VERSION_MAJOR) { |
|
800 |
|
ERR(ctx, "major version check fail: %u instead of %u\n", |
|
801 |
|
hdr.version >> 16, INDEX_VERSION_MAJOR); |
|
802 |
|
goto fail; |
|
803 |
|
} |
|
804 |
|
|
|
805 |
|
idx->root_offset = hdr.root_offset; |
|
806 |
|
idx->size = st.st_size; |
|
807 |
|
idx->ctx = ctx; |
|
808 |
|
close(fd); |
|
809 |
|
|
|
810 |
|
*stamp = stat_mstamp(&st); |
|
811 |
|
|
|
812 |
|
return idx; |
|
813 |
|
|
|
814 |
|
fail: |
|
815 |
|
munmap(idx->mm, st.st_size); |
|
816 |
|
fail_nommap: |
|
817 |
|
close(fd); |
|
818 |
|
fail_open: |
|
819 |
|
free(idx); |
|
820 |
|
return NULL; |
|
821 |
|
} |
|
822 |
|
|
|
823 |
|
void index_mm_close(struct index_mm *idx) |
|
824 |
|
{ |
|
825 |
|
munmap(idx->mm, idx->size); |
|
826 |
|
free(idx); |
|
827 |
|
} |
|
828 |
|
|
|
829 |
|
static struct index_mm_node *index_mm_readroot(struct index_mm *idx) |
|
830 |
|
{ |
|
831 |
|
return index_mm_read_node(idx, idx->root_offset); |
|
832 |
|
} |
|
833 |
|
|
|
834 |
|
static struct index_mm_node *index_mm_readchild(const struct index_mm_node *parent, |
|
835 |
|
int ch) |
|
836 |
|
{ |
|
837 |
|
if (parent->first <= ch && ch <= parent->last) { |
|
838 |
|
return index_mm_read_node(parent->idx, |
|
839 |
|
parent->children[ch - parent->first]); |
|
840 |
|
} |
|
841 |
|
|
|
842 |
|
return NULL; |
|
843 |
|
} |
|
844 |
|
|
|
845 |
|
static void index_mm_dump_node(struct index_mm_node *node, struct strbuf *buf, |
|
846 |
|
int fd) |
|
847 |
|
{ |
|
848 |
|
struct index_mm_value *itr, *itr_end; |
|
849 |
|
int ch, pushed; |
|
850 |
|
|
|
851 |
|
pushed = strbuf_pushchars(buf, node->prefix); |
|
852 |
|
|
|
853 |
|
itr = node->values.values; |
|
854 |
|
itr_end = itr + node->values.len; |
|
855 |
|
for (; itr < itr_end; itr++) { |
|
856 |
|
write_str_safe(fd, buf->bytes, buf->used); |
|
857 |
|
write_str_safe(fd, " ", 1); |
|
858 |
|
write_str_safe(fd, itr->value, itr->len); |
|
859 |
|
write_str_safe(fd, "\n", 1); |
|
860 |
|
} |
|
861 |
|
|
|
862 |
|
for (ch = node->first; ch <= node->last; ch++) { |
|
863 |
|
struct index_mm_node *child = index_mm_readchild(node, ch); |
|
864 |
|
|
|
865 |
|
if (child == NULL) |
|
866 |
|
continue; |
|
867 |
|
|
|
868 |
|
strbuf_pushchar(buf, ch); |
|
869 |
|
index_mm_dump_node(child, buf, fd); |
|
870 |
|
strbuf_popchar(buf); |
|
871 |
|
} |
|
872 |
|
|
|
873 |
|
strbuf_popchars(buf, pushed); |
|
874 |
|
index_mm_free_node(node); |
|
875 |
|
} |
|
876 |
|
|
|
877 |
|
void index_mm_dump(struct index_mm *idx, int fd, const char *prefix) |
|
878 |
|
{ |
|
879 |
|
struct index_mm_node *root; |
|
880 |
|
struct strbuf buf; |
|
881 |
|
|
|
882 |
|
root = index_mm_readroot(idx); |
|
883 |
|
if (root == NULL) |
|
884 |
|
return; |
|
885 |
|
|
|
886 |
|
strbuf_init(&buf); |
|
887 |
|
strbuf_pushchars(&buf, prefix); |
|
888 |
|
index_mm_dump_node(root, &buf, fd); |
|
889 |
|
strbuf_release(&buf); |
|
890 |
|
} |
|
891 |
|
|
|
892 |
|
static char *index_mm_search_node(struct index_mm_node *node, const char *key, |
|
893 |
|
int i) |
|
894 |
|
{ |
|
895 |
|
char *value; |
|
896 |
|
struct index_mm_node *child; |
|
897 |
|
int ch; |
|
898 |
|
int j; |
|
899 |
|
|
|
900 |
|
while(node) { |
|
901 |
|
for (j = 0; node->prefix[j]; j++) { |
|
902 |
|
ch = node->prefix[j]; |
|
903 |
|
|
|
904 |
|
if (ch != key[i+j]) { |
|
905 |
|
index_mm_free_node(node); |
|
906 |
|
return NULL; |
|
907 |
|
} |
|
908 |
|
} |
|
909 |
|
|
|
910 |
|
i += j; |
|
911 |
|
|
|
912 |
|
if (key[i] == '\0') { |
|
913 |
|
value = node->values.len > 0 |
|
914 |
|
? strdup(node->values.values[0].value) |
|
915 |
|
: NULL; |
|
916 |
|
|
|
917 |
|
index_mm_free_node(node); |
|
918 |
|
return value; |
|
919 |
|
} |
|
920 |
|
|
|
921 |
|
child = index_mm_readchild(node, key[i]); |
|
922 |
|
index_mm_free_node(node); |
|
923 |
|
node = child; |
|
924 |
|
i++; |
|
925 |
|
} |
|
926 |
|
|
|
927 |
|
return NULL; |
|
928 |
|
} |
|
929 |
|
|
|
930 |
|
/* |
|
931 |
|
* Search the index for a key |
|
932 |
|
* |
|
933 |
|
* Returns the value of the first match |
|
934 |
|
* |
|
935 |
|
* The recursive functions free their node argument (using index_close). |
|
936 |
|
*/ |
|
937 |
|
char *index_mm_search(struct index_mm *idx, const char *key) |
|
938 |
|
{ |
|
939 |
|
// FIXME: return value by reference instead of strdup |
|
940 |
|
struct index_mm_node *root; |
|
941 |
|
char *value; |
|
942 |
|
|
|
943 |
|
root = index_mm_readroot(idx); |
|
944 |
|
value = index_mm_search_node(root, key, 0); |
|
945 |
|
|
|
946 |
|
return value; |
|
947 |
|
} |
|
948 |
|
|
|
949 |
|
/* Level 4: add all the values from a matching node */ |
|
950 |
|
static void index_mm_searchwild_allvalues(struct index_mm_node *node, |
|
951 |
|
struct index_value **out) |
|
952 |
|
{ |
|
953 |
|
struct index_mm_value *itr, *itr_end; |
|
954 |
|
|
|
955 |
|
itr = node->values.values; |
|
956 |
|
itr_end = itr + node->values.len; |
|
957 |
|
for (; itr < itr_end; itr++) |
|
958 |
|
add_value(out, itr->value, itr->len, itr->priority); |
|
959 |
|
|
|
960 |
|
index_mm_free_node(node); |
|
961 |
|
} |
|
962 |
|
|
|
963 |
|
/* |
|
964 |
|
* Level 3: traverse a sub-keyspace which starts with a wildcard, |
|
965 |
|
* looking for matches. |
|
966 |
|
*/ |
|
967 |
|
static void index_mm_searchwild_all(struct index_mm_node *node, int j, |
|
968 |
|
struct strbuf *buf, |
|
969 |
|
const char *subkey, |
|
970 |
|
struct index_value **out) |
|
971 |
|
{ |
|
972 |
|
int pushed = 0; |
|
973 |
|
int ch; |
|
974 |
|
|
|
975 |
|
while (node->prefix[j]) { |
|
976 |
|
ch = node->prefix[j]; |
|
977 |
|
|
|
978 |
|
strbuf_pushchar(buf, ch); |
|
979 |
|
pushed++; |
|
980 |
|
j++; |
|
981 |
|
} |
|
982 |
|
|
|
983 |
|
for (ch = node->first; ch <= node->last; ch++) { |
|
984 |
|
struct index_mm_node *child = index_mm_readchild(node, ch); |
|
985 |
|
|
|
986 |
|
if (!child) |
|
987 |
|
continue; |
|
988 |
|
|
|
989 |
|
strbuf_pushchar(buf, ch); |
|
990 |
|
index_mm_searchwild_all(child, 0, buf, subkey, out); |
|
991 |
|
strbuf_popchar(buf); |
|
992 |
|
} |
|
993 |
|
|
|
994 |
|
if (node->values.len > 0) { |
|
995 |
|
if (fnmatch(strbuf_str(buf), subkey, 0) == 0) |
|
996 |
|
index_mm_searchwild_allvalues(node, out); |
|
997 |
|
else |
|
998 |
|
index_mm_free_node(node); |
|
999 |
|
} else { |
|
1000 |
|
index_mm_free_node(node); |
|
1001 |
|
} |
|
1002 |
|
|
|
1003 |
|
strbuf_popchars(buf, pushed); |
|
1004 |
|
} |
|
1005 |
|
|
|
1006 |
|
/* Level 2: descend the tree (until we hit a wildcard) */ |
|
1007 |
|
static void index_mm_searchwild_node(struct index_mm_node *node, |
|
1008 |
|
struct strbuf *buf, |
|
1009 |
|
const char *key, int i, |
|
1010 |
|
struct index_value **out) |
|
1011 |
|
{ |
|
1012 |
|
struct index_mm_node *child; |
|
1013 |
|
int j; |
|
1014 |
|
int ch; |
|
1015 |
|
|
|
1016 |
|
while(node) { |
|
1017 |
|
for (j = 0; node->prefix[j]; j++) { |
|
1018 |
|
ch = node->prefix[j]; |
|
1019 |
|
|
|
1020 |
|
if (ch == '*' || ch == '?' || ch == '[') { |
|
1021 |
|
index_mm_searchwild_all(node, j, buf, |
|
1022 |
|
&key[i+j], out); |
|
1023 |
|
return; |
|
1024 |
|
} |
|
1025 |
|
|
|
1026 |
|
if (ch != key[i+j]) { |
|
1027 |
|
index_mm_free_node(node); |
|
1028 |
|
return; |
|
1029 |
|
} |
|
1030 |
|
} |
|
1031 |
|
|
|
1032 |
|
i += j; |
|
1033 |
|
|
|
1034 |
|
child = index_mm_readchild(node, '*'); |
|
1035 |
|
if (child) { |
|
1036 |
|
strbuf_pushchar(buf, '*'); |
|
1037 |
|
index_mm_searchwild_all(child, 0, buf, &key[i], out); |
|
1038 |
|
strbuf_popchar(buf); |
|
1039 |
|
} |
|
1040 |
|
|
|
1041 |
|
child = index_mm_readchild(node, '?'); |
|
1042 |
|
if (child) { |
|
1043 |
|
strbuf_pushchar(buf, '?'); |
|
1044 |
|
index_mm_searchwild_all(child, 0, buf, &key[i], out); |
|
1045 |
|
strbuf_popchar(buf); |
|
1046 |
|
} |
|
1047 |
|
|
|
1048 |
|
child = index_mm_readchild(node, '['); |
|
1049 |
|
if (child) { |
|
1050 |
|
strbuf_pushchar(buf, '['); |
|
1051 |
|
index_mm_searchwild_all(child, 0, buf, &key[i], out); |
|
1052 |
|
strbuf_popchar(buf); |
|
1053 |
|
} |
|
1054 |
|
|
|
1055 |
|
if (key[i] == '\0') { |
|
1056 |
|
index_mm_searchwild_allvalues(node, out); |
|
1057 |
|
|
|
1058 |
|
return; |
|
1059 |
|
} |
|
1060 |
|
|
|
1061 |
|
child = index_mm_readchild(node, key[i]); |
|
1062 |
|
index_mm_free_node(node); |
|
1063 |
|
node = child; |
|
1064 |
|
i++; |
|
1065 |
|
} |
|
1066 |
|
} |
|
1067 |
|
|
|
1068 |
|
/* |
|
1069 |
|
* Search the index for a key. The index may contain wildcards. |
|
1070 |
|
* |
|
1071 |
|
* Returns a list of all the values of matching keys. |
|
1072 |
|
*/ |
|
1073 |
|
struct index_value *index_mm_searchwild(struct index_mm *idx, const char *key) |
|
1074 |
|
{ |
|
1075 |
|
struct index_mm_node *root = index_mm_readroot(idx); |
|
1076 |
|
struct strbuf buf; |
|
1077 |
|
struct index_value *out = NULL; |
|
1078 |
|
|
|
1079 |
|
strbuf_init(&buf); |
|
1080 |
|
index_mm_searchwild_node(root, &buf, key, 0, &out); |
|
1081 |
|
strbuf_release(&buf); |
|
1082 |
|
return out; |
|
1083 |
|
} |
File src/libkmod-module.c added (mode: 100644) (index 0000000..a632059) |
|
1 |
|
/* |
|
2 |
|
* libkmod - interface to kernel module operations |
|
3 |
|
* |
|
4 |
|
* Copyright (C) 2011-2013 ProFUSION embedded systems |
|
5 |
|
* |
|
6 |
|
* This library is free software; you can redistribute it and/or |
|
7 |
|
* modify it under the terms of the GNU Lesser General Public |
|
8 |
|
* License as published by the Free Software Foundation; either |
|
9 |
|
* version 2.1 of the License, or (at your option) any later version. |
|
10 |
|
* |
|
11 |
|
* This library is distributed in the hope that it will be useful, |
|
12 |
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
13 |
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
|
14 |
|
* Lesser General Public License for more details. |
|
15 |
|
* |
|
16 |
|
* You should have received a copy of the GNU Lesser General Public |
|
17 |
|
* License along with this library; if not, see <http://www.gnu.org/licenses/>. |
|
18 |
|
*/ |
|
19 |
|
|
|
20 |
|
#include <assert.h> |
|
21 |
|
#include <ctype.h> |
|
22 |
|
#include <dirent.h> |
|
23 |
|
#include <errno.h> |
|
24 |
|
#include <fnmatch.h> |
|
25 |
|
#include <inttypes.h> |
|
26 |
|
#include <limits.h> |
|
27 |
|
#include <stdarg.h> |
|
28 |
|
#include <stddef.h> |
|
29 |
|
#include <stdio.h> |
|
30 |
|
#include <stdlib.h> |
|
31 |
|
#include <string.h> |
|
32 |
|
#include <unistd.h> |
|
33 |
|
#include <syslog.h> |
|
34 |
|
#include <sys/mman.h> |
|
35 |
|
#include <sys/stat.h> |
|
36 |
|
#include <sys/syscall.h> |
|
37 |
|
#include <sys/types.h> |
|
38 |
|
#include <sys/wait.h> |
|
39 |
|
#include <linux/module.h> |
|
40 |
|
|
|
41 |
|
#include "config.h" |
|
42 |
|
|
|
43 |
|
#include "libkmod.h" |
|
44 |
|
#include "libkmod-namespace.h" |
|
45 |
|
#include "libkmod-paths.h" |
|
46 |
|
#include "shared/linux-syscalls.h" |
|
47 |
|
#include "shared/macro.h" |
|
48 |
|
#include "shared/util.h" |
|
49 |
|
#define LIBKMOD_MODULE_C |
|
50 |
|
#include "libkmod-internal.h" |
|
51 |
|
#undef LIBKMOD_MODULE_C |
|
52 |
|
|
|
53 |
|
/** |
|
54 |
|
* SECTION:libkmod-module |
|
55 |
|
* @short_description: operate on kernel modules |
|
56 |
|
*/ |
|
57 |
|
|
|
58 |
|
enum kmod_module_builtin { |
|
59 |
|
KMOD_MODULE_BUILTIN_UNKNOWN, |
|
60 |
|
KMOD_MODULE_BUILTIN_NO, |
|
61 |
|
KMOD_MODULE_BUILTIN_YES, |
|
62 |
|
}; |
|
63 |
|
|
|
64 |
|
/** |
|
65 |
|
* kmod_module: |
|
66 |
|
* |
|
67 |
|
* Opaque object representing a module. |
|
68 |
|
*/ |
|
69 |
|
struct kmod_module { |
|
70 |
|
struct kmod_ctx *ctx; |
|
71 |
|
char *hashkey; |
|
72 |
|
char *name; |
|
73 |
|
char *path; |
|
74 |
|
struct kmod_list *dep; |
|
75 |
|
char *options; |
|
76 |
|
const char *install_commands; /* owned by kmod_config */ |
|
77 |
|
const char *remove_commands; /* owned by kmod_config */ |
|
78 |
|
char *alias; /* only set if this module was created from an alias */ |
|
79 |
|
struct kmod_file *file; |
|
80 |
|
int n_dep; |
|
81 |
|
int refcount; |
|
82 |
|
struct { |
|
83 |
|
bool dep : 1; |
|
84 |
|
bool options : 1; |
|
85 |
|
bool install_commands : 1; |
|
86 |
|
bool remove_commands : 1; |
|
87 |
|
} init; |
|
88 |
|
|
|
89 |
|
/* |
|
90 |
|
* mark if module is builtin, i.e. it's present on modules.builtin |
|
91 |
|
* file. This is set as soon as it is needed or as soon as we know |
|
92 |
|
* about it, i.e. the module was created from builtin lookup. |
|
93 |
|
*/ |
|
94 |
|
enum kmod_module_builtin builtin; |
|
95 |
|
|
|
96 |
|
/* |
|
97 |
|
* private field used by kmod_module_get_probe_list() to detect |
|
98 |
|
* dependency loops |
|
99 |
|
*/ |
|
100 |
|
bool visited : 1; |
|
101 |
|
|
|
102 |
|
/* |
|
103 |
|
* set by kmod_module_get_probe_list: indicates for probe_insert() |
|
104 |
|
* whether the module's command and softdep should be ignored |
|
105 |
|
*/ |
|
106 |
|
bool ignorecmd : 1; |
|
107 |
|
|
|
108 |
|
/* |
|
109 |
|
* set by kmod_module_get_probe_list: indicates whether this is the |
|
110 |
|
* module the user asked for or its dependency, or whether this |
|
111 |
|
* is a softdep only |
|
112 |
|
*/ |
|
113 |
|
bool required : 1; |
|
114 |
|
}; |
|
115 |
|
|
|
116 |
|
static inline const char *path_join(const char *path, size_t prefixlen, |
|
117 |
|
char buf[PATH_MAX]) |
|
118 |
|
{ |
|
119 |
|
size_t pathlen; |
|
120 |
|
|
|
121 |
|
if (path[0] == '/') |
|
122 |
|
return path; |
|
123 |
|
|
|
124 |
|
pathlen = strlen(path); |
|
125 |
|
if (prefixlen + pathlen + 1 >= PATH_MAX) |
|
126 |
|
return NULL; |
|
127 |
|
|
|
128 |
|
memcpy(buf + prefixlen, path, pathlen + 1); |
|
129 |
|
return buf; |
|
130 |
|
} |
|
131 |
|
|
|
132 |
|
static inline bool module_is_inkernel(struct kmod_module *mod) |
|
133 |
|
{ |
|
134 |
|
int state = kmod_module_get_initstate(mod); |
|
135 |
|
|
|
136 |
|
if (state == KMOD_MODULE_LIVE || |
|
137 |
|
state == KMOD_MODULE_BUILTIN) |
|
138 |
|
return true; |
|
139 |
|
|
|
140 |
|
return false; |
|
141 |
|
} |
|
142 |
|
|
|
143 |
|
int kmod_module_parse_depline(struct kmod_module *mod, char *line) |
|
144 |
|
{ |
|
145 |
|
struct kmod_ctx *ctx = mod->ctx; |
|
146 |
|
struct kmod_list *list = NULL; |
|
147 |
|
const char *dirname; |
|
148 |
|
char buf[PATH_MAX]; |
|
149 |
|
char *p, *saveptr; |
|
150 |
|
int err = 0, n = 0; |
|
151 |
|
size_t dirnamelen; |
|
152 |
|
|
|
153 |
|
if (mod->init.dep) |
|
154 |
|
return mod->n_dep; |
|
155 |
|
assert(mod->dep == NULL); |
|
156 |
|
mod->init.dep = true; |
|
157 |
|
|
|
158 |
|
p = strchr(line, ':'); |
|
159 |
|
if (p == NULL) |
|
160 |
|
return 0; |
|
161 |
|
|
|
162 |
|
*p = '\0'; |
|
163 |
|
dirname = kmod_get_dirname(mod->ctx); |
|
164 |
|
dirnamelen = strlen(dirname); |
|
165 |
|
if (dirnamelen + 2 >= PATH_MAX) |
|
166 |
|
return 0; |
|
167 |
|
|
|
168 |
|
memcpy(buf, dirname, dirnamelen); |
|
169 |
|
buf[dirnamelen] = '/'; |
|
170 |
|
dirnamelen++; |
|
171 |
|
buf[dirnamelen] = '\0'; |
|
172 |
|
|
|
173 |
|
if (mod->path == NULL) { |
|
174 |
|
const char *str = path_join(line, dirnamelen, buf); |
|
175 |
|
if (str == NULL) |
|
176 |
|
return 0; |
|
177 |
|
mod->path = strdup(str); |
|
178 |
|
if (mod->path == NULL) |
|
179 |
|
return 0; |
|
180 |
|
} |
|
181 |
|
|
|
182 |
|
p++; |
|
183 |
|
for (p = strtok_r(p, " \t", &saveptr); p != NULL; |
|
184 |
|
p = strtok_r(NULL, " \t", &saveptr)) { |
|
185 |
|
struct kmod_module *depmod = NULL; |
|
186 |
|
const char *path; |
|
187 |
|
|
|
188 |
|
path = path_join(p, dirnamelen, buf); |
|
189 |
|
if (path == NULL) { |
|
190 |
|
ERR(ctx, "could not join path '%s' and '%s'.\n", |
|
191 |
|
dirname, p); |
|
192 |
|
goto fail; |
|
193 |
|
} |
|
194 |
|
|
|
195 |
|
err = kmod_module_new_from_path(ctx, path, &depmod); |
|
196 |
|
if (err < 0) { |
|
197 |
|
ERR(ctx, "ctx=%p path=%s error=%s\n", |
|
198 |
|
ctx, path, strerror(-err)); |
|
199 |
|
goto fail; |
|
200 |
|
} |
|
201 |
|
|
|
202 |
|
DBG(ctx, "add dep: %s\n", path); |
|
203 |
|
|
|
204 |
|
list = kmod_list_prepend(list, depmod); |
|
205 |
|
n++; |
|
206 |
|
} |
|
207 |
|
|
|
208 |
|
DBG(ctx, "%d dependencies for %s\n", n, mod->name); |
|
209 |
|
|
|
210 |
|
mod->dep = list; |
|
211 |
|
mod->n_dep = n; |
|
212 |
|
return n; |
|
213 |
|
|
|
214 |
|
fail: |
|
215 |
|
kmod_module_unref_list(list); |
|
216 |
|
mod->init.dep = false; |
|
217 |
|
return err; |
|
218 |
|
} |
|
219 |
|
|
|
220 |
|
void kmod_module_set_visited(struct kmod_module *mod, bool visited) |
|
221 |
|
{ |
|
222 |
|
mod->visited = visited; |
|
223 |
|
} |
|
224 |
|
|
|
225 |
|
void kmod_module_set_builtin(struct kmod_module *mod, bool builtin) |
|
226 |
|
{ |
|
227 |
|
mod->builtin = |
|
228 |
|
builtin ? KMOD_MODULE_BUILTIN_YES : KMOD_MODULE_BUILTIN_NO; |
|
229 |
|
} |
|
230 |
|
|
|
231 |
|
void kmod_module_set_required(struct kmod_module *mod, bool required) |
|
232 |
|
{ |
|
233 |
|
mod->required = required; |
|
234 |
|
} |
|
235 |
|
|
|
236 |
|
bool kmod_module_is_builtin(struct kmod_module *mod) |
|
237 |
|
{ |
|
238 |
|
if (mod->builtin == KMOD_MODULE_BUILTIN_UNKNOWN) { |
|
239 |
|
kmod_module_set_builtin(mod, |
|
240 |
|
kmod_lookup_alias_is_builtin(mod->ctx, mod->name)); |
|
241 |
|
} |
|
242 |
|
|
|
243 |
|
return mod->builtin == KMOD_MODULE_BUILTIN_YES; |
|
244 |
|
} |
|
245 |
|
/* |
|
246 |
|
* Memory layout with alias: |
|
247 |
|
* |
|
248 |
|
* struct kmod_module { |
|
249 |
|
* hashkey -----. |
|
250 |
|
* alias -----. | |
|
251 |
|
* name ----. | | |
|
252 |
|
* } | | | |
|
253 |
|
* name <----------' | | |
|
254 |
|
* alias <-----------' | |
|
255 |
|
* name\alias <--------' |
|
256 |
|
* |
|
257 |
|
* Memory layout without alias: |
|
258 |
|
* |
|
259 |
|
* struct kmod_module { |
|
260 |
|
* hashkey ---. |
|
261 |
|
* alias -----|----> NULL |
|
262 |
|
* name ----. | |
|
263 |
|
* } | | |
|
264 |
|
* name <----------'-' |
|
265 |
|
* |
|
266 |
|
* @key is "name\alias" or "name" (in which case alias == NULL) |
|
267 |
|
*/ |
|
268 |
|
static int kmod_module_new(struct kmod_ctx *ctx, const char *key, |
|
269 |
|
const char *name, size_t namelen, |
|
270 |
|
const char *alias, size_t aliaslen, |
|
271 |
|
struct kmod_module **mod) |
|
272 |
|
{ |
|
273 |
|
struct kmod_module *m; |
|
274 |
|
size_t keylen; |
|
275 |
|
|
|
276 |
|
m = kmod_pool_get_module(ctx, key); |
|
277 |
|
if (m != NULL) { |
|
278 |
|
*mod = kmod_module_ref(m); |
|
279 |
|
return 0; |
|
280 |
|
} |
|
281 |
|
|
|
282 |
|
if (alias == NULL) |
|
283 |
|
keylen = namelen; |
|
284 |
|
else |
|
285 |
|
keylen = namelen + aliaslen + 1; |
|
286 |
|
|
|
287 |
|
m = malloc(sizeof(*m) + (alias == NULL ? 1 : 2) * (keylen + 1)); |
|
288 |
|
if (m == NULL) |
|
289 |
|
return -ENOMEM; |
|
290 |
|
|
|
291 |
|
memset(m, 0, sizeof(*m)); |
|
292 |
|
|
|
293 |
|
m->ctx = kmod_ref(ctx); |
|
294 |
|
m->name = (char *)m + sizeof(*m); |
|
295 |
|
memcpy(m->name, key, keylen + 1); |
|
296 |
|
if (alias == NULL) { |
|
297 |
|
m->hashkey = m->name; |
|
298 |
|
m->alias = NULL; |
|
299 |
|
} else { |
|
300 |
|
m->name[namelen] = '\0'; |
|
301 |
|
m->alias = m->name + namelen + 1; |
|
302 |
|
m->hashkey = m->name + keylen + 1; |
|
303 |
|
memcpy(m->hashkey, key, keylen + 1); |
|
304 |
|
} |
|
305 |
|
|
|
306 |
|
m->refcount = 1; |
|
307 |
|
kmod_pool_add_module(ctx, m, m->hashkey); |
|
308 |
|
*mod = m; |
|
309 |
|
|
|
310 |
|
return 0; |
|
311 |
|
} |
|
312 |
|
|
|
313 |
|
/** |
|
314 |
|
* kmod_module_new_from_name: |
|
315 |
|
* @ctx: kmod library context |
|
316 |
|
* @name: name of the module |
|
317 |
|
* @mod: where to save the created struct kmod_module |
|
318 |
|
* |
|
319 |
|
* Create a new struct kmod_module using the module name. @name can not be an |
|
320 |
|
* alias, file name or anything else; it must be a module name. There's no |
|
321 |
|
* check if the module exists in the system. |
|
322 |
|
* |
|
323 |
|
* This function is also used internally by many others that return a new |
|
324 |
|
* struct kmod_module or a new list of modules. |
|
325 |
|
* |
|
326 |
|
* The initial refcount is 1, and needs to be decremented to release the |
|
327 |
|
* resources of the kmod_module. Since libkmod keeps track of all |
|
328 |
|
* kmod_modules created, they are all released upon @ctx destruction too. Do |
|
329 |
|
* not unref @ctx before all the desired operations with the returned |
|
330 |
|
* kmod_module are done. |
|
331 |
|
* |
|
332 |
|
* Returns: 0 on success or < 0 otherwise. It fails if name is not a valid |
|
333 |
|
* module name or if memory allocation failed. |
|
334 |
|
*/ |
|
335 |
|
int kmod_module_new_from_name(struct kmod_ctx *ctx, |
|
336 |
|
const char *name, |
|
337 |
|
struct kmod_module **mod) |
|
338 |
|
{ |
|
339 |
|
size_t namelen; |
|
340 |
|
char name_norm[PATH_MAX]; |
|
341 |
|
|
|
342 |
|
if (ctx == NULL || name == NULL || mod == NULL) |
|
343 |
|
return -ENOENT; |
|
344 |
|
|
|
345 |
|
modname_normalize(name, name_norm, &namelen); |
|
346 |
|
|
|
347 |
|
return kmod_module_new(ctx, name_norm, name_norm, namelen, NULL, 0, mod); |
|
348 |
|
} |
|
349 |
|
|
|
350 |
|
int kmod_module_new_from_alias(struct kmod_ctx *ctx, const char *alias, |
|
351 |
|
const char *name, struct kmod_module **mod) |
|
352 |
|
{ |
|
353 |
|
int err; |
|
354 |
|
char key[PATH_MAX]; |
|
355 |
|
size_t namelen = strlen(name); |
|
356 |
|
size_t aliaslen = strlen(alias); |
|
357 |
|
|
|
358 |
|
if (namelen + aliaslen + 2 > PATH_MAX) |
|
359 |
|
return -ENAMETOOLONG; |
|
360 |
|
|
|
361 |
|
memcpy(key, name, namelen); |
|
362 |
|
memcpy(key + namelen + 1, alias, aliaslen + 1); |
|
363 |
|
key[namelen] = '\\'; |
|
364 |
|
|
|
365 |
|
err = kmod_module_new(ctx, key, name, namelen, alias, aliaslen, mod); |
|
366 |
|
if (err < 0) |
|
367 |
|
return err; |
|
368 |
|
|
|
369 |
|
return 0; |
|
370 |
|
} |
|
371 |
|
|
|
372 |
|
/** |
|
373 |
|
* kmod_module_new_from_path: |
|
374 |
|
* @ctx: kmod library context |
|
375 |
|
* @path: path where to find the given module |
|
376 |
|
* @mod: where to save the created struct kmod_module |
|
377 |
|
* |
|
378 |
|
* Create a new struct kmod_module using the module path. @path must be an |
|
379 |
|
* existent file with in the filesystem and must be accessible to libkmod. |
|
380 |
|
* |
|
381 |
|
* The initial refcount is 1, and needs to be decremented to release the |
|
382 |
|
* resources of the kmod_module. Since libkmod keeps track of all |
|
383 |
|
* kmod_modules created, they are all released upon @ctx destruction too. Do |
|
384 |
|
* not unref @ctx before all the desired operations with the returned |
|
385 |
|
* kmod_module are done. |
|
386 |
|
* |
|
387 |
|
* If @path is relative, it's treated as relative to the current working |
|
388 |
|
* directory. Otherwise, give an absolute path. |
|
389 |
|
* |
|
390 |
|
* Returns: 0 on success or < 0 otherwise. It fails if file does not exist, if |
|
391 |
|
* it's not a valid file for a kmod_module or if memory allocation failed. |
|
392 |
|
*/ |
|
393 |
|
int kmod_module_new_from_path(struct kmod_ctx *ctx, |
|
394 |
|
const char *path, |
|
395 |
|
struct kmod_module **mod) |
|
396 |
|
{ |
|
397 |
|
struct kmod_module *m; |
|
398 |
|
int err; |
|
399 |
|
struct stat st; |
|
400 |
|
char name[PATH_MAX]; |
|
401 |
|
char *abspath; |
|
402 |
|
size_t namelen; |
|
403 |
|
|
|
404 |
|
if (ctx == NULL || path == NULL || mod == NULL) |
|
405 |
|
return -ENOENT; |
|
406 |
|
|
|
407 |
|
abspath = path_make_absolute_cwd(path); |
|
408 |
|
if (abspath == NULL) { |
|
409 |
|
DBG(ctx, "no absolute path for %s\n", path); |
|
410 |
|
return -ENOMEM; |
|
411 |
|
} |
|
412 |
|
|
|
413 |
|
err = stat(abspath, &st); |
|
414 |
|
if (err < 0) { |
|
415 |
|
err = -errno; |
|
416 |
|
DBG(ctx, "stat %s: %s\n", path, strerror(errno)); |
|
417 |
|
free(abspath); |
|
418 |
|
return err; |
|
419 |
|
} |
|
420 |
|
|
|
421 |
|
if (path_to_modname(path, name, &namelen) == NULL) { |
|
422 |
|
DBG(ctx, "could not get modname from path %s\n", path); |
|
423 |
|
free(abspath); |
|
424 |
|
return -ENOENT; |
|
425 |
|
} |
|
426 |
|
|
|
427 |
|
m = kmod_pool_get_module(ctx, name); |
|
428 |
|
if (m != NULL) { |
|
429 |
|
if (m->path == NULL) |
|
430 |
|
m->path = abspath; |
|
431 |
|
else if (streq(m->path, abspath)) |
|
432 |
|
free(abspath); |
|
433 |
|
else { |
|
434 |
|
ERR(ctx, "kmod_module '%s' already exists with different path: new-path='%s' old-path='%s'\n", |
|
435 |
|
name, abspath, m->path); |
|
436 |
|
free(abspath); |
|
437 |
|
return -EEXIST; |
|
438 |
|
} |
|
439 |
|
|
|
440 |
|
*mod = kmod_module_ref(m); |
|
441 |
|
return 0; |
|
442 |
|
} |
|
443 |
|
|
|
444 |
|
err = kmod_module_new(ctx, name, name, namelen, NULL, 0, &m); |
|
445 |
|
if (err < 0) { |
|
446 |
|
free(abspath); |
|
447 |
|
return err; |
|
448 |
|
} |
|
449 |
|
|
|
450 |
|
m->path = abspath; |
|
451 |
|
*mod = m; |
|
452 |
|
|
|
453 |
|
return 0; |
|
454 |
|
} |
|
455 |
|
|
|
456 |
|
/** |
|
457 |
|
* kmod_module_unref: |
|
458 |
|
* @mod: kmod module |
|
459 |
|
* |
|
460 |
|
* Drop a reference of the kmod module. If the refcount reaches zero, its |
|
461 |
|
* resources are released. |
|
462 |
|
* |
|
463 |
|
* Returns: NULL if @mod is NULL or if the module was released. Otherwise it |
|
464 |
|
* returns the passed @mod with its refcount decremented. |
|
465 |
|
*/ |
|
466 |
|
struct kmod_module *kmod_module_unref(struct kmod_module *mod) |
|
467 |
|
{ |
|
468 |
|
if (mod == NULL) |
|
469 |
|
return NULL; |
|
470 |
|
|
|
471 |
|
if (--mod->refcount > 0) |
|
472 |
|
return mod; |
|
473 |
|
|
|
474 |
|
DBG(mod->ctx, "kmod_module %p released\n", mod); |
|
475 |
|
|
|
476 |
|
kmod_pool_del_module(mod->ctx, mod, mod->hashkey); |
|
477 |
|
kmod_module_unref_list(mod->dep); |
|
478 |
|
|
|
479 |
|
if (mod->file) |
|
480 |
|
kmod_file_unref(mod->file); |
|
481 |
|
|
|
482 |
|
kmod_unref(mod->ctx); |
|
483 |
|
free(mod->options); |
|
484 |
|
free(mod->path); |
|
485 |
|
free(mod); |
|
486 |
|
return NULL; |
|
487 |
|
} |
|
488 |
|
|
|
489 |
|
/** |
|
490 |
|
* kmod_module_ref: |
|
491 |
|
* @mod: kmod module |
|
492 |
|
* |
|
493 |
|
* Take a reference of the kmod module, incrementing its refcount. |
|
494 |
|
* |
|
495 |
|
* Returns: the passed @module with its refcount incremented. |
|
496 |
|
*/ |
|
497 |
|
struct kmod_module *kmod_module_ref(struct kmod_module *mod) |
|
498 |
|
{ |
|
499 |
|
if (mod == NULL) |
|
500 |
|
return NULL; |
|
501 |
|
|
|
502 |
|
mod->refcount++; |
|
503 |
|
|
|
504 |
|
return mod; |
|
505 |
|
} |
|
506 |
|
|
|
507 |
|
#define CHECK_ERR_AND_FINISH(_err, _label_err, _list, label_finish) \ |
|
508 |
|
do { \ |
|
509 |
|
if ((_err) < 0) \ |
|
510 |
|
goto _label_err; \ |
|
511 |
|
if (*(_list) != NULL) \ |
|
512 |
|
goto finish; \ |
|
513 |
|
} while (0) |
|
514 |
|
|
|
515 |
|
/** |
|
516 |
|
* kmod_module_new_from_lookup: |
|
517 |
|
* @ctx: kmod library context |
|
518 |
|
* @given_alias: alias to look for |
|
519 |
|
* @list: an empty list where to save the list of modules matching |
|
520 |
|
* @given_alias |
|
521 |
|
* |
|
522 |
|
* Create a new list of kmod modules using an alias or module name and lookup |
|
523 |
|
* libkmod's configuration files and indexes in order to find the module. |
|
524 |
|
* Once it's found in one of the places, it stops searching and create the |
|
525 |
|
* list of modules that is saved in @list. |
|
526 |
|
* |
|
527 |
|
* The search order is: 1. aliases in configuration file; 2. module names in |
|
528 |
|
* modules.dep index; 3. symbol aliases in modules.symbols index; 4. aliases |
|
529 |
|
* in modules.alias index. |
|
530 |
|
* |
|
531 |
|
* The initial refcount is 1, and needs to be decremented to release the |
|
532 |
|
* resources of the kmod_module. The returned @list must be released by |
|
533 |
|
* calling kmod_module_unref_list(). Since libkmod keeps track of all |
|
534 |
|
* kmod_modules created, they are all released upon @ctx destruction too. Do |
|
535 |
|
* not unref @ctx before all the desired operations with the returned list are |
|
536 |
|
* completed. |
|
537 |
|
* |
|
538 |
|
* Returns: 0 on success or < 0 otherwise. It fails if any of the lookup |
|
539 |
|
* methods failed, which is basically due to memory allocation fail. If module |
|
540 |
|
* is not found, it still returns 0, but @list is an empty list. |
|
541 |
|
*/ |
|
542 |
|
int kmod_module_new_from_lookup(struct kmod_ctx *ctx, |
|
543 |
|
const char *given_alias, |
|
544 |
|
struct kmod_list **list) |
|
545 |
|
{ |
|
546 |
|
int err; |
|
547 |
|
char alias[PATH_MAX]; |
|
548 |
|
|
|
549 |
|
if (ctx == NULL || given_alias == NULL) |
|
550 |
|
return -ENOENT; |
|
551 |
|
|
|
552 |
|
if (list == NULL || *list != NULL) { |
|
553 |
|
ERR(ctx, "An empty list is needed to create lookup\n"); |
|
554 |
|
return -ENOSYS; |
|
555 |
|
} |
|
556 |
|
|
|
557 |
|
if (alias_normalize(given_alias, alias, NULL) < 0) { |
|
558 |
|
DBG(ctx, "invalid alias: %s\n", given_alias); |
|
559 |
|
return -EINVAL; |
|
560 |
|
} |
|
561 |
|
|
|
562 |
|
DBG(ctx, "input alias=%s, normalized=%s\n", given_alias, alias); |
|
563 |
|
|
|
564 |
|
/* Aliases from config file override all the others */ |
|
565 |
|
err = kmod_lookup_alias_from_config(ctx, alias, list); |
|
566 |
|
CHECK_ERR_AND_FINISH(err, fail, list, finish); |
|
567 |
|
|
|
568 |
|
DBG(ctx, "lookup modules.dep %s\n", alias); |
|
569 |
|
err = kmod_lookup_alias_from_moddep_file(ctx, alias, list); |
|
570 |
|
CHECK_ERR_AND_FINISH(err, fail, list, finish); |
|
571 |
|
|
|
572 |
|
DBG(ctx, "lookup modules.symbols %s\n", alias); |
|
573 |
|
err = kmod_lookup_alias_from_symbols_file(ctx, alias, list); |
|
574 |
|
CHECK_ERR_AND_FINISH(err, fail, list, finish); |
|
575 |
|
|
|
576 |
|
DBG(ctx, "lookup install and remove commands %s\n", alias); |
|
577 |
|
err = kmod_lookup_alias_from_commands(ctx, alias, list); |
|
578 |
|
CHECK_ERR_AND_FINISH(err, fail, list, finish); |
|
579 |
|
|
|
580 |
|
DBG(ctx, "lookup modules.aliases %s\n", alias); |
|
581 |
|
err = kmod_lookup_alias_from_aliases_file(ctx, alias, list); |
|
582 |
|
CHECK_ERR_AND_FINISH(err, fail, list, finish); |
|
583 |
|
|
|
584 |
|
DBG(ctx, "lookup modules.builtin %s\n", alias); |
|
585 |
|
err = kmod_lookup_alias_from_builtin_file(ctx, alias, list); |
|
586 |
|
CHECK_ERR_AND_FINISH(err, fail, list, finish); |
|
587 |
|
|
|
588 |
|
finish: |
|
589 |
|
DBG(ctx, "lookup %s=%d, list=%p\n", alias, err, *list); |
|
590 |
|
return err; |
|
591 |
|
fail: |
|
592 |
|
DBG(ctx, "Failed to lookup %s\n", alias); |
|
593 |
|
kmod_module_unref_list(*list); |
|
594 |
|
*list = NULL; |
|
595 |
|
return err; |
|
596 |
|
} |
|
597 |
|
#undef CHECK_ERR_AND_FINISH |
|
598 |
|
|
|
599 |
|
/** |
|
600 |
|
* kmod_module_unref_list: |
|
601 |
|
* @list: list of kmod modules |
|
602 |
|
* |
|
603 |
|
* Drop a reference of each kmod module in @list and releases the resources |
|
604 |
|
* taken by the list itself. |
|
605 |
|
* |
|
606 |
|
* Returns: 0 |
|
607 |
|
*/ |
|
608 |
|
int kmod_module_unref_list(struct kmod_list *list) |
|
609 |
|
{ |
|
610 |
|
for (; list != NULL; list = __kmod_list_remove(list)) |
|
611 |
|
kmod_module_unref(list->data); |
|
612 |
|
|
|
613 |
|
return 0; |
|
614 |
|
} |
|
615 |
|
|
|
616 |
|
/** |
|
617 |
|
* kmod_module_get_filtered_blacklist: |
|
618 |
|
* @ctx: kmod library context |
|
619 |
|
* @input: list of kmod_module to be filtered with blacklist |
|
620 |
|
* @output: where to save the new list |
|
621 |
|
* |
|
622 |
|
* This function should not be used. Use kmod_module_apply_filter instead. |
|
623 |
|
* |
|
624 |
|
* Given a list @input, this function filter it out with config's blacklist |
|
625 |
|
* and save it in @output. |
|
626 |
|
* |
|
627 |
|
* Returns: 0 on success or < 0 otherwise. @output is saved with the updated |
|
628 |
|
* list. |
|
629 |
|
*/ |
|
630 |
|
int kmod_module_get_filtered_blacklist(const struct kmod_ctx *ctx, |
|
631 |
|
const struct kmod_list *input, |
|
632 |
|
struct kmod_list **output) |
|
633 |
|
{ |
|
634 |
|
return kmod_module_apply_filter(ctx, KMOD_FILTER_BLACKLIST, input, output); |
|
635 |
|
} |
|
636 |
|
|
|
637 |
|
static const struct kmod_list *module_get_dependencies_noref(const struct kmod_module *mod) |
|
638 |
|
{ |
|
639 |
|
if (!mod->init.dep) { |
|
640 |
|
/* lazy init */ |
|
641 |
|
char *line = kmod_search_moddep(mod->ctx, mod->name); |
|
642 |
|
|
|
643 |
|
if (line == NULL) |
|
644 |
|
return NULL; |
|
645 |
|
|
|
646 |
|
kmod_module_parse_depline((struct kmod_module *)mod, line); |
|
647 |
|
free(line); |
|
648 |
|
|
|
649 |
|
if (!mod->init.dep) |
|
650 |
|
return NULL; |
|
651 |
|
} |
|
652 |
|
|
|
653 |
|
return mod->dep; |
|
654 |
|
} |
|
655 |
|
|
|
656 |
|
/** |
|
657 |
|
* kmod_module_get_dependencies: |
|
658 |
|
* @mod: kmod module |
|
659 |
|
* |
|
660 |
|
* Search the modules.dep index to find the dependencies of the given @mod. |
|
661 |
|
* The result is cached in @mod, so subsequent calls to this function will |
|
662 |
|
* return the already searched list of modules. |
|
663 |
|
* |
|
664 |
|
* Returns: NULL on failure. Otherwise it returns a list of kmod modules |
|
665 |
|
* that can be released by calling kmod_module_unref_list(). |
|
666 |
|
*/ |
|
667 |
|
struct kmod_list *kmod_module_get_dependencies(const struct kmod_module *mod) |
|
668 |
|
{ |
|
669 |
|
struct kmod_list *l, *l_new, *list_new = NULL; |
|
670 |
|
|
|
671 |
|
if (mod == NULL) |
|
672 |
|
return NULL; |
|
673 |
|
|
|
674 |
|
module_get_dependencies_noref(mod); |
|
675 |
|
|
|
676 |
|
kmod_list_foreach(l, mod->dep) { |
|
677 |
|
l_new = __kmod_list_append(list_new, kmod_module_ref(l->data)); |
|
678 |
|
if (l_new == NULL) { |
|
679 |
|
kmod_module_unref(l->data); |
|
680 |
|
goto fail; |
|
681 |
|
} |
|
682 |
|
|
|
683 |
|
list_new = l_new; |
|
684 |
|
} |
|
685 |
|
|
|
686 |
|
return list_new; |
|
687 |
|
|
|
688 |
|
fail: |
|
689 |
|
ERR(mod->ctx, "out of memory\n"); |
|
690 |
|
kmod_module_unref_list(list_new); |
|
691 |
|
return NULL; |
|
692 |
|
} |
|
693 |
|
|
|
694 |
|
/** |
|
695 |
|
* kmod_module_get_module: |
|
696 |
|
* @entry: an entry in a list of kmod modules. |
|
697 |
|
* |
|
698 |
|
* Get the kmod module of this @entry in the list, increasing its refcount. |
|
699 |
|
* After it's used, unref it. Since the refcount is incremented upon return, |
|
700 |
|
* you still have to call kmod_module_unref_list() to release the list of kmod |
|
701 |
|
* modules. |
|
702 |
|
* |
|
703 |
|
* Returns: NULL on failure or the kmod_module contained in this list entry |
|
704 |
|
* with its refcount incremented. |
|
705 |
|
*/ |
|
706 |
|
struct kmod_module *kmod_module_get_module(const struct kmod_list *entry) |
|
707 |
|
{ |
|
708 |
|
if (entry == NULL) |
|
709 |
|
return NULL; |
|
710 |
|
|
|
711 |
|
return kmod_module_ref(entry->data); |
|
712 |
|
} |
|
713 |
|
|
|
714 |
|
/** |
|
715 |
|
* kmod_module_get_name: |
|
716 |
|
* @mod: kmod module |
|
717 |
|
* |
|
718 |
|
* Get the name of this kmod module. Name is always available, independently |
|
719 |
|
* if it was created by kmod_module_new_from_name() or another function and |
|
720 |
|
* it's always normalized (dashes are replaced with underscores). |
|
721 |
|
* |
|
722 |
|
* Returns: the name of this kmod module. |
|
723 |
|
*/ |
|
724 |
|
const char *kmod_module_get_name(const struct kmod_module *mod) |
|
725 |
|
{ |
|
726 |
|
if (mod == NULL) |
|
727 |
|
return NULL; |
|
728 |
|
|
|
729 |
|
return mod->name; |
|
730 |
|
} |
|
731 |
|
|
|
732 |
|
/** |
|
733 |
|
* kmod_module_get_path: |
|
734 |
|
* @mod: kmod module |
|
735 |
|
* |
|
736 |
|
* Get the path of this kmod module. If this kmod module was not created by |
|
737 |
|
* path, it can search the modules.dep index in order to find out the module |
|
738 |
|
* under context's dirname. |
|
739 |
|
* |
|
740 |
|
* Returns: the path of this kmod module or NULL if such information is not |
|
741 |
|
* available. |
|
742 |
|
*/ |
|
743 |
|
const char *kmod_module_get_path(const struct kmod_module *mod) |
|
744 |
|
{ |
|
745 |
|
char *line; |
|
746 |
|
|
|
747 |
|
if (mod == NULL) |
|
748 |
|
return NULL; |
|
749 |
|
|
|
750 |
|
DBG(mod->ctx, "name='%s' path='%s'\n", mod->name, mod->path); |
|
751 |
|
|
|
752 |
|
if (mod->path != NULL) |
|
753 |
|
return mod->path; |
|
754 |
|
if (mod->init.dep) |
|
755 |
|
return NULL; |
|
756 |
|
|
|
757 |
|
/* lazy init */ |
|
758 |
|
line = kmod_search_moddep(mod->ctx, mod->name); |
|
759 |
|
if (line == NULL) |
|
760 |
|
return NULL; |
|
761 |
|
|
|
762 |
|
kmod_module_parse_depline((struct kmod_module *) mod, line); |
|
763 |
|
free(line); |
|
764 |
|
|
|
765 |
|
return mod->path; |
|
766 |
|
} |
|
767 |
|
|
|
768 |
|
|
|
769 |
|
extern long delete_module(const char *name, unsigned int flags); |
|
770 |
|
|
|
771 |
|
/** |
|
772 |
|
* kmod_module_remove_module: |
|
773 |
|
* @mod: kmod module |
|
774 |
|
* @flags: flags to pass to Linux kernel when removing the module. The only valid flag is |
|
775 |
|
* KMOD_REMOVE_FORCE: force remove module regardless if it's still in |
|
776 |
|
* use by a kernel subsystem or other process; |
|
777 |
|
* KMOD_REMOVE_NOWAIT is always enforced, causing us to pass O_NONBLOCK to |
|
778 |
|
* delete_module(2). |
|
779 |
|
* |
|
780 |
|
* Remove a module from Linux kernel. |
|
781 |
|
* |
|
782 |
|
* Returns: 0 on success or < 0 on failure. |
|
783 |
|
*/ |
|
784 |
|
int kmod_module_remove_module(struct kmod_module *mod, |
|
785 |
|
unsigned int flags) |
|
786 |
|
{ |
|
787 |
|
int err; |
|
788 |
|
|
|
789 |
|
if (mod == NULL) |
|
790 |
|
return -ENOENT; |
|
791 |
|
|
|
792 |
|
/* Filter out other flags and force ONONBLOCK */ |
|
793 |
|
flags &= KMOD_REMOVE_FORCE; |
|
794 |
|
flags |= KMOD_REMOVE_NOWAIT; |
|
795 |
|
|
|
796 |
|
err = delete_module(mod->name, flags); |
|
797 |
|
if (err != 0) { |
|
798 |
|
err = -errno; |
|
799 |
|
ERR(mod->ctx, "could not remove '%s': %m\n", mod->name); |
|
800 |
|
} |
|
801 |
|
|
|
802 |
|
return err; |
|
803 |
|
} |
|
804 |
|
|
|
805 |
|
extern long init_module(const void *mem, unsigned long len, const char *args); |
|
806 |
|
|
|
807 |
|
/** |
|
808 |
|
* kmod_module_insert_module: |
|
809 |
|
* @mod: kmod module |
|
810 |
|
* @flags: flags are not passed to Linux Kernel, but instead they dictate the |
|
811 |
|
* behavior of this function, valid flags are |
|
812 |
|
* KMOD_INSERT_FORCE_VERMAGIC: ignore kernel version magic; |
|
813 |
|
* KMOD_INSERT_FORCE_MODVERSION: ignore symbol version hashes. |
|
814 |
|
* @options: module's options to pass to Linux Kernel. |
|
815 |
|
* |
|
816 |
|
* Insert a module in Linux kernel. It opens the file pointed by @mod, |
|
817 |
|
* mmap'ing it and passing to kernel. |
|
818 |
|
* |
|
819 |
|
* Returns: 0 on success or < 0 on failure. If module is already loaded it |
|
820 |
|
* returns -EEXIST. |
|
821 |
|
*/ |
|
822 |
|
int kmod_module_insert_module(struct kmod_module *mod, |
|
823 |
|
unsigned int flags, |
|
824 |
|
const char *options) |
|
825 |
|
{ |
|
826 |
|
int err; |
|
827 |
|
const void *mem; |
|
828 |
|
off_t size; |
|
829 |
|
struct kmod_elf *elf; |
|
830 |
|
const char *path; |
|
831 |
|
const char *args = options ? options : ""; |
|
832 |
|
|
|
833 |
|
if (mod == NULL) |
|
834 |
|
return -ENOENT; |
|
835 |
|
|
|
836 |
|
path = kmod_module_get_path(mod); |
|
837 |
|
if (path == NULL) { |
|
838 |
|
ERR(mod->ctx, "could not find module by name='%s'\n", mod->name); |
|
839 |
|
return -ENOENT; |
|
840 |
|
} |
|
841 |
|
|
|
842 |
|
if (!mod->file) { |
|
843 |
|
mod->file = kmod_file_open(mod->ctx, path); |
|
844 |
|
if (mod->file == NULL) { |
|
845 |
|
err = -errno; |
|
846 |
|
return err; |
|
847 |
|
} |
|
848 |
|
} |
|
849 |
|
|
|
850 |
|
if (kmod_file_get_direct(mod->file)) { |
|
851 |
|
unsigned int kernel_flags = 0; |
|
852 |
|
|
|
853 |
|
if (flags & KMOD_INSERT_FORCE_VERMAGIC) |
|
854 |
|
kernel_flags |= MODULE_INIT_IGNORE_VERMAGIC; |
|
855 |
|
if (flags & KMOD_INSERT_FORCE_MODVERSION) |
|
856 |
|
kernel_flags |= MODULE_INIT_IGNORE_MODVERSIONS; |
|
857 |
|
|
|
858 |
|
err = finit_module(kmod_file_get_fd(mod->file), args, kernel_flags); |
|
859 |
|
if (err == 0 || errno != ENOSYS) |
|
860 |
|
goto init_finished; |
|
861 |
|
} |
|
862 |
|
|
|
863 |
|
if (flags & (KMOD_INSERT_FORCE_VERMAGIC | KMOD_INSERT_FORCE_MODVERSION)) { |
|
864 |
|
elf = kmod_file_get_elf(mod->file); |
|
865 |
|
if (elf == NULL) { |
|
866 |
|
err = -errno; |
|
867 |
|
return err; |
|
868 |
|
} |
|
869 |
|
|
|
870 |
|
if (flags & KMOD_INSERT_FORCE_MODVERSION) { |
|
871 |
|
err = kmod_elf_strip_section(elf, "__versions"); |
|
872 |
|
if (err < 0) |
|
873 |
|
INFO(mod->ctx, "Failed to strip modversion: %s\n", strerror(-err)); |
|
874 |
|
} |
|
875 |
|
|
|
876 |
|
if (flags & KMOD_INSERT_FORCE_VERMAGIC) { |
|
877 |
|
err = kmod_elf_strip_vermagic(elf); |
|
878 |
|
if (err < 0) |
|
879 |
|
INFO(mod->ctx, "Failed to strip vermagic: %s\n", strerror(-err)); |
|
880 |
|
} |
|
881 |
|
|
|
882 |
|
mem = kmod_elf_get_memory(elf); |
|
883 |
|
} else { |
|
884 |
|
mem = kmod_file_get_contents(mod->file); |
|
885 |
|
} |
|
886 |
|
size = kmod_file_get_size(mod->file); |
|
887 |
|
|
|
888 |
|
err = init_module(mem, size, args); |
|
889 |
|
init_finished: |
|
890 |
|
if (err < 0) { |
|
891 |
|
err = -errno; |
|
892 |
|
INFO(mod->ctx, "Failed to insert module '%s': %m\n", path); |
|
893 |
|
} |
|
894 |
|
return err; |
|
895 |
|
} |
|
896 |
|
|
|
897 |
|
static bool module_is_blacklisted(struct kmod_module *mod) |
|
898 |
|
{ |
|
899 |
|
struct kmod_ctx *ctx = mod->ctx; |
|
900 |
|
const struct kmod_config *config = kmod_get_config(ctx); |
|
901 |
|
const struct kmod_list *bl = config->blacklists; |
|
902 |
|
const struct kmod_list *l; |
|
903 |
|
|
|
904 |
|
kmod_list_foreach(l, bl) { |
|
905 |
|
const char *modname = kmod_blacklist_get_modname(l); |
|
906 |
|
|
|
907 |
|
if (streq(modname, mod->name)) |
|
908 |
|
return true; |
|
909 |
|
} |
|
910 |
|
|
|
911 |
|
return false; |
|
912 |
|
} |
|
913 |
|
|
|
914 |
|
/** |
|
915 |
|
* kmod_module_apply_filter |
|
916 |
|
* @ctx: kmod library context |
|
917 |
|
* @filter_type: bitmask to filter modules out, valid types are |
|
918 |
|
* KMOD_FILTER_BLACKLIST: filter modules in blacklist out; |
|
919 |
|
* KMOD_FILTER_BUILTIN: filter builtin modules out. |
|
920 |
|
* @input: list of kmod_module to be filtered |
|
921 |
|
* @output: where to save the new list |
|
922 |
|
* |
|
923 |
|
* Given a list @input, this function filter it out by the filter mask |
|
924 |
|
* and save it in @output. |
|
925 |
|
* |
|
926 |
|
* Returns: 0 on success or < 0 otherwise. @output is saved with the updated |
|
927 |
|
* list. |
|
928 |
|
*/ |
|
929 |
|
int kmod_module_apply_filter(const struct kmod_ctx *ctx, |
|
930 |
|
enum kmod_filter filter_type, |
|
931 |
|
const struct kmod_list *input, |
|
932 |
|
struct kmod_list **output) |
|
933 |
|
{ |
|
934 |
|
const struct kmod_list *li; |
|
935 |
|
|
|
936 |
|
if (ctx == NULL || output == NULL) |
|
937 |
|
return -ENOENT; |
|
938 |
|
|
|
939 |
|
*output = NULL; |
|
940 |
|
if (input == NULL) |
|
941 |
|
return 0; |
|
942 |
|
|
|
943 |
|
kmod_list_foreach(li, input) { |
|
944 |
|
struct kmod_module *mod = li->data; |
|
945 |
|
struct kmod_list *node; |
|
946 |
|
|
|
947 |
|
if ((filter_type & KMOD_FILTER_BLACKLIST) && |
|
948 |
|
module_is_blacklisted(mod)) |
|
949 |
|
continue; |
|
950 |
|
|
|
951 |
|
if ((filter_type & KMOD_FILTER_BUILTIN) |
|
952 |
|
&& kmod_module_is_builtin(mod)) |
|
953 |
|
continue; |
|
954 |
|
|
|
955 |
|
node = __kmod_list_append(*output, mod); |
|
956 |
|
if (node == NULL) |
|
957 |
|
goto fail; |
|
958 |
|
|
|
959 |
|
*output = node; |
|
960 |
|
kmod_module_ref(mod); |
|
961 |
|
} |
|
962 |
|
|
|
963 |
|
return 0; |
|
964 |
|
|
|
965 |
|
fail: |
|
966 |
|
kmod_module_unref_list(*output); |
|
967 |
|
*output = NULL; |
|
968 |
|
return -ENOMEM; |
|
969 |
|
} |
|
970 |
|
|
|
971 |
|
static int command_do(struct kmod_module *mod, const char *type, |
|
972 |
|
const char *cmd) |
|
973 |
|
{ |
|
974 |
|
const char *modname = kmod_module_get_name(mod); |
|
975 |
|
int err; |
|
976 |
|
|
|
977 |
|
DBG(mod->ctx, "%s %s\n", type, cmd); |
|
978 |
|
|
|
979 |
|
setenv("MODPROBE_MODULE", modname, 1); |
|
980 |
|
err = system(cmd); |
|
981 |
|
unsetenv("MODPROBE_MODULE"); |
|
982 |
|
|
|
983 |
|
if (err == -1 || WEXITSTATUS(err)) { |
|
984 |
|
ERR(mod->ctx, "Error running %s command for %s\n", |
|
985 |
|
type, modname); |
|
986 |
|
if (err != -1) |
|
987 |
|
err = -WEXITSTATUS(err); |
|
988 |
|
} |
|
989 |
|
|
|
990 |
|
return err; |
|
991 |
|
} |
|
992 |
|
|
|
993 |
|
struct probe_insert_cb { |
|
994 |
|
int (*run_install)(struct kmod_module *m, const char *cmd, void *data); |
|
995 |
|
void *data; |
|
996 |
|
}; |
|
997 |
|
|
|
998 |
|
static int module_do_install_commands(struct kmod_module *mod, |
|
999 |
|
const char *options, |
|
1000 |
|
struct probe_insert_cb *cb) |
|
1001 |
|
{ |
|
1002 |
|
const char *command = kmod_module_get_install_commands(mod); |
|
1003 |
|
char *p; |
|
1004 |
|
char *cmd = NULL; |
|
1005 |
|
int err; |
|
1006 |
|
size_t cmdlen, options_len, varlen; |
|
1007 |
|
|
|
1008 |
|
assert(command); |
|
1009 |
|
|
|
1010 |
|
if (options == NULL) |
|
1011 |
|
options = ""; |
|
1012 |
|
|
|
1013 |
|
options_len = strlen(options); |
|
1014 |
|
cmdlen = strlen(command); |
|
1015 |
|
varlen = sizeof("$CMDLINE_OPTS") - 1; |
|
1016 |
|
|
|
1017 |
|
cmd = memdup(command, cmdlen + 1); |
|
1018 |
|
if (cmd == NULL) |
|
1019 |
|
return -ENOMEM; |
|
1020 |
|
|
|
1021 |
|
while ((p = strstr(cmd, "$CMDLINE_OPTS")) != NULL) { |
|
1022 |
|
size_t prefixlen = p - cmd; |
|
1023 |
|
size_t suffixlen = cmdlen - prefixlen - varlen; |
|
1024 |
|
size_t slen = cmdlen - varlen + options_len; |
|
1025 |
|
char *suffix = p + varlen; |
|
1026 |
|
char *s = malloc(slen + 1); |
|
1027 |
|
if (!s) { |
|
1028 |
|
if (cmd) |
|
1029 |
|
free(cmd); |
|
1030 |
|
return -ENOMEM; |
|
1031 |
|
} |
|
1032 |
|
|
|
1033 |
|
memcpy(s, cmd, p - cmd); |
|
1034 |
|
memcpy(s + prefixlen, options, options_len); |
|
1035 |
|
memcpy(s + prefixlen + options_len, suffix, suffixlen); |
|
1036 |
|
s[slen] = '\0'; |
|
1037 |
|
|
|
1038 |
|
free(cmd); |
|
1039 |
|
cmd = s; |
|
1040 |
|
cmdlen = slen; |
|
1041 |
|
} |
|
1042 |
|
|
|
1043 |
|
if (cb->run_install != NULL) |
|
1044 |
|
err = cb->run_install(mod, cmd, cb->data); |
|
1045 |
|
else |
|
1046 |
|
err = command_do(mod, "install", cmd); |
|
1047 |
|
return err; |
|
1048 |
|
} |
|
1049 |
|
|
|
1050 |
|
static char *module_options_concat(const char *opt, const char *xopt) |
|
1051 |
|
{ |
|
1052 |
|
// TODO: we might need to check if xopt overrides options on opt |
|
1053 |
|
size_t optlen = opt == NULL ? 0 : strlen(opt); |
|
1054 |
|
size_t xoptlen = xopt == NULL ? 0 : strlen(xopt); |
|
1055 |
|
char *r; |
|
1056 |
|
|
|
1057 |
|
if (optlen == 0 && xoptlen == 0) |
|
1058 |
|
return NULL; |
|
1059 |
|
|
|
1060 |
|
r = malloc(optlen + xoptlen + 2); |
|
1061 |
|
|
|
1062 |
|
if (opt != NULL) { |
|
1063 |
|
memcpy(r, opt, optlen); |
|
1064 |
|
r[optlen] = ' '; |
|
1065 |
|
optlen++; |
|
1066 |
|
} |
|
1067 |
|
|
|
1068 |
|
if (xopt != NULL) |
|
1069 |
|
memcpy(r + optlen, xopt, xoptlen); |
|
1070 |
|
|
|
1071 |
|
r[optlen + xoptlen] = '\0'; |
|
1072 |
|
|
|
1073 |
|
return r; |
|
1074 |
|
} |
|
1075 |
|
|
|
1076 |
|
static int __kmod_module_get_probe_list(struct kmod_module *mod, |
|
1077 |
|
bool required, |
|
1078 |
|
bool ignorecmd, |
|
1079 |
|
struct kmod_list **list); |
|
1080 |
|
|
|
1081 |
|
/* re-entrant */ |
|
1082 |
|
static int __kmod_module_fill_softdep(struct kmod_module *mod, |
|
1083 |
|
struct kmod_list **list) |
|
1084 |
|
{ |
|
1085 |
|
struct kmod_list *pre = NULL, *post = NULL, *l; |
|
1086 |
|
int err; |
|
1087 |
|
|
|
1088 |
|
err = kmod_module_get_softdeps(mod, &pre, &post); |
|
1089 |
|
if (err < 0) { |
|
1090 |
|
ERR(mod->ctx, "could not get softdep: %s\n", |
|
1091 |
|
strerror(-err)); |
|
1092 |
|
goto fail; |
|
1093 |
|
} |
|
1094 |
|
|
|
1095 |
|
kmod_list_foreach(l, pre) { |
|
1096 |
|
struct kmod_module *m = l->data; |
|
1097 |
|
err = __kmod_module_get_probe_list(m, false, false, list); |
|
1098 |
|
if (err < 0) |
|
1099 |
|
goto fail; |
|
1100 |
|
} |
|
1101 |
|
|
|
1102 |
|
l = __kmod_list_append(*list, kmod_module_ref(mod)); |
|
1103 |
|
if (l == NULL) { |
|
1104 |
|
kmod_module_unref(mod); |
|
1105 |
|
err = -ENOMEM; |
|
1106 |
|
goto fail; |
|
1107 |
|
} |
|
1108 |
|
*list = l; |
|
1109 |
|
mod->ignorecmd = (pre != NULL || post != NULL); |
|
1110 |
|
|
|
1111 |
|
kmod_list_foreach(l, post) { |
|
1112 |
|
struct kmod_module *m = l->data; |
|
1113 |
|
err = __kmod_module_get_probe_list(m, false, false, list); |
|
1114 |
|
if (err < 0) |
|
1115 |
|
goto fail; |
|
1116 |
|
} |
|
1117 |
|
|
|
1118 |
|
fail: |
|
1119 |
|
kmod_module_unref_list(pre); |
|
1120 |
|
kmod_module_unref_list(post); |
|
1121 |
|
|
|
1122 |
|
return err; |
|
1123 |
|
} |
|
1124 |
|
|
|
1125 |
|
/* re-entrant */ |
|
1126 |
|
static int __kmod_module_get_probe_list(struct kmod_module *mod, |
|
1127 |
|
bool required, |
|
1128 |
|
bool ignorecmd, |
|
1129 |
|
struct kmod_list **list) |
|
1130 |
|
{ |
|
1131 |
|
struct kmod_list *dep, *l; |
|
1132 |
|
int err = 0; |
|
1133 |
|
|
|
1134 |
|
if (mod->visited) { |
|
1135 |
|
DBG(mod->ctx, "Ignore module '%s': already visited\n", |
|
1136 |
|
mod->name); |
|
1137 |
|
return 0; |
|
1138 |
|
} |
|
1139 |
|
mod->visited = true; |
|
1140 |
|
|
|
1141 |
|
dep = kmod_module_get_dependencies(mod); |
|
1142 |
|
if (required) { |
|
1143 |
|
/* |
|
1144 |
|
* Called from kmod_module_probe_insert_module(); set the |
|
1145 |
|
* ->required flag on mod and all its dependencies before |
|
1146 |
|
* they are possibly visited through some softdeps. |
|
1147 |
|
*/ |
|
1148 |
|
mod->required = true; |
|
1149 |
|
kmod_list_foreach(l, dep) { |
|
1150 |
|
struct kmod_module *m = l->data; |
|
1151 |
|
m->required = true; |
|
1152 |
|
} |
|
1153 |
|
} |
|
1154 |
|
|
|
1155 |
|
kmod_list_foreach(l, dep) { |
|
1156 |
|
struct kmod_module *m = l->data; |
|
1157 |
|
err = __kmod_module_fill_softdep(m, list); |
|
1158 |
|
if (err < 0) |
|
1159 |
|
goto finish; |
|
1160 |
|
} |
|
1161 |
|
|
|
1162 |
|
if (ignorecmd) { |
|
1163 |
|
l = __kmod_list_append(*list, kmod_module_ref(mod)); |
|
1164 |
|
if (l == NULL) { |
|
1165 |
|
kmod_module_unref(mod); |
|
1166 |
|
err = -ENOMEM; |
|
1167 |
|
goto finish; |
|
1168 |
|
} |
|
1169 |
|
*list = l; |
|
1170 |
|
mod->ignorecmd = true; |
|
1171 |
|
} else |
|
1172 |
|
err = __kmod_module_fill_softdep(mod, list); |
|
1173 |
|
|
|
1174 |
|
finish: |
|
1175 |
|
kmod_module_unref_list(dep); |
|
1176 |
|
return err; |
|
1177 |
|
} |
|
1178 |
|
|
|
1179 |
|
static int kmod_module_get_probe_list(struct kmod_module *mod, |
|
1180 |
|
bool ignorecmd, |
|
1181 |
|
struct kmod_list **list) |
|
1182 |
|
{ |
|
1183 |
|
int err; |
|
1184 |
|
|
|
1185 |
|
assert(mod != NULL); |
|
1186 |
|
assert(list != NULL && *list == NULL); |
|
1187 |
|
|
|
1188 |
|
/* |
|
1189 |
|
* Make sure we don't get screwed by previous calls to this function |
|
1190 |
|
*/ |
|
1191 |
|
kmod_set_modules_visited(mod->ctx, false); |
|
1192 |
|
kmod_set_modules_required(mod->ctx, false); |
|
1193 |
|
|
|
1194 |
|
err = __kmod_module_get_probe_list(mod, true, ignorecmd, list); |
|
1195 |
|
if (err < 0) { |
|
1196 |
|
kmod_module_unref_list(*list); |
|
1197 |
|
*list = NULL; |
|
1198 |
|
} |
|
1199 |
|
|
|
1200 |
|
return err; |
|
1201 |
|
} |
|
1202 |
|
|
|
1203 |
|
/** |
|
1204 |
|
* kmod_module_probe_insert_module: |
|
1205 |
|
* @mod: kmod module |
|
1206 |
|
* @flags: flags are not passed to Linux Kernel, but instead they dictate the |
|
1207 |
|
* behavior of this function, valid flags are |
|
1208 |
|
* KMOD_PROBE_FORCE_VERMAGIC: ignore kernel version magic; |
|
1209 |
|
* KMOD_PROBE_FORCE_MODVERSION: ignore symbol version hashes; |
|
1210 |
|
* KMOD_PROBE_IGNORE_COMMAND: whether the probe should ignore install |
|
1211 |
|
* commands and softdeps configured in the system; |
|
1212 |
|
* KMOD_PROBE_IGNORE_LOADED: do not check whether the module is already |
|
1213 |
|
* live in kernel or not; |
|
1214 |
|
* KMOD_PROBE_DRY_RUN: dry run, do not insert module, just call the |
|
1215 |
|
* associated callback function; |
|
1216 |
|
* KMOD_PROBE_FAIL_ON_LOADED: if KMOD_PROBE_IGNORE_LOADED is not specified |
|
1217 |
|
* and the module is already live in kernel, the function will fail if this |
|
1218 |
|
* flag is specified; |
|
1219 |
|
* KMOD_PROBE_APPLY_BLACKLIST_ALL: probe will apply KMOD_FILTER_BLACKLIST |
|
1220 |
|
* filter to this module and its dependencies. If any of the dependencies (or |
|
1221 |
|
* the module) is blacklisted, the probe will fail, unless the blacklisted |
|
1222 |
|
* module is already live in kernel; |
|
1223 |
|
* KMOD_PROBE_APPLY_BLACKLIST: probe will fail if the module is blacklisted; |
|
1224 |
|
* KMOD_PROBE_APPLY_BLACKLIST_ALIAS_ONLY: probe will fail if the module is an |
|
1225 |
|
* alias and is blacklisted. |
|
1226 |
|
* @extra_options: module's options to pass to Linux Kernel. It applies only |
|
1227 |
|
* to @mod, not to its dependencies. |
|
1228 |
|
* @run_install: function to run when @mod is backed by an install command. |
|
1229 |
|
* @data: data to give back to @run_install callback |
|
1230 |
|
* @print_action: function to call with the action being taken (install or |
|
1231 |
|
* insmod). It's useful for tools like modprobe when running with verbose |
|
1232 |
|
* output or in dry-run mode. |
|
1233 |
|
* |
|
1234 |
|
* Insert a module in Linux kernel resolving dependencies, soft dependencies, |
|
1235 |
|
* install commands and applying blacklist. |
|
1236 |
|
* |
|
1237 |
|
* If @run_install is NULL, this function will fork and exec by calling |
|
1238 |
|
* system(3). Don't pass a NULL argument in @run_install if your binary is |
|
1239 |
|
* setuid/setgid (see warning in system(3)). If you need control over the |
|
1240 |
|
* execution of an install command, give a callback function instead. |
|
1241 |
|
* |
|
1242 |
|
* Returns: 0 on success, > 0 if stopped by a reason given in @flags or < 0 on |
|
1243 |
|
* failure. |
|
1244 |
|
*/ |
|
1245 |
|
int kmod_module_probe_insert_module(struct kmod_module *mod, |
|
1246 |
|
unsigned int flags, const char *extra_options, |
|
1247 |
|
int (*run_install)(struct kmod_module *m, |
|
1248 |
|
const char *cmd, void *data), |
|
1249 |
|
const void *data, |
|
1250 |
|
void (*print_action)(struct kmod_module *m, |
|
1251 |
|
bool install, |
|
1252 |
|
const char *options)) |
|
1253 |
|
{ |
|
1254 |
|
struct kmod_list *list = NULL, *l; |
|
1255 |
|
struct probe_insert_cb cb; |
|
1256 |
|
int err; |
|
1257 |
|
|
|
1258 |
|
if (mod == NULL) |
|
1259 |
|
return -ENOENT; |
|
1260 |
|
|
|
1261 |
|
if (!(flags & KMOD_PROBE_IGNORE_LOADED) |
|
1262 |
|
&& module_is_inkernel(mod)) { |
|
1263 |
|
if (flags & KMOD_PROBE_FAIL_ON_LOADED) |
|
1264 |
|
return -EEXIST; |
|
1265 |
|
else |
|
1266 |
|
return 0; |
|
1267 |
|
} |
|
1268 |
|
|
|
1269 |
|
/* |
|
1270 |
|
* Ugly assignement + check. We need to check if we were told to check |
|
1271 |
|
* blacklist and also return the reason why we failed. |
|
1272 |
|
* KMOD_PROBE_APPLY_BLACKLIST_ALIAS_ONLY will take effect only if the |
|
1273 |
|
* module is an alias, so we also need to check it |
|
1274 |
|
*/ |
|
1275 |
|
if ((mod->alias != NULL && ((err = flags & KMOD_PROBE_APPLY_BLACKLIST_ALIAS_ONLY))) |
|
1276 |
|
|| (err = flags & KMOD_PROBE_APPLY_BLACKLIST_ALL) |
|
1277 |
|
|| (err = flags & KMOD_PROBE_APPLY_BLACKLIST)) { |
|
1278 |
|
if (module_is_blacklisted(mod)) |
|
1279 |
|
return err; |
|
1280 |
|
} |
|
1281 |
|
|
|
1282 |
|
err = kmod_module_get_probe_list(mod, |
|
1283 |
|
!!(flags & KMOD_PROBE_IGNORE_COMMAND), &list); |
|
1284 |
|
if (err < 0) |
|
1285 |
|
return err; |
|
1286 |
|
|
|
1287 |
|
if (flags & KMOD_PROBE_APPLY_BLACKLIST_ALL) { |
|
1288 |
|
struct kmod_list *filtered = NULL; |
|
1289 |
|
|
|
1290 |
|
err = kmod_module_apply_filter(mod->ctx, |
|
1291 |
|
KMOD_FILTER_BLACKLIST, list, &filtered); |
|
1292 |
|
if (err < 0) |
|
1293 |
|
return err; |
|
1294 |
|
|
|
1295 |
|
kmod_module_unref_list(list); |
|
1296 |
|
if (filtered == NULL) |
|
1297 |
|
return KMOD_PROBE_APPLY_BLACKLIST_ALL; |
|
1298 |
|
|
|
1299 |
|
list = filtered; |
|
1300 |
|
} |
|
1301 |
|
|
|
1302 |
|
cb.run_install = run_install; |
|
1303 |
|
cb.data = (void *) data; |
|
1304 |
|
|
|
1305 |
|
kmod_list_foreach(l, list) { |
|
1306 |
|
struct kmod_module *m = l->data; |
|
1307 |
|
const char *moptions = kmod_module_get_options(m); |
|
1308 |
|
const char *cmd = kmod_module_get_install_commands(m); |
|
1309 |
|
char *options; |
|
1310 |
|
|
|
1311 |
|
if (!(flags & KMOD_PROBE_IGNORE_LOADED) |
|
1312 |
|
&& module_is_inkernel(m)) { |
|
1313 |
|
DBG(mod->ctx, "Ignoring module '%s': already loaded\n", |
|
1314 |
|
m->name); |
|
1315 |
|
err = -EEXIST; |
|
1316 |
|
goto finish_module; |
|
1317 |
|
} |
|
1318 |
|
|
|
1319 |
|
options = module_options_concat(moptions, |
|
1320 |
|
m == mod ? extra_options : NULL); |
|
1321 |
|
|
|
1322 |
|
if (cmd != NULL && !m->ignorecmd) { |
|
1323 |
|
if (print_action != NULL) |
|
1324 |
|
print_action(m, true, options ?: ""); |
|
1325 |
|
|
|
1326 |
|
if (!(flags & KMOD_PROBE_DRY_RUN)) |
|
1327 |
|
err = module_do_install_commands(m, options, |
|
1328 |
|
&cb); |
|
1329 |
|
} else { |
|
1330 |
|
if (print_action != NULL) |
|
1331 |
|
print_action(m, false, options ?: ""); |
|
1332 |
|
|
|
1333 |
|
if (!(flags & KMOD_PROBE_DRY_RUN)) |
|
1334 |
|
err = kmod_module_insert_module(m, flags, |
|
1335 |
|
options); |
|
1336 |
|
} |
|
1337 |
|
|
|
1338 |
|
free(options); |
|
1339 |
|
|
|
1340 |
|
finish_module: |
|
1341 |
|
/* |
|
1342 |
|
* Treat "already loaded" error. If we were told to stop on |
|
1343 |
|
* already loaded and the module being loaded is not a softdep |
|
1344 |
|
* or dep, bail out. Otherwise, just ignore and continue. |
|
1345 |
|
* |
|
1346 |
|
* We need to check here because of race conditions. We |
|
1347 |
|
* checked first if module was already loaded but it may have |
|
1348 |
|
* been loaded between the check and the moment we try to |
|
1349 |
|
* insert it. |
|
1350 |
|
*/ |
|
1351 |
|
if (err == -EEXIST && m == mod && |
|
1352 |
|
(flags & KMOD_PROBE_FAIL_ON_LOADED)) |
|
1353 |
|
break; |
|
1354 |
|
|
|
1355 |
|
/* |
|
1356 |
|
* Ignore errors from softdeps |
|
1357 |
|
*/ |
|
1358 |
|
if (err == -EEXIST || !m->required) |
|
1359 |
|
err = 0; |
|
1360 |
|
|
|
1361 |
|
else if (err < 0) |
|
1362 |
|
break; |
|
1363 |
|
} |
|
1364 |
|
|
|
1365 |
|
kmod_module_unref_list(list); |
|
1366 |
|
return err; |
|
1367 |
|
} |
|
1368 |
|
|
|
1369 |
|
/** |
|
1370 |
|
* kmod_module_get_options: |
|
1371 |
|
* @mod: kmod module |
|
1372 |
|
* |
|
1373 |
|
* Get options of this kmod module. Options come from the configuration file |
|
1374 |
|
* and are cached in @mod. The first call to this function will search for |
|
1375 |
|
* this module in configuration and subsequent calls return the cached string. |
|
1376 |
|
* |
|
1377 |
|
* Returns: a string with all the options separated by spaces. This string is |
|
1378 |
|
* owned by @mod, do not free it. |
|
1379 |
|
*/ |
|
1380 |
|
const char *kmod_module_get_options(const struct kmod_module *mod) |
|
1381 |
|
{ |
|
1382 |
|
if (mod == NULL) |
|
1383 |
|
return NULL; |
|
1384 |
|
|
|
1385 |
|
if (!mod->init.options) { |
|
1386 |
|
/* lazy init */ |
|
1387 |
|
struct kmod_module *m = (struct kmod_module *)mod; |
|
1388 |
|
const struct kmod_list *l; |
|
1389 |
|
const struct kmod_config *config; |
|
1390 |
|
char *opts = NULL; |
|
1391 |
|
size_t optslen = 0; |
|
1392 |
|
|
|
1393 |
|
config = kmod_get_config(mod->ctx); |
|
1394 |
|
|
|
1395 |
|
kmod_list_foreach(l, config->options) { |
|
1396 |
|
const char *modname = kmod_option_get_modname(l); |
|
1397 |
|
const char *str; |
|
1398 |
|
size_t len; |
|
1399 |
|
void *tmp; |
|
1400 |
|
|
|
1401 |
|
DBG(mod->ctx, "modname=%s mod->name=%s mod->alias=%s\n", modname, mod->name, mod->alias); |
|
1402 |
|
if (!(streq(modname, mod->name) || (mod->alias != NULL && |
|
1403 |
|
streq(modname, mod->alias)))) |
|
1404 |
|
continue; |
|
1405 |
|
|
|
1406 |
|
DBG(mod->ctx, "passed = modname=%s mod->name=%s mod->alias=%s\n", modname, mod->name, mod->alias); |
|
1407 |
|
str = kmod_option_get_options(l); |
|
1408 |
|
len = strlen(str); |
|
1409 |
|
if (len < 1) |
|
1410 |
|
continue; |
|
1411 |
|
|
|
1412 |
|
tmp = realloc(opts, optslen + len + 2); |
|
1413 |
|
if (tmp == NULL) { |
|
1414 |
|
free(opts); |
|
1415 |
|
goto failed; |
|
1416 |
|
} |
|
1417 |
|
|
|
1418 |
|
opts = tmp; |
|
1419 |
|
|
|
1420 |
|
if (optslen > 0) { |
|
1421 |
|
opts[optslen] = ' '; |
|
1422 |
|
optslen++; |
|
1423 |
|
} |
|
1424 |
|
|
|
1425 |
|
memcpy(opts + optslen, str, len); |
|
1426 |
|
optslen += len; |
|
1427 |
|
opts[optslen] = '\0'; |
|
1428 |
|
} |
|
1429 |
|
|
|
1430 |
|
m->init.options = true; |
|
1431 |
|
m->options = opts; |
|
1432 |
|
} |
|
1433 |
|
|
|
1434 |
|
return mod->options; |
|
1435 |
|
|
|
1436 |
|
failed: |
|
1437 |
|
ERR(mod->ctx, "out of memory\n"); |
|
1438 |
|
return NULL; |
|
1439 |
|
} |
|
1440 |
|
|
|
1441 |
|
/** |
|
1442 |
|
* kmod_module_get_install_commands: |
|
1443 |
|
* @mod: kmod module |
|
1444 |
|
* |
|
1445 |
|
* Get install commands for this kmod module. Install commands come from the |
|
1446 |
|
* configuration file and are cached in @mod. The first call to this function |
|
1447 |
|
* will search for this module in configuration and subsequent calls return |
|
1448 |
|
* the cached string. The install commands are returned as they were in the |
|
1449 |
|
* configuration, concatenated by ';'. No other processing is made in this |
|
1450 |
|
* string. |
|
1451 |
|
* |
|
1452 |
|
* Returns: a string with all install commands separated by semicolons. This |
|
1453 |
|
* string is owned by @mod, do not free it. |
|
1454 |
|
*/ |
|
1455 |
|
const char *kmod_module_get_install_commands(const struct kmod_module *mod) |
|
1456 |
|
{ |
|
1457 |
|
if (mod == NULL) |
|
1458 |
|
return NULL; |
|
1459 |
|
|
|
1460 |
|
if (!mod->init.install_commands) { |
|
1461 |
|
/* lazy init */ |
|
1462 |
|
struct kmod_module *m = (struct kmod_module *)mod; |
|
1463 |
|
const struct kmod_list *l; |
|
1464 |
|
const struct kmod_config *config; |
|
1465 |
|
|
|
1466 |
|
config = kmod_get_config(mod->ctx); |
|
1467 |
|
|
|
1468 |
|
kmod_list_foreach(l, config->install_commands) { |
|
1469 |
|
const char *modname = kmod_command_get_modname(l); |
|
1470 |
|
|
|
1471 |
|
if (fnmatch(modname, mod->name, 0) != 0) |
|
1472 |
|
continue; |
|
1473 |
|
|
|
1474 |
|
m->install_commands = kmod_command_get_command(l); |
|
1475 |
|
|
|
1476 |
|
/* |
|
1477 |
|
* find only the first command, as modprobe from |
|
1478 |
|
* module-init-tools does |
|
1479 |
|
*/ |
|
1480 |
|
break; |
|
1481 |
|
} |
|
1482 |
|
|
|
1483 |
|
m->init.install_commands = true; |
|
1484 |
|
} |
|
1485 |
|
|
|
1486 |
|
return mod->install_commands; |
|
1487 |
|
} |
|
1488 |
|
|
|
1489 |
|
void kmod_module_set_install_commands(struct kmod_module *mod, const char *cmd) |
|
1490 |
|
{ |
|
1491 |
|
mod->init.install_commands = true; |
|
1492 |
|
mod->install_commands = cmd; |
|
1493 |
|
} |
|
1494 |
|
|
|
1495 |
|
static struct kmod_list *lookup_softdep(struct kmod_ctx *ctx, const char * const * array, unsigned int count) |
|
1496 |
|
{ |
|
1497 |
|
struct kmod_list *ret = NULL; |
|
1498 |
|
unsigned i; |
|
1499 |
|
|
|
1500 |
|
for (i = 0; i < count; i++) { |
|
1501 |
|
const char *depname = array[i]; |
|
1502 |
|
struct kmod_list *lst = NULL; |
|
1503 |
|
int err; |
|
1504 |
|
|
|
1505 |
|
err = kmod_module_new_from_lookup(ctx, depname, &lst); |
|
1506 |
|
if (err < 0) { |
|
1507 |
|
ERR(ctx, "failed to lookup soft dependency '%s', continuing anyway.\n", depname); |
|
1508 |
|
continue; |
|
1509 |
|
} else if (lst != NULL) |
|
1510 |
|
ret = kmod_list_append_list(ret, lst); |
|
1511 |
|
} |
|
1512 |
|
return ret; |
|
1513 |
|
} |
|
1514 |
|
|
|
1515 |
|
/** |
|
1516 |
|
* kmod_module_get_softdeps: |
|
1517 |
|
* @mod: kmod module |
|
1518 |
|
* @pre: where to save the list of preceding soft dependencies. |
|
1519 |
|
* @post: where to save the list of post soft dependencies. |
|
1520 |
|
* |
|
1521 |
|
* Get soft dependencies for this kmod module. Soft dependencies come |
|
1522 |
|
* from configuration file and are not cached in @mod because it may include |
|
1523 |
|
* dependency cycles that would make we leak kmod_module. Any call |
|
1524 |
|
* to this function will search for this module in configuration, allocate a |
|
1525 |
|
* list and return the result. |
|
1526 |
|
* |
|
1527 |
|
* Both @pre and @post are newly created list of kmod_module and |
|
1528 |
|
* should be unreferenced with kmod_module_unref_list(). |
|
1529 |
|
* |
|
1530 |
|
* Returns: 0 on success or < 0 otherwise. |
|
1531 |
|
*/ |
|
1532 |
|
int kmod_module_get_softdeps(const struct kmod_module *mod, |
|
1533 |
|
struct kmod_list **pre, |
|
1534 |
|
struct kmod_list **post) |
|
1535 |
|
{ |
|
1536 |
|
const struct kmod_list *l; |
|
1537 |
|
const struct kmod_config *config; |
|
1538 |
|
|
|
1539 |
|
if (mod == NULL || pre == NULL || post == NULL) |
|
1540 |
|
return -ENOENT; |
|
1541 |
|
|
|
1542 |
|
assert(*pre == NULL); |
|
1543 |
|
assert(*post == NULL); |
|
1544 |
|
|
|
1545 |
|
config = kmod_get_config(mod->ctx); |
|
1546 |
|
|
|
1547 |
|
kmod_list_foreach(l, config->softdeps) { |
|
1548 |
|
const char *modname = kmod_softdep_get_name(l); |
|
1549 |
|
const char * const *array; |
|
1550 |
|
unsigned count; |
|
1551 |
|
|
|
1552 |
|
if (fnmatch(modname, mod->name, 0) != 0) |
|
1553 |
|
continue; |
|
1554 |
|
|
|
1555 |
|
array = kmod_softdep_get_pre(l, &count); |
|
1556 |
|
*pre = lookup_softdep(mod->ctx, array, count); |
|
1557 |
|
array = kmod_softdep_get_post(l, &count); |
|
1558 |
|
*post = lookup_softdep(mod->ctx, array, count); |
|
1559 |
|
|
|
1560 |
|
/* |
|
1561 |
|
* find only the first command, as modprobe from |
|
1562 |
|
* module-init-tools does |
|
1563 |
|
*/ |
|
1564 |
|
break; |
|
1565 |
|
} |
|
1566 |
|
|
|
1567 |
|
return 0; |
|
1568 |
|
} |
|
1569 |
|
|
|
1570 |
|
/** |
|
1571 |
|
* kmod_module_get_remove_commands: |
|
1572 |
|
* @mod: kmod module |
|
1573 |
|
* |
|
1574 |
|
* Get remove commands for this kmod module. Remove commands come from the |
|
1575 |
|
* configuration file and are cached in @mod. The first call to this function |
|
1576 |
|
* will search for this module in configuration and subsequent calls return |
|
1577 |
|
* the cached string. The remove commands are returned as they were in the |
|
1578 |
|
* configuration, concatenated by ';'. No other processing is made in this |
|
1579 |
|
* string. |
|
1580 |
|
* |
|
1581 |
|
* Returns: a string with all remove commands separated by semicolons. This |
|
1582 |
|
* string is owned by @mod, do not free it. |
|
1583 |
|
*/ |
|
1584 |
|
const char *kmod_module_get_remove_commands(const struct kmod_module *mod) |
|
1585 |
|
{ |
|
1586 |
|
if (mod == NULL) |
|
1587 |
|
return NULL; |
|
1588 |
|
|
|
1589 |
|
if (!mod->init.remove_commands) { |
|
1590 |
|
/* lazy init */ |
|
1591 |
|
struct kmod_module *m = (struct kmod_module *)mod; |
|
1592 |
|
const struct kmod_list *l; |
|
1593 |
|
const struct kmod_config *config; |
|
1594 |
|
|
|
1595 |
|
config = kmod_get_config(mod->ctx); |
|
1596 |
|
|
|
1597 |
|
kmod_list_foreach(l, config->remove_commands) { |
|
1598 |
|
const char *modname = kmod_command_get_modname(l); |
|
1599 |
|
|
|
1600 |
|
if (fnmatch(modname, mod->name, 0) != 0) |
|
1601 |
|
continue; |
|
1602 |
|
|
|
1603 |
|
m->remove_commands = kmod_command_get_command(l); |
|
1604 |
|
|
|
1605 |
|
/* |
|
1606 |
|
* find only the first command, as modprobe from |
|
1607 |
|
* module-init-tools does |
|
1608 |
|
*/ |
|
1609 |
|
break; |
|
1610 |
|
} |
|
1611 |
|
|
|
1612 |
|
m->init.remove_commands = true; |
|
1613 |
|
} |
|
1614 |
|
|
|
1615 |
|
return mod->remove_commands; |
|
1616 |
|
} |
|
1617 |
|
|
|
1618 |
|
void kmod_module_set_remove_commands(struct kmod_module *mod, const char *cmd) |
|
1619 |
|
{ |
|
1620 |
|
mod->init.remove_commands = true; |
|
1621 |
|
mod->remove_commands = cmd; |
|
1622 |
|
} |
|
1623 |
|
|
|
1624 |
|
/** |
|
1625 |
|
* SECTION:libkmod-loaded |
|
1626 |
|
* @short_description: currently loaded modules |
|
1627 |
|
* |
|
1628 |
|
* Information about currently loaded modules, as reported by Linux kernel. |
|
1629 |
|
* These information are not cached by libkmod and are always read from /sys |
|
1630 |
|
* and /proc/modules. |
|
1631 |
|
*/ |
|
1632 |
|
|
|
1633 |
|
/** |
|
1634 |
|
* kmod_module_new_from_loaded: |
|
1635 |
|
* @ctx: kmod library context |
|
1636 |
|
* @list: where to save the list of loaded modules |
|
1637 |
|
* |
|
1638 |
|
* Create a new list of kmod modules with all modules currently loaded in |
|
1639 |
|
* kernel. It uses /proc/modules to get the names of loaded modules and to |
|
1640 |
|
* create kmod modules by calling kmod_module_new_from_name() in each of them. |
|
1641 |
|
* They are put in @list in no particular order. |
|
1642 |
|
* |
|
1643 |
|
* The initial refcount is 1, and needs to be decremented to release the |
|
1644 |
|
* resources of the kmod_module. The returned @list must be released by |
|
1645 |
|
* calling kmod_module_unref_list(). Since libkmod keeps track of all |
|
1646 |
|
* kmod_modules created, they are all released upon @ctx destruction too. Do |
|
1647 |
|
* not unref @ctx before all the desired operations with the returned list are |
|
1648 |
|
* completed. |
|
1649 |
|
* |
|
1650 |
|
* Returns: 0 on success or < 0 on error. |
|
1651 |
|
*/ |
|
1652 |
|
int kmod_module_new_from_loaded(struct kmod_ctx *ctx, |
|
1653 |
|
struct kmod_list **list) |
|
1654 |
|
{ |
|
1655 |
|
struct kmod_list *l = NULL; |
|
1656 |
|
FILE *fp; |
|
1657 |
|
char line[4096]; |
|
1658 |
|
|
|
1659 |
|
if (ctx == NULL || list == NULL) |
|
1660 |
|
return -ENOENT; |
|
1661 |
|
|
|
1662 |
|
fp = fopen("/proc/modules", "re"); |
|
1663 |
|
if (fp == NULL) { |
|
1664 |
|
int err = -errno; |
|
1665 |
|
ERR(ctx, "could not open /proc/modules: %s\n", strerror(errno)); |
|
1666 |
|
return err; |
|
1667 |
|
} |
|
1668 |
|
|
|
1669 |
|
while (fgets(line, sizeof(line), fp)) { |
|
1670 |
|
struct kmod_module *m; |
|
1671 |
|
struct kmod_list *node; |
|
1672 |
|
int err; |
|
1673 |
|
size_t len = strlen(line); |
|
1674 |
|
char *saveptr, *name = strtok_r(line, " \t", &saveptr); |
|
1675 |
|
|
|
1676 |
|
err = kmod_module_new_from_name(ctx, name, &m); |
|
1677 |
|
if (err < 0) { |
|
1678 |
|
ERR(ctx, "could not get module from name '%s': %s\n", |
|
1679 |
|
name, strerror(-err)); |
|
1680 |
|
goto eat_line; |
|
1681 |
|
} |
|
1682 |
|
|
|
1683 |
|
node = __kmod_list_append(l, m); |
|
1684 |
|
if (node) |
|
1685 |
|
l = node; |
|
1686 |
|
else { |
|
1687 |
|
ERR(ctx, "out of memory\n"); |
|
1688 |
|
kmod_module_unref(m); |
|
1689 |
|
} |
|
1690 |
|
eat_line: |
|
1691 |
|
while (line[len - 1] != '\n' && fgets(line, sizeof(line), fp)) |
|
1692 |
|
len = strlen(line); |
|
1693 |
|
} |
|
1694 |
|
|
|
1695 |
|
fclose(fp); |
|
1696 |
|
*list = l; |
|
1697 |
|
|
|
1698 |
|
return 0; |
|
1699 |
|
} |
|
1700 |
|
|
|
1701 |
|
/** |
|
1702 |
|
* kmod_module_initstate_str: |
|
1703 |
|
* @state: the state as returned by kmod_module_get_initstate() |
|
1704 |
|
* |
|
1705 |
|
* Translate a initstate to a string. |
|
1706 |
|
* |
|
1707 |
|
* Returns: the string associated to the @state. This string is statically |
|
1708 |
|
* allocated, do not free it. |
|
1709 |
|
*/ |
|
1710 |
|
const char *kmod_module_initstate_str(enum kmod_module_initstate state) |
|
1711 |
|
{ |
|
1712 |
|
switch (state) { |
|
1713 |
|
case KMOD_MODULE_BUILTIN: |
|
1714 |
|
return "builtin"; |
|
1715 |
|
case KMOD_MODULE_LIVE: |
|
1716 |
|
return "live"; |
|
1717 |
|
case KMOD_MODULE_COMING: |
|
1718 |
|
return "coming"; |
|
1719 |
|
case KMOD_MODULE_GOING: |
|
1720 |
|
return "going"; |
|
1721 |
|
default: |
|
1722 |
|
return NULL; |
|
1723 |
|
} |
|
1724 |
|
} |
|
1725 |
|
|
|
1726 |
|
/** |
|
1727 |
|
* kmod_module_get_initstate: |
|
1728 |
|
* @mod: kmod module |
|
1729 |
|
* |
|
1730 |
|
* Get the initstate of this @mod, as returned by Linux Kernel, by reading |
|
1731 |
|
* /sys filesystem. |
|
1732 |
|
* |
|
1733 |
|
* Returns: < 0 on error or module state if module is found in kernel, valid states are |
|
1734 |
|
* KMOD_MODULE_BUILTIN: module is builtin; |
|
1735 |
|
* KMOD_MODULE_LIVE: module is live in kernel; |
|
1736 |
|
* KMOD_MODULE_COMING: module is being loaded; |
|
1737 |
|
* KMOD_MODULE_GOING: module is being unloaded. |
|
1738 |
|
*/ |
|
1739 |
|
int kmod_module_get_initstate(const struct kmod_module *mod) |
|
1740 |
|
{ |
|
1741 |
|
char path[PATH_MAX], buf[32]; |
|
1742 |
|
int fd, err, pathlen; |
|
1743 |
|
|
|
1744 |
|
if (mod == NULL) |
|
1745 |
|
return -ENOENT; |
|
1746 |
|
|
|
1747 |
|
/* remove const: this can only change internal state */ |
|
1748 |
|
if (kmod_module_is_builtin((struct kmod_module *)mod)) |
|
1749 |
|
return KMOD_MODULE_BUILTIN; |
|
1750 |
|
|
|
1751 |
|
pathlen = snprintf(path, sizeof(path), |
|
1752 |
|
"/sys/module/%s/initstate", mod->name); |
|
1753 |
|
fd = open(path, O_RDONLY|O_CLOEXEC); |
|
1754 |
|
if (fd < 0) { |
|
1755 |
|
err = -errno; |
|
1756 |
|
|
|
1757 |
|
DBG(mod->ctx, "could not open '%s': %s\n", |
|
1758 |
|
path, strerror(-err)); |
|
1759 |
|
|
|
1760 |
|
if (pathlen > (int)sizeof("/initstate") - 1) { |
|
1761 |
|
struct stat st; |
|
1762 |
|
path[pathlen - (sizeof("/initstate") - 1)] = '\0'; |
|
1763 |
|
if (stat(path, &st) == 0 && S_ISDIR(st.st_mode)) |
|
1764 |
|
return KMOD_MODULE_COMING; |
|
1765 |
|
} |
|
1766 |
|
|
|
1767 |
|
DBG(mod->ctx, "could not open '%s': %s\n", |
|
1768 |
|
path, strerror(-err)); |
|
1769 |
|
return err; |
|
1770 |
|
} |
|
1771 |
|
|
|
1772 |
|
err = read_str_safe(fd, buf, sizeof(buf)); |
|
1773 |
|
close(fd); |
|
1774 |
|
if (err < 0) { |
|
1775 |
|
ERR(mod->ctx, "could not read from '%s': %s\n", |
|
1776 |
|
path, strerror(-err)); |
|
1777 |
|
return err; |
|
1778 |
|
} |
|
1779 |
|
|
|
1780 |
|
if (streq(buf, "live\n")) |
|
1781 |
|
return KMOD_MODULE_LIVE; |
|
1782 |
|
else if (streq(buf, "coming\n")) |
|
1783 |
|
return KMOD_MODULE_COMING; |
|
1784 |
|
else if (streq(buf, "going\n")) |
|
1785 |
|
return KMOD_MODULE_GOING; |
|
1786 |
|
|
|
1787 |
|
ERR(mod->ctx, "unknown %s: '%s'\n", path, buf); |
|
1788 |
|
return -EINVAL; |
|
1789 |
|
} |
|
1790 |
|
|
|
1791 |
|
/** |
|
1792 |
|
* kmod_module_get_size: |
|
1793 |
|
* @mod: kmod module |
|
1794 |
|
* |
|
1795 |
|
* Get the size of this kmod module as returned by Linux kernel. If supported, |
|
1796 |
|
* the size is read from the coresize attribute in /sys/module. For older |
|
1797 |
|
* kernels, this falls back on /proc/modules and searches for the specified |
|
1798 |
|
* module to get its size. |
|
1799 |
|
* |
|
1800 |
|
* Returns: the size of this kmod module. |
|
1801 |
|
*/ |
|
1802 |
|
long kmod_module_get_size(const struct kmod_module *mod) |
|
1803 |
|
{ |
|
1804 |
|
FILE *fp; |
|
1805 |
|
char line[4096]; |
|
1806 |
|
int lineno = 0; |
|
1807 |
|
long size = -ENOENT; |
|
1808 |
|
int dfd, cfd; |
|
1809 |
|
|
|
1810 |
|
if (mod == NULL) |
|
1811 |
|
return -ENOENT; |
|
1812 |
|
|
|
1813 |
|
/* try to open the module dir in /sys. If this fails, don't |
|
1814 |
|
* bother trying to find the size as we know the module isn't |
|
1815 |
|
* loaded. |
|
1816 |
|
*/ |
|
1817 |
|
snprintf(line, sizeof(line), "/sys/module/%s", mod->name); |
|
1818 |
|
dfd = open(line, O_RDONLY|O_CLOEXEC); |
|
1819 |
|
if (dfd < 0) |
|
1820 |
|
return -errno; |
|
1821 |
|
|
|
1822 |
|
/* available as of linux 3.3.x */ |
|
1823 |
|
cfd = openat(dfd, "coresize", O_RDONLY|O_CLOEXEC); |
|
1824 |
|
if (cfd >= 0) { |
|
1825 |
|
if (read_str_long(cfd, &size, 10) < 0) |
|
1826 |
|
ERR(mod->ctx, "failed to read coresize from %s\n", line); |
|
1827 |
|
close(cfd); |
|
1828 |
|
goto done; |
|
1829 |
|
} |
|
1830 |
|
|
|
1831 |
|
/* fall back on parsing /proc/modules */ |
|
1832 |
|
fp = fopen("/proc/modules", "re"); |
|
1833 |
|
if (fp == NULL) { |
|
1834 |
|
int err = -errno; |
|
1835 |
|
ERR(mod->ctx, |
|
1836 |
|
"could not open /proc/modules: %s\n", strerror(errno)); |
|
1837 |
|
close(dfd); |
|
1838 |
|
return err; |
|
1839 |
|
} |
|
1840 |
|
|
|
1841 |
|
while (fgets(line, sizeof(line), fp)) { |
|
1842 |
|
size_t len = strlen(line); |
|
1843 |
|
char *saveptr, *endptr, *tok = strtok_r(line, " \t", &saveptr); |
|
1844 |
|
long value; |
|
1845 |
|
|
|
1846 |
|
lineno++; |
|
1847 |
|
if (tok == NULL || !streq(tok, mod->name)) |
|
1848 |
|
goto eat_line; |
|
1849 |
|
|
|
1850 |
|
tok = strtok_r(NULL, " \t", &saveptr); |
|
1851 |
|
if (tok == NULL) { |
|
1852 |
|
ERR(mod->ctx, |
|
1853 |
|
"invalid line format at /proc/modules:%d\n", lineno); |
|
1854 |
|
break; |
|
1855 |
|
} |
|
1856 |
|
|
|
1857 |
|
value = strtol(tok, &endptr, 10); |
|
1858 |
|
if (endptr == tok || *endptr != '\0') { |
|
1859 |
|
ERR(mod->ctx, |
|
1860 |
|
"invalid line format at /proc/modules:%d\n", lineno); |
|
1861 |
|
break; |
|
1862 |
|
} |
|
1863 |
|
|
|
1864 |
|
size = value; |
|
1865 |
|
break; |
|
1866 |
|
eat_line: |
|
1867 |
|
while (line[len - 1] != '\n' && fgets(line, sizeof(line), fp)) |
|
1868 |
|
len = strlen(line); |
|
1869 |
|
} |
|
1870 |
|
fclose(fp); |
|
1871 |
|
|
|
1872 |
|
done: |
|
1873 |
|
close(dfd); |
|
1874 |
|
return size; |
|
1875 |
|
} |
|
1876 |
|
|
|
1877 |
|
/** |
|
1878 |
|
* kmod_module_get_refcnt: |
|
1879 |
|
* @mod: kmod module |
|
1880 |
|
* |
|
1881 |
|
* Get the ref count of this @mod, as returned by Linux Kernel, by reading |
|
1882 |
|
* /sys filesystem. |
|
1883 |
|
* |
|
1884 |
|
* Returns: the reference count on success or < 0 on failure. |
|
1885 |
|
*/ |
|
1886 |
|
int kmod_module_get_refcnt(const struct kmod_module *mod) |
|
1887 |
|
{ |
|
1888 |
|
char path[PATH_MAX]; |
|
1889 |
|
long refcnt; |
|
1890 |
|
int fd, err; |
|
1891 |
|
|
|
1892 |
|
if (mod == NULL) |
|
1893 |
|
return -ENOENT; |
|
1894 |
|
|
|
1895 |
|
snprintf(path, sizeof(path), "/sys/module/%s/refcnt", mod->name); |
|
1896 |
|
fd = open(path, O_RDONLY|O_CLOEXEC); |
|
1897 |
|
if (fd < 0) { |
|
1898 |
|
err = -errno; |
|
1899 |
|
DBG(mod->ctx, "could not open '%s': %s\n", |
|
1900 |
|
path, strerror(errno)); |
|
1901 |
|
return err; |
|
1902 |
|
} |
|
1903 |
|
|
|
1904 |
|
err = read_str_long(fd, &refcnt, 10); |
|
1905 |
|
close(fd); |
|
1906 |
|
if (err < 0) { |
|
1907 |
|
ERR(mod->ctx, "could not read integer from '%s': '%s'\n", |
|
1908 |
|
path, strerror(-err)); |
|
1909 |
|
return err; |
|
1910 |
|
} |
|
1911 |
|
|
|
1912 |
|
return (int)refcnt; |
|
1913 |
|
} |
|
1914 |
|
|
|
1915 |
|
/** |
|
1916 |
|
* kmod_module_get_holders: |
|
1917 |
|
* @mod: kmod module |
|
1918 |
|
* |
|
1919 |
|
* Get a list of kmod modules that are holding this @mod, as returned by Linux |
|
1920 |
|
* Kernel. After use, free the @list by calling kmod_module_unref_list(). |
|
1921 |
|
* |
|
1922 |
|
* Returns: a new list of kmod modules on success or NULL on failure. |
|
1923 |
|
*/ |
|
1924 |
|
struct kmod_list *kmod_module_get_holders(const struct kmod_module *mod) |
|
1925 |
|
{ |
|
1926 |
|
char dname[PATH_MAX]; |
|
1927 |
|
struct kmod_list *list = NULL; |
|
1928 |
|
struct dirent *dent; |
|
1929 |
|
DIR *d; |
|
1930 |
|
|
|
1931 |
|
if (mod == NULL || mod->ctx == NULL) |
|
1932 |
|
return NULL; |
|
1933 |
|
|
|
1934 |
|
snprintf(dname, sizeof(dname), "/sys/module/%s/holders", mod->name); |
|
1935 |
|
|
|
1936 |
|
d = opendir(dname); |
|
1937 |
|
if (d == NULL) { |
|
1938 |
|
ERR(mod->ctx, "could not open '%s': %s\n", |
|
1939 |
|
dname, strerror(errno)); |
|
1940 |
|
return NULL; |
|
1941 |
|
} |
|
1942 |
|
|
|
1943 |
|
for (dent = readdir(d); dent != NULL; dent = readdir(d)) { |
|
1944 |
|
struct kmod_module *holder; |
|
1945 |
|
struct kmod_list *l; |
|
1946 |
|
int err; |
|
1947 |
|
|
|
1948 |
|
if (dent->d_name[0] == '.') { |
|
1949 |
|
if (dent->d_name[1] == '\0' || |
|
1950 |
|
(dent->d_name[1] == '.' && dent->d_name[2] == '\0')) |
|
1951 |
|
continue; |
|
1952 |
|
} |
|
1953 |
|
|
|
1954 |
|
err = kmod_module_new_from_name(mod->ctx, dent->d_name, |
|
1955 |
|
&holder); |
|
1956 |
|
if (err < 0) { |
|
1957 |
|
ERR(mod->ctx, "could not create module for '%s': %s\n", |
|
1958 |
|
dent->d_name, strerror(-err)); |
|
1959 |
|
goto fail; |
|
1960 |
|
} |
|
1961 |
|
|
|
1962 |
|
l = __kmod_list_append(list, holder); |
|
1963 |
|
if (l != NULL) { |
|
1964 |
|
list = l; |
|
1965 |
|
} else { |
|
1966 |
|
ERR(mod->ctx, "out of memory\n"); |
|
1967 |
|
kmod_module_unref(holder); |
|
1968 |
|
goto fail; |
|
1969 |
|
} |
|
1970 |
|
} |
|
1971 |
|
|
|
1972 |
|
closedir(d); |
|
1973 |
|
return list; |
|
1974 |
|
|
|
1975 |
|
fail: |
|
1976 |
|
closedir(d); |
|
1977 |
|
kmod_module_unref_list(list); |
|
1978 |
|
return NULL; |
|
1979 |
|
} |
|
1980 |
|
|
|
1981 |
|
struct kmod_module_section { |
|
1982 |
|
unsigned long address; |
|
1983 |
|
char name[]; |
|
1984 |
|
}; |
|
1985 |
|
|
|
1986 |
|
static void kmod_module_section_free(struct kmod_module_section *section) |
|
1987 |
|
{ |
|
1988 |
|
free(section); |
|
1989 |
|
} |
|
1990 |
|
|
|
1991 |
|
/** |
|
1992 |
|
* kmod_module_get_sections: |
|
1993 |
|
* @mod: kmod module |
|
1994 |
|
* |
|
1995 |
|
* Get a list of kmod sections of this @mod, as returned by Linux Kernel. The |
|
1996 |
|
* structure contained in this list is internal to libkmod and their fields |
|
1997 |
|
* can be obtained by calling kmod_module_section_get_name() and |
|
1998 |
|
* kmod_module_section_get_address(). |
|
1999 |
|
* |
|
2000 |
|
* After use, free the @list by calling kmod_module_section_free_list(). |
|
2001 |
|
* |
|
2002 |
|
* Returns: a new list of kmod module sections on success or NULL on failure. |
|
2003 |
|
*/ |
|
2004 |
|
struct kmod_list *kmod_module_get_sections(const struct kmod_module *mod) |
|
2005 |
|
{ |
|
2006 |
|
char dname[PATH_MAX]; |
|
2007 |
|
struct kmod_list *list = NULL; |
|
2008 |
|
struct dirent *dent; |
|
2009 |
|
DIR *d; |
|
2010 |
|
int dfd; |
|
2011 |
|
|
|
2012 |
|
if (mod == NULL) |
|
2013 |
|
return NULL; |
|
2014 |
|
|
|
2015 |
|
snprintf(dname, sizeof(dname), "/sys/module/%s/sections", mod->name); |
|
2016 |
|
|
|
2017 |
|
d = opendir(dname); |
|
2018 |
|
if (d == NULL) { |
|
2019 |
|
ERR(mod->ctx, "could not open '%s': %s\n", |
|
2020 |
|
dname, strerror(errno)); |
|
2021 |
|
return NULL; |
|
2022 |
|
} |
|
2023 |
|
|
|
2024 |
|
dfd = dirfd(d); |
|
2025 |
|
|
|
2026 |
|
for (dent = readdir(d); dent; dent = readdir(d)) { |
|
2027 |
|
struct kmod_module_section *section; |
|
2028 |
|
struct kmod_list *l; |
|
2029 |
|
unsigned long address; |
|
2030 |
|
size_t namesz; |
|
2031 |
|
int fd, err; |
|
2032 |
|
|
|
2033 |
|
if (dent->d_name[0] == '.') { |
|
2034 |
|
if (dent->d_name[1] == '\0' || |
|
2035 |
|
(dent->d_name[1] == '.' && dent->d_name[2] == '\0')) |
|
2036 |
|
continue; |
|
2037 |
|
} |
|
2038 |
|
|
|
2039 |
|
fd = openat(dfd, dent->d_name, O_RDONLY|O_CLOEXEC); |
|
2040 |
|
if (fd < 0) { |
|
2041 |
|
ERR(mod->ctx, "could not open '%s/%s': %m\n", |
|
2042 |
|
dname, dent->d_name); |
|
2043 |
|
goto fail; |
|
2044 |
|
} |
|
2045 |
|
|
|
2046 |
|
err = read_str_ulong(fd, &address, 16); |
|
2047 |
|
close(fd); |
|
2048 |
|
if (err < 0) { |
|
2049 |
|
ERR(mod->ctx, "could not read long from '%s/%s': %m\n", |
|
2050 |
|
dname, dent->d_name); |
|
2051 |
|
goto fail; |
|
2052 |
|
} |
|
2053 |
|
|
|
2054 |
|
namesz = strlen(dent->d_name) + 1; |
|
2055 |
|
section = malloc(sizeof(*section) + namesz); |
|
2056 |
|
|
|
2057 |
|
if (section == NULL) { |
|
2058 |
|
ERR(mod->ctx, "out of memory\n"); |
|
2059 |
|
goto fail; |
|
2060 |
|
} |
|
2061 |
|
|
|
2062 |
|
section->address = address; |
|
2063 |
|
memcpy(section->name, dent->d_name, namesz); |
|
2064 |
|
|
|
2065 |
|
l = __kmod_list_append(list, section); |
|
2066 |
|
if (l != NULL) { |
|
2067 |
|
list = l; |
|
2068 |
|
} else { |
|
2069 |
|
ERR(mod->ctx, "out of memory\n"); |
|
2070 |
|
free(section); |
|
2071 |
|
goto fail; |
|
2072 |
|
} |
|
2073 |
|
} |
|
2074 |
|
|
|
2075 |
|
closedir(d); |
|
2076 |
|
return list; |
|
2077 |
|
|
|
2078 |
|
fail: |
|
2079 |
|
closedir(d); |
|
2080 |
|
kmod_module_unref_list(list); |
|
2081 |
|
return NULL; |
|
2082 |
|
} |
|
2083 |
|
|
|
2084 |
|
/** |
|
2085 |
|
* kmod_module_section_get_module_name: |
|
2086 |
|
* @entry: a list entry representing a kmod module section |
|
2087 |
|
* |
|
2088 |
|
* Get the name of a kmod module section. |
|
2089 |
|
* |
|
2090 |
|
* After use, free the @list by calling kmod_module_section_free_list(). |
|
2091 |
|
* |
|
2092 |
|
* Returns: the name of this kmod module section on success or NULL on |
|
2093 |
|
* failure. The string is owned by the section, do not free it. |
|
2094 |
|
*/ |
|
2095 |
|
const char *kmod_module_section_get_name(const struct kmod_list *entry) |
|
2096 |
|
{ |
|
2097 |
|
struct kmod_module_section *section; |
|
2098 |
|
|
|
2099 |
|
if (entry == NULL) |
|
2100 |
|
return NULL; |
|
2101 |
|
|
|
2102 |
|
section = entry->data; |
|
2103 |
|
return section->name; |
|
2104 |
|
} |
|
2105 |
|
|
|
2106 |
|
/** |
|
2107 |
|
* kmod_module_section_get_address: |
|
2108 |
|
* @entry: a list entry representing a kmod module section |
|
2109 |
|
* |
|
2110 |
|
* Get the address of a kmod module section. |
|
2111 |
|
* |
|
2112 |
|
* After use, free the @list by calling kmod_module_section_free_list(). |
|
2113 |
|
* |
|
2114 |
|
* Returns: the address of this kmod module section on success or ULONG_MAX |
|
2115 |
|
* on failure. |
|
2116 |
|
*/ |
|
2117 |
|
unsigned long kmod_module_section_get_address(const struct kmod_list *entry) |
|
2118 |
|
{ |
|
2119 |
|
struct kmod_module_section *section; |
|
2120 |
|
|
|
2121 |
|
if (entry == NULL) |
|
2122 |
|
return (unsigned long)-1; |
|
2123 |
|
|
|
2124 |
|
section = entry->data; |
|
2125 |
|
return section->address; |
|
2126 |
|
} |
|
2127 |
|
|
|
2128 |
|
/** |
|
2129 |
|
* kmod_module_section_free_list: |
|
2130 |
|
* @list: kmod module section list |
|
2131 |
|
* |
|
2132 |
|
* Release the resources taken by @list |
|
2133 |
|
*/ |
|
2134 |
|
void kmod_module_section_free_list(struct kmod_list *list) |
|
2135 |
|
{ |
|
2136 |
|
while (list) { |
|
2137 |
|
kmod_module_section_free(list->data); |
|
2138 |
|
list = __kmod_list_remove(list); |
|
2139 |
|
} |
|
2140 |
|
} |
|
2141 |
|
|
|
2142 |
|
static struct kmod_elf *kmod_module_get_elf(const struct kmod_module *mod) |
|
2143 |
|
{ |
|
2144 |
|
if (mod->file == NULL) { |
|
2145 |
|
const char *path = kmod_module_get_path(mod); |
|
2146 |
|
|
|
2147 |
|
if (path == NULL) { |
|
2148 |
|
errno = ENOENT; |
|
2149 |
|
return NULL; |
|
2150 |
|
} |
|
2151 |
|
|
|
2152 |
|
((struct kmod_module *)mod)->file = kmod_file_open(mod->ctx, |
|
2153 |
|
path); |
|
2154 |
|
if (mod->file == NULL) |
|
2155 |
|
return NULL; |
|
2156 |
|
} |
|
2157 |
|
|
|
2158 |
|
return kmod_file_get_elf(mod->file); |
|
2159 |
|
} |
|
2160 |
|
|
|
2161 |
|
struct kmod_module_info { |
|
2162 |
|
char *key; |
|
2163 |
|
char value[]; |
|
2164 |
|
}; |
|
2165 |
|
|
|
2166 |
|
static struct kmod_module_info *kmod_module_info_new(const char *key, size_t keylen, const char *value, size_t valuelen) |
|
2167 |
|
{ |
|
2168 |
|
struct kmod_module_info *info; |
|
2169 |
|
|
|
2170 |
|
info = malloc(sizeof(struct kmod_module_info) + keylen + valuelen + 2); |
|
2171 |
|
if (info == NULL) |
|
2172 |
|
return NULL; |
|
2173 |
|
|
|
2174 |
|
info->key = (char *)info + sizeof(struct kmod_module_info) |
|
2175 |
|
+ valuelen + 1; |
|
2176 |
|
memcpy(info->key, key, keylen); |
|
2177 |
|
info->key[keylen] = '\0'; |
|
2178 |
|
memcpy(info->value, value, valuelen); |
|
2179 |
|
info->value[valuelen] = '\0'; |
|
2180 |
|
return info; |
|
2181 |
|
} |
|
2182 |
|
|
|
2183 |
|
static void kmod_module_info_free(struct kmod_module_info *info) |
|
2184 |
|
{ |
|
2185 |
|
free(info); |
|
2186 |
|
} |
|
2187 |
|
|
|
2188 |
|
static struct kmod_list *kmod_module_info_append(struct kmod_list **list, const char *key, size_t keylen, const char *value, size_t valuelen) |
|
2189 |
|
{ |
|
2190 |
|
struct kmod_module_info *info; |
|
2191 |
|
struct kmod_list *n; |
|
2192 |
|
|
|
2193 |
|
info = kmod_module_info_new(key, keylen, value, valuelen); |
|
2194 |
|
if (info == NULL) |
|
2195 |
|
return NULL; |
|
2196 |
|
n = __kmod_list_append(*list, info); |
|
2197 |
|
if (n != NULL) |
|
2198 |
|
*list = n; |
|
2199 |
|
else |
|
2200 |
|
kmod_module_info_free(info); |
|
2201 |
|
return n; |
|
2202 |
|
} |
|
2203 |
|
|
|
2204 |
|
/** |
|
2205 |
|
* kmod_module_get_info: |
|
2206 |
|
* @mod: kmod module |
|
2207 |
|
* @list: where to return list of module information. Use |
|
2208 |
|
* kmod_module_info_get_key() and |
|
2209 |
|
* kmod_module_info_get_value(). Release this list with |
|
2210 |
|
* kmod_module_info_free_list() |
|
2211 |
|
* |
|
2212 |
|
* Get a list of entries in ELF section ".modinfo", these contain |
|
2213 |
|
* alias, license, depends, vermagic and other keys with respective |
|
2214 |
|
* values. If the module is signed (CONFIG_MODULE_SIG), information |
|
2215 |
|
* about the module signature is included as well: signer, |
|
2216 |
|
* sig_key and sig_hashalgo. |
|
2217 |
|
* |
|
2218 |
|
* After use, free the @list by calling kmod_module_info_free_list(). |
|
2219 |
|
* |
|
2220 |
|
* Returns: 0 on success or < 0 otherwise. |
|
2221 |
|
*/ |
|
2222 |
|
int kmod_module_get_info(const struct kmod_module *mod, struct kmod_list **list) |
|
2223 |
|
{ |
|
2224 |
|
struct kmod_elf *elf; |
|
2225 |
|
char **strings; |
|
2226 |
|
int i, count, ret = -ENOMEM; |
|
2227 |
|
struct kmod_signature_info sig_info; |
|
2228 |
|
|
|
2229 |
|
if (mod == NULL || list == NULL) |
|
2230 |
|
return -ENOENT; |
|
2231 |
|
|
|
2232 |
|
assert(*list == NULL); |
|
2233 |
|
|
|
2234 |
|
elf = kmod_module_get_elf(mod); |
|
2235 |
|
if (elf == NULL) |
|
2236 |
|
return -errno; |
|
2237 |
|
|
|
2238 |
|
count = kmod_elf_get_strings(elf, ".modinfo", &strings); |
|
2239 |
|
if (count < 0) |
|
2240 |
|
return count; |
|
2241 |
|
|
|
2242 |
|
for (i = 0; i < count; i++) { |
|
2243 |
|
struct kmod_list *n; |
|
2244 |
|
const char *key, *value; |
|
2245 |
|
size_t keylen, valuelen; |
|
2246 |
|
|
|
2247 |
|
key = strings[i]; |
|
2248 |
|
value = strchr(key, '='); |
|
2249 |
|
if (value == NULL) { |
|
2250 |
|
keylen = strlen(key); |
|
2251 |
|
valuelen = 0; |
|
2252 |
|
value = key; |
|
2253 |
|
} else { |
|
2254 |
|
keylen = value - key; |
|
2255 |
|
value++; |
|
2256 |
|
valuelen = strlen(value); |
|
2257 |
|
} |
|
2258 |
|
|
|
2259 |
|
n = kmod_module_info_append(list, key, keylen, value, valuelen); |
|
2260 |
|
if (n == NULL) |
|
2261 |
|
goto list_error; |
|
2262 |
|
} |
|
2263 |
|
|
|
2264 |
|
if (kmod_module_signature_info(mod->file, &sig_info)) { |
|
2265 |
|
struct kmod_list *n; |
|
2266 |
|
char *key_hex; |
|
2267 |
|
|
|
2268 |
|
n = kmod_module_info_append(list, "signature", strlen("sig_id"), |
|
2269 |
|
sig_info.id_type, strlen(sig_info.id_type)); |
|
2270 |
|
if (n == NULL) |
|
2271 |
|
goto list_error; |
|
2272 |
|
count++; |
|
2273 |
|
|
|
2274 |
|
n = kmod_module_info_append(list, "signer", strlen("signer"), |
|
2275 |
|
sig_info.signer, sig_info.signer_len); |
|
2276 |
|
if (n == NULL) |
|
2277 |
|
goto list_error; |
|
2278 |
|
count++; |
|
2279 |
|
|
|
2280 |
|
if (sig_info.key_id_len) { |
|
2281 |
|
/* Display the key id as 01:12:DE:AD:BE:EF:... */ |
|
2282 |
|
key_hex = malloc(sig_info.key_id_len * 3); |
|
2283 |
|
if (key_hex == NULL) |
|
2284 |
|
goto list_error; |
|
2285 |
|
for (i = 0; i < (int)sig_info.key_id_len; i++) { |
|
2286 |
|
sprintf(key_hex + i * 3, "%02X", |
|
2287 |
|
(unsigned char)sig_info.key_id[i]); |
|
2288 |
|
if (i < (int)sig_info.key_id_len - 1) |
|
2289 |
|
key_hex[i * 3 + 2] = ':'; |
|
2290 |
|
} |
|
2291 |
|
n = kmod_module_info_append(list, "sig_key", strlen("sig_key"), |
|
2292 |
|
key_hex, sig_info.key_id_len * 3 - 1); |
|
2293 |
|
free(key_hex); |
|
2294 |
|
if (n == NULL) |
|
2295 |
|
goto list_error; |
|
2296 |
|
count++; |
|
2297 |
|
} else { |
|
2298 |
|
n = kmod_module_info_append(list, "sig_key", strlen("sig_key"), |
|
2299 |
|
NULL, 0); |
|
2300 |
|
if (n == NULL) |
|
2301 |
|
goto list_error; |
|
2302 |
|
count++; |
|
2303 |
|
} |
|
2304 |
|
|
|
2305 |
|
n = kmod_module_info_append(list, |
|
2306 |
|
"sig_hashalgo", strlen("sig_hashalgo"), |
|
2307 |
|
sig_info.hash_algo, strlen(sig_info.hash_algo)); |
|
2308 |
|
if (n == NULL) |
|
2309 |
|
goto list_error; |
|
2310 |
|
count++; |
|
2311 |
|
|
|
2312 |
|
/* |
|
2313 |
|
* Omit sig_info.algo for now, as these |
|
2314 |
|
* are currently constant. |
|
2315 |
|
*/ |
|
2316 |
|
} |
|
2317 |
|
ret = count; |
|
2318 |
|
|
|
2319 |
|
list_error: |
|
2320 |
|
if (ret < 0) { |
|
2321 |
|
kmod_module_info_free_list(*list); |
|
2322 |
|
*list = NULL; |
|
2323 |
|
} |
|
2324 |
|
free(strings); |
|
2325 |
|
return ret; |
|
2326 |
|
} |
|
2327 |
|
|
|
2328 |
|
/** |
|
2329 |
|
* kmod_module_info_get_key: |
|
2330 |
|
* @entry: a list entry representing a kmod module info |
|
2331 |
|
* |
|
2332 |
|
* Get the key of a kmod module info. |
|
2333 |
|
* |
|
2334 |
|
* Returns: the key of this kmod module info on success or NULL on |
|
2335 |
|
* failure. The string is owned by the info, do not free it. |
|
2336 |
|
*/ |
|
2337 |
|
const char *kmod_module_info_get_key(const struct kmod_list *entry) |
|
2338 |
|
{ |
|
2339 |
|
struct kmod_module_info *info; |
|
2340 |
|
|
|
2341 |
|
if (entry == NULL) |
|
2342 |
|
return NULL; |
|
2343 |
|
|
|
2344 |
|
info = entry->data; |
|
2345 |
|
return info->key; |
|
2346 |
|
} |
|
2347 |
|
|
|
2348 |
|
/** |
|
2349 |
|
* kmod_module_info_get_value: |
|
2350 |
|
* @entry: a list entry representing a kmod module info |
|
2351 |
|
* |
|
2352 |
|
* Get the value of a kmod module info. |
|
2353 |
|
* |
|
2354 |
|
* Returns: the value of this kmod module info on success or NULL on |
|
2355 |
|
* failure. The string is owned by the info, do not free it. |
|
2356 |
|
*/ |
|
2357 |
|
const char *kmod_module_info_get_value(const struct kmod_list *entry) |
|
2358 |
|
{ |
|
2359 |
|
struct kmod_module_info *info; |
|
2360 |
|
|
|
2361 |
|
if (entry == NULL) |
|
2362 |
|
return NULL; |
|
2363 |
|
|
|
2364 |
|
info = entry->data; |
|
2365 |
|
return info->value; |
|
2366 |
|
} |
|
2367 |
|
|
|
2368 |
|
/** |
|
2369 |
|
* kmod_module_info_free_list: |
|
2370 |
|
* @list: kmod module info list |
|
2371 |
|
* |
|
2372 |
|
* Release the resources taken by @list |
|
2373 |
|
*/ |
|
2374 |
|
void kmod_module_info_free_list(struct kmod_list *list) |
|
2375 |
|
{ |
|
2376 |
|
while (list) { |
|
2377 |
|
kmod_module_info_free(list->data); |
|
2378 |
|
list = __kmod_list_remove(list); |
|
2379 |
|
} |
|
2380 |
|
} |
|
2381 |
|
|
|
2382 |
|
struct kmod_module_version { |
|
2383 |
|
uint64_t crc; |
|
2384 |
|
char symbol[]; |
|
2385 |
|
}; |
|
2386 |
|
|
|
2387 |
|
static struct kmod_module_version *kmod_module_versions_new(uint64_t crc, const char *symbol) |
|
2388 |
|
{ |
|
2389 |
|
struct kmod_module_version *mv; |
|
2390 |
|
size_t symbollen = strlen(symbol) + 1; |
|
2391 |
|
|
|
2392 |
|
mv = malloc(sizeof(struct kmod_module_version) + symbollen); |
|
2393 |
|
if (mv == NULL) |
|
2394 |
|
return NULL; |
|
2395 |
|
|
|
2396 |
|
mv->crc = crc; |
|
2397 |
|
memcpy(mv->symbol, symbol, symbollen); |
|
2398 |
|
return mv; |
|
2399 |
|
} |
|
2400 |
|
|
|
2401 |
|
static void kmod_module_version_free(struct kmod_module_version *version) |
|
2402 |
|
{ |
|
2403 |
|
free(version); |
|
2404 |
|
} |
|
2405 |
|
|
|
2406 |
|
/** |
|
2407 |
|
* kmod_module_get_versions: |
|
2408 |
|
* @mod: kmod module |
|
2409 |
|
* @list: where to return list of module versions. Use |
|
2410 |
|
* kmod_module_version_get_symbol() and |
|
2411 |
|
* kmod_module_version_get_crc(). Release this list with |
|
2412 |
|
* kmod_module_versions_free_list() |
|
2413 |
|
* |
|
2414 |
|
* Get a list of entries in ELF section "__versions". |
|
2415 |
|
* |
|
2416 |
|
* After use, free the @list by calling kmod_module_versions_free_list(). |
|
2417 |
|
* |
|
2418 |
|
* Returns: 0 on success or < 0 otherwise. |
|
2419 |
|
*/ |
|
2420 |
|
int kmod_module_get_versions(const struct kmod_module *mod, struct kmod_list **list) |
|
2421 |
|
{ |
|
2422 |
|
struct kmod_elf *elf; |
|
2423 |
|
struct kmod_modversion *versions; |
|
2424 |
|
int i, count, ret = 0; |
|
2425 |
|
|
|
2426 |
|
if (mod == NULL || list == NULL) |
|
2427 |
|
return -ENOENT; |
|
2428 |
|
|
|
2429 |
|
assert(*list == NULL); |
|
2430 |
|
|
|
2431 |
|
elf = kmod_module_get_elf(mod); |
|
2432 |
|
if (elf == NULL) |
|
2433 |
|
return -errno; |
|
2434 |
|
|
|
2435 |
|
count = kmod_elf_get_modversions(elf, &versions); |
|
2436 |
|
if (count < 0) |
|
2437 |
|
return count; |
|
2438 |
|
|
|
2439 |
|
for (i = 0; i < count; i++) { |
|
2440 |
|
struct kmod_module_version *mv; |
|
2441 |
|
struct kmod_list *n; |
|
2442 |
|
|
|
2443 |
|
mv = kmod_module_versions_new(versions[i].crc, versions[i].symbol); |
|
2444 |
|
if (mv == NULL) { |
|
2445 |
|
ret = -errno; |
|
2446 |
|
kmod_module_versions_free_list(*list); |
|
2447 |
|
*list = NULL; |
|
2448 |
|
goto list_error; |
|
2449 |
|
} |
|
2450 |
|
|
|
2451 |
|
n = __kmod_list_append(*list, mv); |
|
2452 |
|
if (n != NULL) |
|
2453 |
|
*list = n; |
|
2454 |
|
else { |
|
2455 |
|
kmod_module_version_free(mv); |
|
2456 |
|
kmod_module_versions_free_list(*list); |
|
2457 |
|
*list = NULL; |
|
2458 |
|
ret = -ENOMEM; |
|
2459 |
|
goto list_error; |
|
2460 |
|
} |
|
2461 |
|
} |
|
2462 |
|
ret = count; |
|
2463 |
|
|
|
2464 |
|
list_error: |
|
2465 |
|
free(versions); |
|
2466 |
|
return ret; |
|
2467 |
|
} |
|
2468 |
|
|
|
2469 |
|
/** |
|
2470 |
|
* kmod_module_version_get_symbol: |
|
2471 |
|
* @entry: a list entry representing a kmod module versions |
|
2472 |
|
* |
|
2473 |
|
* Get the symbol of a kmod module versions. |
|
2474 |
|
* |
|
2475 |
|
* Returns: the symbol of this kmod module versions on success or NULL |
|
2476 |
|
* on failure. The string is owned by the versions, do not free it. |
|
2477 |
|
*/ |
|
2478 |
|
const char *kmod_module_version_get_symbol(const struct kmod_list *entry) |
|
2479 |
|
{ |
|
2480 |
|
struct kmod_module_version *version; |
|
2481 |
|
|
|
2482 |
|
if (entry == NULL) |
|
2483 |
|
return NULL; |
|
2484 |
|
|
|
2485 |
|
version = entry->data; |
|
2486 |
|
return version->symbol; |
|
2487 |
|
} |
|
2488 |
|
|
|
2489 |
|
/** |
|
2490 |
|
* kmod_module_version_get_crc: |
|
2491 |
|
* @entry: a list entry representing a kmod module version |
|
2492 |
|
* |
|
2493 |
|
* Get the crc of a kmod module version. |
|
2494 |
|
* |
|
2495 |
|
* Returns: the crc of this kmod module version on success or NULL on |
|
2496 |
|
* failure. The string is owned by the version, do not free it. |
|
2497 |
|
*/ |
|
2498 |
|
uint64_t kmod_module_version_get_crc(const struct kmod_list *entry) |
|
2499 |
|
{ |
|
2500 |
|
struct kmod_module_version *version; |
|
2501 |
|
|
|
2502 |
|
if (entry == NULL) |
|
2503 |
|
return 0; |
|
2504 |
|
|
|
2505 |
|
version = entry->data; |
|
2506 |
|
return version->crc; |
|
2507 |
|
} |
|
2508 |
|
|
|
2509 |
|
/** |
|
2510 |
|
* kmod_module_versions_free_list: |
|
2511 |
|
* @list: kmod module versions list |
|
2512 |
|
* |
|
2513 |
|
* Release the resources taken by @list |
|
2514 |
|
*/ |
|
2515 |
|
void kmod_module_versions_free_list(struct kmod_list *list) |
|
2516 |
|
{ |
|
2517 |
|
while (list) { |
|
2518 |
|
kmod_module_version_free(list->data); |
|
2519 |
|
list = __kmod_list_remove(list); |
|
2520 |
|
} |
|
2521 |
|
} |
|
2522 |
|
|
|
2523 |
|
struct kmod_module_symbol { |
|
2524 |
|
uint64_t crc; |
|
2525 |
|
char symbol[]; |
|
2526 |
|
}; |
|
2527 |
|
|
|
2528 |
|
static struct kmod_module_symbol *kmod_module_symbols_new(uint64_t crc, const char *symbol) |
|
2529 |
|
{ |
|
2530 |
|
struct kmod_module_symbol *mv; |
|
2531 |
|
size_t symbollen = strlen(symbol) + 1; |
|
2532 |
|
|
|
2533 |
|
mv = malloc(sizeof(struct kmod_module_symbol) + symbollen); |
|
2534 |
|
if (mv == NULL) |
|
2535 |
|
return NULL; |
|
2536 |
|
|
|
2537 |
|
mv->crc = crc; |
|
2538 |
|
memcpy(mv->symbol, symbol, symbollen); |
|
2539 |
|
return mv; |
|
2540 |
|
} |
|
2541 |
|
|
|
2542 |
|
static void kmod_module_symbol_free(struct kmod_module_symbol *symbol) |
|
2543 |
|
{ |
|
2544 |
|
free(symbol); |
|
2545 |
|
} |
|
2546 |
|
|
|
2547 |
|
/** |
|
2548 |
|
* kmod_module_get_symbols: |
|
2549 |
|
* @mod: kmod module |
|
2550 |
|
* @list: where to return list of module symbols. Use |
|
2551 |
|
* kmod_module_symbol_get_symbol() and |
|
2552 |
|
* kmod_module_symbol_get_crc(). Release this list with |
|
2553 |
|
* kmod_module_symbols_free_list() |
|
2554 |
|
* |
|
2555 |
|
* Get a list of entries in ELF section ".symtab" or "__ksymtab_strings". |
|
2556 |
|
* |
|
2557 |
|
* After use, free the @list by calling kmod_module_symbols_free_list(). |
|
2558 |
|
* |
|
2559 |
|
* Returns: 0 on success or < 0 otherwise. |
|
2560 |
|
*/ |
|
2561 |
|
int kmod_module_get_symbols(const struct kmod_module *mod, struct kmod_list **list) |
|
2562 |
|
{ |
|
2563 |
|
struct kmod_elf *elf; |
|
2564 |
|
struct kmod_modversion *symbols; |
|
2565 |
|
int i, count, ret = 0; |
|
2566 |
|
|
|
2567 |
|
if (mod == NULL || list == NULL) |
|
2568 |
|
return -ENOENT; |
|
2569 |
|
|
|
2570 |
|
assert(*list == NULL); |
|
2571 |
|
|
|
2572 |
|
elf = kmod_module_get_elf(mod); |
|
2573 |
|
if (elf == NULL) |
|
2574 |
|
return -errno; |
|
2575 |
|
|
|
2576 |
|
count = kmod_elf_get_symbols(elf, &symbols); |
|
2577 |
|
if (count < 0) |
|
2578 |
|
return count; |
|
2579 |
|
|
|
2580 |
|
for (i = 0; i < count; i++) { |
|
2581 |
|
struct kmod_module_symbol *mv; |
|
2582 |
|
struct kmod_list *n; |
|
2583 |
|
|
|
2584 |
|
mv = kmod_module_symbols_new(symbols[i].crc, symbols[i].symbol); |
|
2585 |
|
if (mv == NULL) { |
|
2586 |
|
ret = -errno; |
|
2587 |
|
kmod_module_symbols_free_list(*list); |
|
2588 |
|
*list = NULL; |
|
2589 |
|
goto list_error; |
|
2590 |
|
} |
|
2591 |
|
|
|
2592 |
|
n = __kmod_list_append(*list, mv); |
|
2593 |
|
if (n != NULL) |
|
2594 |
|
*list = n; |
|
2595 |
|
else { |
|
2596 |
|
kmod_module_symbol_free(mv); |
|
2597 |
|
kmod_module_symbols_free_list(*list); |
|
2598 |
|
*list = NULL; |
|
2599 |
|
ret = -ENOMEM; |
|
2600 |
|
goto list_error; |
|
2601 |
|
} |
|
2602 |
|
} |
|
2603 |
|
ret = count; |
|
2604 |
|
|
|
2605 |
|
list_error: |
|
2606 |
|
free(symbols); |
|
2607 |
|
return ret; |
|
2608 |
|
} |
|
2609 |
|
|
|
2610 |
|
/** |
|
2611 |
|
* kmod_module_symbol_get_symbol: |
|
2612 |
|
* @entry: a list entry representing a kmod module symbols |
|
2613 |
|
* |
|
2614 |
|
* Get the symbol of a kmod module symbols. |
|
2615 |
|
* |
|
2616 |
|
* Returns: the symbol of this kmod module symbols on success or NULL |
|
2617 |
|
* on failure. The string is owned by the symbols, do not free it. |
|
2618 |
|
*/ |
|
2619 |
|
const char *kmod_module_symbol_get_symbol(const struct kmod_list *entry) |
|
2620 |
|
{ |
|
2621 |
|
struct kmod_module_symbol *symbol; |
|
2622 |
|
|
|
2623 |
|
if (entry == NULL) |
|
2624 |
|
return NULL; |
|
2625 |
|
|
|
2626 |
|
symbol = entry->data; |
|
2627 |
|
return symbol->symbol; |
|
2628 |
|
} |
|
2629 |
|
|
|
2630 |
|
/** |
|
2631 |
|
* kmod_module_symbol_get_crc: |
|
2632 |
|
* @entry: a list entry representing a kmod module symbol |
|
2633 |
|
* |
|
2634 |
|
* Get the crc of a kmod module symbol. |
|
2635 |
|
* |
|
2636 |
|
* Returns: the crc of this kmod module symbol on success or NULL on |
|
2637 |
|
* failure. The string is owned by the symbol, do not free it. |
|
2638 |
|
*/ |
|
2639 |
|
uint64_t kmod_module_symbol_get_crc(const struct kmod_list *entry) |
|
2640 |
|
{ |
|
2641 |
|
struct kmod_module_symbol *symbol; |
|
2642 |
|
|
|
2643 |
|
if (entry == NULL) |
|
2644 |
|
return 0; |
|
2645 |
|
|
|
2646 |
|
symbol = entry->data; |
|
2647 |
|
return symbol->crc; |
|
2648 |
|
} |
|
2649 |
|
|
|
2650 |
|
/** |
|
2651 |
|
* kmod_module_symbols_free_list: |
|
2652 |
|
* @list: kmod module symbols list |
|
2653 |
|
* |
|
2654 |
|
* Release the resources taken by @list |
|
2655 |
|
*/ |
|
2656 |
|
void kmod_module_symbols_free_list(struct kmod_list *list) |
|
2657 |
|
{ |
|
2658 |
|
while (list) { |
|
2659 |
|
kmod_module_symbol_free(list->data); |
|
2660 |
|
list = __kmod_list_remove(list); |
|
2661 |
|
} |
|
2662 |
|
} |
|
2663 |
|
|
|
2664 |
|
struct kmod_module_dependency_symbol { |
|
2665 |
|
uint64_t crc; |
|
2666 |
|
uint8_t bind; |
|
2667 |
|
char symbol[]; |
|
2668 |
|
}; |
|
2669 |
|
|
|
2670 |
|
static struct kmod_module_dependency_symbol *kmod_module_dependency_symbols_new(uint64_t crc, uint8_t bind, const char *symbol) |
|
2671 |
|
{ |
|
2672 |
|
struct kmod_module_dependency_symbol *mv; |
|
2673 |
|
size_t symbollen = strlen(symbol) + 1; |
|
2674 |
|
|
|
2675 |
|
mv = malloc(sizeof(struct kmod_module_dependency_symbol) + symbollen); |
|
2676 |
|
if (mv == NULL) |
|
2677 |
|
return NULL; |
|
2678 |
|
|
|
2679 |
|
mv->crc = crc; |
|
2680 |
|
mv->bind = bind; |
|
2681 |
|
memcpy(mv->symbol, symbol, symbollen); |
|
2682 |
|
return mv; |
|
2683 |
|
} |
|
2684 |
|
|
|
2685 |
|
static void kmod_module_dependency_symbol_free(struct kmod_module_dependency_symbol *dependency_symbol) |
|
2686 |
|
{ |
|
2687 |
|
free(dependency_symbol); |
|
2688 |
|
} |
|
2689 |
|
|
|
2690 |
|
/** |
|
2691 |
|
* kmod_module_get_dependency_symbols: |
|
2692 |
|
* @mod: kmod module |
|
2693 |
|
* @list: where to return list of module dependency_symbols. Use |
|
2694 |
|
* kmod_module_dependency_symbol_get_symbol() and |
|
2695 |
|
* kmod_module_dependency_symbol_get_crc(). Release this list with |
|
2696 |
|
* kmod_module_dependency_symbols_free_list() |
|
2697 |
|
* |
|
2698 |
|
* Get a list of entries in ELF section ".symtab" or "__ksymtab_strings". |
|
2699 |
|
* |
|
2700 |
|
* After use, free the @list by calling |
|
2701 |
|
* kmod_module_dependency_symbols_free_list(). |
|
2702 |
|
* |
|
2703 |
|
* Returns: 0 on success or < 0 otherwise. |
|
2704 |
|
*/ |
|
2705 |
|
int kmod_module_get_dependency_symbols(const struct kmod_module *mod, struct kmod_list **list) |
|
2706 |
|
{ |
|
2707 |
|
struct kmod_elf *elf; |
|
2708 |
|
struct kmod_modversion *symbols; |
|
2709 |
|
int i, count, ret = 0; |
|
2710 |
|
|
|
2711 |
|
if (mod == NULL || list == NULL) |
|
2712 |
|
return -ENOENT; |
|
2713 |
|
|
|
2714 |
|
assert(*list == NULL); |
|
2715 |
|
|
|
2716 |
|
elf = kmod_module_get_elf(mod); |
|
2717 |
|
if (elf == NULL) |
|
2718 |
|
return -errno; |
|
2719 |
|
|
|
2720 |
|
count = kmod_elf_get_dependency_symbols(elf, &symbols); |
|
2721 |
|
if (count < 0) |
|
2722 |
|
return count; |
|
2723 |
|
|
|
2724 |
|
for (i = 0; i < count; i++) { |
|
2725 |
|
struct kmod_module_dependency_symbol *mv; |
|
2726 |
|
struct kmod_list *n; |
|
2727 |
|
|
|
2728 |
|
mv = kmod_module_dependency_symbols_new(symbols[i].crc, |
|
2729 |
|
symbols[i].bind, |
|
2730 |
|
symbols[i].symbol); |
|
2731 |
|
if (mv == NULL) { |
|
2732 |
|
ret = -errno; |
|
2733 |
|
kmod_module_dependency_symbols_free_list(*list); |
|
2734 |
|
*list = NULL; |
|
2735 |
|
goto list_error; |
|
2736 |
|
} |
|
2737 |
|
|
|
2738 |
|
n = __kmod_list_append(*list, mv); |
|
2739 |
|
if (n != NULL) |
|
2740 |
|
*list = n; |
|
2741 |
|
else { |
|
2742 |
|
kmod_module_dependency_symbol_free(mv); |
|
2743 |
|
kmod_module_dependency_symbols_free_list(*list); |
|
2744 |
|
*list = NULL; |
|
2745 |
|
ret = -ENOMEM; |
|
2746 |
|
goto list_error; |
|
2747 |
|
} |
|
2748 |
|
} |
|
2749 |
|
ret = count; |
|
2750 |
|
|
|
2751 |
|
list_error: |
|
2752 |
|
free(symbols); |
|
2753 |
|
return ret; |
|
2754 |
|
} |
|
2755 |
|
|
|
2756 |
|
/** |
|
2757 |
|
* kmod_module_dependency_symbol_get_symbol: |
|
2758 |
|
* @entry: a list entry representing a kmod module dependency_symbols |
|
2759 |
|
* |
|
2760 |
|
* Get the dependency symbol of a kmod module |
|
2761 |
|
* |
|
2762 |
|
* Returns: the symbol of this kmod module dependency_symbols on success or NULL |
|
2763 |
|
* on failure. The string is owned by the dependency_symbols, do not free it. |
|
2764 |
|
*/ |
|
2765 |
|
const char *kmod_module_dependency_symbol_get_symbol(const struct kmod_list *entry) |
|
2766 |
|
{ |
|
2767 |
|
struct kmod_module_dependency_symbol *dependency_symbol; |
|
2768 |
|
|
|
2769 |
|
if (entry == NULL) |
|
2770 |
|
return NULL; |
|
2771 |
|
|
|
2772 |
|
dependency_symbol = entry->data; |
|
2773 |
|
return dependency_symbol->symbol; |
|
2774 |
|
} |
|
2775 |
|
|
|
2776 |
|
/** |
|
2777 |
|
* kmod_module_dependency_symbol_get_crc: |
|
2778 |
|
* @entry: a list entry representing a kmod module dependency_symbol |
|
2779 |
|
* |
|
2780 |
|
* Get the crc of a kmod module dependency_symbol. |
|
2781 |
|
* |
|
2782 |
|
* Returns: the crc of this kmod module dependency_symbol on success or NULL on |
|
2783 |
|
* failure. The string is owned by the dependency_symbol, do not free it. |
|
2784 |
|
*/ |
|
2785 |
|
uint64_t kmod_module_dependency_symbol_get_crc(const struct kmod_list *entry) |
|
2786 |
|
{ |
|
2787 |
|
struct kmod_module_dependency_symbol *dependency_symbol; |
|
2788 |
|
|
|
2789 |
|
if (entry == NULL) |
|
2790 |
|
return 0; |
|
2791 |
|
|
|
2792 |
|
dependency_symbol = entry->data; |
|
2793 |
|
return dependency_symbol->crc; |
|
2794 |
|
} |
|
2795 |
|
|
|
2796 |
|
/** |
|
2797 |
|
* kmod_module_dependency_symbol_get_bind: |
|
2798 |
|
* @entry: a list entry representing a kmod module dependency_symbol |
|
2799 |
|
* |
|
2800 |
|
* Get the bind type of a kmod module dependency_symbol. |
|
2801 |
|
* |
|
2802 |
|
* Returns: the bind of this kmod module dependency_symbol on success |
|
2803 |
|
* or < 0 on failure. |
|
2804 |
|
*/ |
|
2805 |
|
int kmod_module_dependency_symbol_get_bind(const struct kmod_list *entry) |
|
2806 |
|
{ |
|
2807 |
|
struct kmod_module_dependency_symbol *dependency_symbol; |
|
2808 |
|
|
|
2809 |
|
if (entry == NULL) |
|
2810 |
|
return 0; |
|
2811 |
|
|
|
2812 |
|
dependency_symbol = entry->data; |
|
2813 |
|
return dependency_symbol->bind; |
|
2814 |
|
} |
|
2815 |
|
|
|
2816 |
|
/** |
|
2817 |
|
* kmod_module_dependency_symbols_free_list: |
|
2818 |
|
* @list: kmod module dependency_symbols list |
|
2819 |
|
* |
|
2820 |
|
* Release the resources taken by @list |
|
2821 |
|
*/ |
|
2822 |
|
void kmod_module_dependency_symbols_free_list(struct kmod_list *list) |
|
2823 |
|
{ |
|
2824 |
|
while (list) { |
|
2825 |
|
kmod_module_dependency_symbol_free(list->data); |
|
2826 |
|
list = __kmod_list_remove(list); |
|
2827 |
|
} |
|
2828 |
|
} |
File src/libkmod.c added (mode: 100644) (index 0000000..5134e7b) |
|
1 |
|
/* |
|
2 |
|
* libkmod - interface to kernel module operations |
|
3 |
|
* |
|
4 |
|
* Copyright (C) 2011-2013 ProFUSION embedded systems |
|
5 |
|
* |
|
6 |
|
* This library is free software; you can redistribute it and/or |
|
7 |
|
* modify it under the terms of the GNU Lesser General Public |
|
8 |
|
* License as published by the Free Software Foundation; either |
|
9 |
|
* version 2.1 of the License, or (at your option) any later version. |
|
10 |
|
* |
|
11 |
|
* This library is distributed in the hope that it will be useful, |
|
12 |
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
13 |
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
|
14 |
|
* Lesser General Public License for more details. |
|
15 |
|
* |
|
16 |
|
* You should have received a copy of the GNU Lesser General Public |
|
17 |
|
* License along with this library; if not, see <http://www.gnu.org/licenses/>. |
|
18 |
|
*/ |
|
19 |
|
|
|
20 |
|
#include <assert.h> |
|
21 |
|
#include <ctype.h> |
|
22 |
|
#include <errno.h> |
|
23 |
|
#include <fnmatch.h> |
|
24 |
|
#include <limits.h> |
|
25 |
|
#include <stdarg.h> |
|
26 |
|
#include <stddef.h> |
|
27 |
|
#include <stdio.h> |
|
28 |
|
#include <stdlib.h> |
|
29 |
|
#include <string.h> |
|
30 |
|
#include <unistd.h> |
|
31 |
|
#include <stdbool.h> |
|
32 |
|
#include <stdint.h> |
|
33 |
|
#include <syslog.h> |
|
34 |
|
#include <sys/stat.h> |
|
35 |
|
#include <sys/utsname.h> |
|
36 |
|
|
|
37 |
|
#include "config.h" |
|
38 |
|
|
|
39 |
|
#include "libkmod.h" |
|
40 |
|
|
|
41 |
|
#include "libkmod-namespace.h" |
|
42 |
|
#include "libkmod-paths.h" |
|
43 |
|
#include "shared/macro.h" |
|
44 |
|
#include "shared/hash.h" |
|
45 |
|
#include "shared/util.h" |
|
46 |
|
#define LIBKMOD_C |
|
47 |
|
#include "libkmod-internal.h" |
|
48 |
|
#undef LIBKMOD_C |
|
49 |
|
#include "libkmod-index.h" |
|
50 |
|
|
|
51 |
|
#define KMOD_HASH_SIZE (256) |
|
52 |
|
#define KMOD_LRU_MAX (128) |
|
53 |
|
#define _KMOD_INDEX_MODULES_SIZE KMOD_INDEX_MODULES_BUILTIN + 1 |
|
54 |
|
|
|
55 |
|
/** |
|
56 |
|
* SECTION:libkmod |
|
57 |
|
* @short_description: libkmod context |
|
58 |
|
* |
|
59 |
|
* The context contains the default values for the library user, |
|
60 |
|
* and is passed to all library operations. |
|
61 |
|
*/ |
|
62 |
|
|
|
63 |
|
static struct _index_files { |
|
64 |
|
const char *fn; |
|
65 |
|
const char *prefix; |
|
66 |
|
} index_files[] = { |
|
67 |
|
[KMOD_INDEX_MODULES_DEP] = { .fn = "modules.dep", .prefix = "" }, |
|
68 |
|
[KMOD_INDEX_MODULES_ALIAS] = { .fn = "modules.alias", .prefix = "alias " }, |
|
69 |
|
[KMOD_INDEX_MODULES_SYMBOL] = { .fn = "modules.symbols", .prefix = "alias "}, |
|
70 |
|
[KMOD_INDEX_MODULES_BUILTIN] = { .fn = "modules.builtin", .prefix = ""}, |
|
71 |
|
}; |
|
72 |
|
|
|
73 |
|
static const char *default_config_paths[] = { |
|
74 |
|
SYSCONFDIR "/modprobe.d", |
|
75 |
|
"/run/modprobe.d", |
|
76 |
|
"/lib/modprobe.d", |
|
77 |
|
NULL |
|
78 |
|
}; |
|
79 |
|
|
|
80 |
|
/** |
|
81 |
|
* kmod_ctx: |
|
82 |
|
* |
|
83 |
|
* Opaque object representing the library context. |
|
84 |
|
*/ |
|
85 |
|
struct kmod_ctx { |
|
86 |
|
int refcount; |
|
87 |
|
int log_priority; |
|
88 |
|
void (*log_fn)(void *data, |
|
89 |
|
int priority, const char *file, int line, |
|
90 |
|
const char *fn, const char *format, va_list args); |
|
91 |
|
void *log_data; |
|
92 |
|
const void *userdata; |
|
93 |
|
char *dirname; |
|
94 |
|
struct kmod_config *config; |
|
95 |
|
struct hash *modules_by_name; |
|
96 |
|
struct index_mm *indexes[_KMOD_INDEX_MODULES_SIZE]; |
|
97 |
|
unsigned long long indexes_stamp[_KMOD_INDEX_MODULES_SIZE]; |
|
98 |
|
}; |
|
99 |
|
|
|
100 |
|
void kmod_log(const struct kmod_ctx *ctx, |
|
101 |
|
int priority, const char *file, int line, const char *fn, |
|
102 |
|
const char *format, ...) |
|
103 |
|
{ |
|
104 |
|
va_list args; |
|
105 |
|
|
|
106 |
|
if (ctx->log_fn == NULL) |
|
107 |
|
return; |
|
108 |
|
|
|
109 |
|
va_start(args, format); |
|
110 |
|
ctx->log_fn(ctx->log_data, priority, file, line, fn, format, args); |
|
111 |
|
va_end(args); |
|
112 |
|
} |
|
113 |
|
|
|
114 |
|
static void log_filep(void *data, |
|
115 |
|
int priority, const char *file, int line, |
|
116 |
|
const char *fn, const char *format, va_list args) |
|
117 |
|
{ |
|
118 |
|
FILE *fp = data; |
|
119 |
|
#ifdef ENABLE_DEBUG |
|
120 |
|
char buf[16]; |
|
121 |
|
const char *priname; |
|
122 |
|
switch (priority) { |
|
123 |
|
case LOG_EMERG: |
|
124 |
|
priname = "EMERGENCY"; |
|
125 |
|
break; |
|
126 |
|
case LOG_ALERT: |
|
127 |
|
priname = "ALERT"; |
|
128 |
|
break; |
|
129 |
|
case LOG_CRIT: |
|
130 |
|
priname = "CRITICAL"; |
|
131 |
|
break; |
|
132 |
|
case LOG_ERR: |
|
133 |
|
priname = "ERROR"; |
|
134 |
|
break; |
|
135 |
|
case LOG_WARNING: |
|
136 |
|
priname = "WARNING"; |
|
137 |
|
break; |
|
138 |
|
case LOG_NOTICE: |
|
139 |
|
priname = "NOTICE"; |
|
140 |
|
break; |
|
141 |
|
case LOG_INFO: |
|
142 |
|
priname = "INFO"; |
|
143 |
|
break; |
|
144 |
|
case LOG_DEBUG: |
|
145 |
|
priname = "DEBUG"; |
|
146 |
|
break; |
|
147 |
|
default: |
|
148 |
|
snprintf(buf, sizeof(buf), "L:%d", priority); |
|
149 |
|
priname = buf; |
|
150 |
|
} |
|
151 |
|
fprintf(fp, "libkmod: %s %s:%d %s: ", priname, file, line, fn); |
|
152 |
|
#else |
|
153 |
|
fprintf(fp, "libkmod: %s: ", fn); |
|
154 |
|
#endif |
|
155 |
|
vfprintf(fp, format, args); |
|
156 |
|
} |
|
157 |
|
|
|
158 |
|
|
|
159 |
|
/** |
|
160 |
|
* kmod_get_dirname: |
|
161 |
|
* @ctx: kmod library context |
|
162 |
|
* |
|
163 |
|
* Retrieve the absolute path used for linux modules in this context. The path |
|
164 |
|
* is computed from the arguments to kmod_new(). |
|
165 |
|
*/ |
|
166 |
|
const char *kmod_get_dirname(const struct kmod_ctx *ctx) |
|
167 |
|
{ |
|
168 |
|
return ctx->dirname; |
|
169 |
|
} |
|
170 |
|
|
|
171 |
|
/** |
|
172 |
|
* kmod_get_userdata: |
|
173 |
|
* @ctx: kmod library context |
|
174 |
|
* |
|
175 |
|
* Retrieve stored data pointer from library context. This might be useful |
|
176 |
|
* to access from callbacks. |
|
177 |
|
* |
|
178 |
|
* Returns: stored userdata |
|
179 |
|
*/ |
|
180 |
|
void *kmod_get_userdata(const struct kmod_ctx *ctx) |
|
181 |
|
{ |
|
182 |
|
if (ctx == NULL) |
|
183 |
|
return NULL; |
|
184 |
|
return (void *)ctx->userdata; |
|
185 |
|
} |
|
186 |
|
|
|
187 |
|
/** |
|
188 |
|
* kmod_set_userdata: |
|
189 |
|
* @ctx: kmod library context |
|
190 |
|
* @userdata: data pointer |
|
191 |
|
* |
|
192 |
|
* Store custom @userdata in the library context. |
|
193 |
|
*/ |
|
194 |
|
void kmod_set_userdata(struct kmod_ctx *ctx, const void *userdata) |
|
195 |
|
{ |
|
196 |
|
if (ctx == NULL) |
|
197 |
|
return; |
|
198 |
|
ctx->userdata = userdata; |
|
199 |
|
} |
|
200 |
|
|
|
201 |
|
static int log_priority(const char *priority) |
|
202 |
|
{ |
|
203 |
|
char *endptr; |
|
204 |
|
int prio; |
|
205 |
|
|
|
206 |
|
prio = strtol(priority, &endptr, 10); |
|
207 |
|
if (endptr[0] == '\0' || isspace(endptr[0])) |
|
208 |
|
return prio; |
|
209 |
|
if (strncmp(priority, "err", 3) == 0) |
|
210 |
|
return LOG_ERR; |
|
211 |
|
if (strncmp(priority, "info", 4) == 0) |
|
212 |
|
return LOG_INFO; |
|
213 |
|
if (strncmp(priority, "debug", 5) == 0) |
|
214 |
|
return LOG_DEBUG; |
|
215 |
|
return 0; |
|
216 |
|
} |
|
217 |
|
|
|
218 |
|
static const char *dirname_default_prefix = "/lib/modules"; |
|
219 |
|
|
|
220 |
|
static char *get_kernel_release(const char *dirname) |
|
221 |
|
{ |
|
222 |
|
struct utsname u; |
|
223 |
|
char *p; |
|
224 |
|
|
|
225 |
|
if (dirname != NULL) |
|
226 |
|
return path_make_absolute_cwd(dirname); |
|
227 |
|
|
|
228 |
|
if (uname(&u) < 0) |
|
229 |
|
return NULL; |
|
230 |
|
|
|
231 |
|
if (asprintf(&p, "%s/%s", dirname_default_prefix, u.release) < 0) |
|
232 |
|
return NULL; |
|
233 |
|
|
|
234 |
|
return p; |
|
235 |
|
} |
|
236 |
|
|
|
237 |
|
/** |
|
238 |
|
* kmod_new: |
|
239 |
|
* @dirname: what to consider as linux module's directory, if NULL |
|
240 |
|
* defaults to /lib/modules/`uname -r`. If it's relative, |
|
241 |
|
* it's treated as relative to the current working directory. |
|
242 |
|
* Otherwise, give an absolute dirname. |
|
243 |
|
* @config_paths: ordered array of paths (directories or files) where |
|
244 |
|
* to load from user-defined configuration parameters such as |
|
245 |
|
* alias, blacklists, commands (install, remove). If |
|
246 |
|
* NULL defaults to /run/modprobe.d, /etc/modprobe.d and |
|
247 |
|
* /lib/modprobe.d. Give an empty vector if configuration should |
|
248 |
|
* not be read. This array must be null terminated. |
|
249 |
|
* |
|
250 |
|
* Create kmod library context. This reads the kmod configuration |
|
251 |
|
* and fills in the default values. |
|
252 |
|
* |
|
253 |
|
* The initial refcount is 1, and needs to be decremented to |
|
254 |
|
* release the resources of the kmod library context. |
|
255 |
|
* |
|
256 |
|
* Returns: a new kmod library context |
|
257 |
|
*/ |
|
258 |
|
struct kmod_ctx *kmod_new(const char *dirname, |
|
259 |
|
const char * const *config_paths) |
|
260 |
|
{ |
|
261 |
|
const char *env; |
|
262 |
|
struct kmod_ctx *ctx; |
|
263 |
|
int err; |
|
264 |
|
|
|
265 |
|
ctx = calloc(1, sizeof(struct kmod_ctx)); |
|
266 |
|
if (!ctx) |
|
267 |
|
return NULL; |
|
268 |
|
|
|
269 |
|
ctx->refcount = 1; |
|
270 |
|
ctx->log_fn = log_filep; |
|
271 |
|
ctx->log_data = stderr; |
|
272 |
|
ctx->log_priority = LOG_ERR; |
|
273 |
|
|
|
274 |
|
ctx->dirname = get_kernel_release(dirname); |
|
275 |
|
|
|
276 |
|
/* environment overwrites config */ |
|
277 |
|
env = getenv("KMOD_LOG"); |
|
278 |
|
if (env != NULL) |
|
279 |
|
kmod_set_log_priority(ctx, log_priority(env)); |
|
280 |
|
|
|
281 |
|
if (config_paths == NULL) |
|
282 |
|
config_paths = default_config_paths; |
|
283 |
|
err = kmod_config_new(ctx, &ctx->config, config_paths); |
|
284 |
|
if (err < 0) { |
|
285 |
|
ERR(ctx, "could not create config\n"); |
|
286 |
|
goto fail; |
|
287 |
|
} |
|
288 |
|
|
|
289 |
|
ctx->modules_by_name = hash_new(KMOD_HASH_SIZE, NULL); |
|
290 |
|
if (ctx->modules_by_name == NULL) { |
|
291 |
|
ERR(ctx, "could not create by-name hash\n"); |
|
292 |
|
goto fail; |
|
293 |
|
} |
|
294 |
|
|
|
295 |
|
INFO(ctx, "ctx %p created\n", ctx); |
|
296 |
|
DBG(ctx, "log_priority=%d\n", ctx->log_priority); |
|
297 |
|
|
|
298 |
|
return ctx; |
|
299 |
|
|
|
300 |
|
fail: |
|
301 |
|
free(ctx->modules_by_name); |
|
302 |
|
free(ctx->dirname); |
|
303 |
|
free(ctx); |
|
304 |
|
return NULL; |
|
305 |
|
} |
|
306 |
|
|
|
307 |
|
/** |
|
308 |
|
* kmod_ref: |
|
309 |
|
* @ctx: kmod library context |
|
310 |
|
* |
|
311 |
|
* Take a reference of the kmod library context. |
|
312 |
|
* |
|
313 |
|
* Returns: the passed kmod library context |
|
314 |
|
*/ |
|
315 |
|
struct kmod_ctx *kmod_ref(struct kmod_ctx *ctx) |
|
316 |
|
{ |
|
317 |
|
if (ctx == NULL) |
|
318 |
|
return NULL; |
|
319 |
|
ctx->refcount++; |
|
320 |
|
return ctx; |
|
321 |
|
} |
|
322 |
|
|
|
323 |
|
/** |
|
324 |
|
* kmod_unref: |
|
325 |
|
* @ctx: kmod library context |
|
326 |
|
* |
|
327 |
|
* Drop a reference of the kmod library context. If the refcount |
|
328 |
|
* reaches zero, the resources of the context will be released. |
|
329 |
|
* |
|
330 |
|
* Returns: the passed kmod library context or NULL if it's freed |
|
331 |
|
*/ |
|
332 |
|
struct kmod_ctx *kmod_unref(struct kmod_ctx *ctx) |
|
333 |
|
{ |
|
334 |
|
if (ctx == NULL) |
|
335 |
|
return NULL; |
|
336 |
|
|
|
337 |
|
if (--ctx->refcount > 0) |
|
338 |
|
return ctx; |
|
339 |
|
|
|
340 |
|
INFO(ctx, "context %p released\n", ctx); |
|
341 |
|
|
|
342 |
|
kmod_unload_resources(ctx); |
|
343 |
|
hash_free(ctx->modules_by_name); |
|
344 |
|
free(ctx->dirname); |
|
345 |
|
if (ctx->config) |
|
346 |
|
kmod_config_free(ctx->config); |
|
347 |
|
|
|
348 |
|
free(ctx); |
|
349 |
|
return NULL; |
|
350 |
|
} |
|
351 |
|
|
|
352 |
|
/** |
|
353 |
|
* kmod_set_log_fn: |
|
354 |
|
* @ctx: kmod library context |
|
355 |
|
* @log_fn: function to be called for logging messages |
|
356 |
|
* @data: data to pass to log function |
|
357 |
|
* |
|
358 |
|
* The built-in logging writes to stderr. It can be |
|
359 |
|
* overridden by a custom function, to plug log messages |
|
360 |
|
* into the user's logging functionality. |
|
361 |
|
*/ |
|
362 |
|
void kmod_set_log_fn(struct kmod_ctx *ctx, |
|
363 |
|
void (*log_fn)(void *data, |
|
364 |
|
int priority, const char *file, |
|
365 |
|
int line, const char *fn, |
|
366 |
|
const char *format, va_list args), |
|
367 |
|
const void *data) |
|
368 |
|
{ |
|
369 |
|
if (ctx == NULL) |
|
370 |
|
return; |
|
371 |
|
ctx->log_fn = log_fn; |
|
372 |
|
ctx->log_data = (void *)data; |
|
373 |
|
INFO(ctx, "custom logging function %p registered\n", log_fn); |
|
374 |
|
} |
|
375 |
|
|
|
376 |
|
/** |
|
377 |
|
* kmod_get_log_priority: |
|
378 |
|
* @ctx: kmod library context |
|
379 |
|
* |
|
380 |
|
* Returns: the current logging priority |
|
381 |
|
*/ |
|
382 |
|
int kmod_get_log_priority(const struct kmod_ctx *ctx) |
|
383 |
|
{ |
|
384 |
|
if (ctx == NULL) |
|
385 |
|
return -1; |
|
386 |
|
return ctx->log_priority; |
|
387 |
|
} |
|
388 |
|
|
|
389 |
|
/** |
|
390 |
|
* kmod_set_log_priority: |
|
391 |
|
* @ctx: kmod library context |
|
392 |
|
* @priority: the new logging priority |
|
393 |
|
* |
|
394 |
|
* Set the current logging priority. The value controls which messages |
|
395 |
|
* are logged. |
|
396 |
|
*/ |
|
397 |
|
void kmod_set_log_priority(struct kmod_ctx *ctx, int priority) |
|
398 |
|
{ |
|
399 |
|
if (ctx == NULL) |
|
400 |
|
return; |
|
401 |
|
ctx->log_priority = priority; |
|
402 |
|
} |
|
403 |
|
|
|
404 |
|
struct kmod_module *kmod_pool_get_module(struct kmod_ctx *ctx, |
|
405 |
|
const char *key) |
|
406 |
|
{ |
|
407 |
|
struct kmod_module *mod; |
|
408 |
|
|
|
409 |
|
mod = hash_find(ctx->modules_by_name, key); |
|
410 |
|
|
|
411 |
|
DBG(ctx, "get module name='%s' found=%p\n", key, mod); |
|
412 |
|
|
|
413 |
|
return mod; |
|
414 |
|
} |
|
415 |
|
|
|
416 |
|
void kmod_pool_add_module(struct kmod_ctx *ctx, struct kmod_module *mod, |
|
417 |
|
const char *key) |
|
418 |
|
{ |
|
419 |
|
DBG(ctx, "add %p key='%s'\n", mod, key); |
|
420 |
|
|
|
421 |
|
hash_add(ctx->modules_by_name, key, mod); |
|
422 |
|
} |
|
423 |
|
|
|
424 |
|
void kmod_pool_del_module(struct kmod_ctx *ctx, struct kmod_module *mod, |
|
425 |
|
const char *key) |
|
426 |
|
{ |
|
427 |
|
DBG(ctx, "del %p key='%s'\n", mod, key); |
|
428 |
|
|
|
429 |
|
hash_del(ctx->modules_by_name, key); |
|
430 |
|
} |
|
431 |
|
|
|
432 |
|
static int kmod_lookup_alias_from_alias_bin(struct kmod_ctx *ctx, |
|
433 |
|
enum kmod_index index_number, |
|
434 |
|
const char *name, |
|
435 |
|
struct kmod_list **list) |
|
436 |
|
{ |
|
437 |
|
int err, nmatch = 0; |
|
438 |
|
struct index_file *idx; |
|
439 |
|
struct index_value *realnames, *realname; |
|
440 |
|
|
|
441 |
|
if (ctx->indexes[index_number] != NULL) { |
|
442 |
|
DBG(ctx, "use mmaped index '%s' for name=%s\n", |
|
443 |
|
index_files[index_number].fn, name); |
|
444 |
|
realnames = index_mm_searchwild(ctx->indexes[index_number], |
|
445 |
|
name); |
|
446 |
|
} else { |
|
447 |
|
char fn[PATH_MAX]; |
|
448 |
|
|
|
449 |
|
snprintf(fn, sizeof(fn), "%s/%s.bin", ctx->dirname, |
|
450 |
|
index_files[index_number].fn); |
|
451 |
|
|
|
452 |
|
DBG(ctx, "file=%s name=%s\n", fn, name); |
|
453 |
|
|
|
454 |
|
idx = index_file_open(fn); |
|
455 |
|
if (idx == NULL) |
|
456 |
|
return -ENOSYS; |
|
457 |
|
|
|
458 |
|
realnames = index_searchwild(idx, name); |
|
459 |
|
index_file_close(idx); |
|
460 |
|
} |
|
461 |
|
|
|
462 |
|
for (realname = realnames; realname; realname = realname->next) { |
|
463 |
|
struct kmod_module *mod; |
|
464 |
|
|
|
465 |
|
err = kmod_module_new_from_alias(ctx, name, realname->value, &mod); |
|
466 |
|
if (err < 0) { |
|
467 |
|
ERR(ctx, "Could not create module for alias=%s realname=%s: %s\n", |
|
468 |
|
name, realname->value, strerror(-err)); |
|
469 |
|
goto fail; |
|
470 |
|
} |
|
471 |
|
|
|
472 |
|
*list = __kmod_list_append(*list, mod); |
|
473 |
|
nmatch++; |
|
474 |
|
} |
|
475 |
|
|
|
476 |
|
index_values_free(realnames); |
|
477 |
|
return nmatch; |
|
478 |
|
|
|
479 |
|
fail: |
|
480 |
|
*list = kmod_list_remove_n_latest(*list, nmatch); |
|
481 |
|
index_values_free(realnames); |
|
482 |
|
return err; |
|
483 |
|
|
|
484 |
|
} |
|
485 |
|
|
|
486 |
|
int kmod_lookup_alias_from_symbols_file(struct kmod_ctx *ctx, const char *name, |
|
487 |
|
struct kmod_list **list) |
|
488 |
|
{ |
|
489 |
|
if (!strstartswith(name, "symbol:")) |
|
490 |
|
return 0; |
|
491 |
|
|
|
492 |
|
return kmod_lookup_alias_from_alias_bin(ctx, KMOD_INDEX_MODULES_SYMBOL, |
|
493 |
|
name, list); |
|
494 |
|
} |
|
495 |
|
|
|
496 |
|
int kmod_lookup_alias_from_aliases_file(struct kmod_ctx *ctx, const char *name, |
|
497 |
|
struct kmod_list **list) |
|
498 |
|
{ |
|
499 |
|
return kmod_lookup_alias_from_alias_bin(ctx, KMOD_INDEX_MODULES_ALIAS, |
|
500 |
|
name, list); |
|
501 |
|
} |
|
502 |
|
|
|
503 |
|
static char *lookup_builtin_file(struct kmod_ctx *ctx, const char *name) |
|
504 |
|
{ |
|
505 |
|
char *line; |
|
506 |
|
|
|
507 |
|
if (ctx->indexes[KMOD_INDEX_MODULES_BUILTIN]) { |
|
508 |
|
DBG(ctx, "use mmaped index '%s' modname=%s\n", |
|
509 |
|
index_files[KMOD_INDEX_MODULES_BUILTIN].fn, |
|
510 |
|
name); |
|
511 |
|
line = index_mm_search(ctx->indexes[KMOD_INDEX_MODULES_BUILTIN], |
|
512 |
|
name); |
|
513 |
|
} else { |
|
514 |
|
struct index_file *idx; |
|
515 |
|
char fn[PATH_MAX]; |
|
516 |
|
|
|
517 |
|
snprintf(fn, sizeof(fn), "%s/%s.bin", ctx->dirname, |
|
518 |
|
index_files[KMOD_INDEX_MODULES_BUILTIN].fn); |
|
519 |
|
DBG(ctx, "file=%s modname=%s\n", fn, name); |
|
520 |
|
|
|
521 |
|
idx = index_file_open(fn); |
|
522 |
|
if (idx == NULL) { |
|
523 |
|
DBG(ctx, "could not open builtin file '%s'\n", fn); |
|
524 |
|
return NULL; |
|
525 |
|
} |
|
526 |
|
|
|
527 |
|
line = index_search(idx, name); |
|
528 |
|
index_file_close(idx); |
|
529 |
|
} |
|
530 |
|
|
|
531 |
|
return line; |
|
532 |
|
} |
|
533 |
|
|
|
534 |
|
int kmod_lookup_alias_from_builtin_file(struct kmod_ctx *ctx, const char *name, |
|
535 |
|
struct kmod_list **list) |
|
536 |
|
{ |
|
537 |
|
char *line; |
|
538 |
|
int err = 0; |
|
539 |
|
|
|
540 |
|
assert(*list == NULL); |
|
541 |
|
|
|
542 |
|
line = lookup_builtin_file(ctx, name); |
|
543 |
|
if (line != NULL) { |
|
544 |
|
struct kmod_module *mod; |
|
545 |
|
|
|
546 |
|
err = kmod_module_new_from_name(ctx, name, &mod); |
|
547 |
|
if (err < 0) { |
|
548 |
|
ERR(ctx, "Could not create module from name %s: %s\n", |
|
549 |
|
name, strerror(-err)); |
|
550 |
|
goto finish; |
|
551 |
|
} |
|
552 |
|
|
|
553 |
|
/* already mark it as builtin since it's being created from |
|
554 |
|
* this index */ |
|
555 |
|
kmod_module_set_builtin(mod, true); |
|
556 |
|
*list = __kmod_list_append(*list, mod); |
|
557 |
|
if (*list == NULL) |
|
558 |
|
err = -ENOMEM; |
|
559 |
|
} |
|
560 |
|
|
|
561 |
|
finish: |
|
562 |
|
free(line); |
|
563 |
|
return err; |
|
564 |
|
} |
|
565 |
|
|
|
566 |
|
bool kmod_lookup_alias_is_builtin(struct kmod_ctx *ctx, const char *name) |
|
567 |
|
{ |
|
568 |
|
char *line; |
|
569 |
|
bool found; |
|
570 |
|
|
|
571 |
|
line = lookup_builtin_file(ctx, name); |
|
572 |
|
found = line != NULL; |
|
573 |
|
if (found) |
|
574 |
|
free(line); |
|
575 |
|
return found; |
|
576 |
|
} |
|
577 |
|
|
|
578 |
|
char *kmod_search_moddep(struct kmod_ctx *ctx, const char *name) |
|
579 |
|
{ |
|
580 |
|
struct index_file *idx; |
|
581 |
|
char fn[PATH_MAX]; |
|
582 |
|
char *line; |
|
583 |
|
|
|
584 |
|
if (ctx->indexes[KMOD_INDEX_MODULES_DEP]) { |
|
585 |
|
DBG(ctx, "use mmaped index '%s' modname=%s\n", |
|
586 |
|
index_files[KMOD_INDEX_MODULES_DEP].fn, name); |
|
587 |
|
return index_mm_search(ctx->indexes[KMOD_INDEX_MODULES_DEP], |
|
588 |
|
name); |
|
589 |
|
} |
|
590 |
|
|
|
591 |
|
snprintf(fn, sizeof(fn), "%s/%s.bin", ctx->dirname, |
|
592 |
|
index_files[KMOD_INDEX_MODULES_DEP].fn); |
|
593 |
|
|
|
594 |
|
DBG(ctx, "file=%s modname=%s\n", fn, name); |
|
595 |
|
|
|
596 |
|
idx = index_file_open(fn); |
|
597 |
|
if (idx == NULL) { |
|
598 |
|
DBG(ctx, "could not open moddep file '%s'\n", fn); |
|
599 |
|
return NULL; |
|
600 |
|
} |
|
601 |
|
|
|
602 |
|
line = index_search(idx, name); |
|
603 |
|
index_file_close(idx); |
|
604 |
|
|
|
605 |
|
return line; |
|
606 |
|
} |
|
607 |
|
|
|
608 |
|
int kmod_lookup_alias_from_moddep_file(struct kmod_ctx *ctx, const char *name, |
|
609 |
|
struct kmod_list **list) |
|
610 |
|
{ |
|
611 |
|
char *line; |
|
612 |
|
int n = 0; |
|
613 |
|
|
|
614 |
|
/* |
|
615 |
|
* Module names do not contain ':'. Return early if we know it will |
|
616 |
|
* not be found. |
|
617 |
|
*/ |
|
618 |
|
if (strchr(name, ':')) |
|
619 |
|
return 0; |
|
620 |
|
|
|
621 |
|
line = kmod_search_moddep(ctx, name); |
|
622 |
|
if (line != NULL) { |
|
623 |
|
struct kmod_module *mod; |
|
624 |
|
|
|
625 |
|
n = kmod_module_new_from_name(ctx, name, &mod); |
|
626 |
|
if (n < 0) { |
|
627 |
|
ERR(ctx, "Could not create module from name %s: %s\n", |
|
628 |
|
name, strerror(-n)); |
|
629 |
|
goto finish; |
|
630 |
|
} |
|
631 |
|
|
|
632 |
|
*list = __kmod_list_append(*list, mod); |
|
633 |
|
kmod_module_parse_depline(mod, line); |
|
634 |
|
} |
|
635 |
|
|
|
636 |
|
finish: |
|
637 |
|
free(line); |
|
638 |
|
|
|
639 |
|
return n; |
|
640 |
|
} |
|
641 |
|
|
|
642 |
|
int kmod_lookup_alias_from_config(struct kmod_ctx *ctx, const char *name, |
|
643 |
|
struct kmod_list **list) |
|
644 |
|
{ |
|
645 |
|
struct kmod_config *config = ctx->config; |
|
646 |
|
struct kmod_list *l; |
|
647 |
|
int err, nmatch = 0; |
|
648 |
|
|
|
649 |
|
kmod_list_foreach(l, config->aliases) { |
|
650 |
|
const char *aliasname = kmod_alias_get_name(l); |
|
651 |
|
const char *modname = kmod_alias_get_modname(l); |
|
652 |
|
|
|
653 |
|
if (fnmatch(aliasname, name, 0) == 0) { |
|
654 |
|
struct kmod_module *mod; |
|
655 |
|
|
|
656 |
|
err = kmod_module_new_from_alias(ctx, aliasname, |
|
657 |
|
modname, &mod); |
|
658 |
|
if (err < 0) { |
|
659 |
|
ERR(ctx, "Could not create module for alias=%s modname=%s: %s\n", |
|
660 |
|
name, modname, strerror(-err)); |
|
661 |
|
goto fail; |
|
662 |
|
} |
|
663 |
|
|
|
664 |
|
*list = __kmod_list_append(*list, mod); |
|
665 |
|
nmatch++; |
|
666 |
|
} |
|
667 |
|
} |
|
668 |
|
|
|
669 |
|
return nmatch; |
|
670 |
|
|
|
671 |
|
fail: |
|
672 |
|
*list = kmod_list_remove_n_latest(*list, nmatch); |
|
673 |
|
return err; |
|
674 |
|
} |
|
675 |
|
|
|
676 |
|
int kmod_lookup_alias_from_commands(struct kmod_ctx *ctx, const char *name, |
|
677 |
|
struct kmod_list **list) |
|
678 |
|
{ |
|
679 |
|
struct kmod_config *config = ctx->config; |
|
680 |
|
struct kmod_list *l, *node; |
|
681 |
|
int err, nmatch = 0; |
|
682 |
|
|
|
683 |
|
kmod_list_foreach(l, config->install_commands) { |
|
684 |
|
const char *modname = kmod_command_get_modname(l); |
|
685 |
|
|
|
686 |
|
if (streq(modname, name)) { |
|
687 |
|
const char *cmd = kmod_command_get_command(l); |
|
688 |
|
struct kmod_module *mod; |
|
689 |
|
|
|
690 |
|
err = kmod_module_new_from_name(ctx, modname, &mod); |
|
691 |
|
if (err < 0) { |
|
692 |
|
ERR(ctx, "Could not create module from name %s: %s\n", |
|
693 |
|
modname, strerror(-err)); |
|
694 |
|
return err; |
|
695 |
|
} |
|
696 |
|
|
|
697 |
|
node = __kmod_list_append(*list, mod); |
|
698 |
|
if (node == NULL) { |
|
699 |
|
ERR(ctx, "out of memory\n"); |
|
700 |
|
return -ENOMEM; |
|
701 |
|
} |
|
702 |
|
|
|
703 |
|
*list = node; |
|
704 |
|
nmatch = 1; |
|
705 |
|
|
|
706 |
|
kmod_module_set_install_commands(mod, cmd); |
|
707 |
|
|
|
708 |
|
/* |
|
709 |
|
* match only the first one, like modprobe from |
|
710 |
|
* module-init-tools does |
|
711 |
|
*/ |
|
712 |
|
break; |
|
713 |
|
} |
|
714 |
|
} |
|
715 |
|
|
|
716 |
|
if (nmatch) |
|
717 |
|
return nmatch; |
|
718 |
|
|
|
719 |
|
kmod_list_foreach(l, config->remove_commands) { |
|
720 |
|
const char *modname = kmod_command_get_modname(l); |
|
721 |
|
|
|
722 |
|
if (streq(modname, name)) { |
|
723 |
|
const char *cmd = kmod_command_get_command(l); |
|
724 |
|
struct kmod_module *mod; |
|
725 |
|
|
|
726 |
|
err = kmod_module_new_from_name(ctx, modname, &mod); |
|
727 |
|
if (err < 0) { |
|
728 |
|
ERR(ctx, "Could not create module from name %s: %s\n", |
|
729 |
|
modname, strerror(-err)); |
|
730 |
|
return err; |
|
731 |
|
} |
|
732 |
|
|
|
733 |
|
node = __kmod_list_append(*list, mod); |
|
734 |
|
if (node == NULL) { |
|
735 |
|
ERR(ctx, "out of memory\n"); |
|
736 |
|
return -ENOMEM; |
|
737 |
|
} |
|
738 |
|
|
|
739 |
|
*list = node; |
|
740 |
|
nmatch = 1; |
|
741 |
|
|
|
742 |
|
kmod_module_set_remove_commands(mod, cmd); |
|
743 |
|
|
|
744 |
|
/* |
|
745 |
|
* match only the first one, like modprobe from |
|
746 |
|
* module-init-tools does |
|
747 |
|
*/ |
|
748 |
|
break; |
|
749 |
|
} |
|
750 |
|
} |
|
751 |
|
|
|
752 |
|
return nmatch; |
|
753 |
|
} |
|
754 |
|
|
|
755 |
|
void kmod_set_modules_visited(struct kmod_ctx *ctx, bool visited) |
|
756 |
|
{ |
|
757 |
|
struct hash_iter iter; |
|
758 |
|
const void *v; |
|
759 |
|
|
|
760 |
|
hash_iter_init(ctx->modules_by_name, &iter); |
|
761 |
|
while (hash_iter_next(&iter, NULL, &v)) |
|
762 |
|
kmod_module_set_visited((struct kmod_module *)v, visited); |
|
763 |
|
} |
|
764 |
|
|
|
765 |
|
void kmod_set_modules_required(struct kmod_ctx *ctx, bool required) |
|
766 |
|
{ |
|
767 |
|
struct hash_iter iter; |
|
768 |
|
const void *v; |
|
769 |
|
|
|
770 |
|
hash_iter_init(ctx->modules_by_name, &iter); |
|
771 |
|
while (hash_iter_next(&iter, NULL, &v)) |
|
772 |
|
kmod_module_set_required((struct kmod_module *)v, required); |
|
773 |
|
} |
|
774 |
|
|
|
775 |
|
static bool is_cache_invalid(const char *path, unsigned long long stamp) |
|
776 |
|
{ |
|
777 |
|
struct stat st; |
|
778 |
|
|
|
779 |
|
if (stat(path, &st) < 0) |
|
780 |
|
return true; |
|
781 |
|
|
|
782 |
|
if (stamp != stat_mstamp(&st)) |
|
783 |
|
return true; |
|
784 |
|
|
|
785 |
|
return false; |
|
786 |
|
} |
|
787 |
|
|
|
788 |
|
/** |
|
789 |
|
* kmod_validate_resources: |
|
790 |
|
* @ctx: kmod library context |
|
791 |
|
* |
|
792 |
|
* Check if indexes and configuration files changed on disk and the current |
|
793 |
|
* context is not valid anymore. |
|
794 |
|
* |
|
795 |
|
* Returns: KMOD_RESOURCES_OK if resources are still valid, |
|
796 |
|
* KMOD_RESOURCES_MUST_RELOAD if it's sufficient to call |
|
797 |
|
* kmod_unload_resources() and kmod_load_resources() or |
|
798 |
|
* KMOD_RESOURCES_MUST_RECREATE if @ctx must be re-created. |
|
799 |
|
*/ |
|
800 |
|
int kmod_validate_resources(struct kmod_ctx *ctx) |
|
801 |
|
{ |
|
802 |
|
struct kmod_list *l; |
|
803 |
|
size_t i; |
|
804 |
|
|
|
805 |
|
if (ctx == NULL || ctx->config == NULL) |
|
806 |
|
return KMOD_RESOURCES_MUST_RECREATE; |
|
807 |
|
|
|
808 |
|
kmod_list_foreach(l, ctx->config->paths) { |
|
809 |
|
struct kmod_config_path *cf = l->data; |
|
810 |
|
|
|
811 |
|
if (is_cache_invalid(cf->path, cf->stamp)) |
|
812 |
|
return KMOD_RESOURCES_MUST_RECREATE; |
|
813 |
|
} |
|
814 |
|
|
|
815 |
|
for (i = 0; i < _KMOD_INDEX_MODULES_SIZE; i++) { |
|
816 |
|
char path[PATH_MAX]; |
|
817 |
|
|
|
818 |
|
if (ctx->indexes[i] == NULL) |
|
819 |
|
continue; |
|
820 |
|
|
|
821 |
|
snprintf(path, sizeof(path), "%s/%s.bin", ctx->dirname, |
|
822 |
|
index_files[i].fn); |
|
823 |
|
|
|
824 |
|
if (is_cache_invalid(path, ctx->indexes_stamp[i])) |
|
825 |
|
return KMOD_RESOURCES_MUST_RELOAD; |
|
826 |
|
} |
|
827 |
|
|
|
828 |
|
return KMOD_RESOURCES_OK; |
|
829 |
|
} |
|
830 |
|
|
|
831 |
|
/** |
|
832 |
|
* kmod_load_resources: |
|
833 |
|
* @ctx: kmod library context |
|
834 |
|
* |
|
835 |
|
* Load indexes and keep them open in @ctx. This way it's faster to lookup |
|
836 |
|
* information within the indexes. If this function is not called before a |
|
837 |
|
* search, the necessary index is always opened and closed. |
|
838 |
|
* |
|
839 |
|
* If user will do more than one or two lookups, insertions, deletions, most |
|
840 |
|
* likely it's good to call this function first. Particularly in a daemon like |
|
841 |
|
* udev that on bootup issues hundreds of calls to lookup the index, calling |
|
842 |
|
* this function will speedup the searches. |
|
843 |
|
* |
|
844 |
|
* Returns: 0 on success or < 0 otherwise. |
|
845 |
|
*/ |
|
846 |
|
int kmod_load_resources(struct kmod_ctx *ctx) |
|
847 |
|
{ |
|
848 |
|
size_t i; |
|
849 |
|
|
|
850 |
|
if (ctx == NULL) |
|
851 |
|
return -ENOENT; |
|
852 |
|
|
|
853 |
|
for (i = 0; i < _KMOD_INDEX_MODULES_SIZE; i++) { |
|
854 |
|
char path[PATH_MAX]; |
|
855 |
|
|
|
856 |
|
if (ctx->indexes[i] != NULL) { |
|
857 |
|
INFO(ctx, "Index %s already loaded\n", |
|
858 |
|
index_files[i].fn); |
|
859 |
|
continue; |
|
860 |
|
} |
|
861 |
|
|
|
862 |
|
snprintf(path, sizeof(path), "%s/%s.bin", ctx->dirname, |
|
863 |
|
index_files[i].fn); |
|
864 |
|
ctx->indexes[i] = index_mm_open(ctx, path, |
|
865 |
|
&ctx->indexes_stamp[i]); |
|
866 |
|
if (ctx->indexes[i] == NULL) |
|
867 |
|
goto fail; |
|
868 |
|
} |
|
869 |
|
|
|
870 |
|
return 0; |
|
871 |
|
|
|
872 |
|
fail: |
|
873 |
|
kmod_unload_resources(ctx); |
|
874 |
|
return -ENOMEM; |
|
875 |
|
} |
|
876 |
|
|
|
877 |
|
/** |
|
878 |
|
* kmod_unload_resources: |
|
879 |
|
* @ctx: kmod library context |
|
880 |
|
* |
|
881 |
|
* Unload all the indexes. This will free the resources to maintain the index |
|
882 |
|
* open and all subsequent searches will need to open and close the index. |
|
883 |
|
* |
|
884 |
|
* User is free to call kmod_load_resources() and kmod_unload_resources() as |
|
885 |
|
* many times as wanted during the lifecycle of @ctx. For example, if a daemon |
|
886 |
|
* knows that when starting up it will lookup a lot of modules, it could call |
|
887 |
|
* kmod_load_resources() and after the first burst of searches is gone, it |
|
888 |
|
* could free the resources by calling kmod_unload_resources(). |
|
889 |
|
* |
|
890 |
|
* Returns: 0 on success or < 0 otherwise. |
|
891 |
|
*/ |
|
892 |
|
void kmod_unload_resources(struct kmod_ctx *ctx) |
|
893 |
|
{ |
|
894 |
|
size_t i; |
|
895 |
|
|
|
896 |
|
if (ctx == NULL) |
|
897 |
|
return; |
|
898 |
|
|
|
899 |
|
for (i = 0; i < _KMOD_INDEX_MODULES_SIZE; i++) { |
|
900 |
|
if (ctx->indexes[i] != NULL) { |
|
901 |
|
index_mm_close(ctx->indexes[i]); |
|
902 |
|
ctx->indexes[i] = NULL; |
|
903 |
|
ctx->indexes_stamp[i] = 0; |
|
904 |
|
} |
|
905 |
|
} |
|
906 |
|
} |
|
907 |
|
|
|
908 |
|
/** |
|
909 |
|
* kmod_dump_index: |
|
910 |
|
* @ctx: kmod library context |
|
911 |
|
* @type: index to dump, valid indexes are |
|
912 |
|
* KMOD_INDEX_MODULES_DEP: index of module dependencies; |
|
913 |
|
* KMOD_INDEX_MODULES_ALIAS: index of module aliases; |
|
914 |
|
* KMOD_INDEX_MODULES_SYMBOL: index of symbol aliases; |
|
915 |
|
* KMOD_INDEX_MODULES_BUILTIN: index of builtin module. |
|
916 |
|
* @fd: file descriptor to dump index to |
|
917 |
|
* |
|
918 |
|
* Dump index to file descriptor. Note that this function doesn't use stdio.h |
|
919 |
|
* so call fflush() before calling this function to be sure data is written in |
|
920 |
|
* order. |
|
921 |
|
* |
|
922 |
|
* Returns: 0 on success or < 0 otherwise. |
|
923 |
|
*/ |
|
924 |
|
int kmod_dump_index(struct kmod_ctx *ctx, enum kmod_index type, |
|
925 |
|
int fd) |
|
926 |
|
{ |
|
927 |
|
if (ctx == NULL) |
|
928 |
|
return -ENOSYS; |
|
929 |
|
|
|
930 |
|
if (type < 0 || type >= _KMOD_INDEX_MODULES_SIZE) |
|
931 |
|
return -ENOENT; |
|
932 |
|
|
|
933 |
|
if (ctx->indexes[type] != NULL) { |
|
934 |
|
DBG(ctx, "use mmaped index '%s'\n", index_files[type].fn); |
|
935 |
|
index_mm_dump(ctx->indexes[type], fd, |
|
936 |
|
index_files[type].prefix); |
|
937 |
|
} else { |
|
938 |
|
char fn[PATH_MAX]; |
|
939 |
|
struct index_file *idx; |
|
940 |
|
|
|
941 |
|
snprintf(fn, sizeof(fn), "%s/%s.bin", ctx->dirname, |
|
942 |
|
index_files[type].fn); |
|
943 |
|
|
|
944 |
|
DBG(ctx, "file=%s\n", fn); |
|
945 |
|
|
|
946 |
|
idx = index_file_open(fn); |
|
947 |
|
if (idx == NULL) |
|
948 |
|
return -ENOSYS; |
|
949 |
|
|
|
950 |
|
index_dump(idx, fd, index_files[type].prefix); |
|
951 |
|
index_file_close(idx); |
|
952 |
|
} |
|
953 |
|
|
|
954 |
|
return 0; |
|
955 |
|
} |
|
956 |
|
|
|
957 |
|
const struct kmod_config *kmod_get_config(const struct kmod_ctx *ctx) |
|
958 |
|
{ |
|
959 |
|
return ctx->config; |
|
960 |
|
} |
File src/tools/depmod.c added (mode: 100644) (index 0000000..fcfb327) |
|
1 |
|
/* |
|
2 |
|
* kmod-depmod - calculate modules.dep using libkmod. |
|
3 |
|
* |
|
4 |
|
* Copyright (C) 2011-2013 ProFUSION embedded systems |
|
5 |
|
* |
|
6 |
|
* This program is free software: you can redistribute it and/or modify |
|
7 |
|
* it under the terms of the GNU General Public License as published by |
|
8 |
|
* the Free Software Foundation, either version 2 of the License, or |
|
9 |
|
* (at your option) any later version. |
|
10 |
|
* |
|
11 |
|
* This program is distributed in the hope that it will be useful, |
|
12 |
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
13 |
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
14 |
|
* GNU General Public License for more details. |
|
15 |
|
* |
|
16 |
|
* You should have received a copy of the GNU General Public License |
|
17 |
|
* along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
18 |
|
*/ |
|
19 |
|
|
|
20 |
|
#include <assert.h> |
|
21 |
|
#include <ctype.h> |
|
22 |
|
#include <dirent.h> |
|
23 |
|
#include <errno.h> |
|
24 |
|
#include <getopt.h> |
|
25 |
|
#include <limits.h> |
|
26 |
|
#include <regex.h> |
|
27 |
|
#include <stdio.h> |
|
28 |
|
#include <stdlib.h> |
|
29 |
|
#include <string.h> |
|
30 |
|
#include <unistd.h> |
|
31 |
|
#include <stddef.h> |
|
32 |
|
#include <syslog.h> |
|
33 |
|
#include <libgen.h> |
|
34 |
|
#include <sys/stat.h> |
|
35 |
|
#include <sys/utsname.h> |
|
36 |
|
|
|
37 |
|
#include "config.h" |
|
38 |
|
|
|
39 |
|
#include "libkmod.h" |
|
40 |
|
#include "libkmod-paths.h" |
|
41 |
|
|
|
42 |
|
#include "shared/array.h" |
|
43 |
|
#include "shared/hash.h" |
|
44 |
|
#include "shared/macro.h" |
|
45 |
|
#include "shared/util.h" |
|
46 |
|
#include "shared/scratchbuf.h" |
|
47 |
|
|
|
48 |
|
#include "libkmod-internal.h" |
|
49 |
|
|
|
50 |
|
#undef ERR |
|
51 |
|
#undef DBG |
|
52 |
|
#define TOOLS_DEPMOD_C |
|
53 |
|
#include "kmod.h" |
|
54 |
|
#undef TOOLS_DEPMOD_C |
|
55 |
|
#include "log.h" |
|
56 |
|
|
|
57 |
|
#define DEFAULT_VERBOSE LOG_WARNING |
|
58 |
|
static int verbose = DEFAULT_VERBOSE; |
|
59 |
|
|
|
60 |
|
static const char CFG_BUILTIN_KEY[] = "built-in"; |
|
61 |
|
static const char *default_cfg_paths[] = { |
|
62 |
|
"/run/depmod.d", |
|
63 |
|
SYSCONFDIR "/depmod.d", |
|
64 |
|
"/lib/depmod.d", |
|
65 |
|
NULL |
|
66 |
|
}; |
|
67 |
|
|
|
68 |
|
static const char cmdopts_s[] = "aAb:C:E:F:euqrvnP:wmVh"; |
|
69 |
|
static const struct option cmdopts[] = { |
|
70 |
|
{ "all", no_argument, 0, 'a' }, |
|
71 |
|
{ "quick", no_argument, 0, 'A' }, |
|
72 |
|
{ "basedir", required_argument, 0, 'b' }, |
|
73 |
|
{ "config", required_argument, 0, 'C' }, |
|
74 |
|
{ "symvers", required_argument, 0, 'E' }, |
|
75 |
|
{ "filesyms", required_argument, 0, 'F' }, |
|
76 |
|
{ "errsyms", no_argument, 0, 'e' }, |
|
77 |
|
{ "unresolved-error", no_argument, 0, 'u' }, /* deprecated */ |
|
78 |
|
{ "quiet", no_argument, 0, 'q' }, /* deprecated */ |
|
79 |
|
{ "root", no_argument, 0, 'r' }, /* deprecated */ |
|
80 |
|
{ "verbose", no_argument, 0, 'v' }, |
|
81 |
|
{ "show", no_argument, 0, 'n' }, |
|
82 |
|
{ "dry-run", no_argument, 0, 'n' }, |
|
83 |
|
{ "symbol-prefix", required_argument, 0, 'P' }, |
|
84 |
|
{ "warn", no_argument, 0, 'w' }, |
|
85 |
|
{ "map", no_argument, 0, 'm' }, /* deprecated */ |
|
86 |
|
{ "version", no_argument, 0, 'V' }, |
|
87 |
|
{ "help", no_argument, 0, 'h' }, |
|
88 |
|
{ } |
|
89 |
|
}; |
|
90 |
|
|
|
91 |
|
static void help(void) |
|
92 |
|
{ |
|
93 |
|
printf("Usage:\n" |
|
94 |
|
"\t%s -[aA] [options] [forced_version]\n" |
|
95 |
|
"\n" |
|
96 |
|
"If no arguments (except options) are given, \"depmod -a\" is assumed\n" |
|
97 |
|
"\n" |
|
98 |
|
"depmod will output a dependency list suitable for the modprobe utility.\n" |
|
99 |
|
"\n" |
|
100 |
|
"Options:\n" |
|
101 |
|
"\t-a, --all Probe all modules\n" |
|
102 |
|
"\t-A, --quick Only does the work if there's a new module\n" |
|
103 |
|
"\t-e, --errsyms Report not supplied symbols\n" |
|
104 |
|
"\t-n, --show Write the dependency file on stdout only\n" |
|
105 |
|
"\t-P, --symbol-prefix Architecture symbol prefix\n" |
|
106 |
|
"\t-C, --config=PATH Read configuration from PATH\n" |
|
107 |
|
"\t-v, --verbose Enable verbose mode\n" |
|
108 |
|
"\t-w, --warn Warn on duplicates\n" |
|
109 |
|
"\t-V, --version show version\n" |
|
110 |
|
"\t-h, --help show this help\n" |
|
111 |
|
"\n" |
|
112 |
|
"The following options are useful for people managing distributions:\n" |
|
113 |
|
"\t-b, --basedir=DIR Use an image of a module tree.\n" |
|
114 |
|
"\t-F, --filesyms=FILE Use the file instead of the\n" |
|
115 |
|
"\t current kernel symbols.\n" |
|
116 |
|
"\t-E, --symvers=FILE Use Module.symvers file to check\n" |
|
117 |
|
"\t symbol versions.\n", |
|
118 |
|
program_invocation_short_name); |
|
119 |
|
} |
|
120 |
|
|
|
121 |
|
static inline void _show(const char *fmt, ...) |
|
122 |
|
{ |
|
123 |
|
va_list args; |
|
124 |
|
|
|
125 |
|
if (verbose <= DEFAULT_VERBOSE) |
|
126 |
|
return; |
|
127 |
|
|
|
128 |
|
va_start(args, fmt); |
|
129 |
|
vfprintf(stdout, fmt, args); |
|
130 |
|
fflush(stdout); |
|
131 |
|
va_end(args); |
|
132 |
|
} |
|
133 |
|
#define SHOW(...) _show(__VA_ARGS__) |
|
134 |
|
|
|
135 |
|
|
|
136 |
|
/* binary index write *************************************************/ |
|
137 |
|
#include <arpa/inet.h> |
|
138 |
|
/* BEGIN: code from module-init-tools/index.c just modified to compile here. |
|
139 |
|
* |
|
140 |
|
* Original copyright: |
|
141 |
|
* index.c: module index file shared functions for modprobe and depmod |
|
142 |
|
* Copyright (C) 2008 Alan Jenkins <alan-jenkins@tuffmail.co.uk>. |
|
143 |
|
* |
|
144 |
|
* These programs are free software; you can redistribute it and/or modify |
|
145 |
|
* it under the terms of the GNU General Public License as published by |
|
146 |
|
* the Free Software Foundation; either version 2 of the License, or |
|
147 |
|
* (at your option) any later version. |
|
148 |
|
* |
|
149 |
|
* This program is distributed in the hope that it will be useful, |
|
150 |
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
151 |
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
152 |
|
* GNU General Public License for more details. |
|
153 |
|
* |
|
154 |
|
* You should have received a copy of the GNU General Public License |
|
155 |
|
* along with these programs. If not, see <http://www.gnu.org/licenses/>. |
|
156 |
|
*/ |
|
157 |
|
|
|
158 |
|
/* see documentation in libkmod/libkmod-index.c */ |
|
159 |
|
|
|
160 |
|
#define INDEX_MAGIC 0xB007F457 |
|
161 |
|
#define INDEX_VERSION_MAJOR 0x0002 |
|
162 |
|
#define INDEX_VERSION_MINOR 0x0001 |
|
163 |
|
#define INDEX_VERSION ((INDEX_VERSION_MAJOR<<16)|INDEX_VERSION_MINOR) |
|
164 |
|
#define INDEX_CHILDMAX 128 |
|
165 |
|
|
|
166 |
|
struct index_value { |
|
167 |
|
struct index_value *next; |
|
168 |
|
unsigned int priority; |
|
169 |
|
char value[0]; |
|
170 |
|
}; |
|
171 |
|
|
|
172 |
|
/* In-memory index (depmod only) */ |
|
173 |
|
struct index_node { |
|
174 |
|
char *prefix; /* path compression */ |
|
175 |
|
struct index_value *values; |
|
176 |
|
unsigned char first; /* range of child nodes */ |
|
177 |
|
unsigned char last; |
|
178 |
|
struct index_node *children[INDEX_CHILDMAX]; /* indexed by character */ |
|
179 |
|
}; |
|
180 |
|
|
|
181 |
|
|
|
182 |
|
/* Format of node offsets within index file */ |
|
183 |
|
enum node_offset { |
|
184 |
|
INDEX_NODE_FLAGS = 0xF0000000, /* Flags in high nibble */ |
|
185 |
|
INDEX_NODE_PREFIX = 0x80000000, |
|
186 |
|
INDEX_NODE_VALUES = 0x40000000, |
|
187 |
|
INDEX_NODE_CHILDS = 0x20000000, |
|
188 |
|
|
|
189 |
|
INDEX_NODE_MASK = 0x0FFFFFFF, /* Offset value */ |
|
190 |
|
}; |
|
191 |
|
|
|
192 |
|
static struct index_node *index_create(void) |
|
193 |
|
{ |
|
194 |
|
struct index_node *node; |
|
195 |
|
|
|
196 |
|
node = NOFAIL(calloc(sizeof(struct index_node), 1)); |
|
197 |
|
node->prefix = NOFAIL(strdup("")); |
|
198 |
|
node->first = INDEX_CHILDMAX; |
|
199 |
|
|
|
200 |
|
return node; |
|
201 |
|
} |
|
202 |
|
|
|
203 |
|
static void index_values_free(struct index_value *values) |
|
204 |
|
{ |
|
205 |
|
while (values) { |
|
206 |
|
struct index_value *value = values; |
|
207 |
|
|
|
208 |
|
values = value->next; |
|
209 |
|
free(value); |
|
210 |
|
} |
|
211 |
|
} |
|
212 |
|
|
|
213 |
|
static void index_destroy(struct index_node *node) |
|
214 |
|
{ |
|
215 |
|
int c; |
|
216 |
|
|
|
217 |
|
for (c = node->first; c <= node->last; c++) { |
|
218 |
|
struct index_node *child = node->children[c]; |
|
219 |
|
|
|
220 |
|
if (child) |
|
221 |
|
index_destroy(child); |
|
222 |
|
} |
|
223 |
|
index_values_free(node->values); |
|
224 |
|
free(node->prefix); |
|
225 |
|
free(node); |
|
226 |
|
} |
|
227 |
|
|
|
228 |
|
static void index__checkstring(const char *str) |
|
229 |
|
{ |
|
230 |
|
int i; |
|
231 |
|
|
|
232 |
|
for (i = 0; str[i]; i++) { |
|
233 |
|
int ch = str[i]; |
|
234 |
|
|
|
235 |
|
if (ch >= INDEX_CHILDMAX) |
|
236 |
|
CRIT("Module index: bad character '%c'=0x%x - only 7-bit ASCII is supported:" |
|
237 |
|
"\n%s\n", (char) ch, (int) ch, str); |
|
238 |
|
} |
|
239 |
|
} |
|
240 |
|
|
|
241 |
|
static int index_add_value(struct index_value **values, |
|
242 |
|
const char *value, unsigned int priority) |
|
243 |
|
{ |
|
244 |
|
struct index_value *v; |
|
245 |
|
int duplicate = 0; |
|
246 |
|
int len; |
|
247 |
|
|
|
248 |
|
/* report the presence of duplicate values */ |
|
249 |
|
for (v = *values; v; v = v->next) { |
|
250 |
|
if (streq(v->value, value)) |
|
251 |
|
duplicate = 1; |
|
252 |
|
} |
|
253 |
|
|
|
254 |
|
/* find position to insert value */ |
|
255 |
|
while (*values && (*values)->priority < priority) |
|
256 |
|
values = &(*values)->next; |
|
257 |
|
|
|
258 |
|
len = strlen(value); |
|
259 |
|
v = NOFAIL(calloc(sizeof(struct index_value) + len + 1, 1)); |
|
260 |
|
v->next = *values; |
|
261 |
|
v->priority = priority; |
|
262 |
|
memcpy(v->value, value, len + 1); |
|
263 |
|
*values = v; |
|
264 |
|
|
|
265 |
|
return duplicate; |
|
266 |
|
} |
|
267 |
|
|
|
268 |
|
static int index_insert(struct index_node *node, const char *key, |
|
269 |
|
const char *value, unsigned int priority) |
|
270 |
|
{ |
|
271 |
|
int i = 0; /* index within str */ |
|
272 |
|
int ch; |
|
273 |
|
|
|
274 |
|
index__checkstring(key); |
|
275 |
|
index__checkstring(value); |
|
276 |
|
|
|
277 |
|
while(1) { |
|
278 |
|
int j; /* index within node->prefix */ |
|
279 |
|
|
|
280 |
|
/* Ensure node->prefix is a prefix of &str[i]. |
|
281 |
|
If it is not already, then we must split node. */ |
|
282 |
|
for (j = 0; node->prefix[j]; j++) { |
|
283 |
|
ch = node->prefix[j]; |
|
284 |
|
|
|
285 |
|
if (ch != key[i+j]) { |
|
286 |
|
char *prefix = node->prefix; |
|
287 |
|
struct index_node *n; |
|
288 |
|
|
|
289 |
|
/* New child is copy of node with prefix[j+1..N] */ |
|
290 |
|
n = NOFAIL(calloc(sizeof(struct index_node), 1)); |
|
291 |
|
memcpy(n, node, sizeof(struct index_node)); |
|
292 |
|
n->prefix = NOFAIL(strdup(&prefix[j+1])); |
|
293 |
|
|
|
294 |
|
/* Parent has prefix[0..j], child at prefix[j] */ |
|
295 |
|
memset(node, 0, sizeof(struct index_node)); |
|
296 |
|
prefix[j] = '\0'; |
|
297 |
|
node->prefix = prefix; |
|
298 |
|
node->first = ch; |
|
299 |
|
node->last = ch; |
|
300 |
|
node->children[ch] = n; |
|
301 |
|
|
|
302 |
|
break; |
|
303 |
|
} |
|
304 |
|
} |
|
305 |
|
/* j is now length of node->prefix */ |
|
306 |
|
i += j; |
|
307 |
|
|
|
308 |
|
ch = key[i]; |
|
309 |
|
if(ch == '\0') |
|
310 |
|
return index_add_value(&node->values, value, priority); |
|
311 |
|
|
|
312 |
|
if (!node->children[ch]) { |
|
313 |
|
struct index_node *child; |
|
314 |
|
|
|
315 |
|
if (ch < node->first) |
|
316 |
|
node->first = ch; |
|
317 |
|
if (ch > node->last) |
|
318 |
|
node->last = ch; |
|
319 |
|
node->children[ch] = NOFAIL(calloc(sizeof(struct index_node), 1)); |
|
320 |
|
|
|
321 |
|
child = node->children[ch]; |
|
322 |
|
child->prefix = NOFAIL(strdup(&key[i+1])); |
|
323 |
|
child->first = INDEX_CHILDMAX; |
|
324 |
|
index_add_value(&child->values, value, priority); |
|
325 |
|
|
|
326 |
|
return 0; |
|
327 |
|
} |
|
328 |
|
|
|
329 |
|
/* Descend into child node and continue */ |
|
330 |
|
node = node->children[ch]; |
|
331 |
|
i++; |
|
332 |
|
} |
|
333 |
|
} |
|
334 |
|
|
|
335 |
|
static int index__haschildren(const struct index_node *node) |
|
336 |
|
{ |
|
337 |
|
return node->first < INDEX_CHILDMAX; |
|
338 |
|
} |
|
339 |
|
|
|
340 |
|
/* Recursive post-order traversal |
|
341 |
|
|
|
342 |
|
Pre-order would make for better read-side buffering / readahead / caching. |
|
343 |
|
(post-order means you go backwards in the file as you descend the tree). |
|
344 |
|
However, index reading is already fast enough. |
|
345 |
|
Pre-order is simpler for writing, and depmod is already slow. |
|
346 |
|
*/ |
|
347 |
|
static uint32_t index_write__node(const struct index_node *node, FILE *out) |
|
348 |
|
{ |
|
349 |
|
uint32_t *child_offs = NULL; |
|
350 |
|
int child_count = 0; |
|
351 |
|
long offset; |
|
352 |
|
|
|
353 |
|
if (!node) |
|
354 |
|
return 0; |
|
355 |
|
|
|
356 |
|
/* Write children and save their offsets */ |
|
357 |
|
if (index__haschildren(node)) { |
|
358 |
|
const struct index_node *child; |
|
359 |
|
int i; |
|
360 |
|
|
|
361 |
|
child_count = node->last - node->first + 1; |
|
362 |
|
child_offs = NOFAIL(malloc(child_count * sizeof(uint32_t))); |
|
363 |
|
|
|
364 |
|
for (i = 0; i < child_count; i++) { |
|
365 |
|
child = node->children[node->first + i]; |
|
366 |
|
child_offs[i] = htonl(index_write__node(child, out)); |
|
367 |
|
} |
|
368 |
|
} |
|
369 |
|
|
|
370 |
|
/* Now write this node */ |
|
371 |
|
offset = ftell(out); |
|
372 |
|
|
|
373 |
|
if (node->prefix[0]) { |
|
374 |
|
fputs(node->prefix, out); |
|
375 |
|
fputc('\0', out); |
|
376 |
|
offset |= INDEX_NODE_PREFIX; |
|
377 |
|
} |
|
378 |
|
|
|
379 |
|
if (child_count) { |
|
380 |
|
fputc(node->first, out); |
|
381 |
|
fputc(node->last, out); |
|
382 |
|
fwrite(child_offs, sizeof(uint32_t), child_count, out); |
|
383 |
|
offset |= INDEX_NODE_CHILDS; |
|
384 |
|
} |
|
385 |
|
|
|
386 |
|
free(child_offs); |
|
387 |
|
|
|
388 |
|
if (node->values) { |
|
389 |
|
const struct index_value *v; |
|
390 |
|
unsigned int value_count; |
|
391 |
|
uint32_t u; |
|
392 |
|
|
|
393 |
|
value_count = 0; |
|
394 |
|
for (v = node->values; v != NULL; v = v->next) |
|
395 |
|
value_count++; |
|
396 |
|
u = htonl(value_count); |
|
397 |
|
fwrite(&u, sizeof(u), 1, out); |
|
398 |
|
|
|
399 |
|
for (v = node->values; v != NULL; v = v->next) { |
|
400 |
|
u = htonl(v->priority); |
|
401 |
|
fwrite(&u, sizeof(u), 1, out); |
|
402 |
|
fputs(v->value, out); |
|
403 |
|
fputc('\0', out); |
|
404 |
|
} |
|
405 |
|
offset |= INDEX_NODE_VALUES; |
|
406 |
|
} |
|
407 |
|
|
|
408 |
|
return offset; |
|
409 |
|
} |
|
410 |
|
|
|
411 |
|
static void index_write(const struct index_node *node, FILE *out) |
|
412 |
|
{ |
|
413 |
|
long initial_offset, final_offset; |
|
414 |
|
uint32_t u; |
|
415 |
|
|
|
416 |
|
u = htonl(INDEX_MAGIC); |
|
417 |
|
fwrite(&u, sizeof(u), 1, out); |
|
418 |
|
u = htonl(INDEX_VERSION); |
|
419 |
|
fwrite(&u, sizeof(u), 1, out); |
|
420 |
|
|
|
421 |
|
/* Second word is reserved for the offset of the root node */ |
|
422 |
|
initial_offset = ftell(out); |
|
423 |
|
assert(initial_offset >= 0); |
|
424 |
|
u = 0; |
|
425 |
|
fwrite(&u, sizeof(uint32_t), 1, out); |
|
426 |
|
|
|
427 |
|
/* Dump trie */ |
|
428 |
|
u = htonl(index_write__node(node, out)); |
|
429 |
|
|
|
430 |
|
/* Update first word */ |
|
431 |
|
final_offset = ftell(out); |
|
432 |
|
assert(final_offset >= 0); |
|
433 |
|
(void)fseek(out, initial_offset, SEEK_SET); |
|
434 |
|
fwrite(&u, sizeof(uint32_t), 1, out); |
|
435 |
|
(void)fseek(out, final_offset, SEEK_SET); |
|
436 |
|
} |
|
437 |
|
|
|
438 |
|
/* END: code from module-init-tools/index.c just modified to compile here. |
|
439 |
|
*/ |
|
440 |
|
|
|
441 |
|
/* configuration parsing **********************************************/ |
|
442 |
|
struct cfg_override { |
|
443 |
|
struct cfg_override *next; |
|
444 |
|
size_t len; |
|
445 |
|
char path[]; |
|
446 |
|
}; |
|
447 |
|
|
|
448 |
|
struct cfg_search { |
|
449 |
|
struct cfg_search *next; |
|
450 |
|
uint8_t builtin; |
|
451 |
|
size_t len; |
|
452 |
|
char path[]; |
|
453 |
|
}; |
|
454 |
|
|
|
455 |
|
struct cfg { |
|
456 |
|
const char *kversion; |
|
457 |
|
char dirname[PATH_MAX]; |
|
458 |
|
size_t dirnamelen; |
|
459 |
|
char sym_prefix; |
|
460 |
|
uint8_t check_symvers; |
|
461 |
|
uint8_t print_unknown; |
|
462 |
|
uint8_t warn_dups; |
|
463 |
|
struct cfg_override *overrides; |
|
464 |
|
struct cfg_search *searches; |
|
465 |
|
}; |
|
466 |
|
|
|
467 |
|
static int cfg_search_add(struct cfg *cfg, const char *path, uint8_t builtin) |
|
468 |
|
{ |
|
469 |
|
struct cfg_search *s; |
|
470 |
|
size_t len; |
|
471 |
|
|
|
472 |
|
if (builtin) |
|
473 |
|
len = 0; |
|
474 |
|
else |
|
475 |
|
len = strlen(path) + 1; |
|
476 |
|
|
|
477 |
|
s = malloc(sizeof(struct cfg_search) + len); |
|
478 |
|
if (s == NULL) { |
|
479 |
|
ERR("search add: out of memory\n"); |
|
480 |
|
return -ENOMEM; |
|
481 |
|
} |
|
482 |
|
s->builtin = builtin; |
|
483 |
|
if (builtin) |
|
484 |
|
s->len = 0; |
|
485 |
|
else { |
|
486 |
|
s->len = len - 1; |
|
487 |
|
memcpy(s->path, path, len); |
|
488 |
|
} |
|
489 |
|
|
|
490 |
|
DBG("search add: %s, builtin=%hhu\n", path, builtin); |
|
491 |
|
|
|
492 |
|
s->next = cfg->searches; |
|
493 |
|
cfg->searches = s; |
|
494 |
|
return 0; |
|
495 |
|
} |
|
496 |
|
|
|
497 |
|
static void cfg_search_free(struct cfg_search *s) |
|
498 |
|
{ |
|
499 |
|
free(s); |
|
500 |
|
} |
|
501 |
|
|
|
502 |
|
static int cfg_override_add(struct cfg *cfg, const char *modname, const char *subdir) |
|
503 |
|
{ |
|
504 |
|
struct cfg_override *o; |
|
505 |
|
size_t modnamelen = strlen(modname); |
|
506 |
|
size_t subdirlen = strlen(subdir); |
|
507 |
|
size_t i; |
|
508 |
|
|
|
509 |
|
o = malloc(sizeof(struct cfg_override) + subdirlen + 1 |
|
510 |
|
+ modnamelen + 1); |
|
511 |
|
if (o == NULL) { |
|
512 |
|
ERR("override add: out of memory\n"); |
|
513 |
|
return -ENOMEM; |
|
514 |
|
} |
|
515 |
|
memcpy(o->path, subdir, subdirlen); |
|
516 |
|
i = subdirlen; |
|
517 |
|
o->path[i] = '/'; |
|
518 |
|
i++; |
|
519 |
|
|
|
520 |
|
memcpy(o->path + i, modname, modnamelen); |
|
521 |
|
i += modnamelen; |
|
522 |
|
o->path[i] = '\0'; /* no extension, so we can match .ko/.ko.gz */ |
|
523 |
|
|
|
524 |
|
o->len = i; |
|
525 |
|
|
|
526 |
|
DBG("override add: %s\n", o->path); |
|
527 |
|
|
|
528 |
|
o->next = cfg->overrides; |
|
529 |
|
cfg->overrides = o; |
|
530 |
|
return 0; |
|
531 |
|
} |
|
532 |
|
|
|
533 |
|
static void cfg_override_free(struct cfg_override *o) |
|
534 |
|
{ |
|
535 |
|
free(o); |
|
536 |
|
} |
|
537 |
|
|
|
538 |
|
static int cfg_kernel_matches(const struct cfg *cfg, const char *pattern) |
|
539 |
|
{ |
|
540 |
|
regex_t re; |
|
541 |
|
int status; |
|
542 |
|
|
|
543 |
|
/* old style */ |
|
544 |
|
if (streq(pattern, "*")) |
|
545 |
|
return 1; |
|
546 |
|
|
|
547 |
|
if (regcomp(&re, pattern, REG_EXTENDED|REG_NOSUB) != 0) |
|
548 |
|
return 0; |
|
549 |
|
|
|
550 |
|
status = regexec(&re, cfg->kversion, 0, NULL, 0); |
|
551 |
|
regfree(&re); |
|
552 |
|
|
|
553 |
|
return status == 0; |
|
554 |
|
} |
|
555 |
|
|
|
556 |
|
static int cfg_file_parse(struct cfg *cfg, const char *filename) |
|
557 |
|
{ |
|
558 |
|
char *line; |
|
559 |
|
FILE *fp; |
|
560 |
|
unsigned int linenum = 0; |
|
561 |
|
int err; |
|
562 |
|
|
|
563 |
|
fp = fopen(filename, "r"); |
|
564 |
|
if (fp == NULL) { |
|
565 |
|
err = -errno; |
|
566 |
|
ERR("file parse %s: %m\n", filename); |
|
567 |
|
return err; |
|
568 |
|
} |
|
569 |
|
|
|
570 |
|
while ((line = freadline_wrapped(fp, &linenum)) != NULL) { |
|
571 |
|
char *cmd, *saveptr; |
|
572 |
|
|
|
573 |
|
if (line[0] == '\0' || line[0] == '#') |
|
574 |
|
goto done_next; |
|
575 |
|
|
|
576 |
|
cmd = strtok_r(line, "\t ", &saveptr); |
|
577 |
|
if (cmd == NULL) |
|
578 |
|
goto done_next; |
|
579 |
|
|
|
580 |
|
if (streq(cmd, "search")) { |
|
581 |
|
const char *sp; |
|
582 |
|
while ((sp = strtok_r(NULL, "\t ", &saveptr)) != NULL) { |
|
583 |
|
uint8_t builtin = streq(sp, CFG_BUILTIN_KEY); |
|
584 |
|
cfg_search_add(cfg, sp, builtin); |
|
585 |
|
} |
|
586 |
|
} else if (streq(cmd, "override")) { |
|
587 |
|
const char *modname = strtok_r(NULL, "\t ", &saveptr); |
|
588 |
|
const char *version = strtok_r(NULL, "\t ", &saveptr); |
|
589 |
|
const char *subdir = strtok_r(NULL, "\t ", &saveptr); |
|
590 |
|
|
|
591 |
|
if (modname == NULL || version == NULL || |
|
592 |
|
subdir == NULL) |
|
593 |
|
goto syntax_error; |
|
594 |
|
|
|
595 |
|
if (!cfg_kernel_matches(cfg, version)) { |
|
596 |
|
INF("%s:%u: override kernel did not match %s\n", |
|
597 |
|
filename, linenum, version); |
|
598 |
|
goto done_next; |
|
599 |
|
} |
|
600 |
|
|
|
601 |
|
cfg_override_add(cfg, modname, subdir); |
|
602 |
|
} else if (streq(cmd, "include") |
|
603 |
|
|| streq(cmd, "make_map_files")) { |
|
604 |
|
INF("%s:%u: command %s not implemented yet\n", |
|
605 |
|
filename, linenum, cmd); |
|
606 |
|
} else { |
|
607 |
|
syntax_error: |
|
608 |
|
ERR("%s:%u: ignoring bad line starting with '%s'\n", |
|
609 |
|
filename, linenum, cmd); |
|
610 |
|
} |
|
611 |
|
|
|
612 |
|
done_next: |
|
613 |
|
free(line); |
|
614 |
|
} |
|
615 |
|
|
|
616 |
|
fclose(fp); |
|
617 |
|
|
|
618 |
|
return 0; |
|
619 |
|
} |
|
620 |
|
|
|
621 |
|
static int cfg_files_filter_out(DIR *d, const char *dir, const char *name) |
|
622 |
|
{ |
|
623 |
|
size_t len = strlen(name); |
|
624 |
|
struct stat st; |
|
625 |
|
|
|
626 |
|
if (name[0] == '.') |
|
627 |
|
return 1; |
|
628 |
|
|
|
629 |
|
if (len < 6 || !streq(name + len - 5, ".conf")) { |
|
630 |
|
INF("All cfg files need .conf: %s/%s\n", dir, name); |
|
631 |
|
return 1; |
|
632 |
|
} |
|
633 |
|
|
|
634 |
|
fstatat(dirfd(d), name, &st, 0); |
|
635 |
|
if (S_ISDIR(st.st_mode)) { |
|
636 |
|
ERR("Directories inside directories are not supported: %s/%s\n", |
|
637 |
|
dir, name); |
|
638 |
|
return 1; |
|
639 |
|
} |
|
640 |
|
|
|
641 |
|
return 0; |
|
642 |
|
} |
|
643 |
|
|
|
644 |
|
struct cfg_file { |
|
645 |
|
size_t dirlen; |
|
646 |
|
size_t namelen; |
|
647 |
|
const char *name; |
|
648 |
|
char path[]; |
|
649 |
|
}; |
|
650 |
|
|
|
651 |
|
static void cfg_file_free(struct cfg_file *f) |
|
652 |
|
{ |
|
653 |
|
free(f); |
|
654 |
|
} |
|
655 |
|
|
|
656 |
|
static int cfg_files_insert_sorted(struct cfg_file ***p_files, size_t *p_n_files, |
|
657 |
|
const char *dir, const char *name) |
|
658 |
|
{ |
|
659 |
|
struct cfg_file **files, *f; |
|
660 |
|
size_t i, n_files, namelen, dirlen; |
|
661 |
|
void *tmp; |
|
662 |
|
char *dir_cpy = NULL; |
|
663 |
|
|
|
664 |
|
dirlen = strlen(dir); |
|
665 |
|
if (name != NULL) |
|
666 |
|
namelen = strlen(name); |
|
667 |
|
else { |
|
668 |
|
dir_cpy = strdup(dir); |
|
669 |
|
if (!dir_cpy) |
|
670 |
|
return -ENOMEM; |
|
671 |
|
name = basename(dir_cpy); |
|
672 |
|
namelen = strlen(name); |
|
673 |
|
dirlen -= namelen + 1; |
|
674 |
|
} |
|
675 |
|
|
|
676 |
|
n_files = *p_n_files; |
|
677 |
|
files = *p_files; |
|
678 |
|
for (i = 0; i < n_files; i++) { |
|
679 |
|
int cmp = strcmp(name, files[i]->name); |
|
680 |
|
if (cmp == 0) { |
|
681 |
|
DBG("Ignoring duplicate config file: %.*s/%s\n", |
|
682 |
|
(int)dirlen, dir, name); |
|
683 |
|
free(dir_cpy); |
|
684 |
|
return -EEXIST; |
|
685 |
|
} else if (cmp < 0) |
|
686 |
|
break; |
|
687 |
|
} |
|
688 |
|
|
|
689 |
|
f = malloc(sizeof(struct cfg_file) + dirlen + namelen + 2); |
|
690 |
|
if (f == NULL) { |
|
691 |
|
ERR("files insert sorted: out of memory\n"); |
|
692 |
|
free(dir_cpy); |
|
693 |
|
return -ENOMEM; |
|
694 |
|
} |
|
695 |
|
|
|
696 |
|
tmp = realloc(files, sizeof(struct cfg_file *) * (n_files + 1)); |
|
697 |
|
if (tmp == NULL) { |
|
698 |
|
ERR("files insert sorted: out of memory\n"); |
|
699 |
|
free(f); |
|
700 |
|
free(dir_cpy); |
|
701 |
|
return -ENOMEM; |
|
702 |
|
} |
|
703 |
|
*p_files = files = tmp; |
|
704 |
|
|
|
705 |
|
if (i < n_files) { |
|
706 |
|
memmove(files + i + 1, files + i, |
|
707 |
|
sizeof(struct cfg_file *) * (n_files - i)); |
|
708 |
|
} |
|
709 |
|
files[i] = f; |
|
710 |
|
|
|
711 |
|
f->dirlen = dirlen; |
|
712 |
|
f->namelen = namelen; |
|
713 |
|
f->name = f->path + dirlen + 1; |
|
714 |
|
memcpy(f->path, dir, dirlen); |
|
715 |
|
f->path[dirlen] = '/'; |
|
716 |
|
memcpy(f->path + dirlen + 1, name, namelen); |
|
717 |
|
f->path[dirlen + 1 + namelen] = '\0'; |
|
718 |
|
|
|
719 |
|
*p_n_files = n_files + 1; |
|
720 |
|
free(dir_cpy); |
|
721 |
|
return 0; |
|
722 |
|
} |
|
723 |
|
|
|
724 |
|
/* |
|
725 |
|
* Insert configuration files ignoring duplicates |
|
726 |
|
*/ |
|
727 |
|
static int cfg_files_list(struct cfg_file ***p_files, size_t *p_n_files, |
|
728 |
|
const char *path) |
|
729 |
|
{ |
|
730 |
|
struct dirent *dent; |
|
731 |
|
DIR *d; |
|
732 |
|
int err = 0; |
|
733 |
|
struct stat st; |
|
734 |
|
|
|
735 |
|
if (stat(path, &st) != 0) { |
|
736 |
|
err = -errno; |
|
737 |
|
DBG("could not stat '%s': %m\n", path); |
|
738 |
|
return err; |
|
739 |
|
} |
|
740 |
|
|
|
741 |
|
if (!S_ISDIR(st.st_mode)) { |
|
742 |
|
cfg_files_insert_sorted(p_files, p_n_files, path, NULL); |
|
743 |
|
return 0; |
|
744 |
|
} |
|
745 |
|
|
|
746 |
|
d = opendir(path); |
|
747 |
|
if (d == NULL) { |
|
748 |
|
ERR("files list %s: %m\n", path); |
|
749 |
|
return -EINVAL; |
|
750 |
|
} |
|
751 |
|
|
|
752 |
|
for (dent = readdir(d); dent != NULL; dent = readdir(d)) { |
|
753 |
|
if (cfg_files_filter_out(d, path, dent->d_name)) |
|
754 |
|
continue; |
|
755 |
|
|
|
756 |
|
cfg_files_insert_sorted(p_files, p_n_files, path, dent->d_name); |
|
757 |
|
} |
|
758 |
|
|
|
759 |
|
closedir(d); |
|
760 |
|
DBG("parsed configuration files from %s\n", path); |
|
761 |
|
return err; |
|
762 |
|
} |
|
763 |
|
|
|
764 |
|
static int cfg_load(struct cfg *cfg, const char * const *cfg_paths) |
|
765 |
|
{ |
|
766 |
|
size_t i, n_files = 0; |
|
767 |
|
struct cfg_file **files = NULL; |
|
768 |
|
|
|
769 |
|
if (cfg_paths == NULL) |
|
770 |
|
cfg_paths = default_cfg_paths; |
|
771 |
|
|
|
772 |
|
for (i = 0; cfg_paths[i] != NULL; i++) |
|
773 |
|
cfg_files_list(&files, &n_files, cfg_paths[i]); |
|
774 |
|
|
|
775 |
|
for (i = 0; i < n_files; i++) { |
|
776 |
|
struct cfg_file *f = files[i]; |
|
777 |
|
cfg_file_parse(cfg, f->path); |
|
778 |
|
cfg_file_free(f); |
|
779 |
|
} |
|
780 |
|
free(files); |
|
781 |
|
|
|
782 |
|
/* For backward compatibility add "updates" to the head of the search |
|
783 |
|
* list here. But only if there was no "search" option specified. |
|
784 |
|
*/ |
|
785 |
|
if (cfg->searches == NULL) |
|
786 |
|
cfg_search_add(cfg, "updates", 0); |
|
787 |
|
|
|
788 |
|
return 0; |
|
789 |
|
} |
|
790 |
|
|
|
791 |
|
static void cfg_free(struct cfg *cfg) |
|
792 |
|
{ |
|
793 |
|
while (cfg->overrides) { |
|
794 |
|
struct cfg_override *tmp = cfg->overrides; |
|
795 |
|
cfg->overrides = cfg->overrides->next; |
|
796 |
|
cfg_override_free(tmp); |
|
797 |
|
} |
|
798 |
|
|
|
799 |
|
while (cfg->searches) { |
|
800 |
|
struct cfg_search *tmp = cfg->searches; |
|
801 |
|
cfg->searches = cfg->searches->next; |
|
802 |
|
cfg_search_free(tmp); |
|
803 |
|
} |
|
804 |
|
} |
|
805 |
|
|
|
806 |
|
|
|
807 |
|
/* depmod calculations ***********************************************/ |
|
808 |
|
struct vertex; |
|
809 |
|
struct mod { |
|
810 |
|
struct kmod_module *kmod; |
|
811 |
|
char *path; |
|
812 |
|
const char *relpath; /* path relative to '$ROOT/lib/modules/$VER/' */ |
|
813 |
|
char *uncrelpath; /* same as relpath but ending in .ko */ |
|
814 |
|
struct kmod_list *info_list; |
|
815 |
|
struct kmod_list *dep_sym_list; |
|
816 |
|
struct array deps; /* struct symbol */ |
|
817 |
|
size_t baselen; /* points to start of basename/filename */ |
|
818 |
|
size_t modnamesz; |
|
819 |
|
int sort_idx; /* sort index using modules.order */ |
|
820 |
|
int dep_sort_idx; /* topological sort index */ |
|
821 |
|
uint16_t idx; /* index in depmod->modules.array */ |
|
822 |
|
uint16_t users; /* how many modules depend on this one */ |
|
823 |
|
bool visited; /* helper field to report cycles */ |
|
824 |
|
struct vertex *vertex; /* helper field to report cycles */ |
|
825 |
|
char modname[]; |
|
826 |
|
}; |
|
827 |
|
|
|
828 |
|
struct symbol { |
|
829 |
|
struct mod *owner; |
|
830 |
|
uint64_t crc; |
|
831 |
|
char name[]; |
|
832 |
|
}; |
|
833 |
|
|
|
834 |
|
struct depmod { |
|
835 |
|
const struct cfg *cfg; |
|
836 |
|
struct kmod_ctx *ctx; |
|
837 |
|
struct array modules; |
|
838 |
|
struct hash *modules_by_uncrelpath; |
|
839 |
|
struct hash *modules_by_name; |
|
840 |
|
struct hash *symbols; |
|
841 |
|
}; |
|
842 |
|
|
|
843 |
|
static void mod_free(struct mod *mod) |
|
844 |
|
{ |
|
845 |
|
DBG("free %p kmod=%p, path=%s\n", mod, mod->kmod, mod->path); |
|
846 |
|
array_free_array(&mod->deps); |
|
847 |
|
kmod_module_unref(mod->kmod); |
|
848 |
|
kmod_module_info_free_list(mod->info_list); |
|
849 |
|
kmod_module_dependency_symbols_free_list(mod->dep_sym_list); |
|
850 |
|
free(mod->uncrelpath); |
|
851 |
|
free(mod->path); |
|
852 |
|
free(mod); |
|
853 |
|
} |
|
854 |
|
|
|
855 |
|
static int mod_add_dependency(struct mod *mod, struct symbol *sym) |
|
856 |
|
{ |
|
857 |
|
int err; |
|
858 |
|
|
|
859 |
|
DBG("%s depends on %s %s\n", mod->path, sym->name, |
|
860 |
|
sym->owner != NULL ? sym->owner->path : "(unknown)"); |
|
861 |
|
|
|
862 |
|
if (sym->owner == NULL) |
|
863 |
|
return 0; |
|
864 |
|
|
|
865 |
|
err = array_append_unique(&mod->deps, sym->owner); |
|
866 |
|
if (err == -EEXIST) |
|
867 |
|
return 0; |
|
868 |
|
if (err < 0) |
|
869 |
|
return err; |
|
870 |
|
|
|
871 |
|
sym->owner->users++; |
|
872 |
|
SHOW("%s needs \"%s\": %s\n", mod->path, sym->name, sym->owner->path); |
|
873 |
|
return 0; |
|
874 |
|
} |
|
875 |
|
|
|
876 |
|
static void symbol_free(struct symbol *sym) |
|
877 |
|
{ |
|
878 |
|
DBG("free %p sym=%s, owner=%p %s\n", sym, sym->name, sym->owner, |
|
879 |
|
sym->owner != NULL ? sym->owner->path : ""); |
|
880 |
|
free(sym); |
|
881 |
|
} |
|
882 |
|
|
|
883 |
|
static int depmod_init(struct depmod *depmod, struct cfg *cfg, |
|
884 |
|
struct kmod_ctx *ctx) |
|
885 |
|
{ |
|
886 |
|
int err = 0; |
|
887 |
|
|
|
888 |
|
depmod->cfg = cfg; |
|
889 |
|
depmod->ctx = ctx; |
|
890 |
|
|
|
891 |
|
array_init(&depmod->modules, 128); |
|
892 |
|
|
|
893 |
|
depmod->modules_by_uncrelpath = hash_new(512, NULL); |
|
894 |
|
if (depmod->modules_by_uncrelpath == NULL) { |
|
895 |
|
err = -errno; |
|
896 |
|
goto modules_by_uncrelpath_failed; |
|
897 |
|
} |
|
898 |
|
|
|
899 |
|
depmod->modules_by_name = hash_new(512, NULL); |
|
900 |
|
if (depmod->modules_by_name == NULL) { |
|
901 |
|
err = -errno; |
|
902 |
|
goto modules_by_name_failed; |
|
903 |
|
} |
|
904 |
|
|
|
905 |
|
depmod->symbols = hash_new(2048, (void (*)(void *))symbol_free); |
|
906 |
|
if (depmod->symbols == NULL) { |
|
907 |
|
err = -errno; |
|
908 |
|
goto symbols_failed; |
|
909 |
|
} |
|
910 |
|
|
|
911 |
|
return 0; |
|
912 |
|
|
|
913 |
|
symbols_failed: |
|
914 |
|
hash_free(depmod->modules_by_name); |
|
915 |
|
modules_by_name_failed: |
|
916 |
|
hash_free(depmod->modules_by_uncrelpath); |
|
917 |
|
modules_by_uncrelpath_failed: |
|
918 |
|
return err; |
|
919 |
|
} |
|
920 |
|
|
|
921 |
|
static void depmod_shutdown(struct depmod *depmod) |
|
922 |
|
{ |
|
923 |
|
size_t i; |
|
924 |
|
|
|
925 |
|
hash_free(depmod->symbols); |
|
926 |
|
|
|
927 |
|
hash_free(depmod->modules_by_uncrelpath); |
|
928 |
|
|
|
929 |
|
hash_free(depmod->modules_by_name); |
|
930 |
|
|
|
931 |
|
for (i = 0; i < depmod->modules.count; i++) |
|
932 |
|
mod_free(depmod->modules.array[i]); |
|
933 |
|
array_free_array(&depmod->modules); |
|
934 |
|
|
|
935 |
|
kmod_unref(depmod->ctx); |
|
936 |
|
} |
|
937 |
|
|
|
938 |
|
static int depmod_module_add(struct depmod *depmod, struct kmod_module *kmod) |
|
939 |
|
{ |
|
940 |
|
const struct cfg *cfg = depmod->cfg; |
|
941 |
|
const char *modname, *lastslash; |
|
942 |
|
size_t modnamesz; |
|
943 |
|
struct mod *mod; |
|
944 |
|
int err; |
|
945 |
|
|
|
946 |
|
modname = kmod_module_get_name(kmod); |
|
947 |
|
modnamesz = strlen(modname) + 1; |
|
948 |
|
|
|
949 |
|
mod = calloc(1, sizeof(struct mod) + modnamesz); |
|
950 |
|
if (mod == NULL) |
|
951 |
|
return -ENOMEM; |
|
952 |
|
mod->kmod = kmod; |
|
953 |
|
mod->sort_idx = depmod->modules.count + 1; |
|
954 |
|
mod->dep_sort_idx = INT32_MAX; |
|
955 |
|
memcpy(mod->modname, modname, modnamesz); |
|
956 |
|
mod->modnamesz = modnamesz; |
|
957 |
|
|
|
958 |
|
array_init(&mod->deps, 4); |
|
959 |
|
|
|
960 |
|
mod->path = strdup(kmod_module_get_path(kmod)); |
|
961 |
|
lastslash = strrchr(mod->path, '/'); |
|
962 |
|
mod->baselen = lastslash - mod->path; |
|
963 |
|
if (strncmp(mod->path, cfg->dirname, cfg->dirnamelen) == 0 && |
|
964 |
|
mod->path[cfg->dirnamelen] == '/') |
|
965 |
|
mod->relpath = mod->path + cfg->dirnamelen + 1; |
|
966 |
|
else |
|
967 |
|
mod->relpath = NULL; |
|
968 |
|
|
|
969 |
|
err = hash_add_unique(depmod->modules_by_name, mod->modname, mod); |
|
970 |
|
if (err < 0) { |
|
971 |
|
ERR("hash_add_unique %s: %s\n", mod->modname, strerror(-err)); |
|
972 |
|
goto fail; |
|
973 |
|
} |
|
974 |
|
|
|
975 |
|
if (mod->relpath != NULL) { |
|
976 |
|
size_t uncrelpathlen = lastslash - mod->relpath + modnamesz |
|
977 |
|
+ strlen(KMOD_EXTENSION_UNCOMPRESSED); |
|
978 |
|
mod->uncrelpath = memdup(mod->relpath, uncrelpathlen + 1); |
|
979 |
|
mod->uncrelpath[uncrelpathlen] = '\0'; |
|
980 |
|
err = hash_add_unique(depmod->modules_by_uncrelpath, |
|
981 |
|
mod->uncrelpath, mod); |
|
982 |
|
if (err < 0) { |
|
983 |
|
ERR("hash_add_unique %s: %s\n", |
|
984 |
|
mod->uncrelpath, strerror(-err)); |
|
985 |
|
hash_del(depmod->modules_by_name, mod->modname); |
|
986 |
|
goto fail; |
|
987 |
|
} |
|
988 |
|
} |
|
989 |
|
|
|
990 |
|
DBG("add %p kmod=%p, path=%s\n", mod, kmod, mod->path); |
|
991 |
|
|
|
992 |
|
return 0; |
|
993 |
|
|
|
994 |
|
fail: |
|
995 |
|
free(mod->uncrelpath); |
|
996 |
|
free(mod); |
|
997 |
|
return err; |
|
998 |
|
} |
|
999 |
|
|
|
1000 |
|
static int depmod_module_del(struct depmod *depmod, struct mod *mod) |
|
1001 |
|
{ |
|
1002 |
|
DBG("del %p kmod=%p, path=%s\n", mod, mod->kmod, mod->path); |
|
1003 |
|
|
|
1004 |
|
if (mod->uncrelpath != NULL) |
|
1005 |
|
hash_del(depmod->modules_by_uncrelpath, mod->uncrelpath); |
|
1006 |
|
|
|
1007 |
|
hash_del(depmod->modules_by_name, mod->modname); |
|
1008 |
|
|
|
1009 |
|
mod_free(mod); |
|
1010 |
|
return 0; |
|
1011 |
|
} |
|
1012 |
|
|
|
1013 |
|
/* returns if existing module @mod is higher priority than newpath. |
|
1014 |
|
* note this is the inverse of module-init-tools is_higher_priority() |
|
1015 |
|
*/ |
|
1016 |
|
static int depmod_module_is_higher_priority(const struct depmod *depmod, const struct mod *mod, size_t baselen, size_t namelen, size_t modnamelen, const char *newpath) |
|
1017 |
|
{ |
|
1018 |
|
const struct cfg *cfg = depmod->cfg; |
|
1019 |
|
const struct cfg_override *ov; |
|
1020 |
|
const struct cfg_search *se; |
|
1021 |
|
|
|
1022 |
|
/* baselen includes the last '/' and mod->baselen doesn't. So it's |
|
1023 |
|
* actually correct to use modnamelen in the first and modnamesz in |
|
1024 |
|
* the latter */ |
|
1025 |
|
size_t newlen = baselen + modnamelen; |
|
1026 |
|
size_t oldlen = mod->baselen + mod->modnamesz; |
|
1027 |
|
const char *oldpath = mod->path; |
|
1028 |
|
int i, bprio = -1, oldprio = -1, newprio = -1; |
|
1029 |
|
|
|
1030 |
|
assert(strncmp(newpath, cfg->dirname, cfg->dirnamelen) == 0); |
|
1031 |
|
assert(strncmp(oldpath, cfg->dirname, cfg->dirnamelen) == 0); |
|
1032 |
|
|
|
1033 |
|
newpath += cfg->dirnamelen + 1; |
|
1034 |
|
newlen -= cfg->dirnamelen + 1; |
|
1035 |
|
oldpath += cfg->dirnamelen + 1; |
|
1036 |
|
oldlen -= cfg->dirnamelen + 1; |
|
1037 |
|
|
|
1038 |
|
DBG("comparing priorities of %s and %s\n", |
|
1039 |
|
oldpath, newpath); |
|
1040 |
|
|
|
1041 |
|
for (ov = cfg->overrides; ov != NULL; ov = ov->next) { |
|
1042 |
|
DBG("override %s\n", ov->path); |
|
1043 |
|
if (newlen == ov->len && memcmp(ov->path, newpath, newlen) == 0) |
|
1044 |
|
return 0; |
|
1045 |
|
if (oldlen == ov->len && memcmp(ov->path, oldpath, oldlen) == 0) |
|
1046 |
|
return 1; |
|
1047 |
|
} |
|
1048 |
|
|
|
1049 |
|
for (i = 0, se = cfg->searches; se != NULL; se = se->next, i++) { |
|
1050 |
|
DBG("search %s\n", se->builtin ? "built-in" : se->path); |
|
1051 |
|
if (se->builtin) |
|
1052 |
|
bprio = i; |
|
1053 |
|
else if (newlen > se->len && newpath[se->len] == '/' && |
|
1054 |
|
memcmp(se->path, newpath, se->len) == 0) |
|
1055 |
|
newprio = i; |
|
1056 |
|
else if (oldlen > se->len && oldpath[se->len] == '/' && |
|
1057 |
|
memcmp(se->path, oldpath, se->len) == 0) |
|
1058 |
|
oldprio = i; |
|
1059 |
|
} |
|
1060 |
|
|
|
1061 |
|
if (newprio < 0) |
|
1062 |
|
newprio = bprio; |
|
1063 |
|
if (oldprio < 0) |
|
1064 |
|
oldprio = bprio; |
|
1065 |
|
|
|
1066 |
|
DBG("priorities: built-in: %d, old: %d, new: %d\n", |
|
1067 |
|
bprio, oldprio, newprio); |
|
1068 |
|
|
|
1069 |
|
return newprio <= oldprio; |
|
1070 |
|
} |
|
1071 |
|
|
|
1072 |
|
static int depmod_modules_search_file(struct depmod *depmod, size_t baselen, size_t namelen, const char *path) |
|
1073 |
|
{ |
|
1074 |
|
struct kmod_module *kmod; |
|
1075 |
|
struct mod *mod; |
|
1076 |
|
const char *relpath; |
|
1077 |
|
char modname[PATH_MAX]; |
|
1078 |
|
size_t modnamelen; |
|
1079 |
|
int err; |
|
1080 |
|
|
|
1081 |
|
if (!path_ends_with_kmod_ext(path + baselen, namelen)) |
|
1082 |
|
return 0; |
|
1083 |
|
|
|
1084 |
|
if (path_to_modname(path, modname, &modnamelen) == NULL) { |
|
1085 |
|
ERR("could not get modname from path %s\n", path); |
|
1086 |
|
return -EINVAL; |
|
1087 |
|
} |
|
1088 |
|
|
|
1089 |
|
relpath = path + depmod->cfg->dirnamelen + 1; |
|
1090 |
|
DBG("try %s (%s)\n", relpath, modname); |
|
1091 |
|
|
|
1092 |
|
mod = hash_find(depmod->modules_by_name, modname); |
|
1093 |
|
if (mod == NULL) |
|
1094 |
|
goto add; |
|
1095 |
|
|
|
1096 |
|
if (depmod_module_is_higher_priority(depmod, mod, baselen, |
|
1097 |
|
namelen, modnamelen, path)) { |
|
1098 |
|
DBG("Ignored lower priority: %s, higher: %s\n", |
|
1099 |
|
path, mod->path); |
|
1100 |
|
return 0; |
|
1101 |
|
} |
|
1102 |
|
|
|
1103 |
|
DBG("Replace lower priority %s with new module %s\n", |
|
1104 |
|
mod->relpath, relpath); |
|
1105 |
|
err = depmod_module_del(depmod, mod); |
|
1106 |
|
if (err < 0) { |
|
1107 |
|
ERR("could not del module %s: %s\n", mod->path, strerror(-err)); |
|
1108 |
|
return err; |
|
1109 |
|
} |
|
1110 |
|
|
|
1111 |
|
add: |
|
1112 |
|
err = kmod_module_new_from_path(depmod->ctx, path, &kmod); |
|
1113 |
|
if (err < 0) { |
|
1114 |
|
ERR("could not create module %s: %s\n", path, strerror(-err)); |
|
1115 |
|
return err; |
|
1116 |
|
} |
|
1117 |
|
|
|
1118 |
|
err = depmod_module_add(depmod, kmod); |
|
1119 |
|
if (err < 0) { |
|
1120 |
|
ERR("could not add module %s: %s\n", |
|
1121 |
|
path, strerror(-err)); |
|
1122 |
|
kmod_module_unref(kmod); |
|
1123 |
|
return err; |
|
1124 |
|
} |
|
1125 |
|
return 0; |
|
1126 |
|
} |
|
1127 |
|
|
|
1128 |
|
static int depmod_modules_search_dir(struct depmod *depmod, DIR *d, size_t baselen, char *path) |
|
1129 |
|
{ |
|
1130 |
|
struct dirent *de; |
|
1131 |
|
int err = 0, dfd = dirfd(d); |
|
1132 |
|
|
|
1133 |
|
while ((de = readdir(d)) != NULL) { |
|
1134 |
|
const char *name = de->d_name; |
|
1135 |
|
size_t namelen; |
|
1136 |
|
uint8_t is_dir; |
|
1137 |
|
|
|
1138 |
|
if (name[0] == '.' && (name[1] == '\0' || |
|
1139 |
|
(name[1] == '.' && name[2] == '\0'))) |
|
1140 |
|
continue; |
|
1141 |
|
if (streq(name, "build") || streq(name, "source")) |
|
1142 |
|
continue; |
|
1143 |
|
namelen = strlen(name); |
|
1144 |
|
if (baselen + namelen + 2 >= PATH_MAX) { |
|
1145 |
|
path[baselen] = '\0'; |
|
1146 |
|
ERR("path is too long %s%s\n", path, name); |
|
1147 |
|
continue; |
|
1148 |
|
} |
|
1149 |
|
memcpy(path + baselen, name, namelen + 1); |
|
1150 |
|
|
|
1151 |
|
if (de->d_type == DT_REG) |
|
1152 |
|
is_dir = 0; |
|
1153 |
|
else if (de->d_type == DT_DIR) |
|
1154 |
|
is_dir = 1; |
|
1155 |
|
else { |
|
1156 |
|
struct stat st; |
|
1157 |
|
if (fstatat(dfd, name, &st, 0) < 0) { |
|
1158 |
|
ERR("fstatat(%d, %s): %m\n", dfd, name); |
|
1159 |
|
continue; |
|
1160 |
|
} else if (S_ISREG(st.st_mode)) |
|
1161 |
|
is_dir = 0; |
|
1162 |
|
else if (S_ISDIR(st.st_mode)) |
|
1163 |
|
is_dir = 1; |
|
1164 |
|
else { |
|
1165 |
|
ERR("unsupported file type %s: %o\n", |
|
1166 |
|
path, st.st_mode & S_IFMT); |
|
1167 |
|
continue; |
|
1168 |
|
} |
|
1169 |
|
} |
|
1170 |
|
|
|
1171 |
|
if (is_dir) { |
|
1172 |
|
int fd; |
|
1173 |
|
DIR *subdir; |
|
1174 |
|
if (baselen + namelen + 2 + NAME_MAX >= PATH_MAX) { |
|
1175 |
|
ERR("directory path is too long %s\n", path); |
|
1176 |
|
continue; |
|
1177 |
|
} |
|
1178 |
|
fd = openat(dfd, name, O_RDONLY); |
|
1179 |
|
if (fd < 0) { |
|
1180 |
|
ERR("openat(%d, %s, O_RDONLY): %m\n", |
|
1181 |
|
dfd, name); |
|
1182 |
|
continue; |
|
1183 |
|
} |
|
1184 |
|
subdir = fdopendir(fd); |
|
1185 |
|
if (subdir == NULL) { |
|
1186 |
|
ERR("fdopendir(%d): %m\n", fd); |
|
1187 |
|
close(fd); |
|
1188 |
|
continue; |
|
1189 |
|
} |
|
1190 |
|
path[baselen + namelen] = '/'; |
|
1191 |
|
path[baselen + namelen + 1] = '\0'; |
|
1192 |
|
err = depmod_modules_search_dir(depmod, subdir, |
|
1193 |
|
baselen + namelen + 1, |
|
1194 |
|
path); |
|
1195 |
|
closedir(subdir); |
|
1196 |
|
} else { |
|
1197 |
|
err = depmod_modules_search_file(depmod, baselen, |
|
1198 |
|
namelen, path); |
|
1199 |
|
} |
|
1200 |
|
|
|
1201 |
|
if (err < 0) { |
|
1202 |
|
path[baselen + namelen] = '\0'; |
|
1203 |
|
ERR("failed %s: %s\n", path, strerror(-err)); |
|
1204 |
|
err = 0; /* ignore errors */ |
|
1205 |
|
} |
|
1206 |
|
} |
|
1207 |
|
|
|
1208 |
|
return err; |
|
1209 |
|
} |
|
1210 |
|
|
|
1211 |
|
static int depmod_modules_search(struct depmod *depmod) |
|
1212 |
|
{ |
|
1213 |
|
char path[PATH_MAX]; |
|
1214 |
|
DIR *d = opendir(depmod->cfg->dirname); |
|
1215 |
|
size_t baselen; |
|
1216 |
|
int err; |
|
1217 |
|
if (d == NULL) { |
|
1218 |
|
err = -errno; |
|
1219 |
|
ERR("could not open directory %s: %m\n", depmod->cfg->dirname); |
|
1220 |
|
return err; |
|
1221 |
|
} |
|
1222 |
|
|
|
1223 |
|
baselen = depmod->cfg->dirnamelen; |
|
1224 |
|
memcpy(path, depmod->cfg->dirname, baselen); |
|
1225 |
|
path[baselen] = '/'; |
|
1226 |
|
baselen++; |
|
1227 |
|
path[baselen] = '\0'; |
|
1228 |
|
|
|
1229 |
|
err = depmod_modules_search_dir(depmod, d, baselen, path); |
|
1230 |
|
closedir(d); |
|
1231 |
|
return err; |
|
1232 |
|
} |
|
1233 |
|
|
|
1234 |
|
static int mod_cmp(const void *pa, const void *pb) { |
|
1235 |
|
const struct mod *a = *(const struct mod **)pa; |
|
1236 |
|
const struct mod *b = *(const struct mod **)pb; |
|
1237 |
|
return a->sort_idx - b->sort_idx; |
|
1238 |
|
} |
|
1239 |
|
|
|
1240 |
|
static int depmod_modules_build_array(struct depmod *depmod) |
|
1241 |
|
{ |
|
1242 |
|
struct hash_iter module_iter; |
|
1243 |
|
const void *v; |
|
1244 |
|
int err; |
|
1245 |
|
|
|
1246 |
|
hash_iter_init(depmod->modules_by_name, &module_iter); |
|
1247 |
|
while (hash_iter_next(&module_iter, NULL, &v)) { |
|
1248 |
|
struct mod *mod = (struct mod *) v; |
|
1249 |
|
mod->idx = depmod->modules.count; |
|
1250 |
|
err = array_append(&depmod->modules, mod); |
|
1251 |
|
if (err < 0) |
|
1252 |
|
return err; |
|
1253 |
|
} |
|
1254 |
|
|
|
1255 |
|
return 0; |
|
1256 |
|
} |
|
1257 |
|
|
|
1258 |
|
static void depmod_modules_sort(struct depmod *depmod) |
|
1259 |
|
{ |
|
1260 |
|
char order_file[PATH_MAX], line[PATH_MAX]; |
|
1261 |
|
FILE *fp; |
|
1262 |
|
unsigned idx = 0, total = 0; |
|
1263 |
|
|
|
1264 |
|
snprintf(order_file, sizeof(order_file), "%s/modules.order", |
|
1265 |
|
depmod->cfg->dirname); |
|
1266 |
|
fp = fopen(order_file, "r"); |
|
1267 |
|
if (fp == NULL) { |
|
1268 |
|
WRN("could not open %s: %m\n", order_file); |
|
1269 |
|
return; |
|
1270 |
|
} |
|
1271 |
|
|
|
1272 |
|
while (fgets(line, sizeof(line), fp) != NULL) { |
|
1273 |
|
size_t len = strlen(line); |
|
1274 |
|
idx++; |
|
1275 |
|
if (len == 0) |
|
1276 |
|
continue; |
|
1277 |
|
if (line[len - 1] != '\n') { |
|
1278 |
|
ERR("%s:%u corrupted line misses '\\n'\n", |
|
1279 |
|
order_file, idx); |
|
1280 |
|
goto corrupted; |
|
1281 |
|
} |
|
1282 |
|
} |
|
1283 |
|
total = idx + 1; |
|
1284 |
|
idx = 0; |
|
1285 |
|
fseek(fp, 0, SEEK_SET); |
|
1286 |
|
while (fgets(line, sizeof(line), fp) != NULL) { |
|
1287 |
|
size_t len = strlen(line); |
|
1288 |
|
struct mod *mod; |
|
1289 |
|
|
|
1290 |
|
idx++; |
|
1291 |
|
if (len == 0) |
|
1292 |
|
continue; |
|
1293 |
|
line[len - 1] = '\0'; |
|
1294 |
|
|
|
1295 |
|
mod = hash_find(depmod->modules_by_uncrelpath, line); |
|
1296 |
|
if (mod == NULL) |
|
1297 |
|
continue; |
|
1298 |
|
mod->sort_idx = idx - total; |
|
1299 |
|
} |
|
1300 |
|
|
|
1301 |
|
array_sort(&depmod->modules, mod_cmp); |
|
1302 |
|
for (idx = 0; idx < depmod->modules.count; idx++) { |
|
1303 |
|
struct mod *m = depmod->modules.array[idx]; |
|
1304 |
|
m->idx = idx; |
|
1305 |
|
} |
|
1306 |
|
|
|
1307 |
|
corrupted: |
|
1308 |
|
fclose(fp); |
|
1309 |
|
} |
|
1310 |
|
|
|
1311 |
|
static int depmod_symbol_add(struct depmod *depmod, const char *name, |
|
1312 |
|
bool prefix_skipped, uint64_t crc, |
|
1313 |
|
const struct mod *owner) |
|
1314 |
|
{ |
|
1315 |
|
size_t namelen; |
|
1316 |
|
int err; |
|
1317 |
|
struct symbol *sym; |
|
1318 |
|
|
|
1319 |
|
if (!prefix_skipped && (name[0] == depmod->cfg->sym_prefix)) |
|
1320 |
|
name++; |
|
1321 |
|
|
|
1322 |
|
namelen = strlen(name) + 1; |
|
1323 |
|
sym = malloc(sizeof(struct symbol) + namelen); |
|
1324 |
|
if (sym == NULL) |
|
1325 |
|
return -ENOMEM; |
|
1326 |
|
|
|
1327 |
|
sym->owner = (struct mod *)owner; |
|
1328 |
|
sym->crc = crc; |
|
1329 |
|
memcpy(sym->name, name, namelen); |
|
1330 |
|
|
|
1331 |
|
err = hash_add(depmod->symbols, sym->name, sym); |
|
1332 |
|
if (err < 0) { |
|
1333 |
|
free(sym); |
|
1334 |
|
return err; |
|
1335 |
|
} |
|
1336 |
|
|
|
1337 |
|
DBG("add %p sym=%s, owner=%p %s\n", sym, sym->name, owner, |
|
1338 |
|
owner != NULL ? owner->path : ""); |
|
1339 |
|
|
|
1340 |
|
return 0; |
|
1341 |
|
} |
|
1342 |
|
|
|
1343 |
|
static struct symbol *depmod_symbol_find(const struct depmod *depmod, |
|
1344 |
|
const char *name) |
|
1345 |
|
{ |
|
1346 |
|
if (name[0] == '.') /* PPC64 needs this: .foo == foo */ |
|
1347 |
|
name++; |
|
1348 |
|
if (name[0] == depmod->cfg->sym_prefix) |
|
1349 |
|
name++; |
|
1350 |
|
return hash_find(depmod->symbols, name); |
|
1351 |
|
} |
|
1352 |
|
|
|
1353 |
|
static int depmod_load_modules(struct depmod *depmod) |
|
1354 |
|
{ |
|
1355 |
|
struct mod **itr, **itr_end; |
|
1356 |
|
|
|
1357 |
|
DBG("load symbols (%zd modules)\n", depmod->modules.count); |
|
1358 |
|
|
|
1359 |
|
itr = (struct mod **)depmod->modules.array; |
|
1360 |
|
itr_end = itr + depmod->modules.count; |
|
1361 |
|
for (; itr < itr_end; itr++) { |
|
1362 |
|
struct mod *mod = *itr; |
|
1363 |
|
struct kmod_list *l, *list = NULL; |
|
1364 |
|
int err = kmod_module_get_symbols(mod->kmod, &list); |
|
1365 |
|
if (err < 0) { |
|
1366 |
|
if (err == -ENOENT) |
|
1367 |
|
DBG("ignoring %s: no symbols\n", mod->path); |
|
1368 |
|
else |
|
1369 |
|
ERR("failed to load symbols from %s: %s\n", |
|
1370 |
|
mod->path, strerror(-err)); |
|
1371 |
|
goto load_info; |
|
1372 |
|
} |
|
1373 |
|
kmod_list_foreach(l, list) { |
|
1374 |
|
const char *name = kmod_module_symbol_get_symbol(l); |
|
1375 |
|
uint64_t crc = kmod_module_symbol_get_crc(l); |
|
1376 |
|
depmod_symbol_add(depmod, name, false, crc, mod); |
|
1377 |
|
} |
|
1378 |
|
kmod_module_symbols_free_list(list); |
|
1379 |
|
|
|
1380 |
|
load_info: |
|
1381 |
|
kmod_module_get_info(mod->kmod, &mod->info_list); |
|
1382 |
|
kmod_module_get_dependency_symbols(mod->kmod, |
|
1383 |
|
&mod->dep_sym_list); |
|
1384 |
|
kmod_module_unref(mod->kmod); |
|
1385 |
|
mod->kmod = NULL; |
|
1386 |
|
} |
|
1387 |
|
|
|
1388 |
|
DBG("loaded symbols (%zd modules, %u symbols)\n", |
|
1389 |
|
depmod->modules.count, hash_get_count(depmod->symbols)); |
|
1390 |
|
|
|
1391 |
|
return 0; |
|
1392 |
|
} |
|
1393 |
|
|
|
1394 |
|
static int depmod_load_module_dependencies(struct depmod *depmod, struct mod *mod) |
|
1395 |
|
{ |
|
1396 |
|
const struct cfg *cfg = depmod->cfg; |
|
1397 |
|
struct kmod_list *l; |
|
1398 |
|
|
|
1399 |
|
DBG("do dependencies of %s\n", mod->path); |
|
1400 |
|
kmod_list_foreach(l, mod->dep_sym_list) { |
|
1401 |
|
const char *name = kmod_module_dependency_symbol_get_symbol(l); |
|
1402 |
|
uint64_t crc = kmod_module_dependency_symbol_get_crc(l); |
|
1403 |
|
int bindtype = kmod_module_dependency_symbol_get_bind(l); |
|
1404 |
|
struct symbol *sym = depmod_symbol_find(depmod, name); |
|
1405 |
|
uint8_t is_weak = bindtype == KMOD_SYMBOL_WEAK; |
|
1406 |
|
|
|
1407 |
|
if (sym == NULL) { |
|
1408 |
|
DBG("%s needs (%c) unknown symbol %s\n", |
|
1409 |
|
mod->path, bindtype, name); |
|
1410 |
|
if (cfg->print_unknown && !is_weak) |
|
1411 |
|
WRN("%s needs unknown symbol %s\n", |
|
1412 |
|
mod->path, name); |
|
1413 |
|
continue; |
|
1414 |
|
} |
|
1415 |
|
|
|
1416 |
|
if (cfg->check_symvers && sym->crc != crc && !is_weak) { |
|
1417 |
|
DBG("symbol %s (%#"PRIx64") module %s (%#"PRIx64")\n", |
|
1418 |
|
sym->name, sym->crc, mod->path, crc); |
|
1419 |
|
if (cfg->print_unknown) |
|
1420 |
|
WRN("%s disagrees about version of symbol %s\n", |
|
1421 |
|
mod->path, name); |
|
1422 |
|
} |
|
1423 |
|
|
|
1424 |
|
mod_add_dependency(mod, sym); |
|
1425 |
|
} |
|
1426 |
|
|
|
1427 |
|
return 0; |
|
1428 |
|
} |
|
1429 |
|
|
|
1430 |
|
static int depmod_load_dependencies(struct depmod *depmod) |
|
1431 |
|
{ |
|
1432 |
|
struct mod **itr, **itr_end; |
|
1433 |
|
|
|
1434 |
|
DBG("load dependencies (%zd modules, %u symbols)\n", |
|
1435 |
|
depmod->modules.count, hash_get_count(depmod->symbols)); |
|
1436 |
|
|
|
1437 |
|
itr = (struct mod **)depmod->modules.array; |
|
1438 |
|
itr_end = itr + depmod->modules.count; |
|
1439 |
|
for (; itr < itr_end; itr++) { |
|
1440 |
|
struct mod *mod = *itr; |
|
1441 |
|
|
|
1442 |
|
if (mod->dep_sym_list == NULL) { |
|
1443 |
|
DBG("ignoring %s: no dependency symbols\n", mod->path); |
|
1444 |
|
continue; |
|
1445 |
|
} |
|
1446 |
|
|
|
1447 |
|
depmod_load_module_dependencies(depmod, mod); |
|
1448 |
|
} |
|
1449 |
|
|
|
1450 |
|
DBG("loaded dependencies (%zd modules, %u symbols)\n", |
|
1451 |
|
depmod->modules.count, hash_get_count(depmod->symbols)); |
|
1452 |
|
|
|
1453 |
|
return 0; |
|
1454 |
|
} |
|
1455 |
|
|
|
1456 |
|
static int dep_cmp(const void *pa, const void *pb) |
|
1457 |
|
{ |
|
1458 |
|
const struct mod *a = *(const struct mod **)pa; |
|
1459 |
|
const struct mod *b = *(const struct mod **)pb; |
|
1460 |
|
return a->dep_sort_idx - b->dep_sort_idx; |
|
1461 |
|
} |
|
1462 |
|
|
|
1463 |
|
static void depmod_sort_dependencies(struct depmod *depmod) |
|
1464 |
|
{ |
|
1465 |
|
struct mod **itr, **itr_end; |
|
1466 |
|
itr = (struct mod **)depmod->modules.array; |
|
1467 |
|
itr_end = itr + depmod->modules.count; |
|
1468 |
|
for (; itr < itr_end; itr++) { |
|
1469 |
|
struct mod *m = *itr; |
|
1470 |
|
if (m->deps.count > 1) |
|
1471 |
|
array_sort(&m->deps, dep_cmp); |
|
1472 |
|
} |
|
1473 |
|
} |
|
1474 |
|
|
|
1475 |
|
struct vertex { |
|
1476 |
|
struct vertex *parent; |
|
1477 |
|
struct mod *mod; |
|
1478 |
|
}; |
|
1479 |
|
|
|
1480 |
|
static struct vertex *vertex_new(struct mod *mod, struct vertex *parent) |
|
1481 |
|
{ |
|
1482 |
|
struct vertex *v; |
|
1483 |
|
|
|
1484 |
|
v = malloc(sizeof(*v)); |
|
1485 |
|
if (v == NULL) |
|
1486 |
|
return NULL; |
|
1487 |
|
|
|
1488 |
|
v->parent = parent; |
|
1489 |
|
v->mod = mod; |
|
1490 |
|
return v; |
|
1491 |
|
} |
|
1492 |
|
|
|
1493 |
|
static void depmod_list_remove_data(struct kmod_list **list, void *data) |
|
1494 |
|
{ |
|
1495 |
|
struct kmod_list *l; |
|
1496 |
|
|
|
1497 |
|
l = __kmod_list_remove_data(*list, data); |
|
1498 |
|
*list = l; |
|
1499 |
|
} |
|
1500 |
|
|
|
1501 |
|
static void depmod_report_one_cycle(struct depmod *depmod, |
|
1502 |
|
struct vertex *vertex, |
|
1503 |
|
struct kmod_list **roots, |
|
1504 |
|
struct hash *loop_set) |
|
1505 |
|
{ |
|
1506 |
|
const char sep[] = " -> "; |
|
1507 |
|
size_t sz; |
|
1508 |
|
char *buf; |
|
1509 |
|
struct array reverse; |
|
1510 |
|
int i; |
|
1511 |
|
int n; |
|
1512 |
|
struct vertex *v; |
|
1513 |
|
|
|
1514 |
|
array_init(&reverse, 3); |
|
1515 |
|
|
|
1516 |
|
sz = 0; |
|
1517 |
|
for (v = vertex->parent, n = 0; |
|
1518 |
|
v != NULL; |
|
1519 |
|
v = v->parent, n++) { |
|
1520 |
|
|
|
1521 |
|
sz += v->mod->modnamesz - 1; |
|
1522 |
|
array_append(&reverse, v); |
|
1523 |
|
hash_add(loop_set, v->mod->modname, NULL); |
|
1524 |
|
} |
|
1525 |
|
sz += vertex->mod->modnamesz - 1; |
|
1526 |
|
|
|
1527 |
|
buf = malloc(sz + n * strlen(sep) + 1); |
|
1528 |
|
|
|
1529 |
|
sz = 0; |
|
1530 |
|
for (i = reverse.count - 1; i >= 0; i--) { |
|
1531 |
|
size_t len; |
|
1532 |
|
|
|
1533 |
|
v = reverse.array[i]; |
|
1534 |
|
|
|
1535 |
|
len = v->mod->modnamesz - 1; |
|
1536 |
|
memcpy(buf + sz, v->mod->modname, len); |
|
1537 |
|
sz += len; |
|
1538 |
|
strcpy(buf + sz, sep); |
|
1539 |
|
sz += strlen(sep); |
|
1540 |
|
|
|
1541 |
|
depmod_list_remove_data(roots, v->mod); |
|
1542 |
|
} |
|
1543 |
|
strcpy(buf + sz, vertex->mod->modname); |
|
1544 |
|
ERR("Cycle detected: %s\n", buf); |
|
1545 |
|
|
|
1546 |
|
free(buf); |
|
1547 |
|
array_free_array(&reverse); |
|
1548 |
|
} |
|
1549 |
|
|
|
1550 |
|
static int depmod_report_cycles_from_root(struct depmod *depmod, |
|
1551 |
|
struct mod *root_mod, |
|
1552 |
|
struct kmod_list **roots, |
|
1553 |
|
void **stack, |
|
1554 |
|
size_t stack_size, |
|
1555 |
|
struct hash *loop_set) |
|
1556 |
|
{ |
|
1557 |
|
struct kmod_list *free_list = NULL; /* struct vertex */ |
|
1558 |
|
struct kmod_list *l; |
|
1559 |
|
struct vertex *root; |
|
1560 |
|
struct vertex *vertex; |
|
1561 |
|
struct vertex *v; |
|
1562 |
|
struct mod *m; |
|
1563 |
|
struct mod **itr, **itr_end; |
|
1564 |
|
size_t is; |
|
1565 |
|
|
|
1566 |
|
root = vertex_new(root_mod, NULL); |
|
1567 |
|
if (root == NULL) { |
|
1568 |
|
ERR("No memory to report cycles\n"); |
|
1569 |
|
return -ENOMEM; |
|
1570 |
|
} |
|
1571 |
|
|
|
1572 |
|
l = __kmod_list_append(free_list, root); |
|
1573 |
|
if (l == NULL) { |
|
1574 |
|
ERR("No memory to report cycles\n"); |
|
1575 |
|
return -ENOMEM; |
|
1576 |
|
} |
|
1577 |
|
free_list = l; |
|
1578 |
|
|
|
1579 |
|
is = 0; |
|
1580 |
|
stack[is++] = (void *)root; |
|
1581 |
|
|
|
1582 |
|
while (is > 0) { |
|
1583 |
|
vertex = stack[--is]; |
|
1584 |
|
m = vertex->mod; |
|
1585 |
|
/* |
|
1586 |
|
* because of the topological sort we can start only |
|
1587 |
|
* from part of a loop or from a branch after a loop |
|
1588 |
|
*/ |
|
1589 |
|
if (m->visited && m == root->mod) { |
|
1590 |
|
depmod_report_one_cycle(depmod, vertex, |
|
1591 |
|
roots, loop_set); |
|
1592 |
|
continue; |
|
1593 |
|
} |
|
1594 |
|
|
|
1595 |
|
m->visited = true; |
|
1596 |
|
if (m->deps.count == 0) { |
|
1597 |
|
/* |
|
1598 |
|
* boundary condition: if there is more than one |
|
1599 |
|
* single node branch (not a loop), it is |
|
1600 |
|
* recognized as a loop by the code above: |
|
1601 |
|
* m->visited because more then one, |
|
1602 |
|
* m == root->mod since it is a single node. |
|
1603 |
|
* So, prevent deeping into the branch second |
|
1604 |
|
* time. |
|
1605 |
|
*/ |
|
1606 |
|
depmod_list_remove_data(roots, m); |
|
1607 |
|
|
|
1608 |
|
continue; |
|
1609 |
|
} |
|
1610 |
|
|
|
1611 |
|
itr = (struct mod **) m->deps.array; |
|
1612 |
|
itr_end = itr + m->deps.count; |
|
1613 |
|
for (; itr < itr_end; itr++) { |
|
1614 |
|
struct mod *dep = *itr; |
|
1615 |
|
v = vertex_new(dep, vertex); |
|
1616 |
|
if (v == NULL) { |
|
1617 |
|
ERR("No memory to report cycles\n"); |
|
1618 |
|
return -ENOMEM; |
|
1619 |
|
} |
|
1620 |
|
assert(is < stack_size); |
|
1621 |
|
stack[is++] = v; |
|
1622 |
|
|
|
1623 |
|
l = __kmod_list_append(free_list, v); |
|
1624 |
|
if (l == NULL) { |
|
1625 |
|
ERR("No memory to report cycles\n"); |
|
1626 |
|
return -ENOMEM; |
|
1627 |
|
} |
|
1628 |
|
free_list = l; |
|
1629 |
|
|
|
1630 |
|
} |
|
1631 |
|
} |
|
1632 |
|
while (free_list) { |
|
1633 |
|
v = free_list->data; |
|
1634 |
|
l = __kmod_list_remove(free_list); |
|
1635 |
|
free_list = l; |
|
1636 |
|
free(v); |
|
1637 |
|
} |
|
1638 |
|
|
|
1639 |
|
return 0; |
|
1640 |
|
} |
|
1641 |
|
|
|
1642 |
|
static void depmod_report_cycles(struct depmod *depmod, uint16_t n_mods, |
|
1643 |
|
uint16_t *users) |
|
1644 |
|
{ |
|
1645 |
|
int num_cyclic = 0; |
|
1646 |
|
struct kmod_list *roots = NULL; /* struct mod */ |
|
1647 |
|
struct kmod_list *l; |
|
1648 |
|
size_t n_r; /* local n_roots */ |
|
1649 |
|
int i; |
|
1650 |
|
int err; |
|
1651 |
|
void **stack = NULL; |
|
1652 |
|
struct mod *m; |
|
1653 |
|
struct mod *root; |
|
1654 |
|
struct hash *loop_set; |
|
1655 |
|
|
|
1656 |
|
for (i = 0, n_r = 0; i < n_mods; i++) { |
|
1657 |
|
if (users[i] <= 0) |
|
1658 |
|
continue; |
|
1659 |
|
m = depmod->modules.array[i]; |
|
1660 |
|
l = __kmod_list_append(roots, m); |
|
1661 |
|
if (l == NULL) { |
|
1662 |
|
ERR("No memory to report cycles\n"); |
|
1663 |
|
return; |
|
1664 |
|
} |
|
1665 |
|
roots = l; |
|
1666 |
|
n_r++; |
|
1667 |
|
} |
|
1668 |
|
|
|
1669 |
|
stack = malloc(n_r * sizeof(void *)); |
|
1670 |
|
if (stack == NULL) { |
|
1671 |
|
ERR("No memory to report cycles\n"); |
|
1672 |
|
return; |
|
1673 |
|
} |
|
1674 |
|
|
|
1675 |
|
loop_set = hash_new(16, NULL); |
|
1676 |
|
if (loop_set == NULL) { |
|
1677 |
|
ERR("No memory to report cycles\n"); |
|
1678 |
|
free(stack); |
|
1679 |
|
return; |
|
1680 |
|
} |
|
1681 |
|
|
|
1682 |
|
while (roots != NULL) { |
|
1683 |
|
root = roots->data; |
|
1684 |
|
l = __kmod_list_remove(roots); |
|
1685 |
|
roots = l; |
|
1686 |
|
err = depmod_report_cycles_from_root(depmod, |
|
1687 |
|
root, |
|
1688 |
|
&roots, |
|
1689 |
|
stack, n_r, loop_set); |
|
1690 |
|
if (err < 0) |
|
1691 |
|
goto err; |
|
1692 |
|
} |
|
1693 |
|
|
|
1694 |
|
num_cyclic = hash_get_count(loop_set); |
|
1695 |
|
ERR("Found %d modules in dependency cycles!\n", num_cyclic); |
|
1696 |
|
|
|
1697 |
|
err: |
|
1698 |
|
hash_free(loop_set); |
|
1699 |
|
free(stack); |
|
1700 |
|
} |
|
1701 |
|
|
|
1702 |
|
static int depmod_calculate_dependencies(struct depmod *depmod) |
|
1703 |
|
{ |
|
1704 |
|
const struct mod **itrm; |
|
1705 |
|
uint16_t *users, *roots, *sorted; |
|
1706 |
|
uint16_t i, n_roots = 0, n_sorted = 0, n_mods = depmod->modules.count; |
|
1707 |
|
int ret = 0; |
|
1708 |
|
|
|
1709 |
|
users = malloc(sizeof(uint16_t) * n_mods * 3); |
|
1710 |
|
if (users == NULL) |
|
1711 |
|
return -ENOMEM; |
|
1712 |
|
roots = users + n_mods; |
|
1713 |
|
sorted = roots + n_mods; |
|
1714 |
|
|
|
1715 |
|
DBG("calculate dependencies and ordering (%hu modules)\n", n_mods); |
|
1716 |
|
|
|
1717 |
|
assert(depmod->modules.count < UINT16_MAX); |
|
1718 |
|
|
|
1719 |
|
/* populate modules users (how many modules uses it) */ |
|
1720 |
|
itrm = (const struct mod **)depmod->modules.array; |
|
1721 |
|
for (i = 0; i < n_mods; i++, itrm++) { |
|
1722 |
|
const struct mod *m = *itrm; |
|
1723 |
|
users[i] = m->users; |
|
1724 |
|
if (users[i] == 0) { |
|
1725 |
|
roots[n_roots] = i; |
|
1726 |
|
n_roots++; |
|
1727 |
|
} |
|
1728 |
|
} |
|
1729 |
|
|
|
1730 |
|
/* topological sort (outputs modules without users first) */ |
|
1731 |
|
while (n_roots > 0) { |
|
1732 |
|
const struct mod **itr_dst, **itr_dst_end; |
|
1733 |
|
struct mod *src; |
|
1734 |
|
uint16_t src_idx = roots[--n_roots]; |
|
1735 |
|
|
|
1736 |
|
src = depmod->modules.array[src_idx]; |
|
1737 |
|
src->dep_sort_idx = n_sorted; |
|
1738 |
|
sorted[n_sorted] = src_idx; |
|
1739 |
|
n_sorted++; |
|
1740 |
|
|
|
1741 |
|
itr_dst = (const struct mod **)src->deps.array; |
|
1742 |
|
itr_dst_end = itr_dst + src->deps.count; |
|
1743 |
|
for (; itr_dst < itr_dst_end; itr_dst++) { |
|
1744 |
|
const struct mod *dst = *itr_dst; |
|
1745 |
|
uint16_t dst_idx = dst->idx; |
|
1746 |
|
assert(users[dst_idx] > 0); |
|
1747 |
|
users[dst_idx]--; |
|
1748 |
|
if (users[dst_idx] == 0) { |
|
1749 |
|
roots[n_roots] = dst_idx; |
|
1750 |
|
n_roots++; |
|
1751 |
|
} |
|
1752 |
|
} |
|
1753 |
|
} |
|
1754 |
|
|
|
1755 |
|
if (n_sorted < n_mods) { |
|
1756 |
|
depmod_report_cycles(depmod, n_mods, users); |
|
1757 |
|
ret = -EINVAL; |
|
1758 |
|
goto exit; |
|
1759 |
|
} |
|
1760 |
|
|
|
1761 |
|
depmod_sort_dependencies(depmod); |
|
1762 |
|
|
|
1763 |
|
DBG("calculated dependencies and ordering (%hu modules)\n", n_mods); |
|
1764 |
|
|
|
1765 |
|
exit: |
|
1766 |
|
free(users); |
|
1767 |
|
return ret; |
|
1768 |
|
} |
|
1769 |
|
|
|
1770 |
|
static int depmod_load(struct depmod *depmod) |
|
1771 |
|
{ |
|
1772 |
|
int err; |
|
1773 |
|
|
|
1774 |
|
err = depmod_load_modules(depmod); |
|
1775 |
|
if (err < 0) |
|
1776 |
|
return err; |
|
1777 |
|
|
|
1778 |
|
err = depmod_load_dependencies(depmod); |
|
1779 |
|
if (err < 0) |
|
1780 |
|
return err; |
|
1781 |
|
|
|
1782 |
|
err = depmod_calculate_dependencies(depmod); |
|
1783 |
|
if (err < 0) |
|
1784 |
|
return err; |
|
1785 |
|
|
|
1786 |
|
return 0; |
|
1787 |
|
} |
|
1788 |
|
|
|
1789 |
|
static size_t mod_count_all_dependencies(const struct mod *mod) |
|
1790 |
|
{ |
|
1791 |
|
size_t i, count = 0; |
|
1792 |
|
for (i = 0; i < mod->deps.count; i++) { |
|
1793 |
|
const struct mod *d = mod->deps.array[i]; |
|
1794 |
|
count += 1 + mod_count_all_dependencies(d); |
|
1795 |
|
} |
|
1796 |
|
return count; |
|
1797 |
|
} |
|
1798 |
|
|
|
1799 |
|
static int mod_fill_all_unique_dependencies(const struct mod *mod, const struct mod **deps, size_t n_deps, size_t *last) |
|
1800 |
|
{ |
|
1801 |
|
size_t i; |
|
1802 |
|
int err = 0; |
|
1803 |
|
for (i = 0; i < mod->deps.count; i++) { |
|
1804 |
|
const struct mod *d = mod->deps.array[i]; |
|
1805 |
|
size_t j; |
|
1806 |
|
uint8_t exists = 0; |
|
1807 |
|
|
|
1808 |
|
for (j = 0; j < *last; j++) { |
|
1809 |
|
if (deps[j] == d) { |
|
1810 |
|
exists = 1; |
|
1811 |
|
break; |
|
1812 |
|
} |
|
1813 |
|
} |
|
1814 |
|
|
|
1815 |
|
if (exists) |
|
1816 |
|
continue; |
|
1817 |
|
|
|
1818 |
|
if (*last >= n_deps) |
|
1819 |
|
return -ENOSPC; |
|
1820 |
|
deps[*last] = d; |
|
1821 |
|
(*last)++; |
|
1822 |
|
err = mod_fill_all_unique_dependencies(d, deps, n_deps, last); |
|
1823 |
|
if (err < 0) |
|
1824 |
|
break; |
|
1825 |
|
} |
|
1826 |
|
return err; |
|
1827 |
|
} |
|
1828 |
|
|
|
1829 |
|
static const struct mod **mod_get_all_sorted_dependencies(const struct mod *mod, size_t *n_deps) |
|
1830 |
|
{ |
|
1831 |
|
const struct mod **deps; |
|
1832 |
|
size_t last = 0; |
|
1833 |
|
|
|
1834 |
|
*n_deps = mod_count_all_dependencies(mod); |
|
1835 |
|
if (*n_deps == 0) |
|
1836 |
|
return NULL; |
|
1837 |
|
|
|
1838 |
|
deps = malloc(sizeof(struct mod *) * (*n_deps)); |
|
1839 |
|
if (deps == NULL) |
|
1840 |
|
return NULL; |
|
1841 |
|
|
|
1842 |
|
if (mod_fill_all_unique_dependencies(mod, deps, *n_deps, &last) < 0) { |
|
1843 |
|
free(deps); |
|
1844 |
|
return NULL; |
|
1845 |
|
} |
|
1846 |
|
|
|
1847 |
|
qsort(deps, last, sizeof(struct mod *), dep_cmp); |
|
1848 |
|
*n_deps = last; |
|
1849 |
|
return deps; |
|
1850 |
|
} |
|
1851 |
|
|
|
1852 |
|
static inline const char *mod_get_compressed_path(const struct mod *mod) |
|
1853 |
|
{ |
|
1854 |
|
if (mod->relpath != NULL) |
|
1855 |
|
return mod->relpath; |
|
1856 |
|
return mod->path; |
|
1857 |
|
} |
|
1858 |
|
|
|
1859 |
|
static int output_deps(struct depmod *depmod, FILE *out) |
|
1860 |
|
{ |
|
1861 |
|
size_t i; |
|
1862 |
|
|
|
1863 |
|
for (i = 0; i < depmod->modules.count; i++) { |
|
1864 |
|
const struct mod **deps, *mod = depmod->modules.array[i]; |
|
1865 |
|
const char *p = mod_get_compressed_path(mod); |
|
1866 |
|
size_t j, n_deps; |
|
1867 |
|
|
|
1868 |
|
fprintf(out, "%s:", p); |
|
1869 |
|
|
|
1870 |
|
if (mod->deps.count == 0) |
|
1871 |
|
goto end; |
|
1872 |
|
|
|
1873 |
|
deps = mod_get_all_sorted_dependencies(mod, &n_deps); |
|
1874 |
|
if (deps == NULL) { |
|
1875 |
|
ERR("could not get all sorted dependencies of %s\n", p); |
|
1876 |
|
goto end; |
|
1877 |
|
} |
|
1878 |
|
|
|
1879 |
|
for (j = 0; j < n_deps; j++) { |
|
1880 |
|
const struct mod *d = deps[j]; |
|
1881 |
|
fprintf(out, " %s", mod_get_compressed_path(d)); |
|
1882 |
|
} |
|
1883 |
|
free(deps); |
|
1884 |
|
end: |
|
1885 |
|
putc('\n', out); |
|
1886 |
|
} |
|
1887 |
|
|
|
1888 |
|
return 0; |
|
1889 |
|
} |
|
1890 |
|
|
|
1891 |
|
static int output_deps_bin(struct depmod *depmod, FILE *out) |
|
1892 |
|
{ |
|
1893 |
|
struct index_node *idx; |
|
1894 |
|
size_t i; |
|
1895 |
|
|
|
1896 |
|
if (out == stdout) |
|
1897 |
|
return 0; |
|
1898 |
|
|
|
1899 |
|
idx = index_create(); |
|
1900 |
|
if (idx == NULL) |
|
1901 |
|
return -ENOMEM; |
|
1902 |
|
|
|
1903 |
|
for (i = 0; i < depmod->modules.count; i++) { |
|
1904 |
|
const struct mod **deps, *mod = depmod->modules.array[i]; |
|
1905 |
|
const char *p = mod_get_compressed_path(mod); |
|
1906 |
|
char *line; |
|
1907 |
|
size_t j, n_deps, linepos, linelen, slen; |
|
1908 |
|
int duplicate; |
|
1909 |
|
|
|
1910 |
|
deps = mod_get_all_sorted_dependencies(mod, &n_deps); |
|
1911 |
|
if (deps == NULL && n_deps > 0) { |
|
1912 |
|
ERR("could not get all sorted dependencies of %s\n", p); |
|
1913 |
|
continue; |
|
1914 |
|
} |
|
1915 |
|
|
|
1916 |
|
linelen = strlen(p) + 1; |
|
1917 |
|
for (j = 0; j < n_deps; j++) { |
|
1918 |
|
const struct mod *d = deps[j]; |
|
1919 |
|
linelen += 1 + strlen(mod_get_compressed_path(d)); |
|
1920 |
|
} |
|
1921 |
|
|
|
1922 |
|
line = malloc(linelen + 1); |
|
1923 |
|
if (line == NULL) { |
|
1924 |
|
free(deps); |
|
1925 |
|
ERR("modules.deps.bin: out of memory\n"); |
|
1926 |
|
continue; |
|
1927 |
|
} |
|
1928 |
|
|
|
1929 |
|
linepos = 0; |
|
1930 |
|
slen = strlen(p); |
|
1931 |
|
memcpy(line + linepos, p, slen); |
|
1932 |
|
linepos += slen; |
|
1933 |
|
line[linepos] = ':'; |
|
1934 |
|
linepos++; |
|
1935 |
|
|
|
1936 |
|
for (j = 0; j < n_deps; j++) { |
|
1937 |
|
const struct mod *d = deps[j]; |
|
1938 |
|
const char *dp; |
|
1939 |
|
|
|
1940 |
|
line[linepos] = ' '; |
|
1941 |
|
linepos++; |
|
1942 |
|
|
|
1943 |
|
dp = mod_get_compressed_path(d); |
|
1944 |
|
slen = strlen(dp); |
|
1945 |
|
memcpy(line + linepos, dp, slen); |
|
1946 |
|
linepos += slen; |
|
1947 |
|
} |
|
1948 |
|
line[linepos] = '\0'; |
|
1949 |
|
|
|
1950 |
|
duplicate = index_insert(idx, mod->modname, line, mod->idx); |
|
1951 |
|
if (duplicate && depmod->cfg->warn_dups) |
|
1952 |
|
WRN("duplicate module deps:\n%s\n", line); |
|
1953 |
|
free(line); |
|
1954 |
|
free(deps); |
|
1955 |
|
} |
|
1956 |
|
|
|
1957 |
|
index_write(idx, out); |
|
1958 |
|
index_destroy(idx); |
|
1959 |
|
|
|
1960 |
|
return 0; |
|
1961 |
|
} |
|
1962 |
|
|
|
1963 |
|
static int output_aliases(struct depmod *depmod, FILE *out) |
|
1964 |
|
{ |
|
1965 |
|
size_t i; |
|
1966 |
|
|
|
1967 |
|
fputs("# Aliases extracted from modules themselves.\n", out); |
|
1968 |
|
|
|
1969 |
|
for (i = 0; i < depmod->modules.count; i++) { |
|
1970 |
|
const struct mod *mod = depmod->modules.array[i]; |
|
1971 |
|
struct kmod_list *l; |
|
1972 |
|
|
|
1973 |
|
kmod_list_foreach(l, mod->info_list) { |
|
1974 |
|
const char *key = kmod_module_info_get_key(l); |
|
1975 |
|
const char *value = kmod_module_info_get_value(l); |
|
1976 |
|
|
|
1977 |
|
if (!streq(key, "alias")) |
|
1978 |
|
continue; |
|
1979 |
|
|
|
1980 |
|
fprintf(out, "alias %s %s\n", value, mod->modname); |
|
1981 |
|
} |
|
1982 |
|
} |
|
1983 |
|
|
|
1984 |
|
return 0; |
|
1985 |
|
} |
|
1986 |
|
|
|
1987 |
|
static int output_aliases_bin(struct depmod *depmod, FILE *out) |
|
1988 |
|
{ |
|
1989 |
|
struct index_node *idx; |
|
1990 |
|
size_t i; |
|
1991 |
|
|
|
1992 |
|
if (out == stdout) |
|
1993 |
|
return 0; |
|
1994 |
|
|
|
1995 |
|
idx = index_create(); |
|
1996 |
|
if (idx == NULL) |
|
1997 |
|
return -ENOMEM; |
|
1998 |
|
|
|
1999 |
|
for (i = 0; i < depmod->modules.count; i++) { |
|
2000 |
|
const struct mod *mod = depmod->modules.array[i]; |
|
2001 |
|
struct kmod_list *l; |
|
2002 |
|
|
|
2003 |
|
kmod_list_foreach(l, mod->info_list) { |
|
2004 |
|
const char *key = kmod_module_info_get_key(l); |
|
2005 |
|
const char *value = kmod_module_info_get_value(l); |
|
2006 |
|
char buf[PATH_MAX]; |
|
2007 |
|
const char *alias; |
|
2008 |
|
int duplicate; |
|
2009 |
|
|
|
2010 |
|
if (!streq(key, "alias")) |
|
2011 |
|
continue; |
|
2012 |
|
|
|
2013 |
|
if (alias_normalize(value, buf, NULL) < 0) { |
|
2014 |
|
WRN("Unmatched bracket in %s\n", value); |
|
2015 |
|
continue; |
|
2016 |
|
} |
|
2017 |
|
alias = buf; |
|
2018 |
|
|
|
2019 |
|
duplicate = index_insert(idx, alias, mod->modname, |
|
2020 |
|
mod->idx); |
|
2021 |
|
if (duplicate && depmod->cfg->warn_dups) |
|
2022 |
|
WRN("duplicate module alias:\n%s %s\n", |
|
2023 |
|
alias, mod->modname); |
|
2024 |
|
} |
|
2025 |
|
} |
|
2026 |
|
|
|
2027 |
|
index_write(idx, out); |
|
2028 |
|
index_destroy(idx); |
|
2029 |
|
|
|
2030 |
|
return 0; |
|
2031 |
|
} |
|
2032 |
|
|
|
2033 |
|
static int output_softdeps(struct depmod *depmod, FILE *out) |
|
2034 |
|
{ |
|
2035 |
|
size_t i; |
|
2036 |
|
|
|
2037 |
|
fputs("# Soft dependencies extracted from modules themselves.\n", out); |
|
2038 |
|
|
|
2039 |
|
for (i = 0; i < depmod->modules.count; i++) { |
|
2040 |
|
const struct mod *mod = depmod->modules.array[i]; |
|
2041 |
|
struct kmod_list *l; |
|
2042 |
|
|
|
2043 |
|
kmod_list_foreach(l, mod->info_list) { |
|
2044 |
|
const char *key = kmod_module_info_get_key(l); |
|
2045 |
|
const char *value = kmod_module_info_get_value(l); |
|
2046 |
|
|
|
2047 |
|
if (!streq(key, "softdep")) |
|
2048 |
|
continue; |
|
2049 |
|
|
|
2050 |
|
fprintf(out, "softdep %s %s\n", mod->modname, value); |
|
2051 |
|
} |
|
2052 |
|
} |
|
2053 |
|
|
|
2054 |
|
return 0; |
|
2055 |
|
} |
|
2056 |
|
|
|
2057 |
|
static int output_symbols(struct depmod *depmod, FILE *out) |
|
2058 |
|
{ |
|
2059 |
|
struct hash_iter iter; |
|
2060 |
|
const void *v; |
|
2061 |
|
|
|
2062 |
|
fputs("# Aliases for symbols, used by symbol_request().\n", out); |
|
2063 |
|
|
|
2064 |
|
hash_iter_init(depmod->symbols, &iter); |
|
2065 |
|
|
|
2066 |
|
while (hash_iter_next(&iter, NULL, &v)) { |
|
2067 |
|
const struct symbol *sym = v; |
|
2068 |
|
if (sym->owner == NULL) |
|
2069 |
|
continue; |
|
2070 |
|
|
|
2071 |
|
fprintf(out, "alias symbol:%s %s\n", |
|
2072 |
|
sym->name, sym->owner->modname); |
|
2073 |
|
} |
|
2074 |
|
|
|
2075 |
|
return 0; |
|
2076 |
|
} |
|
2077 |
|
|
|
2078 |
|
static int output_symbols_bin(struct depmod *depmod, FILE *out) |
|
2079 |
|
{ |
|
2080 |
|
struct index_node *idx; |
|
2081 |
|
char alias[1024]; |
|
2082 |
|
struct scratchbuf salias = SCRATCHBUF_INITIALIZER(alias); |
|
2083 |
|
size_t baselen = sizeof("symbol:") - 1; |
|
2084 |
|
struct hash_iter iter; |
|
2085 |
|
const void *v; |
|
2086 |
|
int ret = 0; |
|
2087 |
|
|
|
2088 |
|
if (out == stdout) { |
|
2089 |
|
scratchbuf_release(&salias); |
|
2090 |
|
return 0; |
|
2091 |
|
} |
|
2092 |
|
|
|
2093 |
|
idx = index_create(); |
|
2094 |
|
if (idx == NULL) { |
|
2095 |
|
scratchbuf_release(&salias); |
|
2096 |
|
return -ENOMEM; |
|
2097 |
|
} |
|
2098 |
|
|
|
2099 |
|
memcpy(alias, "symbol:", baselen); |
|
2100 |
|
|
|
2101 |
|
hash_iter_init(depmod->symbols, &iter); |
|
2102 |
|
|
|
2103 |
|
while (hash_iter_next(&iter, NULL, &v)) { |
|
2104 |
|
int duplicate; |
|
2105 |
|
const struct symbol *sym = v; |
|
2106 |
|
size_t len; |
|
2107 |
|
|
|
2108 |
|
if (sym->owner == NULL) |
|
2109 |
|
continue; |
|
2110 |
|
|
|
2111 |
|
len = strlen(sym->name); |
|
2112 |
|
|
|
2113 |
|
if (scratchbuf_alloc(&salias, baselen + len + 1) < 0) { |
|
2114 |
|
ret = -ENOMEM; |
|
2115 |
|
goto err_scratchbuf; |
|
2116 |
|
} |
|
2117 |
|
memcpy(scratchbuf_str(&salias) + baselen, sym->name, len + 1); |
|
2118 |
|
duplicate = index_insert(idx, alias, sym->owner->modname, |
|
2119 |
|
sym->owner->idx); |
|
2120 |
|
|
|
2121 |
|
if (duplicate && depmod->cfg->warn_dups) |
|
2122 |
|
WRN("duplicate module syms:\n%s %s\n", |
|
2123 |
|
alias, sym->owner->modname); |
|
2124 |
|
} |
|
2125 |
|
|
|
2126 |
|
index_write(idx, out); |
|
2127 |
|
|
|
2128 |
|
err_scratchbuf: |
|
2129 |
|
index_destroy(idx); |
|
2130 |
|
|
|
2131 |
|
if (ret < 0) |
|
2132 |
|
ERR("output symbols: %s\n", strerror(-ret)); |
|
2133 |
|
scratchbuf_release(&salias); |
|
2134 |
|
return ret; |
|
2135 |
|
} |
|
2136 |
|
|
|
2137 |
|
static int output_builtin_bin(struct depmod *depmod, FILE *out) |
|
2138 |
|
{ |
|
2139 |
|
FILE *in; |
|
2140 |
|
struct index_node *idx; |
|
2141 |
|
char infile[PATH_MAX], line[PATH_MAX], modname[PATH_MAX]; |
|
2142 |
|
|
|
2143 |
|
if (out == stdout) |
|
2144 |
|
return 0; |
|
2145 |
|
|
|
2146 |
|
snprintf(infile, sizeof(infile), "%s/modules.builtin", |
|
2147 |
|
depmod->cfg->dirname); |
|
2148 |
|
in = fopen(infile, "r"); |
|
2149 |
|
if (in == NULL) { |
|
2150 |
|
WRN("could not open %s: %m\n", infile); |
|
2151 |
|
return 0; |
|
2152 |
|
} |
|
2153 |
|
|
|
2154 |
|
idx = index_create(); |
|
2155 |
|
if (idx == NULL) { |
|
2156 |
|
fclose(in); |
|
2157 |
|
return -ENOMEM; |
|
2158 |
|
} |
|
2159 |
|
|
|
2160 |
|
while (fgets(line, sizeof(line), in) != NULL) { |
|
2161 |
|
if (!isalpha(line[0])) { |
|
2162 |
|
ERR("Invalid modules.builtin line: %s\n", line); |
|
2163 |
|
continue; |
|
2164 |
|
} |
|
2165 |
|
|
|
2166 |
|
path_to_modname(line, modname, NULL); |
|
2167 |
|
index_insert(idx, modname, "", 0); |
|
2168 |
|
} |
|
2169 |
|
|
|
2170 |
|
index_write(idx, out); |
|
2171 |
|
index_destroy(idx); |
|
2172 |
|
fclose(in); |
|
2173 |
|
|
|
2174 |
|
return 0; |
|
2175 |
|
} |
|
2176 |
|
|
|
2177 |
|
static int output_devname(struct depmod *depmod, FILE *out) |
|
2178 |
|
{ |
|
2179 |
|
size_t i; |
|
2180 |
|
bool empty = true; |
|
2181 |
|
|
|
2182 |
|
for (i = 0; i < depmod->modules.count; i++) { |
|
2183 |
|
const struct mod *mod = depmod->modules.array[i]; |
|
2184 |
|
struct kmod_list *l; |
|
2185 |
|
const char *devname = NULL; |
|
2186 |
|
char type = '\0'; |
|
2187 |
|
unsigned int major = 0, minor = 0; |
|
2188 |
|
|
|
2189 |
|
kmod_list_foreach(l, mod->info_list) { |
|
2190 |
|
const char *key = kmod_module_info_get_key(l); |
|
2191 |
|
const char *value = kmod_module_info_get_value(l); |
|
2192 |
|
unsigned int maj, min; |
|
2193 |
|
|
|
2194 |
|
if (!streq(key, "alias")) |
|
2195 |
|
continue; |
|
2196 |
|
|
|
2197 |
|
if (strstartswith(value, "devname:")) |
|
2198 |
|
devname = value + sizeof("devname:") - 1; |
|
2199 |
|
else if (sscanf(value, "char-major-%u-%u", |
|
2200 |
|
&maj, &min) == 2) { |
|
2201 |
|
type = 'c'; |
|
2202 |
|
major = maj; |
|
2203 |
|
minor = min; |
|
2204 |
|
} else if (sscanf(value, "block-major-%u-%u", |
|
2205 |
|
&maj, &min) == 2) { |
|
2206 |
|
type = 'b'; |
|
2207 |
|
major = maj; |
|
2208 |
|
minor = min; |
|
2209 |
|
} |
|
2210 |
|
|
|
2211 |
|
if (type != '\0' && devname != NULL) |
|
2212 |
|
break; |
|
2213 |
|
} |
|
2214 |
|
|
|
2215 |
|
if (devname != NULL) { |
|
2216 |
|
if (type != '\0') { |
|
2217 |
|
if (empty) { |
|
2218 |
|
fputs("# Device nodes to trigger on-demand module loading.\n", |
|
2219 |
|
out); |
|
2220 |
|
empty = false; |
|
2221 |
|
} |
|
2222 |
|
fprintf(out, "%s %s %c%u:%u\n", mod->modname, |
|
2223 |
|
devname, type, major, minor); |
|
2224 |
|
} else |
|
2225 |
|
ERR("Module '%s' has devname (%s) but " |
|
2226 |
|
"lacks major and minor information. " |
|
2227 |
|
"Ignoring.\n", mod->modname, devname); |
|
2228 |
|
} |
|
2229 |
|
} |
|
2230 |
|
|
|
2231 |
|
return 0; |
|
2232 |
|
} |
|
2233 |
|
|
|
2234 |
|
static int depmod_output(struct depmod *depmod, FILE *out) |
|
2235 |
|
{ |
|
2236 |
|
static const struct depfile { |
|
2237 |
|
const char *name; |
|
2238 |
|
int (*cb)(struct depmod *depmod, FILE *out); |
|
2239 |
|
} *itr, depfiles[] = { |
|
2240 |
|
{ "modules.dep", output_deps }, |
|
2241 |
|
{ "modules.dep.bin", output_deps_bin }, |
|
2242 |
|
{ "modules.alias", output_aliases }, |
|
2243 |
|
{ "modules.alias.bin", output_aliases_bin }, |
|
2244 |
|
{ "modules.softdep", output_softdeps }, |
|
2245 |
|
{ "modules.symbols", output_symbols }, |
|
2246 |
|
{ "modules.symbols.bin", output_symbols_bin }, |
|
2247 |
|
{ "modules.builtin.bin", output_builtin_bin }, |
|
2248 |
|
{ "modules.devname", output_devname }, |
|
2249 |
|
{ } |
|
2250 |
|
}; |
|
2251 |
|
const char *dname = depmod->cfg->dirname; |
|
2252 |
|
int dfd, err = 0; |
|
2253 |
|
|
|
2254 |
|
if (out != NULL) |
|
2255 |
|
dfd = -1; |
|
2256 |
|
else { |
|
2257 |
|
dfd = open(dname, O_RDONLY); |
|
2258 |
|
if (dfd < 0) { |
|
2259 |
|
err = -errno; |
|
2260 |
|
CRIT("could not open directory %s: %m\n", dname); |
|
2261 |
|
return err; |
|
2262 |
|
} |
|
2263 |
|
} |
|
2264 |
|
|
|
2265 |
|
for (itr = depfiles; itr->name != NULL; itr++) { |
|
2266 |
|
FILE *fp = out; |
|
2267 |
|
char tmp[NAME_MAX] = ""; |
|
2268 |
|
int r, ferr; |
|
2269 |
|
|
|
2270 |
|
if (fp == NULL) { |
|
2271 |
|
int flags = O_CREAT | O_TRUNC | O_WRONLY; |
|
2272 |
|
int mode = 0644; |
|
2273 |
|
int fd; |
|
2274 |
|
|
|
2275 |
|
snprintf(tmp, sizeof(tmp), "%s.tmp", itr->name); |
|
2276 |
|
fd = openat(dfd, tmp, flags, mode); |
|
2277 |
|
if (fd < 0) { |
|
2278 |
|
ERR("openat(%s, %s, %o, %o): %m\n", |
|
2279 |
|
dname, tmp, flags, mode); |
|
2280 |
|
continue; |
|
2281 |
|
} |
|
2282 |
|
fp = fdopen(fd, "wb"); |
|
2283 |
|
if (fp == NULL) { |
|
2284 |
|
ERR("fdopen(%d=%s/%s): %m\n", fd, dname, tmp); |
|
2285 |
|
close(fd); |
|
2286 |
|
continue; |
|
2287 |
|
} |
|
2288 |
|
} |
|
2289 |
|
|
|
2290 |
|
r = itr->cb(depmod, fp); |
|
2291 |
|
if (fp == out) |
|
2292 |
|
continue; |
|
2293 |
|
|
|
2294 |
|
ferr = ferror(fp) | fclose(fp); |
|
2295 |
|
|
|
2296 |
|
if (r < 0) { |
|
2297 |
|
if (unlinkat(dfd, tmp, 0) != 0) |
|
2298 |
|
ERR("unlinkat(%s, %s): %m\n", dname, tmp); |
|
2299 |
|
|
|
2300 |
|
ERR("Could not write index '%s': %s\n", itr->name, |
|
2301 |
|
strerror(-r)); |
|
2302 |
|
err = -errno; |
|
2303 |
|
break; |
|
2304 |
|
} |
|
2305 |
|
|
|
2306 |
|
unlinkat(dfd, itr->name, 0); |
|
2307 |
|
if (renameat(dfd, tmp, dfd, itr->name) != 0) { |
|
2308 |
|
err = -errno; |
|
2309 |
|
CRIT("renameat(%s, %s, %s, %s): %m\n", |
|
2310 |
|
dname, tmp, dname, itr->name); |
|
2311 |
|
break; |
|
2312 |
|
} |
|
2313 |
|
|
|
2314 |
|
if (ferr) { |
|
2315 |
|
err = -ENOSPC; |
|
2316 |
|
ERR("Could not create index '%s'. Output is truncated: %s\n", |
|
2317 |
|
itr->name, strerror(-err)); |
|
2318 |
|
break; |
|
2319 |
|
} |
|
2320 |
|
} |
|
2321 |
|
|
|
2322 |
|
if (dfd >= 0) |
|
2323 |
|
close(dfd); |
|
2324 |
|
|
|
2325 |
|
return err; |
|
2326 |
|
} |
|
2327 |
|
|
|
2328 |
|
static void depmod_add_fake_syms(struct depmod *depmod) |
|
2329 |
|
{ |
|
2330 |
|
/* __this_module is magic inserted by kernel loader. */ |
|
2331 |
|
depmod_symbol_add(depmod, "__this_module", true, 0, NULL); |
|
2332 |
|
/* On S390, this is faked up too */ |
|
2333 |
|
depmod_symbol_add(depmod, "_GLOBAL_OFFSET_TABLE_", true, 0, NULL); |
|
2334 |
|
/* On PowerPC64 ABIv2, .TOC. is more or less _GLOBAL_OFFSET_TABLE_ */ |
|
2335 |
|
depmod_symbol_add(depmod, "TOC.", true, 0, NULL); |
|
2336 |
|
} |
|
2337 |
|
|
|
2338 |
|
static int depmod_load_symvers(struct depmod *depmod, const char *filename) |
|
2339 |
|
{ |
|
2340 |
|
char line[10240]; |
|
2341 |
|
FILE *fp; |
|
2342 |
|
unsigned int linenum = 0; |
|
2343 |
|
|
|
2344 |
|
fp = fopen(filename, "r"); |
|
2345 |
|
if (fp == NULL) { |
|
2346 |
|
int err = -errno; |
|
2347 |
|
DBG("load symvers: %s: %m\n", filename); |
|
2348 |
|
return err; |
|
2349 |
|
} |
|
2350 |
|
DBG("load symvers: %s\n", filename); |
|
2351 |
|
|
|
2352 |
|
/* eg. "0xb352177e\tfind_first_bit\tvmlinux\tEXPORT_SYMBOL" */ |
|
2353 |
|
while (fgets(line, sizeof(line), fp) != NULL) { |
|
2354 |
|
const char *ver, *sym, *where; |
|
2355 |
|
char *verend; |
|
2356 |
|
uint64_t crc; |
|
2357 |
|
|
|
2358 |
|
linenum++; |
|
2359 |
|
|
|
2360 |
|
ver = strtok(line, " \t"); |
|
2361 |
|
sym = strtok(NULL, " \t"); |
|
2362 |
|
where = strtok(NULL, " \t"); |
|
2363 |
|
if (!ver || !sym || !where) |
|
2364 |
|
continue; |
|
2365 |
|
|
|
2366 |
|
if (!streq(where, "vmlinux")) |
|
2367 |
|
continue; |
|
2368 |
|
|
|
2369 |
|
crc = strtoull(ver, &verend, 16); |
|
2370 |
|
if (verend[0] != '\0') { |
|
2371 |
|
ERR("%s:%u Invalid symbol version %s: %m\n", |
|
2372 |
|
filename, linenum, ver); |
|
2373 |
|
continue; |
|
2374 |
|
} |
|
2375 |
|
|
|
2376 |
|
depmod_symbol_add(depmod, sym, false, crc, NULL); |
|
2377 |
|
} |
|
2378 |
|
depmod_add_fake_syms(depmod); |
|
2379 |
|
|
|
2380 |
|
DBG("loaded symvers: %s\n", filename); |
|
2381 |
|
|
|
2382 |
|
fclose(fp); |
|
2383 |
|
return 0; |
|
2384 |
|
} |
|
2385 |
|
|
|
2386 |
|
static int depmod_load_system_map(struct depmod *depmod, const char *filename) |
|
2387 |
|
{ |
|
2388 |
|
const char ksymstr[] = "__ksymtab_"; |
|
2389 |
|
const size_t ksymstr_len = sizeof(ksymstr) - 1; |
|
2390 |
|
char line[10240]; |
|
2391 |
|
FILE *fp; |
|
2392 |
|
unsigned int linenum = 0; |
|
2393 |
|
|
|
2394 |
|
fp = fopen(filename, "r"); |
|
2395 |
|
if (fp == NULL) { |
|
2396 |
|
int err = -errno; |
|
2397 |
|
DBG("load System.map: %s: %m\n", filename); |
|
2398 |
|
return err; |
|
2399 |
|
} |
|
2400 |
|
DBG("load System.map: %s\n", filename); |
|
2401 |
|
|
|
2402 |
|
/* eg. c0294200 R __ksymtab_devfs_alloc_devnum */ |
|
2403 |
|
while (fgets(line, sizeof(line), fp) != NULL) { |
|
2404 |
|
char *p, *end; |
|
2405 |
|
|
|
2406 |
|
linenum++; |
|
2407 |
|
|
|
2408 |
|
p = strchr(line, ' '); |
|
2409 |
|
if (p == NULL) |
|
2410 |
|
goto invalid_syntax; |
|
2411 |
|
p++; |
|
2412 |
|
p = strchr(p, ' '); |
|
2413 |
|
if (p == NULL) |
|
2414 |
|
goto invalid_syntax; |
|
2415 |
|
p++; |
|
2416 |
|
|
|
2417 |
|
/* skip prefix */ |
|
2418 |
|
if (p[0] == depmod->cfg->sym_prefix) |
|
2419 |
|
p++; |
|
2420 |
|
|
|
2421 |
|
/* Covers gpl-only and normal symbols. */ |
|
2422 |
|
if (strncmp(p, ksymstr, ksymstr_len) != 0) |
|
2423 |
|
continue; |
|
2424 |
|
|
|
2425 |
|
end = strchr(p, '\n'); |
|
2426 |
|
if (end != NULL) |
|
2427 |
|
*end = '\0'; |
|
2428 |
|
|
|
2429 |
|
depmod_symbol_add(depmod, p + ksymstr_len, true, 0, NULL); |
|
2430 |
|
continue; |
|
2431 |
|
|
|
2432 |
|
invalid_syntax: |
|
2433 |
|
ERR("%s:%u: invalid line: %s\n", filename, linenum, line); |
|
2434 |
|
} |
|
2435 |
|
depmod_add_fake_syms(depmod); |
|
2436 |
|
|
|
2437 |
|
DBG("loaded System.map: %s\n", filename); |
|
2438 |
|
|
|
2439 |
|
fclose(fp); |
|
2440 |
|
return 0; |
|
2441 |
|
} |
|
2442 |
|
|
|
2443 |
|
|
|
2444 |
|
static int depfile_up_to_date_dir(DIR *d, time_t mtime, size_t baselen, char *path) |
|
2445 |
|
{ |
|
2446 |
|
struct dirent *de; |
|
2447 |
|
int err = 1, dfd = dirfd(d); |
|
2448 |
|
|
|
2449 |
|
while ((de = readdir(d)) != NULL) { |
|
2450 |
|
const char *name = de->d_name; |
|
2451 |
|
size_t namelen; |
|
2452 |
|
struct stat st; |
|
2453 |
|
|
|
2454 |
|
if (name[0] == '.' && (name[1] == '\0' || |
|
2455 |
|
(name[1] == '.' && name[2] == '\0'))) |
|
2456 |
|
continue; |
|
2457 |
|
if (streq(name, "build") || streq(name, "source")) |
|
2458 |
|
continue; |
|
2459 |
|
namelen = strlen(name); |
|
2460 |
|
if (baselen + namelen + 2 >= PATH_MAX) { |
|
2461 |
|
path[baselen] = '\0'; |
|
2462 |
|
ERR("path is too long %s%s\n", path, name); |
|
2463 |
|
continue; |
|
2464 |
|
} |
|
2465 |
|
|
|
2466 |
|
if (fstatat(dfd, name, &st, 0) < 0) { |
|
2467 |
|
ERR("fstatat(%d, %s): %m\n", dfd, name); |
|
2468 |
|
continue; |
|
2469 |
|
} |
|
2470 |
|
|
|
2471 |
|
if (S_ISDIR(st.st_mode)) { |
|
2472 |
|
int fd; |
|
2473 |
|
DIR *subdir; |
|
2474 |
|
memcpy(path + baselen, name, namelen + 1); |
|
2475 |
|
if (baselen + namelen + 2 + NAME_MAX >= PATH_MAX) { |
|
2476 |
|
ERR("directory path is too long %s\n", path); |
|
2477 |
|
continue; |
|
2478 |
|
} |
|
2479 |
|
fd = openat(dfd, name, O_RDONLY); |
|
2480 |
|
if (fd < 0) { |
|
2481 |
|
ERR("openat(%d, %s, O_RDONLY): %m\n", |
|
2482 |
|
dfd, name); |
|
2483 |
|
continue; |
|
2484 |
|
} |
|
2485 |
|
subdir = fdopendir(fd); |
|
2486 |
|
if (subdir == NULL) { |
|
2487 |
|
ERR("fdopendir(%d): %m\n", fd); |
|
2488 |
|
close(fd); |
|
2489 |
|
continue; |
|
2490 |
|
} |
|
2491 |
|
path[baselen + namelen] = '/'; |
|
2492 |
|
path[baselen + namelen + 1] = '\0'; |
|
2493 |
|
err = depfile_up_to_date_dir(subdir, mtime, |
|
2494 |
|
baselen + namelen + 1, |
|
2495 |
|
path); |
|
2496 |
|
closedir(subdir); |
|
2497 |
|
} else if (S_ISREG(st.st_mode)) { |
|
2498 |
|
if (!path_ends_with_kmod_ext(name, namelen)) |
|
2499 |
|
continue; |
|
2500 |
|
|
|
2501 |
|
memcpy(path + baselen, name, namelen + 1); |
|
2502 |
|
err = st.st_mtime <= mtime; |
|
2503 |
|
if (err == 0) { |
|
2504 |
|
DBG("%s %"PRIu64" is newer than %"PRIu64"\n", |
|
2505 |
|
path, (uint64_t)st.st_mtime, |
|
2506 |
|
(uint64_t)mtime); |
|
2507 |
|
} |
|
2508 |
|
} else { |
|
2509 |
|
ERR("unsupported file type %s: %o\n", |
|
2510 |
|
path, st.st_mode & S_IFMT); |
|
2511 |
|
continue; |
|
2512 |
|
} |
|
2513 |
|
|
|
2514 |
|
if (err == 0) |
|
2515 |
|
break; /* outdated! */ |
|
2516 |
|
else if (err < 0) { |
|
2517 |
|
path[baselen + namelen] = '\0'; |
|
2518 |
|
ERR("failed %s: %s\n", path, strerror(-err)); |
|
2519 |
|
err = 1; /* ignore errors */ |
|
2520 |
|
} |
|
2521 |
|
} |
|
2522 |
|
|
|
2523 |
|
return err; |
|
2524 |
|
} |
|
2525 |
|
|
|
2526 |
|
/* uptodate: 1, outdated: 0, errors < 0 */ |
|
2527 |
|
static int depfile_up_to_date(const char *dirname) |
|
2528 |
|
{ |
|
2529 |
|
char path[PATH_MAX]; |
|
2530 |
|
DIR *d = opendir(dirname); |
|
2531 |
|
struct stat st; |
|
2532 |
|
size_t baselen; |
|
2533 |
|
int err; |
|
2534 |
|
if (d == NULL) { |
|
2535 |
|
err = -errno; |
|
2536 |
|
ERR("could not open directory %s: %m\n", dirname); |
|
2537 |
|
return err; |
|
2538 |
|
} |
|
2539 |
|
|
|
2540 |
|
if (fstatat(dirfd(d), "modules.dep", &st, 0) != 0) { |
|
2541 |
|
err = -errno; |
|
2542 |
|
ERR("could not fstatat(%s, modules.dep): %m\n", dirname); |
|
2543 |
|
closedir(d); |
|
2544 |
|
return err; |
|
2545 |
|
} |
|
2546 |
|
|
|
2547 |
|
baselen = strlen(dirname); |
|
2548 |
|
memcpy(path, dirname, baselen); |
|
2549 |
|
path[baselen] = '/'; |
|
2550 |
|
baselen++; |
|
2551 |
|
path[baselen] = '\0'; |
|
2552 |
|
|
|
2553 |
|
err = depfile_up_to_date_dir(d, st.st_mtime, baselen, path); |
|
2554 |
|
closedir(d); |
|
2555 |
|
return err; |
|
2556 |
|
} |
|
2557 |
|
|
|
2558 |
|
static int is_version_number(const char *version) |
|
2559 |
|
{ |
|
2560 |
|
unsigned int d1, d2; |
|
2561 |
|
return (sscanf(version, "%u.%u", &d1, &d2) == 2); |
|
2562 |
|
} |
|
2563 |
|
|
|
2564 |
|
static int do_depmod(int argc, char *argv[]) |
|
2565 |
|
{ |
|
2566 |
|
FILE *out = NULL; |
|
2567 |
|
int err = 0, all = 0, maybe_all = 0, n_config_paths = 0; |
|
2568 |
|
char *root = NULL; |
|
2569 |
|
const char **config_paths = NULL; |
|
2570 |
|
const char *system_map = NULL; |
|
2571 |
|
const char *module_symvers = NULL; |
|
2572 |
|
const char *null_kmod_config = NULL; |
|
2573 |
|
struct utsname un; |
|
2574 |
|
struct kmod_ctx *ctx = NULL; |
|
2575 |
|
struct cfg cfg; |
|
2576 |
|
struct depmod depmod; |
|
2577 |
|
|
|
2578 |
|
memset(&cfg, 0, sizeof(cfg)); |
|
2579 |
|
memset(&depmod, 0, sizeof(depmod)); |
|
2580 |
|
|
|
2581 |
|
for (;;) { |
|
2582 |
|
int c, idx = 0; |
|
2583 |
|
c = getopt_long(argc, argv, cmdopts_s, cmdopts, &idx); |
|
2584 |
|
if (c == -1) |
|
2585 |
|
break; |
|
2586 |
|
switch (c) { |
|
2587 |
|
case 'a': |
|
2588 |
|
all = 1; |
|
2589 |
|
break; |
|
2590 |
|
case 'A': |
|
2591 |
|
maybe_all = 1; |
|
2592 |
|
break; |
|
2593 |
|
case 'b': |
|
2594 |
|
if (root) |
|
2595 |
|
free(root); |
|
2596 |
|
root = path_make_absolute_cwd(optarg); |
|
2597 |
|
break; |
|
2598 |
|
case 'C': { |
|
2599 |
|
size_t bytes = sizeof(char *) * (n_config_paths + 2); |
|
2600 |
|
void *tmp = realloc(config_paths, bytes); |
|
2601 |
|
if (!tmp) { |
|
2602 |
|
fputs("Error: out-of-memory\n", stderr); |
|
2603 |
|
goto cmdline_failed; |
|
2604 |
|
} |
|
2605 |
|
config_paths = tmp; |
|
2606 |
|
config_paths[n_config_paths] = optarg; |
|
2607 |
|
n_config_paths++; |
|
2608 |
|
config_paths[n_config_paths] = NULL; |
|
2609 |
|
break; |
|
2610 |
|
} |
|
2611 |
|
case 'E': |
|
2612 |
|
module_symvers = optarg; |
|
2613 |
|
cfg.check_symvers = 1; |
|
2614 |
|
break; |
|
2615 |
|
case 'F': |
|
2616 |
|
system_map = optarg; |
|
2617 |
|
break; |
|
2618 |
|
case 'e': |
|
2619 |
|
cfg.print_unknown = 1; |
|
2620 |
|
break; |
|
2621 |
|
case 'v': |
|
2622 |
|
verbose++; |
|
2623 |
|
break; |
|
2624 |
|
case 'n': |
|
2625 |
|
out = stdout; |
|
2626 |
|
break; |
|
2627 |
|
case 'P': |
|
2628 |
|
if (optarg[1] != '\0') { |
|
2629 |
|
CRIT("-P only takes a single char\n"); |
|
2630 |
|
goto cmdline_failed; |
|
2631 |
|
} |
|
2632 |
|
cfg.sym_prefix = optarg[0]; |
|
2633 |
|
break; |
|
2634 |
|
case 'w': |
|
2635 |
|
cfg.warn_dups = 1; |
|
2636 |
|
break; |
|
2637 |
|
case 'u': |
|
2638 |
|
case 'q': |
|
2639 |
|
case 'r': |
|
2640 |
|
case 'm': |
|
2641 |
|
if (idx > 0) |
|
2642 |
|
WRN("Ignored deprecated option --%s\n", |
|
2643 |
|
cmdopts[idx].name); |
|
2644 |
|
else |
|
2645 |
|
WRN("Ignored deprecated option -%c\n", c); |
|
2646 |
|
|
|
2647 |
|
break; |
|
2648 |
|
case 'h': |
|
2649 |
|
help(); |
|
2650 |
|
if (root) |
|
2651 |
|
free(root); |
|
2652 |
|
if (config_paths) |
|
2653 |
|
free(config_paths); |
|
2654 |
|
return EXIT_SUCCESS; |
|
2655 |
|
case 'V': |
|
2656 |
|
puts("kmod version " VERSION); |
|
2657 |
|
puts(KMOD_FEATURES); |
|
2658 |
|
if (root) |
|
2659 |
|
free(root); |
|
2660 |
|
if (config_paths) |
|
2661 |
|
free(config_paths); |
|
2662 |
|
return EXIT_SUCCESS; |
|
2663 |
|
case '?': |
|
2664 |
|
goto cmdline_failed; |
|
2665 |
|
default: |
|
2666 |
|
ERR("unexpected getopt_long() value '%c'.\n", c); |
|
2667 |
|
goto cmdline_failed; |
|
2668 |
|
} |
|
2669 |
|
} |
|
2670 |
|
|
|
2671 |
|
if (optind < argc) { |
|
2672 |
|
if (!is_version_number(argv[optind])) { |
|
2673 |
|
ERR("Bad version passed %s\n", argv[optind]); |
|
2674 |
|
goto cmdline_failed; |
|
2675 |
|
} |
|
2676 |
|
cfg.kversion = argv[optind]; |
|
2677 |
|
optind++; |
|
2678 |
|
} else { |
|
2679 |
|
if (uname(&un) < 0) { |
|
2680 |
|
CRIT("uname() failed: %s\n", strerror(errno)); |
|
2681 |
|
goto cmdline_failed; |
|
2682 |
|
} |
|
2683 |
|
cfg.kversion = un.release; |
|
2684 |
|
} |
|
2685 |
|
|
|
2686 |
|
cfg.dirnamelen = snprintf(cfg.dirname, PATH_MAX, |
|
2687 |
|
"%s/lib/modules/%s", |
|
2688 |
|
root == NULL ? "" : root, cfg.kversion); |
|
2689 |
|
|
|
2690 |
|
if (optind == argc) |
|
2691 |
|
all = 1; |
|
2692 |
|
|
|
2693 |
|
if (maybe_all) { |
|
2694 |
|
if (out == stdout) |
|
2695 |
|
goto done; |
|
2696 |
|
/* ignore up-to-date errors (< 0) */ |
|
2697 |
|
if (depfile_up_to_date(cfg.dirname) == 1) |
|
2698 |
|
goto done; |
|
2699 |
|
all = 1; |
|
2700 |
|
} |
|
2701 |
|
|
|
2702 |
|
ctx = kmod_new(cfg.dirname, &null_kmod_config); |
|
2703 |
|
if (ctx == NULL) { |
|
2704 |
|
CRIT("kmod_new(\"%s\", {NULL}) failed: %m\n", cfg.dirname); |
|
2705 |
|
goto cmdline_failed; |
|
2706 |
|
} |
|
2707 |
|
|
|
2708 |
|
log_setup_kmod_log(ctx, verbose); |
|
2709 |
|
|
|
2710 |
|
err = depmod_init(&depmod, &cfg, ctx); |
|
2711 |
|
if (err < 0) { |
|
2712 |
|
CRIT("depmod_init: %s\n", strerror(-err)); |
|
2713 |
|
goto depmod_init_failed; |
|
2714 |
|
} |
|
2715 |
|
ctx = NULL; /* owned by depmod */ |
|
2716 |
|
|
|
2717 |
|
if (module_symvers != NULL) { |
|
2718 |
|
err = depmod_load_symvers(&depmod, module_symvers); |
|
2719 |
|
if (err < 0) { |
|
2720 |
|
CRIT("could not load %s: %s\n", module_symvers, |
|
2721 |
|
strerror(-err)); |
|
2722 |
|
goto cmdline_failed; |
|
2723 |
|
} |
|
2724 |
|
} else if (system_map != NULL) { |
|
2725 |
|
err = depmod_load_system_map(&depmod, system_map); |
|
2726 |
|
if (err < 0) { |
|
2727 |
|
CRIT("could not load %s: %s\n", system_map, |
|
2728 |
|
strerror(-err)); |
|
2729 |
|
goto cmdline_failed; |
|
2730 |
|
} |
|
2731 |
|
} else if (cfg.print_unknown) { |
|
2732 |
|
WRN("-e needs -E or -F\n"); |
|
2733 |
|
cfg.print_unknown = 0; |
|
2734 |
|
} |
|
2735 |
|
|
|
2736 |
|
if (all) { |
|
2737 |
|
err = cfg_load(&cfg, config_paths); |
|
2738 |
|
if (err < 0) { |
|
2739 |
|
CRIT("could not load configuration files\n"); |
|
2740 |
|
goto cmdline_modules_failed; |
|
2741 |
|
} |
|
2742 |
|
err = depmod_modules_search(&depmod); |
|
2743 |
|
if (err < 0) { |
|
2744 |
|
CRIT("could not search modules: %s\n", strerror(-err)); |
|
2745 |
|
goto cmdline_modules_failed; |
|
2746 |
|
} |
|
2747 |
|
} else { |
|
2748 |
|
int i; |
|
2749 |
|
|
|
2750 |
|
for (i = optind; i < argc; i++) { |
|
2751 |
|
const char *path = argv[i]; |
|
2752 |
|
struct kmod_module *mod; |
|
2753 |
|
|
|
2754 |
|
if (path[0] != '/') { |
|
2755 |
|
CRIT("%s: not absolute path.\n", path); |
|
2756 |
|
goto cmdline_modules_failed; |
|
2757 |
|
} |
|
2758 |
|
|
|
2759 |
|
err = kmod_module_new_from_path(depmod.ctx, path, &mod); |
|
2760 |
|
if (err < 0) { |
|
2761 |
|
CRIT("could not create module %s: %s\n", |
|
2762 |
|
path, strerror(-err)); |
|
2763 |
|
goto cmdline_modules_failed; |
|
2764 |
|
} |
|
2765 |
|
|
|
2766 |
|
err = depmod_module_add(&depmod, mod); |
|
2767 |
|
if (err < 0) { |
|
2768 |
|
CRIT("could not add module %s: %s\n", |
|
2769 |
|
path, strerror(-err)); |
|
2770 |
|
kmod_module_unref(mod); |
|
2771 |
|
goto cmdline_modules_failed; |
|
2772 |
|
} |
|
2773 |
|
} |
|
2774 |
|
} |
|
2775 |
|
|
|
2776 |
|
err = depmod_modules_build_array(&depmod); |
|
2777 |
|
if (err < 0) { |
|
2778 |
|
CRIT("could not build module array: %s\n", |
|
2779 |
|
strerror(-err)); |
|
2780 |
|
goto cmdline_modules_failed; |
|
2781 |
|
} |
|
2782 |
|
|
|
2783 |
|
depmod_modules_sort(&depmod); |
|
2784 |
|
err = depmod_load(&depmod); |
|
2785 |
|
if (err < 0) |
|
2786 |
|
goto cmdline_modules_failed; |
|
2787 |
|
|
|
2788 |
|
err = depmod_output(&depmod, out); |
|
2789 |
|
|
|
2790 |
|
done: |
|
2791 |
|
depmod_shutdown(&depmod); |
|
2792 |
|
cfg_free(&cfg); |
|
2793 |
|
if (root) |
|
2794 |
|
free(root); |
|
2795 |
|
if (config_paths) |
|
2796 |
|
free(config_paths); |
|
2797 |
|
return err >= 0 ? EXIT_SUCCESS : EXIT_FAILURE; |
|
2798 |
|
|
|
2799 |
|
cmdline_modules_failed: |
|
2800 |
|
depmod_shutdown(&depmod); |
|
2801 |
|
depmod_init_failed: |
|
2802 |
|
if (ctx != NULL) |
|
2803 |
|
kmod_unref(ctx); |
|
2804 |
|
cmdline_failed: |
|
2805 |
|
cfg_free(&cfg); |
|
2806 |
|
if (root) |
|
2807 |
|
free(root); |
|
2808 |
|
return EXIT_FAILURE; |
|
2809 |
|
} |
|
2810 |
|
|
|
2811 |
|
const struct kmod_cmd kmod_cmd_compat_depmod = { |
|
2812 |
|
.name = "depmod", |
|
2813 |
|
.cmd = do_depmod, |
|
2814 |
|
.help = "compat depmod command", |
|
2815 |
|
}; |
File src/tools/modprobe.c added (mode: 100644) (index 0000000..34eae75) |
|
1 |
|
/* |
|
2 |
|
* kmod-modprobe - manage linux kernel modules using libkmod. |
|
3 |
|
* |
|
4 |
|
* Copyright (C) 2011-2013 ProFUSION embedded systems |
|
5 |
|
* |
|
6 |
|
* This program is free software: you can redistribute it and/or modify |
|
7 |
|
* it under the terms of the GNU General Public License as published by |
|
8 |
|
* the Free Software Foundation, either version 2 of the License, or |
|
9 |
|
* (at your option) any later version. |
|
10 |
|
* |
|
11 |
|
* This program is distributed in the hope that it will be useful, |
|
12 |
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
13 |
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
14 |
|
* GNU General Public License for more details. |
|
15 |
|
* |
|
16 |
|
* You should have received a copy of the GNU General Public License |
|
17 |
|
* along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
18 |
|
*/ |
|
19 |
|
|
|
20 |
|
#include <assert.h> |
|
21 |
|
#include <errno.h> |
|
22 |
|
#include <getopt.h> |
|
23 |
|
#include <limits.h> |
|
24 |
|
#include <stdbool.h> |
|
25 |
|
#include <stdio.h> |
|
26 |
|
#include <stdlib.h> |
|
27 |
|
#include <string.h> |
|
28 |
|
#include <unistd.h> |
|
29 |
|
#include <limits.h> |
|
30 |
|
#include <syslog.h> |
|
31 |
|
#include <sys/stat.h> |
|
32 |
|
#include <sys/types.h> |
|
33 |
|
#include <sys/utsname.h> |
|
34 |
|
#include <sys/wait.h> |
|
35 |
|
|
|
36 |
|
#include "config.h" |
|
37 |
|
|
|
38 |
|
#include "libkmod.h" |
|
39 |
|
#include "shared/array.h" |
|
40 |
|
#include "shared/macro.h" |
|
41 |
|
#include "shared/util.h" |
|
42 |
|
#define TOOLS_MODPROBE_C |
|
43 |
|
#include "kmod.h" |
|
44 |
|
#undef TOOLS_MODPROBE_C |
|
45 |
|
#include "log.h" |
|
46 |
|
|
|
47 |
|
static int log_priority = LOG_CRIT; |
|
48 |
|
static int use_syslog = 0; |
|
49 |
|
#define LOG(...) log_printf(log_priority, __VA_ARGS__) |
|
50 |
|
|
|
51 |
|
#define DEFAULT_VERBOSE LOG_WARNING |
|
52 |
|
static int verbose = DEFAULT_VERBOSE; |
|
53 |
|
static int do_show = 0; |
|
54 |
|
static int dry_run = 0; |
|
55 |
|
static int ignore_loaded = 0; |
|
56 |
|
static int lookup_only = 0; |
|
57 |
|
static int first_time = 0; |
|
58 |
|
static int ignore_commands = 0; |
|
59 |
|
static int use_blacklist = 0; |
|
60 |
|
static int force = 0; |
|
61 |
|
static int strip_modversion = 0; |
|
62 |
|
static int strip_vermagic = 0; |
|
63 |
|
static int remove_dependencies = 0; |
|
64 |
|
static int quiet_inuse = 0; |
|
65 |
|
|
|
66 |
|
static const char cmdopts_s[] = "arRibfDcnC:d:S:sqvVh"; |
|
67 |
|
static const struct option cmdopts[] = { |
|
68 |
|
{"all", no_argument, 0, 'a'}, |
|
69 |
|
{"remove", no_argument, 0, 'r'}, |
|
70 |
|
{"remove-dependencies", no_argument, 0, 5}, |
|
71 |
|
{"resolve-alias", no_argument, 0, 'R'}, |
|
72 |
|
{"first-time", no_argument, 0, 3}, |
|
73 |
|
{"ignore-install", no_argument, 0, 'i'}, |
|
74 |
|
{"ignore-remove", no_argument, 0, 'i'}, |
|
75 |
|
{"use-blacklist", no_argument, 0, 'b'}, |
|
76 |
|
{"force", no_argument, 0, 'f'}, |
|
77 |
|
{"force-modversion", no_argument, 0, 2}, |
|
78 |
|
{"force-vermagic", no_argument, 0, 1}, |
|
79 |
|
|
|
80 |
|
{"show-depends", no_argument, 0, 'D'}, |
|
81 |
|
{"showconfig", no_argument, 0, 'c'}, |
|
82 |
|
{"show-config", no_argument, 0, 'c'}, |
|
83 |
|
{"show-modversions", no_argument, 0, 4}, |
|
84 |
|
{"dump-modversions", no_argument, 0, 4}, |
|
85 |
|
|
|
86 |
|
{"dry-run", no_argument, 0, 'n'}, |
|
87 |
|
{"show", no_argument, 0, 'n'}, |
|
88 |
|
|
|
89 |
|
{"config", required_argument, 0, 'C'}, |
|
90 |
|
{"dirname", required_argument, 0, 'd'}, |
|
91 |
|
{"set-version", required_argument, 0, 'S'}, |
|
92 |
|
|
|
93 |
|
{"syslog", no_argument, 0, 's'}, |
|
94 |
|
{"quiet", no_argument, 0, 'q'}, |
|
95 |
|
{"verbose", no_argument, 0, 'v'}, |
|
96 |
|
{"version", no_argument, 0, 'V'}, |
|
97 |
|
{"help", no_argument, 0, 'h'}, |
|
98 |
|
{NULL, 0, 0, 0} |
|
99 |
|
}; |
|
100 |
|
|
|
101 |
|
static void help(void) |
|
102 |
|
{ |
|
103 |
|
printf("Usage:\n" |
|
104 |
|
"\t%s [options] [-i] [-b] modulename\n" |
|
105 |
|
"\t%s [options] -a [-i] [-b] modulename [modulename...]\n" |
|
106 |
|
"\t%s [options] -r [-i] modulename\n" |
|
107 |
|
"\t%s [options] -r -a [-i] modulename [modulename...]\n" |
|
108 |
|
"\t%s [options] -c\n" |
|
109 |
|
"\t%s [options] --dump-modversions filename\n" |
|
110 |
|
"Management Options:\n" |
|
111 |
|
"\t-a, --all Consider every non-argument to\n" |
|
112 |
|
"\t be a module name to be inserted\n" |
|
113 |
|
"\t or removed (-r)\n" |
|
114 |
|
"\t-r, --remove Remove modules instead of inserting\n" |
|
115 |
|
"\t --remove-dependencies Also remove modules depending on it\n" |
|
116 |
|
"\t-R, --resolve-alias Only lookup and print alias and exit\n" |
|
117 |
|
"\t --first-time Fail if module already inserted or removed\n" |
|
118 |
|
"\t-i, --ignore-install Ignore install commands\n" |
|
119 |
|
"\t-i, --ignore-remove Ignore remove commands\n" |
|
120 |
|
"\t-b, --use-blacklist Apply blacklist to resolved alias.\n" |
|
121 |
|
"\t-f, --force Force module insertion or removal.\n" |
|
122 |
|
"\t implies --force-modversions and\n" |
|
123 |
|
"\t --force-vermagic\n" |
|
124 |
|
"\t --force-modversion Ignore module's version\n" |
|
125 |
|
"\t --force-vermagic Ignore module's version magic\n" |
|
126 |
|
"\n" |
|
127 |
|
"Query Options:\n" |
|
128 |
|
"\t-D, --show-depends Only print module dependencies and exit\n" |
|
129 |
|
"\t-c, --showconfig Print out known configuration and exit\n" |
|
130 |
|
"\t-c, --show-config Same as --showconfig\n" |
|
131 |
|
"\t --show-modversions Dump module symbol version and exit\n" |
|
132 |
|
"\t --dump-modversions Same as --show-modversions\n" |
|
133 |
|
"\n" |
|
134 |
|
"General Options:\n" |
|
135 |
|
"\t-n, --dry-run Do not execute operations, just print out\n" |
|
136 |
|
"\t-n, --show Same as --dry-run\n" |
|
137 |
|
|
|
138 |
|
"\t-C, --config=FILE Use FILE instead of default search paths\n" |
|
139 |
|
"\t-d, --dirname=DIR Use DIR as filesystem root for /lib/modules\n" |
|
140 |
|
"\t-S, --set-version=VERSION Use VERSION instead of `uname -r`\n" |
|
141 |
|
|
|
142 |
|
"\t-s, --syslog print to syslog, not stderr\n" |
|
143 |
|
"\t-q, --quiet disable messages\n" |
|
144 |
|
"\t-v, --verbose enables more messages\n" |
|
145 |
|
"\t-V, --version show version\n" |
|
146 |
|
"\t-h, --help show this help\n", |
|
147 |
|
program_invocation_short_name, program_invocation_short_name, |
|
148 |
|
program_invocation_short_name, program_invocation_short_name, |
|
149 |
|
program_invocation_short_name, program_invocation_short_name); |
|
150 |
|
} |
|
151 |
|
|
|
152 |
|
static inline void _show(const char *fmt, ...) |
|
153 |
|
{ |
|
154 |
|
va_list args; |
|
155 |
|
|
|
156 |
|
if (!do_show && verbose <= DEFAULT_VERBOSE) |
|
157 |
|
return; |
|
158 |
|
|
|
159 |
|
va_start(args, fmt); |
|
160 |
|
vfprintf(stdout, fmt, args); |
|
161 |
|
fflush(stdout); |
|
162 |
|
va_end(args); |
|
163 |
|
} |
|
164 |
|
#define SHOW(...) _show(__VA_ARGS__) |
|
165 |
|
|
|
166 |
|
static int show_config(struct kmod_ctx *ctx) |
|
167 |
|
{ |
|
168 |
|
struct config_iterators { |
|
169 |
|
const char *name; |
|
170 |
|
struct kmod_config_iter *(*get_iter)(const struct kmod_ctx *ctx); |
|
171 |
|
} ci[] = { |
|
172 |
|
{ "blacklist", kmod_config_get_blacklists }, |
|
173 |
|
{ "install", kmod_config_get_install_commands }, |
|
174 |
|
{ "remove", kmod_config_get_remove_commands }, |
|
175 |
|
{ "alias", kmod_config_get_aliases }, |
|
176 |
|
{ "options", kmod_config_get_options }, |
|
177 |
|
{ "softdep", kmod_config_get_softdeps }, |
|
178 |
|
}; |
|
179 |
|
size_t i; |
|
180 |
|
|
|
181 |
|
for (i = 0; i < ARRAY_SIZE(ci); i++) { |
|
182 |
|
struct kmod_config_iter *iter = ci[i].get_iter(ctx); |
|
183 |
|
|
|
184 |
|
if (iter == NULL) |
|
185 |
|
continue; |
|
186 |
|
|
|
187 |
|
while (kmod_config_iter_next(iter)) { |
|
188 |
|
const char *val; |
|
189 |
|
|
|
190 |
|
printf("%s %s", ci[i].name, |
|
191 |
|
kmod_config_iter_get_key(iter)); |
|
192 |
|
val = kmod_config_iter_get_value(iter); |
|
193 |
|
if (val != NULL) { |
|
194 |
|
putchar(' '); |
|
195 |
|
puts(val); |
|
196 |
|
} else |
|
197 |
|
putchar('\n'); |
|
198 |
|
} |
|
199 |
|
|
|
200 |
|
kmod_config_iter_free_iter(iter); |
|
201 |
|
} |
|
202 |
|
|
|
203 |
|
puts("\n# End of configuration files. Dumping indexes now:\n"); |
|
204 |
|
fflush(stdout); |
|
205 |
|
|
|
206 |
|
kmod_dump_index(ctx, KMOD_INDEX_MODULES_ALIAS, STDOUT_FILENO); |
|
207 |
|
kmod_dump_index(ctx, KMOD_INDEX_MODULES_SYMBOL, STDOUT_FILENO); |
|
208 |
|
|
|
209 |
|
return 0; |
|
210 |
|
} |
|
211 |
|
|
|
212 |
|
static int show_modversions(struct kmod_ctx *ctx, const char *filename) |
|
213 |
|
{ |
|
214 |
|
struct kmod_list *l, *list = NULL; |
|
215 |
|
struct kmod_module *mod; |
|
216 |
|
int err = kmod_module_new_from_path(ctx, filename, &mod); |
|
217 |
|
if (err < 0) { |
|
218 |
|
LOG("Module %s not found.\n", filename); |
|
219 |
|
return err; |
|
220 |
|
} |
|
221 |
|
|
|
222 |
|
err = kmod_module_get_versions(mod, &list); |
|
223 |
|
if (err < 0) { |
|
224 |
|
LOG("could not get modversions of %s: %s\n", |
|
225 |
|
filename, strerror(-err)); |
|
226 |
|
kmod_module_unref(mod); |
|
227 |
|
return err; |
|
228 |
|
} |
|
229 |
|
|
|
230 |
|
kmod_list_foreach(l, list) { |
|
231 |
|
const char *symbol = kmod_module_version_get_symbol(l); |
|
232 |
|
uint64_t crc = kmod_module_version_get_crc(l); |
|
233 |
|
printf("0x%08"PRIx64"\t%s\n", crc, symbol); |
|
234 |
|
} |
|
235 |
|
kmod_module_versions_free_list(list); |
|
236 |
|
kmod_module_unref(mod); |
|
237 |
|
return 0; |
|
238 |
|
} |
|
239 |
|
|
|
240 |
|
static int command_do(struct kmod_module *module, const char *type, |
|
241 |
|
const char *command, const char *cmdline_opts) |
|
242 |
|
{ |
|
243 |
|
const char *modname = kmod_module_get_name(module); |
|
244 |
|
char *p, *cmd = NULL; |
|
245 |
|
size_t cmdlen, cmdline_opts_len, varlen; |
|
246 |
|
int ret = 0; |
|
247 |
|
|
|
248 |
|
if (cmdline_opts == NULL) |
|
249 |
|
cmdline_opts = ""; |
|
250 |
|
cmdline_opts_len = strlen(cmdline_opts); |
|
251 |
|
|
|
252 |
|
cmd = strdup(command); |
|
253 |
|
if (cmd == NULL) |
|
254 |
|
return -ENOMEM; |
|
255 |
|
cmdlen = strlen(cmd); |
|
256 |
|
varlen = sizeof("$CMDLINE_OPTS") - 1; |
|
257 |
|
while ((p = strstr(cmd, "$CMDLINE_OPTS")) != NULL) { |
|
258 |
|
size_t prefixlen = p - cmd; |
|
259 |
|
size_t suffixlen = cmdlen - prefixlen - varlen; |
|
260 |
|
size_t slen = cmdlen - varlen + cmdline_opts_len; |
|
261 |
|
char *suffix = p + varlen; |
|
262 |
|
char *s = malloc(slen + 1); |
|
263 |
|
if (s == NULL) { |
|
264 |
|
free(cmd); |
|
265 |
|
return -ENOMEM; |
|
266 |
|
} |
|
267 |
|
memcpy(s, cmd, p - cmd); |
|
268 |
|
memcpy(s + prefixlen, cmdline_opts, cmdline_opts_len); |
|
269 |
|
memcpy(s + prefixlen + cmdline_opts_len, suffix, suffixlen); |
|
270 |
|
s[slen] = '\0'; |
|
271 |
|
|
|
272 |
|
free(cmd); |
|
273 |
|
cmd = s; |
|
274 |
|
cmdlen = slen; |
|
275 |
|
} |
|
276 |
|
|
|
277 |
|
SHOW("%s %s\n", type, cmd); |
|
278 |
|
if (dry_run) |
|
279 |
|
goto end; |
|
280 |
|
|
|
281 |
|
setenv("MODPROBE_MODULE", modname, 1); |
|
282 |
|
ret = system(cmd); |
|
283 |
|
unsetenv("MODPROBE_MODULE"); |
|
284 |
|
if (ret == -1 || WEXITSTATUS(ret)) { |
|
285 |
|
LOG("Error running %s command for %s\n", type, modname); |
|
286 |
|
if (ret != -1) |
|
287 |
|
ret = -WEXITSTATUS(ret); |
|
288 |
|
} |
|
289 |
|
|
|
290 |
|
end: |
|
291 |
|
free(cmd); |
|
292 |
|
return ret; |
|
293 |
|
} |
|
294 |
|
|
|
295 |
|
static int rmmod_do_remove_module(struct kmod_module *mod) |
|
296 |
|
{ |
|
297 |
|
const char *modname = kmod_module_get_name(mod); |
|
298 |
|
struct kmod_list *deps, *itr; |
|
299 |
|
int flags = 0, err; |
|
300 |
|
|
|
301 |
|
SHOW("rmmod %s\n", kmod_module_get_name(mod)); |
|
302 |
|
|
|
303 |
|
if (dry_run) |
|
304 |
|
return 0; |
|
305 |
|
|
|
306 |
|
if (force) |
|
307 |
|
flags |= KMOD_REMOVE_FORCE; |
|
308 |
|
|
|
309 |
|
err = kmod_module_remove_module(mod, flags); |
|
310 |
|
if (err == -EEXIST) { |
|
311 |
|
if (!first_time) |
|
312 |
|
err = 0; |
|
313 |
|
else |
|
314 |
|
LOG("Module %s is not in kernel.\n", modname); |
|
315 |
|
} |
|
316 |
|
|
|
317 |
|
deps = kmod_module_get_dependencies(mod); |
|
318 |
|
if (deps != NULL) { |
|
319 |
|
kmod_list_foreach(itr, deps) { |
|
320 |
|
struct kmod_module *dep = kmod_module_get_module(itr); |
|
321 |
|
if (kmod_module_get_refcnt(dep) == 0) |
|
322 |
|
rmmod_do_remove_module(dep); |
|
323 |
|
kmod_module_unref(dep); |
|
324 |
|
} |
|
325 |
|
kmod_module_unref_list(deps); |
|
326 |
|
} |
|
327 |
|
|
|
328 |
|
return err; |
|
329 |
|
} |
|
330 |
|
|
|
331 |
|
static int rmmod_do_module(struct kmod_module *mod, bool do_dependencies); |
|
332 |
|
|
|
333 |
|
static int rmmod_do_deps_list(struct kmod_list *list, bool stop_on_errors) |
|
334 |
|
{ |
|
335 |
|
struct kmod_list *l; |
|
336 |
|
|
|
337 |
|
kmod_list_foreach_reverse(l, list) { |
|
338 |
|
struct kmod_module *m = kmod_module_get_module(l); |
|
339 |
|
int r = rmmod_do_module(m, false); |
|
340 |
|
kmod_module_unref(m); |
|
341 |
|
|
|
342 |
|
if (r < 0 && stop_on_errors) |
|
343 |
|
return r; |
|
344 |
|
} |
|
345 |
|
|
|
346 |
|
return 0; |
|
347 |
|
} |
|
348 |
|
|
|
349 |
|
static int rmmod_do_module(struct kmod_module *mod, bool do_dependencies) |
|
350 |
|
{ |
|
351 |
|
const char *modname = kmod_module_get_name(mod); |
|
352 |
|
struct kmod_list *pre = NULL, *post = NULL; |
|
353 |
|
const char *cmd = NULL; |
|
354 |
|
int err; |
|
355 |
|
|
|
356 |
|
if (!ignore_commands) { |
|
357 |
|
err = kmod_module_get_softdeps(mod, &pre, &post); |
|
358 |
|
if (err < 0) { |
|
359 |
|
WRN("could not get softdeps of '%s': %s\n", |
|
360 |
|
modname, strerror(-err)); |
|
361 |
|
return err; |
|
362 |
|
} |
|
363 |
|
|
|
364 |
|
cmd = kmod_module_get_remove_commands(mod); |
|
365 |
|
} |
|
366 |
|
|
|
367 |
|
if (cmd == NULL && !ignore_loaded) { |
|
368 |
|
int state = kmod_module_get_initstate(mod); |
|
369 |
|
|
|
370 |
|
if (state < 0) { |
|
371 |
|
if (first_time) { |
|
372 |
|
LOG("Module %s is not in kernel.\n", modname); |
|
373 |
|
err = -ENOENT; |
|
374 |
|
} else { |
|
375 |
|
err = 0; |
|
376 |
|
} |
|
377 |
|
goto error; |
|
378 |
|
} else if (state == KMOD_MODULE_BUILTIN) { |
|
379 |
|
LOG("Module %s is builtin.\n", modname); |
|
380 |
|
err = -ENOENT; |
|
381 |
|
goto error; |
|
382 |
|
} |
|
383 |
|
} |
|
384 |
|
|
|
385 |
|
rmmod_do_deps_list(post, false); |
|
386 |
|
|
|
387 |
|
if (do_dependencies && remove_dependencies) { |
|
388 |
|
struct kmod_list *deps = kmod_module_get_dependencies(mod); |
|
389 |
|
|
|
390 |
|
err = rmmod_do_deps_list(deps, true); |
|
391 |
|
if (err < 0) |
|
392 |
|
goto error; |
|
393 |
|
} |
|
394 |
|
|
|
395 |
|
if (!ignore_loaded && !cmd) { |
|
396 |
|
int usage = kmod_module_get_refcnt(mod); |
|
397 |
|
|
|
398 |
|
if (usage > 0) { |
|
399 |
|
if (!quiet_inuse) |
|
400 |
|
LOG("Module %s is in use.\n", modname); |
|
401 |
|
|
|
402 |
|
err = -EBUSY; |
|
403 |
|
goto error; |
|
404 |
|
} |
|
405 |
|
} |
|
406 |
|
|
|
407 |
|
if (cmd == NULL) |
|
408 |
|
err = rmmod_do_remove_module(mod); |
|
409 |
|
else |
|
410 |
|
err = command_do(mod, "remove", cmd, NULL); |
|
411 |
|
|
|
412 |
|
if (err < 0) |
|
413 |
|
goto error; |
|
414 |
|
|
|
415 |
|
rmmod_do_deps_list(pre, false); |
|
416 |
|
|
|
417 |
|
error: |
|
418 |
|
kmod_module_unref_list(pre); |
|
419 |
|
kmod_module_unref_list(post); |
|
420 |
|
|
|
421 |
|
return err; |
|
422 |
|
} |
|
423 |
|
|
|
424 |
|
static int rmmod(struct kmod_ctx *ctx, const char *alias) |
|
425 |
|
{ |
|
426 |
|
struct kmod_list *l, *list = NULL; |
|
427 |
|
int err; |
|
428 |
|
|
|
429 |
|
err = kmod_module_new_from_lookup(ctx, alias, &list); |
|
430 |
|
if (err < 0) |
|
431 |
|
return err; |
|
432 |
|
|
|
433 |
|
if (list == NULL) { |
|
434 |
|
LOG("Module %s not found.\n", alias); |
|
435 |
|
err = -ENOENT; |
|
436 |
|
} |
|
437 |
|
|
|
438 |
|
kmod_list_foreach(l, list) { |
|
439 |
|
struct kmod_module *mod = kmod_module_get_module(l); |
|
440 |
|
err = rmmod_do_module(mod, true); |
|
441 |
|
kmod_module_unref(mod); |
|
442 |
|
if (err < 0) |
|
443 |
|
break; |
|
444 |
|
} |
|
445 |
|
|
|
446 |
|
kmod_module_unref_list(list); |
|
447 |
|
return err; |
|
448 |
|
} |
|
449 |
|
|
|
450 |
|
static int rmmod_all(struct kmod_ctx *ctx, char **args, int nargs) |
|
451 |
|
{ |
|
452 |
|
int i, err = 0; |
|
453 |
|
|
|
454 |
|
for (i = 0; i < nargs; i++) { |
|
455 |
|
int r = rmmod(ctx, args[i]); |
|
456 |
|
if (r < 0) |
|
457 |
|
err = r; |
|
458 |
|
} |
|
459 |
|
|
|
460 |
|
return err; |
|
461 |
|
} |
|
462 |
|
|
|
463 |
|
static void print_action(struct kmod_module *m, bool install, |
|
464 |
|
const char *options) |
|
465 |
|
{ |
|
466 |
|
const char *path; |
|
467 |
|
|
|
468 |
|
if (install) { |
|
469 |
|
printf("install %s %s\n", kmod_module_get_install_commands(m), |
|
470 |
|
options); |
|
471 |
|
return; |
|
472 |
|
} |
|
473 |
|
|
|
474 |
|
path = kmod_module_get_path(m); |
|
475 |
|
|
|
476 |
|
if (path == NULL) { |
|
477 |
|
/* |
|
478 |
|
* Either a builtin module, or an alias, print only for |
|
479 |
|
* builtin |
|
480 |
|
*/ |
|
481 |
|
if (kmod_module_get_initstate(m) == KMOD_MODULE_BUILTIN) |
|
482 |
|
printf("builtin %s\n", kmod_module_get_name(m)); |
|
483 |
|
} else |
|
484 |
|
printf("insmod %s %s\n", kmod_module_get_path(m), options); |
|
485 |
|
} |
|
486 |
|
|
|
487 |
|
static int insmod(struct kmod_ctx *ctx, const char *alias, |
|
488 |
|
const char *extra_options) |
|
489 |
|
{ |
|
490 |
|
struct kmod_list *l, *list = NULL; |
|
491 |
|
int err, flags = 0; |
|
492 |
|
|
|
493 |
|
void (*show)(struct kmod_module *m, bool install, |
|
494 |
|
const char *options) = NULL; |
|
495 |
|
|
|
496 |
|
err = kmod_module_new_from_lookup(ctx, alias, &list); |
|
497 |
|
|
|
498 |
|
if (list == NULL || err < 0) { |
|
499 |
|
LOG("Module %s not found in directory %s\n", alias, |
|
500 |
|
ctx ? kmod_get_dirname(ctx) : "(missing)"); |
|
501 |
|
return -ENOENT; |
|
502 |
|
} |
|
503 |
|
|
|
504 |
|
if (strip_modversion || force) |
|
505 |
|
flags |= KMOD_PROBE_FORCE_MODVERSION; |
|
506 |
|
if (strip_vermagic || force) |
|
507 |
|
flags |= KMOD_PROBE_FORCE_VERMAGIC; |
|
508 |
|
if (ignore_commands) |
|
509 |
|
flags |= KMOD_PROBE_IGNORE_COMMAND; |
|
510 |
|
if (ignore_loaded) |
|
511 |
|
flags |= KMOD_PROBE_IGNORE_LOADED; |
|
512 |
|
if (dry_run) |
|
513 |
|
flags |= KMOD_PROBE_DRY_RUN; |
|
514 |
|
if (do_show || verbose > DEFAULT_VERBOSE) |
|
515 |
|
show = &print_action; |
|
516 |
|
|
|
517 |
|
flags |= KMOD_PROBE_APPLY_BLACKLIST_ALIAS_ONLY; |
|
518 |
|
|
|
519 |
|
if (use_blacklist) |
|
520 |
|
flags |= KMOD_PROBE_APPLY_BLACKLIST; |
|
521 |
|
if (first_time) |
|
522 |
|
flags |= KMOD_PROBE_FAIL_ON_LOADED; |
|
523 |
|
|
|
524 |
|
kmod_list_foreach(l, list) { |
|
525 |
|
struct kmod_module *mod = kmod_module_get_module(l); |
|
526 |
|
|
|
527 |
|
if (lookup_only) |
|
528 |
|
printf("%s\n", kmod_module_get_name(mod)); |
|
529 |
|
else { |
|
530 |
|
err = kmod_module_probe_insert_module(mod, flags, |
|
531 |
|
extra_options, NULL, NULL, show); |
|
532 |
|
} |
|
533 |
|
|
|
534 |
|
if (err >= 0) |
|
535 |
|
/* ignore flag return values such as a mod being blacklisted */ |
|
536 |
|
err = 0; |
|
537 |
|
else { |
|
538 |
|
switch (err) { |
|
539 |
|
case -EEXIST: |
|
540 |
|
ERR("could not insert '%s': Module already in kernel\n", |
|
541 |
|
kmod_module_get_name(mod)); |
|
542 |
|
break; |
|
543 |
|
case -ENOENT: |
|
544 |
|
ERR("could not insert '%s': Unknown symbol in module, " |
|
545 |
|
"or unknown parameter (see dmesg)\n", |
|
546 |
|
kmod_module_get_name(mod)); |
|
547 |
|
break; |
|
548 |
|
default: |
|
549 |
|
ERR("could not insert '%s': %s\n", |
|
550 |
|
kmod_module_get_name(mod), |
|
551 |
|
strerror(-err)); |
|
552 |
|
break; |
|
553 |
|
} |
|
554 |
|
} |
|
555 |
|
|
|
556 |
|
kmod_module_unref(mod); |
|
557 |
|
} |
|
558 |
|
|
|
559 |
|
kmod_module_unref_list(list); |
|
560 |
|
return err; |
|
561 |
|
} |
|
562 |
|
|
|
563 |
|
static int insmod_all(struct kmod_ctx *ctx, char **args, int nargs) |
|
564 |
|
{ |
|
565 |
|
int i, err = 0; |
|
566 |
|
|
|
567 |
|
for (i = 0; i < nargs; i++) { |
|
568 |
|
int r = insmod(ctx, args[i], NULL); |
|
569 |
|
if (r < 0) |
|
570 |
|
err = r; |
|
571 |
|
} |
|
572 |
|
|
|
573 |
|
return err; |
|
574 |
|
} |
|
575 |
|
|
|
576 |
|
static void env_modprobe_options_append(const char *value) |
|
577 |
|
{ |
|
578 |
|
const char *old = getenv("MODPROBE_OPTIONS"); |
|
579 |
|
char *env; |
|
580 |
|
|
|
581 |
|
if (old == NULL) { |
|
582 |
|
setenv("MODPROBE_OPTIONS", value, 1); |
|
583 |
|
return; |
|
584 |
|
} |
|
585 |
|
|
|
586 |
|
if (asprintf(&env, "%s %s", old, value) < 0) { |
|
587 |
|
ERR("could not append value to $MODPROBE_OPTIONS\n"); |
|
588 |
|
return; |
|
589 |
|
} |
|
590 |
|
|
|
591 |
|
if (setenv("MODPROBE_OPTIONS", env, 1) < 0) |
|
592 |
|
ERR("could not setenv(MODPROBE_OPTIONS, \"%s\")\n", env); |
|
593 |
|
free(env); |
|
594 |
|
} |
|
595 |
|
|
|
596 |
|
static int options_from_array(char **args, int nargs, char **output) |
|
597 |
|
{ |
|
598 |
|
char *opts = NULL; |
|
599 |
|
size_t optslen = 0; |
|
600 |
|
int i, err = 0; |
|
601 |
|
|
|
602 |
|
for (i = 1; i < nargs; i++) { |
|
603 |
|
size_t len = strlen(args[i]); |
|
604 |
|
size_t qlen = 0; |
|
605 |
|
const char *value; |
|
606 |
|
void *tmp; |
|
607 |
|
|
|
608 |
|
value = strchr(args[i], '='); |
|
609 |
|
if (value) { |
|
610 |
|
value++; |
|
611 |
|
if (*value != '"' && *value != '\'') { |
|
612 |
|
if (strchr(value, ' ')) |
|
613 |
|
qlen = 2; |
|
614 |
|
} |
|
615 |
|
} |
|
616 |
|
|
|
617 |
|
tmp = realloc(opts, optslen + len + qlen + 2); |
|
618 |
|
if (!tmp) { |
|
619 |
|
err = -errno; |
|
620 |
|
free(opts); |
|
621 |
|
opts = NULL; |
|
622 |
|
ERR("could not gather module options: out-of-memory\n"); |
|
623 |
|
break; |
|
624 |
|
} |
|
625 |
|
opts = tmp; |
|
626 |
|
if (optslen > 0) { |
|
627 |
|
opts[optslen] = ' '; |
|
628 |
|
optslen++; |
|
629 |
|
} |
|
630 |
|
if (qlen == 0) { |
|
631 |
|
memcpy(opts + optslen, args[i], len + 1); |
|
632 |
|
optslen += len; |
|
633 |
|
} else { |
|
634 |
|
size_t keylen = value - args[i]; |
|
635 |
|
size_t valuelen = len - keylen; |
|
636 |
|
memcpy(opts + optslen, args[i], keylen); |
|
637 |
|
optslen += keylen; |
|
638 |
|
opts[optslen] = '"'; |
|
639 |
|
optslen++; |
|
640 |
|
memcpy(opts + optslen, value, valuelen); |
|
641 |
|
optslen += valuelen; |
|
642 |
|
opts[optslen] = '"'; |
|
643 |
|
optslen++; |
|
644 |
|
opts[optslen] = '\0'; |
|
645 |
|
} |
|
646 |
|
} |
|
647 |
|
|
|
648 |
|
*output = opts; |
|
649 |
|
return err; |
|
650 |
|
} |
|
651 |
|
|
|
652 |
|
static char **prepend_options_from_env(int *p_argc, char **orig_argv) |
|
653 |
|
{ |
|
654 |
|
const char *p, *env = getenv("MODPROBE_OPTIONS"); |
|
655 |
|
char **new_argv, *str_start, *str_end, *str, *s, *quote; |
|
656 |
|
int i, argc = *p_argc; |
|
657 |
|
size_t envlen, space_count = 0; |
|
658 |
|
|
|
659 |
|
if (env == NULL) |
|
660 |
|
return orig_argv; |
|
661 |
|
|
|
662 |
|
for (p = env; *p != '\0'; p++) { |
|
663 |
|
if (*p == ' ') |
|
664 |
|
space_count++; |
|
665 |
|
} |
|
666 |
|
|
|
667 |
|
envlen = p - env; |
|
668 |
|
new_argv = malloc(sizeof(char *) * (argc + space_count + 3 + envlen)); |
|
669 |
|
if (new_argv == NULL) |
|
670 |
|
return NULL; |
|
671 |
|
|
|
672 |
|
new_argv[0] = orig_argv[0]; |
|
673 |
|
str_start = str = (char *) (new_argv + argc + space_count + 3); |
|
674 |
|
memcpy(str, env, envlen + 1); |
|
675 |
|
|
|
676 |
|
str_end = str_start + envlen; |
|
677 |
|
|
|
678 |
|
quote = NULL; |
|
679 |
|
for (i = 1, s = str; *s != '\0'; s++) { |
|
680 |
|
if (quote == NULL) { |
|
681 |
|
if (*s == ' ') { |
|
682 |
|
new_argv[i] = str; |
|
683 |
|
i++; |
|
684 |
|
*s = '\0'; |
|
685 |
|
str = s + 1; |
|
686 |
|
} else if (*s == '"' || *s == '\'') |
|
687 |
|
quote = s; |
|
688 |
|
} else { |
|
689 |
|
if (*s == *quote) { |
|
690 |
|
if (quote == str) { |
|
691 |
|
new_argv[i] = str + 1; |
|
692 |
|
i++; |
|
693 |
|
*s = '\0'; |
|
694 |
|
str = s + 1; |
|
695 |
|
} else { |
|
696 |
|
char *it; |
|
697 |
|
for (it = quote; it < s - 1; it++) |
|
698 |
|
it[0] = it[1]; |
|
699 |
|
for (it = s - 1; it < str_end - 2; it++) |
|
700 |
|
it[0] = it[2]; |
|
701 |
|
str_end -= 2; |
|
702 |
|
*str_end = '\0'; |
|
703 |
|
s -= 2; |
|
704 |
|
} |
|
705 |
|
quote = NULL; |
|
706 |
|
} |
|
707 |
|
} |
|
708 |
|
} |
|
709 |
|
if (str < s) { |
|
710 |
|
new_argv[i] = str; |
|
711 |
|
i++; |
|
712 |
|
} |
|
713 |
|
|
|
714 |
|
memcpy(new_argv + i, orig_argv + 1, sizeof(char *) * (argc - 1)); |
|
715 |
|
new_argv[i + argc] = NULL; |
|
716 |
|
*p_argc = i + argc - 1; |
|
717 |
|
|
|
718 |
|
return new_argv; |
|
719 |
|
} |
|
720 |
|
|
|
721 |
|
static int do_modprobe(int argc, char **orig_argv) |
|
722 |
|
{ |
|
723 |
|
struct kmod_ctx *ctx; |
|
724 |
|
char **args = NULL, **argv; |
|
725 |
|
const char **config_paths = NULL; |
|
726 |
|
int nargs = 0, n_config_paths = 0; |
|
727 |
|
char dirname_buf[PATH_MAX]; |
|
728 |
|
const char *dirname = NULL; |
|
729 |
|
const char *root = NULL; |
|
730 |
|
const char *kversion = NULL; |
|
731 |
|
int use_all = 0; |
|
732 |
|
int do_remove = 0; |
|
733 |
|
int do_show_config = 0; |
|
734 |
|
int do_show_modversions = 0; |
|
735 |
|
int err; |
|
736 |
|
|
|
737 |
|
argv = prepend_options_from_env(&argc, orig_argv); |
|
738 |
|
if (argv == NULL) { |
|
739 |
|
ERR("Could not prepend options from command line\n"); |
|
740 |
|
return EXIT_FAILURE; |
|
741 |
|
} |
|
742 |
|
|
|
743 |
|
for (;;) { |
|
744 |
|
int c, idx = 0; |
|
745 |
|
c = getopt_long(argc, argv, cmdopts_s, cmdopts, &idx); |
|
746 |
|
if (c == -1) |
|
747 |
|
break; |
|
748 |
|
switch (c) { |
|
749 |
|
case 'a': |
|
750 |
|
log_priority = LOG_WARNING; |
|
751 |
|
use_all = 1; |
|
752 |
|
break; |
|
753 |
|
case 'r': |
|
754 |
|
do_remove = 1; |
|
755 |
|
break; |
|
756 |
|
case 5: |
|
757 |
|
remove_dependencies = 1; |
|
758 |
|
break; |
|
759 |
|
case 'R': |
|
760 |
|
lookup_only = 1; |
|
761 |
|
break; |
|
762 |
|
case 3: |
|
763 |
|
first_time = 1; |
|
764 |
|
break; |
|
765 |
|
case 'i': |
|
766 |
|
ignore_commands = 1; |
|
767 |
|
break; |
|
768 |
|
case 'b': |
|
769 |
|
use_blacklist = 1; |
|
770 |
|
break; |
|
771 |
|
case 'f': |
|
772 |
|
force = 1; |
|
773 |
|
break; |
|
774 |
|
case 2: |
|
775 |
|
strip_modversion = 1; |
|
776 |
|
break; |
|
777 |
|
case 1: |
|
778 |
|
strip_vermagic = 1; |
|
779 |
|
break; |
|
780 |
|
case 'D': |
|
781 |
|
ignore_loaded = 1; |
|
782 |
|
dry_run = 1; |
|
783 |
|
do_show = 1; |
|
784 |
|
break; |
|
785 |
|
case 'c': |
|
786 |
|
do_show_config = 1; |
|
787 |
|
break; |
|
788 |
|
case 4: |
|
789 |
|
do_show_modversions = 1; |
|
790 |
|
break; |
|
791 |
|
case 'n': |
|
792 |
|
dry_run = 1; |
|
793 |
|
break; |
|
794 |
|
case 'C': { |
|
795 |
|
size_t bytes = sizeof(char *) * (n_config_paths + 2); |
|
796 |
|
void *tmp = realloc(config_paths, bytes); |
|
797 |
|
if (!tmp) { |
|
798 |
|
ERR("out-of-memory\n"); |
|
799 |
|
err = -1; |
|
800 |
|
goto done; |
|
801 |
|
} |
|
802 |
|
config_paths = tmp; |
|
803 |
|
config_paths[n_config_paths] = optarg; |
|
804 |
|
n_config_paths++; |
|
805 |
|
config_paths[n_config_paths] = NULL; |
|
806 |
|
|
|
807 |
|
env_modprobe_options_append("-C"); |
|
808 |
|
env_modprobe_options_append(optarg); |
|
809 |
|
break; |
|
810 |
|
} |
|
811 |
|
case 'd': |
|
812 |
|
root = optarg; |
|
813 |
|
break; |
|
814 |
|
case 'S': |
|
815 |
|
kversion = optarg; |
|
816 |
|
break; |
|
817 |
|
case 's': |
|
818 |
|
env_modprobe_options_append("-s"); |
|
819 |
|
use_syslog = 1; |
|
820 |
|
break; |
|
821 |
|
case 'q': |
|
822 |
|
env_modprobe_options_append("-q"); |
|
823 |
|
verbose = LOG_EMERG; |
|
824 |
|
break; |
|
825 |
|
case 'v': |
|
826 |
|
env_modprobe_options_append("-v"); |
|
827 |
|
verbose++; |
|
828 |
|
break; |
|
829 |
|
case 'V': |
|
830 |
|
puts("kmod version " VERSION); |
|
831 |
|
puts(KMOD_FEATURES); |
|
832 |
|
err = 0; |
|
833 |
|
goto done; |
|
834 |
|
case 'h': |
|
835 |
|
help(); |
|
836 |
|
err = 0; |
|
837 |
|
goto done; |
|
838 |
|
case '?': |
|
839 |
|
err = -1; |
|
840 |
|
goto done; |
|
841 |
|
default: |
|
842 |
|
ERR("unexpected getopt_long() value '%c'.\n", c); |
|
843 |
|
err = -1; |
|
844 |
|
goto done; |
|
845 |
|
} |
|
846 |
|
} |
|
847 |
|
|
|
848 |
|
args = argv + optind; |
|
849 |
|
nargs = argc - optind; |
|
850 |
|
|
|
851 |
|
log_open(use_syslog); |
|
852 |
|
|
|
853 |
|
if (!do_show_config) { |
|
854 |
|
if (nargs == 0) { |
|
855 |
|
ERR("missing parameters. See -h.\n"); |
|
856 |
|
err = -1; |
|
857 |
|
goto done; |
|
858 |
|
} |
|
859 |
|
} |
|
860 |
|
|
|
861 |
|
if (root != NULL || kversion != NULL) { |
|
862 |
|
struct utsname u; |
|
863 |
|
if (root == NULL) |
|
864 |
|
root = ""; |
|
865 |
|
if (kversion == NULL) { |
|
866 |
|
if (uname(&u) < 0) { |
|
867 |
|
ERR("uname() failed: %m\n"); |
|
868 |
|
err = -1; |
|
869 |
|
goto done; |
|
870 |
|
} |
|
871 |
|
kversion = u.release; |
|
872 |
|
} |
|
873 |
|
snprintf(dirname_buf, sizeof(dirname_buf), |
|
874 |
|
"%s/lib/modules/%s", root, |
|
875 |
|
kversion); |
|
876 |
|
dirname = dirname_buf; |
|
877 |
|
} |
|
878 |
|
|
|
879 |
|
ctx = kmod_new(dirname, config_paths); |
|
880 |
|
if (!ctx) { |
|
881 |
|
ERR("kmod_new() failed!\n"); |
|
882 |
|
err = -1; |
|
883 |
|
goto done; |
|
884 |
|
} |
|
885 |
|
|
|
886 |
|
log_setup_kmod_log(ctx, verbose); |
|
887 |
|
|
|
888 |
|
kmod_load_resources(ctx); |
|
889 |
|
|
|
890 |
|
if (do_show_config) |
|
891 |
|
err = show_config(ctx); |
|
892 |
|
else if (do_show_modversions) |
|
893 |
|
err = show_modversions(ctx, args[0]); |
|
894 |
|
else if (do_remove) |
|
895 |
|
err = rmmod_all(ctx, args, nargs); |
|
896 |
|
else if (use_all) |
|
897 |
|
err = insmod_all(ctx, args, nargs); |
|
898 |
|
else { |
|
899 |
|
char *opts; |
|
900 |
|
err = options_from_array(args, nargs, &opts); |
|
901 |
|
if (err == 0) { |
|
902 |
|
err = insmod(ctx, args[0], opts); |
|
903 |
|
free(opts); |
|
904 |
|
} |
|
905 |
|
} |
|
906 |
|
|
|
907 |
|
kmod_unref(ctx); |
|
908 |
|
|
|
909 |
|
done: |
|
910 |
|
log_close(); |
|
911 |
|
|
|
912 |
|
if (argv != orig_argv) |
|
913 |
|
free(argv); |
|
914 |
|
|
|
915 |
|
free(config_paths); |
|
916 |
|
|
|
917 |
|
return err >= 0 ? EXIT_SUCCESS : EXIT_FAILURE; |
|
918 |
|
} |
|
919 |
|
|
|
920 |
|
const struct kmod_cmd kmod_cmd_compat_modprobe = { |
|
921 |
|
.name = "modprobe", |
|
922 |
|
.cmd = do_modprobe, |
|
923 |
|
.help = "compat modprobe command", |
|
924 |
|
}; |