]> git.ipfire.org Git - thirdparty/kmod.git/blob - tools/modinfo.c
build-sys: require xsltproc when manpages are enabled
[thirdparty/kmod.git] / tools / modinfo.c
1 /*
2 * kmod-modinfo - query kernel module information using libkmod.
3 *
4 * Copyright (C) 2011-2012 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 <stdio.h>
21 #include <stdlib.h>
22 #include <stdbool.h>
23 #include <getopt.h>
24 #include <errno.h>
25 #include <string.h>
26 #include <limits.h>
27 #include <sys/utsname.h>
28 #include <sys/stat.h>
29 #include "libkmod.h"
30
31 #define LOG(fmt, ...) fprintf(stderr, "ERROR: "fmt, ##__VA_ARGS__)
32
33 static char separator = '\n';
34 static const char *field = NULL;
35
36 struct param {
37 struct param *next;
38 const char *name;
39 const char *param;
40 const char *type;
41 int namelen;
42 int paramlen;
43 int typelen;
44 };
45
46 static struct param *add_param(const char *name, int namelen, const char *param, int paramlen, const char *type, int typelen, struct param **list)
47 {
48 struct param *it;
49
50 for (it = *list; it != NULL; it = it->next) {
51 if (it->namelen == namelen &&
52 memcmp(it->name, name, namelen) == 0)
53 break;
54 }
55
56 if (it == NULL) {
57 it = malloc(sizeof(struct param));
58 if (it == NULL)
59 return NULL;
60 it->next = *list;
61 *list = it;
62 it->name = name;
63 it->namelen = namelen;
64 it->param = NULL;
65 it->type = NULL;
66 it->paramlen = 0;
67 it->typelen = 0;
68 }
69
70 if (param != NULL) {
71 it->param = param;
72 it->paramlen = paramlen;
73 }
74
75 if (type != NULL) {
76 it->type = type;
77 it->typelen = typelen;
78 }
79
80 return it;
81 }
82
83 static int process_parm(const char *key, const char *value, struct param **params)
84 {
85 const char *name, *param, *type;
86 int namelen, paramlen, typelen;
87 struct param *it;
88 const char *colon = strchr(value, ':');
89 if (colon == NULL) {
90 LOG("Found invalid \"%s=%s\": missing ':'\n",
91 key, value);
92 return 0;
93 }
94
95 name = value;
96 namelen = colon - value;
97 if (strcmp(key, "parm") == 0) {
98 param = colon + 1;
99 paramlen = strlen(param);
100 type = NULL;
101 typelen = 0;
102 } else {
103 param = NULL;
104 paramlen = 0;
105 type = colon + 1;
106 typelen = strlen(type);
107 }
108
109 it = add_param(name, namelen, param, paramlen, type, typelen, params);
110 if (it == NULL) {
111 LOG("Out of memory!\n");
112 return -ENOMEM;
113 }
114
115 return 0;
116 }
117
118 static int modinfo_params_do(const struct kmod_list *list)
119 {
120 const struct kmod_list *l;
121 struct param *params = NULL;
122 int err = 0;
123
124 kmod_list_foreach(l, list) {
125 const char *key = kmod_module_info_get_key(l);
126 const char *value = kmod_module_info_get_value(l);
127 if (strcmp(key, "parm") != 0 &&
128 strcmp(key, "parmtype") != 0)
129 continue;
130
131 err = process_parm(key, value, &params);
132 if (err < 0)
133 goto end;
134 }
135
136 while (params != NULL) {
137 struct param *p = params;
138 params = p->next;
139
140 if (p->param == NULL)
141 printf("%.*s: (%.*s)%c",
142 p->namelen, p->name, p->typelen, p->type,
143 separator);
144 else if (p->type != NULL)
145 printf("%.*s:%.*s (%.*s)%c",
146 p->namelen, p->name,
147 p->paramlen, p->param,
148 p->typelen, p->type,
149 separator);
150 else
151 printf("%.*s:%.*s%c",
152 p->namelen, p->name,
153 p->paramlen, p->param,
154 separator);
155
156 free(p);
157 }
158
159 end:
160 while (params != NULL) {
161 void *tmp = params;
162 params = params->next;
163 free(tmp);
164 }
165
166 return err;
167 }
168
169 static int modinfo_do(struct kmod_module *mod)
170 {
171 struct kmod_list *l, *list = NULL;
172 struct param *params = NULL;
173 int err;
174
175 if (field != NULL && strcmp(field, "filename") == 0) {
176 printf("%s%c", kmod_module_get_path(mod), separator);
177 return 0;
178 } else if (field == NULL) {
179 printf("%-16s%s%c", "filename:",
180 kmod_module_get_path(mod), separator);
181 }
182
183 err = kmod_module_get_info(mod, &list);
184 if (err < 0) {
185 LOG("could not get modinfo from '%s': %s\n",
186 kmod_module_get_name(mod), strerror(-err));
187 return err;
188 }
189
190 if (field != NULL && strcmp(field, "parm") == 0) {
191 err = modinfo_params_do(list);
192 goto end;
193 }
194
195 kmod_list_foreach(l, list) {
196 const char *key = kmod_module_info_get_key(l);
197 const char *value = kmod_module_info_get_value(l);
198 int keylen;
199
200 if (field != NULL) {
201 if (strcmp(field, key) != 0)
202 continue;
203 /* filtered output contains no key, just value */
204 printf("%s%c", value, separator);
205 continue;
206 }
207
208 if (strcmp(key, "parm") == 0 || strcmp(key, "parmtype") == 0) {
209 err = process_parm(key, value, &params);
210 if (err < 0)
211 goto end;
212 continue;
213 }
214
215 if (separator == '\0') {
216 printf("%s=%s%c", key, value, separator);
217 continue;
218 }
219
220 keylen = strlen(key);
221 printf("%s:%-*s%s%c", key, 15 - keylen, "", value, separator);
222 }
223
224 if (field != NULL)
225 goto end;
226
227 while (params != NULL) {
228 struct param *p = params;
229 params = p->next;
230
231 if (p->param == NULL)
232 printf("%-16s%.*s:%.*s%c", "parm:",
233 p->namelen, p->name, p->typelen, p->type,
234 separator);
235 else if (p->type != NULL)
236 printf("%-16s%.*s:%.*s (%.*s)%c", "parm:",
237 p->namelen, p->name,
238 p->paramlen, p->param,
239 p->typelen, p->type,
240 separator);
241 else
242 printf("%-16s%.*s:%.*s%c",
243 "parm:",
244 p->namelen, p->name,
245 p->paramlen, p->param,
246 separator);
247
248 free(p);
249 }
250
251 end:
252 while (params != NULL) {
253 void *tmp = params;
254 params = params->next;
255 free(tmp);
256 }
257 kmod_module_info_free_list(list);
258
259 return err;
260 }
261
262 static int modinfo_path_do(struct kmod_ctx *ctx, const char *path)
263 {
264 struct kmod_module *mod;
265 int err = kmod_module_new_from_path(ctx, path, &mod);
266 if (err < 0) {
267 LOG("Module file %s not found.\n", path);
268 return err;
269 }
270 err = modinfo_do(mod);
271 kmod_module_unref(mod);
272 return err;
273 }
274
275 static int modinfo_alias_do(struct kmod_ctx *ctx, const char *alias)
276 {
277 struct kmod_list *l, *filtered, *list = NULL;
278 int err = kmod_module_new_from_lookup(ctx, alias, &list);
279 if (err < 0) {
280 LOG("Module alias %s not found.\n", alias);
281 return err;
282 }
283
284 if (list == NULL) {
285 LOG("Module %s not found.\n", alias);
286 return -ENOENT;
287 }
288
289 err = kmod_module_apply_filter(ctx, KMOD_FILTER_BUILTIN, list, &filtered);
290 kmod_module_unref_list(list);
291 if (err < 0) {
292 LOG("Failed to filter list: %m\n");
293 return err;
294 }
295
296 if (filtered == NULL) {
297 LOG("Module %s not found.\n", alias);
298 return -ENOENT;
299 }
300
301 kmod_list_foreach(l, filtered) {
302 struct kmod_module *mod = kmod_module_get_module(l);
303 int r = modinfo_do(mod);
304 kmod_module_unref(mod);
305 if (r < 0)
306 err = r;
307 }
308 kmod_module_unref_list(filtered);
309 return err;
310 }
311
312 static const char cmdopts_s[] = "adlpn0F:k:b:Vh";
313 static const struct option cmdopts[] = {
314 {"author", no_argument, 0, 'a'},
315 {"description", no_argument, 0, 'd'},
316 {"license", no_argument, 0, 'l'},
317 {"parameters", no_argument, 0, 'p'},
318 {"filename", no_argument, 0, 'n'},
319 {"null", no_argument, 0, '0'},
320 {"field", required_argument, 0, 'F'},
321 {"set-version", required_argument, 0, 'k'},
322 {"basedir", required_argument, 0, 'b'},
323 {"version", no_argument, 0, 'V'},
324 {"help", no_argument, 0, 'h'},
325 {NULL, 0, 0, 0}
326 };
327
328 static void help(const char *progname)
329 {
330 fprintf(stderr,
331 "Usage:\n"
332 "\t%s [options] filename [args]\n"
333 "Options:\n"
334 "\t-a, --author Print only 'author'\n"
335 "\t-d, --description Print only 'description'\n"
336 "\t-l, --license Print only 'license'\n"
337 "\t-p, --parameters Print only 'parm'\n"
338 "\t-n, --filename Print only 'filename'\n"
339 "\t-0, --null Use \\0 instead of \\n\n"
340 "\t-F, --field=FIELD Print only provided FIELD\n"
341 "\t-k, --set-version=VERSION Use VERSION instead of `uname -r`\n"
342 "\t-b, --basedir=DIR Use DIR as filesystem root for /lib/modules\n"
343 "\t-V, --version Show version\n"
344 "\t-h, --help Show this help\n",
345 progname);
346 }
347
348 static bool is_module_filename(const char *name)
349 {
350 struct stat st;
351 const char *ptr;
352
353 if (stat(name, &st) == 0 && S_ISREG(st.st_mode) &&
354 (ptr = strstr(name, ".ko")) != NULL) {
355 /*
356 * We screened for .ko; make sure this is either at the end of
357 * the name or followed by another '.' (e.g. gz or xz modules)
358 */
359 if(ptr[3] == '\0' || ptr[3] == '.')
360 return true;
361 }
362
363 return false;
364 }
365
366 static int do_modinfo(int argc, char *argv[])
367 {
368 struct kmod_ctx *ctx;
369 char dirname_buf[PATH_MAX];
370 const char *dirname = NULL;
371 const char *kversion = NULL;
372 const char *root = NULL;
373 const char *null_config = NULL;
374 int i, err;
375
376 for (;;) {
377 int c, idx = 0;
378 c = getopt_long(argc, argv, cmdopts_s, cmdopts, &idx);
379 if (c == -1)
380 break;
381 switch (c) {
382 case 'a':
383 field = "author";
384 break;
385 case 'd':
386 field = "description";
387 break;
388 case 'l':
389 field = "license";
390 break;
391 case 'p':
392 field = "parm";
393 break;
394 case 'n':
395 field = "filename";
396 break;
397 case '0':
398 separator = '\0';
399 break;
400 case 'F':
401 field = optarg;
402 break;
403 case 'k':
404 kversion = optarg;
405 break;
406 case 'b':
407 root = optarg;
408 break;
409 case 'h':
410 help(basename(argv[0]));
411 return EXIT_SUCCESS;
412 case 'V':
413 puts(PACKAGE " version " VERSION);
414 return EXIT_SUCCESS;
415 case '?':
416 return EXIT_FAILURE;
417 default:
418 fprintf(stderr,
419 "Error: unexpected getopt_long() value '%c'.\n",
420 c);
421 return EXIT_FAILURE;
422 }
423 }
424
425 if (optind >= argc) {
426 fprintf(stderr, "Error: missing module or filename.\n");
427 return EXIT_FAILURE;
428 }
429
430 if (root != NULL || kversion != NULL) {
431 struct utsname u;
432 if (root == NULL)
433 root = "";
434 if (kversion == NULL) {
435 if (uname(&u) < 0) {
436 fprintf(stderr, "Error: uname() failed: %s\n",
437 strerror(errno));
438 return EXIT_FAILURE;
439 }
440 kversion = u.release;
441 }
442 snprintf(dirname_buf, sizeof(dirname_buf), "%s/lib/modules/%s",
443 root, kversion);
444 dirname = dirname_buf;
445 }
446
447 ctx = kmod_new(dirname, &null_config);
448 if (!ctx) {
449 fputs("Error: kmod_new() failed!\n", stderr);
450 return EXIT_FAILURE;
451 }
452
453 err = 0;
454 for (i = optind; i < argc; i++) {
455 const char *name = argv[i];
456 int r;
457
458 if (is_module_filename(name))
459 r = modinfo_path_do(ctx, name);
460 else
461 r = modinfo_alias_do(ctx, name);
462
463 if (r < 0)
464 err = r;
465 }
466
467 kmod_unref(ctx);
468 return err >= 0 ? EXIT_SUCCESS : EXIT_FAILURE;
469 }
470
471 #include "kmod.h"
472
473 const struct kmod_cmd kmod_cmd_compat_modinfo = {
474 .name = "modinfo",
475 .cmd = do_modinfo,
476 .help = "compat modinfo command",
477 };