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, is_builtin;
176 	const char *filename = kmod_module_get_path(mod);
177 
178 	is_builtin = (filename == NULL);
179 
180 	if (is_builtin) {
181 		printf("%-16s%s%c", "name:", kmod_module_get_name(mod), separator);
182 		filename = "(builtin)";
183 	}
184 
185 	if (field != NULL && streq(field, "filename")) {
186 		printf("%s%c", filename, separator);
187 		return 0;
188 	} else if (field == NULL) {
189 		printf("%-16s%s%c", "filename:",
190 		       filename, separator);
191 	}
192 
193 	err = kmod_module_get_info(mod, &list);
194 	if (err < 0) {
195 		if (is_builtin && err == -ENOENT) {
196 			/*
197 			 * This is an old kernel that does not have a file
198 			 * with information about built-in modules.
199 			 */
200 			return 0;
201 		}
202 		ERR("could not get modinfo from '%s': %s\n",
203 			kmod_module_get_name(mod), strerror(-err));
204 		return err;
205 	}
206 
207 	if (field != NULL && streq(field, "parm")) {
208 		err = modinfo_params_do(list);
209 		goto end;
210 	}
211 
212 	kmod_list_foreach(l, list) {
213 		const char *key = kmod_module_info_get_key(l);
214 		const char *value = kmod_module_info_get_value(l);
215 		int keylen;
216 
217 		if (field != NULL) {
218 			if (!streq(field, key))
219 				continue;
220 			/* filtered output contains no key, just value */
221 			printf("%s%c", value, separator);
222 			continue;
223 		}
224 
225 		if (streq(key, "parm") || streq(key, "parmtype")) {
226 			err = process_parm(key, value, &params);
227 			if (err < 0)
228 				goto end;
229 			continue;
230 		}
231 
232 		if (separator == '\0') {
233 			printf("%s=%s%c", key, value, separator);
234 			continue;
235 		}
236 
237 		keylen = strlen(key);
238 		printf("%s:%-*s%s%c", key, 15 - keylen, "", value, separator);
239 	}
240 
241 	if (field != NULL)
242 		goto end;
243 
244 	while (params != NULL) {
245 		struct param *p = params;
246 		params = p->next;
247 
248 		if (p->param == NULL)
249 			printf("%-16s%.*s:%.*s%c", "parm:",
250 			       p->namelen, p->name, p->typelen, p->type,
251 			       separator);
252 		else if (p->type != NULL)
253 			printf("%-16s%.*s:%.*s (%.*s)%c", "parm:",
254 			       p->namelen, p->name,
255 			       p->paramlen, p->param,
256 			       p->typelen, p->type,
257 			       separator);
258 		else
259 			printf("%-16s%.*s:%.*s%c",
260 			       "parm:",
261 			       p->namelen, p->name,
262 			       p->paramlen, p->param,
263 			       separator);
264 
265 		free(p);
266 	}
267 
268 end:
269 	while (params != NULL) {
270 		void *tmp = params;
271 		params = params->next;
272 		free(tmp);
273 	}
274 	kmod_module_info_free_list(list);
275 
276 	return err;
277 }
278 
modinfo_path_do(struct kmod_ctx * ctx,const char * path)279 static int modinfo_path_do(struct kmod_ctx *ctx, const char *path)
280 {
281 	struct kmod_module *mod;
282 	int err = kmod_module_new_from_path(ctx, path, &mod);
283 	if (err < 0) {
284 		ERR("Module file %s not found.\n", path);
285 		return err;
286 	}
287 	err = modinfo_do(mod);
288 	kmod_module_unref(mod);
289 	return err;
290 }
291 
modinfo_alias_do(struct kmod_ctx * ctx,const char * alias)292 static int modinfo_alias_do(struct kmod_ctx *ctx, const char *alias)
293 {
294 	struct kmod_list *l, *list = NULL;
295 	int err = kmod_module_new_from_lookup(ctx, alias, &list);
296 	if (err < 0) {
297 		ERR("Module alias %s not found.\n", alias);
298 		return err;
299 	}
300 
301 	if (list == NULL) {
302 		ERR("Module %s not found.\n", alias);
303 		return -ENOENT;
304 	}
305 
306 	kmod_list_foreach(l, list) {
307 		struct kmod_module *mod = kmod_module_get_module(l);
308 		int r = modinfo_do(mod);
309 		kmod_module_unref(mod);
310 		if (r < 0)
311 			err = r;
312 	}
313 	kmod_module_unref_list(list);
314 	return err;
315 }
316 
317 static const char cmdopts_s[] = "adlpn0F:k:b:Vh";
318 static const struct option cmdopts[] = {
319 	{"author", no_argument, 0, 'a'},
320 	{"description", no_argument, 0, 'd'},
321 	{"license", no_argument, 0, 'l'},
322 	{"parameters", no_argument, 0, 'p'},
323 	{"filename", no_argument, 0, 'n'},
324 	{"null", no_argument, 0, '0'},
325 	{"field", required_argument, 0, 'F'},
326 	{"set-version", required_argument, 0, 'k'},
327 	{"basedir", required_argument, 0, 'b'},
328 	{"version", no_argument, 0, 'V'},
329 	{"help", no_argument, 0, 'h'},
330 	{NULL, 0, 0, 0}
331 };
332 
help(void)333 static void help(void)
334 {
335 	printf("Usage:\n"
336 		"\t%s [options] filename [args]\n"
337 		"Options:\n"
338 		"\t-a, --author                Print only 'author'\n"
339 		"\t-d, --description           Print only 'description'\n"
340 		"\t-l, --license               Print only 'license'\n"
341 		"\t-p, --parameters            Print only 'parm'\n"
342 		"\t-n, --filename              Print only 'filename'\n"
343 		"\t-0, --null                  Use \\0 instead of \\n\n"
344 		"\t-F, --field=FIELD           Print only provided FIELD\n"
345 		"\t-k, --set-version=VERSION   Use VERSION instead of `uname -r`\n"
346 		"\t-b, --basedir=DIR           Use DIR as filesystem root for /lib/modules\n"
347 		"\t-V, --version               Show version\n"
348 		"\t-h, --help                  Show this help\n",
349 		program_invocation_short_name);
350 }
351 
is_module_filename(const char * name)352 static bool is_module_filename(const char *name)
353 {
354 	struct stat st;
355 
356 	if (stat(name, &st) == 0 && S_ISREG(st.st_mode) &&
357 		path_ends_with_kmod_ext(name, strlen(name)))
358 			return true;
359 
360 	return false;
361 }
362 
do_modinfo(int argc,char * argv[])363 static int do_modinfo(int argc, char *argv[])
364 {
365 	struct kmod_ctx *ctx;
366 	char dirname_buf[PATH_MAX];
367 	const char *dirname = NULL;
368 	const char *kversion = NULL;
369 	const char *root = NULL;
370 	const char *null_config = NULL;
371 	int i, err;
372 
373 	for (;;) {
374 		int c, idx = 0;
375 		c = getopt_long(argc, argv, cmdopts_s, cmdopts, &idx);
376 		if (c == -1)
377 			break;
378 		switch (c) {
379 		case 'a':
380 			field = "author";
381 			break;
382 		case 'd':
383 			field = "description";
384 			break;
385 		case 'l':
386 			field = "license";
387 			break;
388 		case 'p':
389 			field = "parm";
390 			break;
391 		case 'n':
392 			field = "filename";
393 			break;
394 		case '0':
395 			separator = '\0';
396 			break;
397 		case 'F':
398 			field = optarg;
399 			break;
400 		case 'k':
401 			kversion = optarg;
402 			break;
403 		case 'b':
404 			root = optarg;
405 			break;
406 		case 'h':
407 			help();
408 			return EXIT_SUCCESS;
409 		case 'V':
410 			puts(PACKAGE " version " VERSION);
411 			puts(KMOD_FEATURES);
412 			return EXIT_SUCCESS;
413 		case '?':
414 			return EXIT_FAILURE;
415 		default:
416 			ERR("unexpected getopt_long() value '%c'.\n", c);
417 			return EXIT_FAILURE;
418 		}
419 	}
420 
421 	if (optind >= argc) {
422 		ERR("missing module or filename.\n");
423 		return EXIT_FAILURE;
424 	}
425 
426 	if (root != NULL || kversion != NULL) {
427 		struct utsname u;
428 		if (root == NULL)
429 			root = "";
430 		if (kversion == NULL) {
431 			if (uname(&u) < 0) {
432 				ERR("uname() failed: %m\n");
433 				return EXIT_FAILURE;
434 			}
435 			kversion = u.release;
436 		}
437 		snprintf(dirname_buf, sizeof(dirname_buf), "%s/lib/modules/%s",
438 			 root, kversion);
439 		dirname = dirname_buf;
440 	}
441 
442 	ctx = kmod_new(dirname, &null_config);
443 	if (!ctx) {
444 		ERR("kmod_new() failed!\n");
445 		return EXIT_FAILURE;
446 	}
447 
448 	err = 0;
449 	for (i = optind; i < argc; i++) {
450 		const char *name = argv[i];
451 		int r;
452 
453 		if (is_module_filename(name))
454 			r = modinfo_path_do(ctx, name);
455 		else
456 			r = modinfo_alias_do(ctx, name);
457 
458 		if (r < 0)
459 			err = r;
460 	}
461 
462 	kmod_unref(ctx);
463 	return err >= 0 ? EXIT_SUCCESS : EXIT_FAILURE;
464 }
465 
466 const struct kmod_cmd kmod_cmd_compat_modinfo = {
467 	.name = "modinfo",
468 	.cmd = do_modinfo,
469 	.help = "compat modinfo command",
470 };
471