]> git.ipfire.org Git - thirdparty/kmod.git/blame - libkmod/libkmod.c
modname_normalize: fix const and buffer overflow.
[thirdparty/kmod.git] / libkmod / libkmod.c
CommitLineData
ecd40ee4 1/*
586fc304
LDM
2 * libkmod - interface to kernel module operations
3 *
4 * Copyright (C) 2011 ProFUSION embedded systems
586fc304
LDM
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 version 2.1.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18 */
ecd40ee4
LDM
19
20#include <stdio.h>
21#include <stdlib.h>
22#include <stddef.h>
23#include <stdarg.h>
1eb2ef69 24#include <limits.h>
ecd40ee4
LDM
25#include <unistd.h>
26#include <errno.h>
7f3eb0cc 27#include <fnmatch.h>
ecd40ee4
LDM
28#include <string.h>
29#include <ctype.h>
221631d5 30#include <sys/utsname.h>
ecd40ee4 31
586fc304
LDM
32#include "libkmod.h"
33#include "libkmod-private.h"
9ba6f57b 34#include "libkmod-index.h"
ecd40ee4 35
fd186ae9
LDM
36#define KMOD_HASH_SIZE (256)
37#define KMOD_LRU_MAX (128)
38
ecd40ee4 39/**
586fc304
LDM
40 * SECTION:libkmod
41 * @short_description: libkmod context
ecd40ee4
LDM
42 *
43 * The context contains the default values for the library user,
44 * and is passed to all library operations.
45 */
46
47/**
586fc304 48 * kmod_ctx:
ecd40ee4
LDM
49 *
50 * Opaque object representing the library context.
51 */
586fc304 52struct kmod_ctx {
ecd40ee4 53 int refcount;
8d3f3ef8 54 int log_priority;
586fc304 55 void (*log_fn)(struct kmod_ctx *ctx,
e4351b05
LDM
56 int priority, const char *file, int line,
57 const char *fn, const char *format, va_list args);
1ce08a56
GSB
58 const void *userdata;
59 char *dirname;
d13e606f 60 struct kmod_config *config;
fd186ae9 61 struct kmod_hash *modules_by_name;
ecd40ee4
LDM
62};
63
586fc304 64void kmod_log(struct kmod_ctx *ctx,
e4351b05
LDM
65 int priority, const char *file, int line, const char *fn,
66 const char *format, ...)
ecd40ee4
LDM
67{
68 va_list args;
69
70 va_start(args, format);
71 ctx->log_fn(ctx, priority, file, line, fn, format, args);
72 va_end(args);
73}
74
586fc304 75static void log_stderr(struct kmod_ctx *ctx,
e4351b05
LDM
76 int priority, const char *file, int line,
77 const char *fn, const char *format, va_list args)
ecd40ee4 78{
586fc304 79 fprintf(stderr, "libkmod: %s: ", fn);
ecd40ee4
LDM
80 vfprintf(stderr, format, args);
81}
82
1ce08a56 83const char *kmod_get_dirname(const struct kmod_ctx *ctx)
221631d5
LDM
84{
85 return ctx->dirname;
86}
87
ecd40ee4 88/**
586fc304
LDM
89 * kmod_get_userdata:
90 * @ctx: kmod library context
ecd40ee4
LDM
91 *
92 * Retrieve stored data pointer from library context. This might be useful
93 * to access from callbacks like a custom logging function.
94 *
95 * Returns: stored userdata
96 **/
6d177553 97KMOD_EXPORT void *kmod_get_userdata(const struct kmod_ctx *ctx)
ecd40ee4
LDM
98{
99 if (ctx == NULL)
100 return NULL;
1ce08a56 101 return (void *)ctx->userdata;
ecd40ee4
LDM
102}
103
104/**
586fc304
LDM
105 * kmod_set_userdata:
106 * @ctx: kmod library context
ecd40ee4
LDM
107 * @userdata: data pointer
108 *
109 * Store custom @userdata in the library context.
110 **/
1ce08a56 111KMOD_EXPORT void kmod_set_userdata(struct kmod_ctx *ctx, const void *userdata)
ecd40ee4
LDM
112{
113 if (ctx == NULL)
114 return;
115 ctx->userdata = userdata;
116}
117
118static int log_priority(const char *priority)
119{
120 char *endptr;
121 int prio;
122
123 prio = strtol(priority, &endptr, 10);
124 if (endptr[0] == '\0' || isspace(endptr[0]))
125 return prio;
126 if (strncmp(priority, "err", 3) == 0)
127 return LOG_ERR;
128 if (strncmp(priority, "info", 4) == 0)
129 return LOG_INFO;
130 if (strncmp(priority, "debug", 5) == 0)
131 return LOG_DEBUG;
132 return 0;
133}
134
904c63aa
LDM
135static const char *dirname_default_prefix = "/lib/modules";
136
1ce08a56 137static char *get_kernel_release(const char *dirname)
221631d5
LDM
138{
139 struct utsname u;
904c63aa
LDM
140 char *p;
141
142 if (dirname != NULL)
143 return strdup(dirname);
221631d5
LDM
144
145 if (uname(&u) < 0)
146 return NULL;
147
904c63aa
LDM
148 if (asprintf(&p, "%s/%s", dirname_default_prefix, u.release) < 0)
149 return NULL;
150
151 return p;
221631d5
LDM
152}
153
ecd40ee4 154/**
586fc304 155 * kmod_new:
ecd40ee4 156 *
586fc304 157 * Create kmod library context. This reads the kmod configuration
ecd40ee4
LDM
158 * and fills in the default values.
159 *
160 * The initial refcount is 1, and needs to be decremented to
586fc304 161 * release the resources of the kmod library context.
ecd40ee4 162 *
586fc304 163 * Returns: a new kmod library context
ecd40ee4 164 **/
221631d5 165KMOD_EXPORT struct kmod_ctx *kmod_new(const char *dirname)
ecd40ee4
LDM
166{
167 const char *env;
52a7704f 168 struct kmod_ctx *ctx;
d13e606f 169 int err;
ecd40ee4 170
52a7704f
LDM
171 ctx = calloc(1, sizeof(struct kmod_ctx));
172 if (!ctx)
173 return NULL;
ecd40ee4 174
52a7704f
LDM
175 ctx->refcount = 1;
176 ctx->log_fn = log_stderr;
177 ctx->log_priority = LOG_ERR;
ecd40ee4 178
904c63aa 179 ctx->dirname = get_kernel_release(dirname);
221631d5 180
ecd40ee4 181 /* environment overwrites config */
586fc304 182 env = getenv("KMOD_LOG");
ecd40ee4 183 if (env != NULL)
52a7704f 184 kmod_set_log_priority(ctx, log_priority(env));
ecd40ee4 185
d13e606f
GSB
186 err = kmod_config_new(ctx, &ctx->config);
187 if (err < 0) {
fd186ae9
LDM
188 ERR(ctx, "could not create config\n");
189 goto fail;
190 }
191
192 ctx->modules_by_name = kmod_hash_new(KMOD_HASH_SIZE, NULL);
193 if (ctx->modules_by_name == NULL) {
194 ERR(ctx, "could not create by-name hash\n");
195 goto fail;
d13e606f 196 }
7c2ab358 197
ae6df84a
LDM
198 INFO(ctx, "ctx %p created\n", ctx);
199 DBG(ctx, "log_priority=%d\n", ctx->log_priority);
52a7704f
LDM
200
201 return ctx;
fd186ae9
LDM
202
203fail:
204 free(ctx->modules_by_name);
205 free(ctx->dirname);
206 free(ctx);
207 return NULL;
ecd40ee4
LDM
208}
209
210/**
586fc304
LDM
211 * kmod_ref:
212 * @ctx: kmod library context
ecd40ee4 213 *
586fc304 214 * Take a reference of the kmod library context.
ecd40ee4 215 *
586fc304 216 * Returns: the passed kmod library context
ecd40ee4 217 **/
586fc304 218KMOD_EXPORT struct kmod_ctx *kmod_ref(struct kmod_ctx *ctx)
ecd40ee4
LDM
219{
220 if (ctx == NULL)
221 return NULL;
222 ctx->refcount++;
223 return ctx;
224}
225
226/**
586fc304
LDM
227 * kmod_unref:
228 * @ctx: kmod library context
ecd40ee4 229 *
586fc304 230 * Drop a reference of the kmod library context. If the refcount
ecd40ee4
LDM
231 * reaches zero, the resources of the context will be released.
232 *
233 **/
586fc304 234KMOD_EXPORT struct kmod_ctx *kmod_unref(struct kmod_ctx *ctx)
ecd40ee4
LDM
235{
236 if (ctx == NULL)
237 return NULL;
4d1e689a
LDM
238
239 if (--ctx->refcount > 0)
ecd40ee4 240 return ctx;
ae6df84a 241 INFO(ctx, "context %p released\n", ctx);
fd186ae9 242 kmod_hash_free(ctx->modules_by_name);
1ce08a56 243 free(ctx->dirname);
d13e606f
GSB
244 if (ctx->config)
245 kmod_config_free(ctx->config);
ecd40ee4
LDM
246 free(ctx);
247 return NULL;
248}
249
250/**
586fc304
LDM
251 * kmod_set_log_fn:
252 * @ctx: kmod library context
ecd40ee4
LDM
253 * @log_fn: function to be called for logging messages
254 *
255 * The built-in logging writes to stderr. It can be
256 * overridden by a custom function, to plug log messages
257 * into the user's logging functionality.
258 *
259 **/
586fc304 260KMOD_EXPORT void kmod_set_log_fn(struct kmod_ctx *ctx,
e4351b05
LDM
261 void (*log_fn)(struct kmod_ctx *ctx,
262 int priority, const char *file,
263 int line, const char *fn,
264 const char *format, va_list args))
ecd40ee4
LDM
265{
266 ctx->log_fn = log_fn;
ae6df84a 267 INFO(ctx, "custom logging function %p registered\n", log_fn);
ecd40ee4
LDM
268}
269
270/**
586fc304
LDM
271 * kmod_get_log_priority:
272 * @ctx: kmod library context
ecd40ee4
LDM
273 *
274 * Returns: the current logging priority
275 **/
6d177553 276KMOD_EXPORT int kmod_get_log_priority(const struct kmod_ctx *ctx)
ecd40ee4
LDM
277{
278 return ctx->log_priority;
279}
280
281/**
586fc304
LDM
282 * kmod_set_log_priority:
283 * @ctx: kmod library context
ecd40ee4
LDM
284 * @priority: the new logging priority
285 *
286 * Set the current logging priority. The value controls which messages
287 * are logged.
288 **/
586fc304 289KMOD_EXPORT void kmod_set_log_priority(struct kmod_ctx *ctx, int priority)
ecd40ee4
LDM
290{
291 ctx->log_priority = priority;
292}
7f3eb0cc 293
fd186ae9
LDM
294struct kmod_module *kmod_pool_get_module(struct kmod_ctx *ctx,
295 const char *name)
296{
297 struct kmod_module *mod;
298
299 mod = kmod_hash_find(ctx->modules_by_name, name);
300
301 DBG(ctx, "get module name='%s' found=%p\n", name, mod);
302
303 return mod;
304}
305
306void kmod_pool_add_module(struct kmod_ctx *ctx, struct kmod_module *mod)
307{
308 const char *name = kmod_module_get_name(mod);
309
310 DBG(ctx, "add %p name='%s'\n", mod, name);
311
312 kmod_hash_add(ctx->modules_by_name, name, mod);
313}
314
315void kmod_pool_del_module(struct kmod_ctx *ctx, struct kmod_module *mod)
316{
317 const char *name = kmod_module_get_name(mod);
318
319 DBG(ctx, "del %p name='%s'\n", mod, name);
320
321 kmod_hash_del(ctx->modules_by_name, name);
322}
9ba6f57b 323
a009482c
LDM
324static int kmod_lookup_alias_from_alias_bin(struct kmod_ctx *ctx,
325 const char *file,
7b30f4f4 326 const char *name,
9ba6f57b
LDM
327 struct kmod_list **list)
328{
329 char *fn;
6f1bc6e3 330 int err, nmatch = 0;
0fbdfef3 331 struct index_file *idx;
9ba6f57b
LDM
332 struct index_value *realnames, *realname;
333
7b30f4f4 334 if (asprintf(&fn, "%s/%s.bin", ctx->dirname, file) < 0)
9ba6f57b
LDM
335 return -ENOMEM;
336
e915f92a 337 DBG(ctx, "file=%s name=%s\n", fn, name);
9ba6f57b 338
0fbdfef3
LDM
339 idx = index_file_open(fn);
340 if (idx == NULL) {
9ba6f57b
LDM
341 free(fn);
342 return -ENOSYS;
343 }
344
0fbdfef3 345 realnames = index_searchwild(idx, name);
9ba6f57b
LDM
346 for (realname = realnames; realname; realname = realnames->next) {
347 struct kmod_module *mod;
348
349 err = kmod_module_new_from_name(ctx, realname->value, &mod);
350 if (err < 0) {
351 ERR(ctx, "%s\n", strerror(-err));
23fc91c6 352 goto fail;
9ba6f57b
LDM
353 }
354
355 *list = kmod_list_append(*list, mod);
23fc91c6 356 nmatch++;
9ba6f57b
LDM
357 }
358
9ba6f57b 359 index_values_free(realnames);
0fbdfef3 360 index_file_close(idx);
9ba6f57b
LDM
361 free(fn);
362
23fc91c6
LDM
363 return nmatch;
364
365fail:
366 *list = kmod_list_remove_n_latest(*list, nmatch);
9ba6f57b 367 return err;
7b30f4f4
LDM
368
369}
370
371static const char *symbols_file = "modules.symbols";
372
373int kmod_lookup_alias_from_symbols_file(struct kmod_ctx *ctx, const char *name,
374 struct kmod_list **list)
375{
376 if (!startswith(name, "symbol:"))
377 return 0;
378
379 return kmod_lookup_alias_from_alias_bin(ctx, symbols_file, name, list);
9ba6f57b
LDM
380}
381
49e61ca3
LDM
382
383static const char *aliases_file = "modules.alias";
384
385int kmod_lookup_alias_from_aliases_file(struct kmod_ctx *ctx, const char *name,
386 struct kmod_list **list)
387{
388 return kmod_lookup_alias_from_alias_bin(ctx, aliases_file, name, list);
389}
390
64700e47
LDM
391static const char *moddep_file = "modules.dep";
392
671d4894 393char *kmod_search_moddep(struct kmod_ctx *ctx, const char *name)
1eb2ef69
LDM
394{
395 struct index_file *idx;
396 char fn[PATH_MAX];
397 char *line;
398
399 snprintf(fn, sizeof(fn), "%s/%s.bin", ctx->dirname, moddep_file);
400
401 DBG(ctx, "file=%s modname=%s\n", fn, name);
402
403 idx = index_file_open(fn);
404 if (idx == NULL) {
405 ERR(ctx, "Could not open moddep file '%s'", fn);
406 return NULL;
407 }
408
409 line = index_search(idx, name);
410 index_file_close(idx);
411
412 return line;
413}
414
64700e47
LDM
415int kmod_lookup_alias_from_moddep_file(struct kmod_ctx *ctx, const char *name,
416 struct kmod_list **list)
417{
1eb2ef69 418 char *line;
64700e47
LDM
419 int n = 0;
420
421 /*
422 * Module names do not contain ':'. Return early if we know it will
423 * not be found.
424 */
425 if (strchr(name, ':'))
426 return 0;
427
671d4894 428 line = kmod_search_moddep(ctx, name);
64700e47
LDM
429 if (line != NULL) {
430 struct kmod_module *mod;
431
432 n = kmod_module_new_from_name(ctx, name, &mod);
433 if (n < 0) {
434 ERR(ctx, "%s\n", strerror(-n));
435 goto finish;
436 }
437
438 *list = kmod_list_append(*list, mod);
671d4894 439 kmod_module_parse_depline(mod, line);
64700e47
LDM
440 }
441
442finish:
443 free(line);
64700e47
LDM
444
445 return n;
446}
447
7f3eb0cc
LDM
448int kmod_lookup_alias_from_config(struct kmod_ctx *ctx, const char *name,
449 struct kmod_list **list)
450{
d13e606f 451 struct kmod_config *config = ctx->config;
7f3eb0cc 452 struct kmod_list *l;
23fc91c6 453 int err, nmatch = 0;
7f3eb0cc
LDM
454
455 kmod_list_foreach(l, config->aliases) {
456 const char *aliasname = kmod_alias_get_name(l);
457 const char *modname = kmod_alias_get_modname(l);
458
459 if (fnmatch(aliasname, name, 0) == 0) {
460 struct kmod_module *mod;
461
462 err = kmod_module_new_from_name(ctx, modname, &mod);
463 if (err < 0) {
464 ERR(ctx, "%s", strerror(-err));
23fc91c6 465 goto fail;
7f3eb0cc
LDM
466 }
467
468 *list = kmod_list_append(*list, mod);
23fc91c6 469 nmatch++;
7f3eb0cc
LDM
470 }
471 }
472
23fc91c6
LDM
473 return nmatch;
474
475fail:
476 *list = kmod_list_remove_n_latest(*list, nmatch);
477 return err;
7f3eb0cc 478}
1487a64f
GSB
479
480
481KMOD_EXPORT int kmod_module_get_filtered_blacklist(const struct kmod_ctx *ctx, const struct kmod_list *input, struct kmod_list **output)
482{
483 const struct kmod_config *config;
484 const struct kmod_list *li;
485
486 if (ctx == NULL || output == NULL)
487 return -ENOENT;
488
489 *output = NULL;
490 if (input == NULL)
491 return 0;
492
493 config = ctx->config;
494 kmod_list_foreach(li, input) {
495 struct kmod_module *mod = li->data;
496 const struct kmod_list *lb;
497 struct kmod_list *node;
498 bool filtered = false;
499 kmod_list_foreach(lb, config->blacklists) {
500 const char *name = lb->data;
501 if (streq(name, kmod_module_get_name(mod))) {
502 filtered = true;
503 break;
504 }
505 }
506 if (filtered)
507 continue;
508
509 node = kmod_list_append(*output, mod);
510 if (node == NULL)
511 goto fail;
512 *output = node;
513 kmod_module_ref(mod);
514 }
515 return 0;
516
517fail:
518 kmod_module_unref_list(*output);
519 *output = NULL;
520 return -ENOMEM;
521}