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