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