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