]> git.ipfire.org Git - thirdparty/kmod.git/blame - libkmod/libkmod.c
kmod_module: return a new list and increase ref of dependencies
[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>
24#include <unistd.h>
25#include <errno.h>
7f3eb0cc 26#include <fnmatch.h>
ecd40ee4
LDM
27#include <string.h>
28#include <ctype.h>
221631d5 29#include <sys/utsname.h>
ecd40ee4 30
586fc304
LDM
31#include "libkmod.h"
32#include "libkmod-private.h"
9ba6f57b 33#include "libkmod-index.h"
ecd40ee4
LDM
34
35/**
586fc304
LDM
36 * SECTION:libkmod
37 * @short_description: libkmod context
ecd40ee4
LDM
38 *
39 * The context contains the default values for the library user,
40 * and is passed to all library operations.
41 */
42
43/**
586fc304 44 * kmod_ctx:
ecd40ee4
LDM
45 *
46 * Opaque object representing the library context.
47 */
586fc304 48struct kmod_ctx {
ecd40ee4 49 int refcount;
8d3f3ef8 50 int log_priority;
586fc304 51 void (*log_fn)(struct kmod_ctx *ctx,
e4351b05
LDM
52 int priority, const char *file, int line,
53 const char *fn, const char *format, va_list args);
1ce08a56
GSB
54 const void *userdata;
55 char *dirname;
d13e606f 56 struct kmod_config *config;
ecd40ee4
LDM
57};
58
586fc304 59void kmod_log(struct kmod_ctx *ctx,
e4351b05
LDM
60 int priority, const char *file, int line, const char *fn,
61 const char *format, ...)
ecd40ee4
LDM
62{
63 va_list args;
64
65 va_start(args, format);
66 ctx->log_fn(ctx, priority, file, line, fn, format, args);
67 va_end(args);
68}
69
586fc304 70static void log_stderr(struct kmod_ctx *ctx,
e4351b05
LDM
71 int priority, const char *file, int line,
72 const char *fn, const char *format, va_list args)
ecd40ee4 73{
586fc304 74 fprintf(stderr, "libkmod: %s: ", fn);
ecd40ee4
LDM
75 vfprintf(stderr, format, args);
76}
77
1ce08a56 78const char *kmod_get_dirname(const struct kmod_ctx *ctx)
221631d5
LDM
79{
80 return ctx->dirname;
81}
82
ecd40ee4 83/**
586fc304
LDM
84 * kmod_get_userdata:
85 * @ctx: kmod library context
ecd40ee4
LDM
86 *
87 * Retrieve stored data pointer from library context. This might be useful
88 * to access from callbacks like a custom logging function.
89 *
90 * Returns: stored userdata
91 **/
6d177553 92KMOD_EXPORT void *kmod_get_userdata(const struct kmod_ctx *ctx)
ecd40ee4
LDM
93{
94 if (ctx == NULL)
95 return NULL;
1ce08a56 96 return (void *)ctx->userdata;
ecd40ee4
LDM
97}
98
99/**
586fc304
LDM
100 * kmod_set_userdata:
101 * @ctx: kmod library context
ecd40ee4
LDM
102 * @userdata: data pointer
103 *
104 * Store custom @userdata in the library context.
105 **/
1ce08a56 106KMOD_EXPORT void kmod_set_userdata(struct kmod_ctx *ctx, const void *userdata)
ecd40ee4
LDM
107{
108 if (ctx == NULL)
109 return;
110 ctx->userdata = userdata;
111}
112
113static int log_priority(const char *priority)
114{
115 char *endptr;
116 int prio;
117
118 prio = strtol(priority, &endptr, 10);
119 if (endptr[0] == '\0' || isspace(endptr[0]))
120 return prio;
121 if (strncmp(priority, "err", 3) == 0)
122 return LOG_ERR;
123 if (strncmp(priority, "info", 4) == 0)
124 return LOG_INFO;
125 if (strncmp(priority, "debug", 5) == 0)
126 return LOG_DEBUG;
127 return 0;
128}
129
904c63aa
LDM
130static const char *dirname_default_prefix = "/lib/modules";
131
1ce08a56 132static char *get_kernel_release(const char *dirname)
221631d5
LDM
133{
134 struct utsname u;
904c63aa
LDM
135 char *p;
136
137 if (dirname != NULL)
138 return strdup(dirname);
221631d5
LDM
139
140 if (uname(&u) < 0)
141 return NULL;
142
904c63aa
LDM
143 if (asprintf(&p, "%s/%s", dirname_default_prefix, u.release) < 0)
144 return NULL;
145
146 return p;
221631d5
LDM
147}
148
ecd40ee4 149/**
586fc304 150 * kmod_new:
ecd40ee4 151 *
586fc304 152 * Create kmod library context. This reads the kmod configuration
ecd40ee4
LDM
153 * and fills in the default values.
154 *
155 * The initial refcount is 1, and needs to be decremented to
586fc304 156 * release the resources of the kmod library context.
ecd40ee4 157 *
586fc304 158 * Returns: a new kmod library context
ecd40ee4 159 **/
221631d5 160KMOD_EXPORT struct kmod_ctx *kmod_new(const char *dirname)
ecd40ee4
LDM
161{
162 const char *env;
52a7704f 163 struct kmod_ctx *ctx;
d13e606f 164 int err;
ecd40ee4 165
52a7704f
LDM
166 ctx = calloc(1, sizeof(struct kmod_ctx));
167 if (!ctx)
168 return NULL;
ecd40ee4 169
52a7704f
LDM
170 ctx->refcount = 1;
171 ctx->log_fn = log_stderr;
172 ctx->log_priority = LOG_ERR;
ecd40ee4 173
904c63aa 174 ctx->dirname = get_kernel_release(dirname);
221631d5 175
ecd40ee4 176 /* environment overwrites config */
586fc304 177 env = getenv("KMOD_LOG");
ecd40ee4 178 if (env != NULL)
52a7704f 179 kmod_set_log_priority(ctx, log_priority(env));
ecd40ee4 180
d13e606f
GSB
181 err = kmod_config_new(ctx, &ctx->config);
182 if (err < 0) {
183 ERR(ctx, "could not create config");
184 free(ctx->dirname);
185 free(ctx);
186 return NULL;
187 }
7c2ab358 188
ae6df84a
LDM
189 INFO(ctx, "ctx %p created\n", ctx);
190 DBG(ctx, "log_priority=%d\n", ctx->log_priority);
52a7704f
LDM
191
192 return ctx;
ecd40ee4
LDM
193}
194
195/**
586fc304
LDM
196 * kmod_ref:
197 * @ctx: kmod library context
ecd40ee4 198 *
586fc304 199 * Take a reference of the kmod library context.
ecd40ee4 200 *
586fc304 201 * Returns: the passed kmod library context
ecd40ee4 202 **/
586fc304 203KMOD_EXPORT struct kmod_ctx *kmod_ref(struct kmod_ctx *ctx)
ecd40ee4
LDM
204{
205 if (ctx == NULL)
206 return NULL;
207 ctx->refcount++;
208 return ctx;
209}
210
211/**
586fc304
LDM
212 * kmod_unref:
213 * @ctx: kmod library context
ecd40ee4 214 *
586fc304 215 * Drop a reference of the kmod library context. If the refcount
ecd40ee4
LDM
216 * reaches zero, the resources of the context will be released.
217 *
218 **/
586fc304 219KMOD_EXPORT struct kmod_ctx *kmod_unref(struct kmod_ctx *ctx)
ecd40ee4
LDM
220{
221 if (ctx == NULL)
222 return NULL;
4d1e689a
LDM
223
224 if (--ctx->refcount > 0)
ecd40ee4 225 return ctx;
ae6df84a 226 INFO(ctx, "context %p released\n", ctx);
1ce08a56 227 free(ctx->dirname);
d13e606f
GSB
228 if (ctx->config)
229 kmod_config_free(ctx->config);
ecd40ee4
LDM
230 free(ctx);
231 return NULL;
232}
233
234/**
586fc304
LDM
235 * kmod_set_log_fn:
236 * @ctx: kmod library context
ecd40ee4
LDM
237 * @log_fn: function to be called for logging messages
238 *
239 * The built-in logging writes to stderr. It can be
240 * overridden by a custom function, to plug log messages
241 * into the user's logging functionality.
242 *
243 **/
586fc304 244KMOD_EXPORT void kmod_set_log_fn(struct kmod_ctx *ctx,
e4351b05
LDM
245 void (*log_fn)(struct kmod_ctx *ctx,
246 int priority, const char *file,
247 int line, const char *fn,
248 const char *format, va_list args))
ecd40ee4
LDM
249{
250 ctx->log_fn = log_fn;
ae6df84a 251 INFO(ctx, "custom logging function %p registered\n", log_fn);
ecd40ee4
LDM
252}
253
254/**
586fc304
LDM
255 * kmod_get_log_priority:
256 * @ctx: kmod library context
ecd40ee4
LDM
257 *
258 * Returns: the current logging priority
259 **/
6d177553 260KMOD_EXPORT int kmod_get_log_priority(const struct kmod_ctx *ctx)
ecd40ee4
LDM
261{
262 return ctx->log_priority;
263}
264
265/**
586fc304
LDM
266 * kmod_set_log_priority:
267 * @ctx: kmod library context
ecd40ee4
LDM
268 * @priority: the new logging priority
269 *
270 * Set the current logging priority. The value controls which messages
271 * are logged.
272 **/
586fc304 273KMOD_EXPORT void kmod_set_log_priority(struct kmod_ctx *ctx, int priority)
ecd40ee4
LDM
274{
275 ctx->log_priority = priority;
276}
7f3eb0cc 277
9ba6f57b 278
a009482c
LDM
279static int kmod_lookup_alias_from_alias_bin(struct kmod_ctx *ctx,
280 const char *file,
7b30f4f4 281 const char *name,
9ba6f57b
LDM
282 struct kmod_list **list)
283{
284 char *fn;
6f1bc6e3 285 int err, nmatch = 0;
0fbdfef3 286 struct index_file *idx;
9ba6f57b
LDM
287 struct index_value *realnames, *realname;
288
7b30f4f4 289 if (asprintf(&fn, "%s/%s.bin", ctx->dirname, file) < 0)
9ba6f57b
LDM
290 return -ENOMEM;
291
e915f92a 292 DBG(ctx, "file=%s name=%s\n", fn, name);
9ba6f57b 293
0fbdfef3
LDM
294 idx = index_file_open(fn);
295 if (idx == NULL) {
9ba6f57b
LDM
296 free(fn);
297 return -ENOSYS;
298 }
299
0fbdfef3 300 realnames = index_searchwild(idx, name);
9ba6f57b
LDM
301 for (realname = realnames; realname; realname = realnames->next) {
302 struct kmod_module *mod;
303
304 err = kmod_module_new_from_name(ctx, realname->value, &mod);
305 if (err < 0) {
306 ERR(ctx, "%s\n", strerror(-err));
23fc91c6 307 goto fail;
9ba6f57b
LDM
308 }
309
310 *list = kmod_list_append(*list, mod);
23fc91c6 311 nmatch++;
9ba6f57b
LDM
312 }
313
9ba6f57b 314 index_values_free(realnames);
0fbdfef3 315 index_file_close(idx);
9ba6f57b
LDM
316 free(fn);
317
23fc91c6
LDM
318 return nmatch;
319
320fail:
321 *list = kmod_list_remove_n_latest(*list, nmatch);
9ba6f57b 322 return err;
7b30f4f4
LDM
323
324}
325
326static const char *symbols_file = "modules.symbols";
327
328int kmod_lookup_alias_from_symbols_file(struct kmod_ctx *ctx, const char *name,
329 struct kmod_list **list)
330{
331 if (!startswith(name, "symbol:"))
332 return 0;
333
334 return kmod_lookup_alias_from_alias_bin(ctx, symbols_file, name, list);
9ba6f57b
LDM
335}
336
49e61ca3
LDM
337
338static const char *aliases_file = "modules.alias";
339
340int kmod_lookup_alias_from_aliases_file(struct kmod_ctx *ctx, const char *name,
341 struct kmod_list **list)
342{
343 return kmod_lookup_alias_from_alias_bin(ctx, aliases_file, name, list);
344}
345
64700e47
LDM
346static const char *moddep_file = "modules.dep";
347
348int kmod_lookup_alias_from_moddep_file(struct kmod_ctx *ctx, const char *name,
349 struct kmod_list **list)
350{
6f1bc6e3 351 char *fn, *line;
0fbdfef3 352 struct index_file *idx;
64700e47
LDM
353 int n = 0;
354
355 /*
356 * Module names do not contain ':'. Return early if we know it will
357 * not be found.
358 */
359 if (strchr(name, ':'))
360 return 0;
361
362 if (asprintf(&fn, "%s/%s.bin", ctx->dirname, moddep_file) < 0)
363 return -ENOMEM;
364
e915f92a 365 DBG(ctx, "file=%s modname=%s\n", fn, name);
64700e47 366
0fbdfef3
LDM
367 idx = index_file_open(fn);
368 if (idx == NULL) {
64700e47
LDM
369 free(fn);
370 return -ENOSYS;
371 }
372
0fbdfef3 373 line = index_search(idx, name);
64700e47
LDM
374 if (line != NULL) {
375 struct kmod_module *mod;
376
377 n = kmod_module_new_from_name(ctx, name, &mod);
378 if (n < 0) {
379 ERR(ctx, "%s\n", strerror(-n));
380 goto finish;
381 }
382
383 *list = kmod_list_append(*list, mod);
4a3eb3a4 384 kmod_module_parse_dep(mod, line);
64700e47
LDM
385 }
386
387finish:
388 free(line);
0fbdfef3 389 index_file_close(idx);
64700e47
LDM
390 free(fn);
391
392 return n;
393}
394
7f3eb0cc
LDM
395int kmod_lookup_alias_from_config(struct kmod_ctx *ctx, const char *name,
396 struct kmod_list **list)
397{
d13e606f 398 struct kmod_config *config = ctx->config;
7f3eb0cc 399 struct kmod_list *l;
23fc91c6 400 int err, nmatch = 0;
7f3eb0cc
LDM
401
402 kmod_list_foreach(l, config->aliases) {
403 const char *aliasname = kmod_alias_get_name(l);
404 const char *modname = kmod_alias_get_modname(l);
405
406 if (fnmatch(aliasname, name, 0) == 0) {
407 struct kmod_module *mod;
408
409 err = kmod_module_new_from_name(ctx, modname, &mod);
410 if (err < 0) {
411 ERR(ctx, "%s", strerror(-err));
23fc91c6 412 goto fail;
7f3eb0cc
LDM
413 }
414
415 *list = kmod_list_append(*list, mod);
23fc91c6 416 nmatch++;
7f3eb0cc
LDM
417 }
418 }
419
23fc91c6
LDM
420 return nmatch;
421
422fail:
423 *list = kmod_list_remove_n_latest(*list, nmatch);
424 return err;
7f3eb0cc 425}