Home | History | Annotate | Download | only in tools
      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 
     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 
     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 
    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 
    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 
    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 
    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 
    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 
    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 
    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