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