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