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, ¶ms); 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, ¶ms); 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