blob: a0b2ce74dee7e1c32c8ea9e761a52e10f762da34
1 | /* vi: set sw=4 ts=4: */ |
2 | /* |
3 | * modinfo - retrieve module info |
4 | * Copyright (c) 2008 Pascal Bellard |
5 | * |
6 | * Licensed under GPLv2 or later, see file LICENSE in this source tree. |
7 | */ |
8 | |
9 | //applet:IF_MODINFO(APPLET(modinfo, BB_DIR_SBIN, BB_SUID_DROP)) |
10 | |
11 | //kbuild:lib-$(CONFIG_MODINFO) += modinfo.o modutils.o |
12 | |
13 | //config:config MODINFO |
14 | //config: bool "modinfo" |
15 | //config: default y |
16 | //config: select PLATFORM_LINUX |
17 | //config: help |
18 | //config: Show information about a Linux Kernel module |
19 | |
20 | #include <fnmatch.h> |
21 | #include <sys/utsname.h> /* uname() */ |
22 | #include "libbb.h" |
23 | #include "modutils.h" |
24 | |
25 | #if defined(ANDROID) || defined(__ANDROID__) |
26 | #define DONT_USE_UTS_REL_FOLDER |
27 | #endif |
28 | |
29 | enum { |
30 | OPT_TAGS = (1 << 12) - 1, /* shortcut count */ |
31 | OPT_F = (1 << 12), /* field name */ |
32 | OPT_0 = (1 << 13), /* \0 as separator */ |
33 | }; |
34 | |
35 | struct modinfo_env { |
36 | char *field; |
37 | int tags; |
38 | }; |
39 | |
40 | static int display(const char *data, const char *pattern, int flag) |
41 | { |
42 | if (flag) { |
43 | int n = printf("%s:", pattern); |
44 | while (n++ < 16) |
45 | bb_putchar(' '); |
46 | } |
47 | return printf("%s%c", data, (option_mask32 & OPT_0) ? '\0' : '\n'); |
48 | } |
49 | |
50 | static void modinfo(const char *path, const char *version, |
51 | const struct modinfo_env *env) |
52 | { |
53 | static const char *const shortcuts[] = { |
54 | "filename", |
55 | "license", |
56 | "author", |
57 | "description", |
58 | "version", |
59 | "alias", |
60 | "srcversion", |
61 | "depends", |
62 | "uts_release", |
63 | "vermagic", |
64 | "parm", |
65 | "firmware", |
66 | }; |
67 | size_t len; |
68 | int j, length; |
69 | char *ptr, *fullpath, *the_module; |
70 | const char *field = env->field; |
71 | int tags = env->tags; |
72 | |
73 | if (tags & 1) { /* filename */ |
74 | display(path, shortcuts[0], 1 != tags); |
75 | } |
76 | |
77 | len = MAXINT(ssize_t); |
78 | the_module = xmalloc_open_zipped_read_close(path, &len); |
79 | if (!the_module) { |
80 | if (path[0] == '/') |
81 | return; |
82 | /* Newer depmod puts relative paths in modules.dep */ |
83 | fullpath = xasprintf("%s/%s/%s", CONFIG_DEFAULT_MODULES_DIR, version, path); |
84 | the_module = xmalloc_open_zipped_read_close(fullpath, &len); |
85 | #ifdef DONT_USE_UTS_REL_FOLDER |
86 | if (!the_module) { |
87 | free((char*)fullpath); |
88 | fullpath = xasprintf("%s/%s", CONFIG_DEFAULT_MODULES_DIR, path); |
89 | the_module = xmalloc_open_zipped_read_close(fullpath, &len); |
90 | } |
91 | #endif |
92 | free((char*)fullpath); |
93 | if (!the_module) { |
94 | // outputs system error msg |
95 | bb_perror_msg("\n"); |
96 | return; |
97 | } |
98 | } |
99 | |
100 | if (field) |
101 | tags |= OPT_F; |
102 | for (j = 1; (1<<j) & (OPT_TAGS + OPT_F); j++) { |
103 | const char *pattern; |
104 | |
105 | if (!((1<<j) & tags)) |
106 | continue; |
107 | pattern = field; |
108 | if ((1<<j) & OPT_TAGS) |
109 | pattern = shortcuts[j]; |
110 | length = strlen(pattern); |
111 | ptr = the_module; |
112 | while (1) { |
113 | ptr = memchr(ptr, *pattern, len - (ptr - (char*)the_module)); |
114 | if (ptr == NULL) /* no occurance left, done */ |
115 | break; |
116 | if (strncmp(ptr, pattern, length) == 0 && ptr[length] == '=') { |
117 | /* field prefixes are 0x80 or 0x00 */ |
118 | if ((ptr[-1] & 0x7F) == '\0') { |
119 | ptr += length + 1; |
120 | ptr += display(ptr, pattern, (1<<j) != tags); |
121 | } |
122 | } |
123 | ++ptr; |
124 | } |
125 | } |
126 | free(the_module); |
127 | } |
128 | |
129 | //usage:#define modinfo_trivial_usage |
130 | //usage: "[-adlp0] [-F keyword] MODULE" |
131 | //usage:#define modinfo_full_usage "\n\n" |
132 | //usage: " -a Shortcut for '-F author'" |
133 | //usage: "\n -d Shortcut for '-F description'" |
134 | //usage: "\n -l Shortcut for '-F license'" |
135 | //usage: "\n -p Shortcut for '-F parm'" |
136 | //usage: "\n -F keyword Keyword to look for" |
137 | //usage: "\n -0 Separate output with NULs" |
138 | //usage:#define modinfo_example_usage |
139 | //usage: "$ modinfo -F vermagic loop\n" |
140 | |
141 | int modinfo_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
142 | int modinfo_main(int argc UNUSED_PARAM, char **argv) |
143 | { |
144 | struct modinfo_env env; |
145 | char name[MODULE_NAME_LEN]; |
146 | struct utsname uts; |
147 | parser_t *parser; |
148 | char *colon, *tokens[2]; |
149 | unsigned opts; |
150 | unsigned i; |
151 | |
152 | env.field = NULL; |
153 | opt_complementary = "-1"; /* minimum one param */ |
154 | opts = getopt32(argv, "nladvAsDumpF:0", &env.field); |
155 | env.tags = opts & OPT_TAGS ? opts & OPT_TAGS : OPT_TAGS; |
156 | argv += optind; |
157 | |
158 | uname(&uts); |
159 | parser = config_open2( |
160 | xasprintf("%s/%s/%s", CONFIG_DEFAULT_MODULES_DIR, uts.release, CONFIG_DEFAULT_DEPMOD_FILE), |
161 | fopen_for_read |
162 | ); |
163 | |
164 | #ifdef DONT_USE_UTS_REL_FOLDER |
165 | if (!parser) { |
166 | parser = config_open2( |
167 | xasprintf("%s/%s", CONFIG_DEFAULT_MODULES_DIR, CONFIG_DEFAULT_DEPMOD_FILE), |
168 | fopen_for_read |
169 | ); |
170 | } |
171 | #endif |
172 | if (!parser) { |
173 | strcpy(uts.release,""); |
174 | goto no_modules_dep; |
175 | } |
176 | |
177 | while (config_read(parser, tokens, 2, 1, "# \t", PARSE_NORMAL)) { |
178 | colon = last_char_is(tokens[0], ':'); |
179 | if (colon == NULL) |
180 | continue; |
181 | *colon = '\0'; |
182 | filename2modname(tokens[0], name); |
183 | for (i = 0; argv[i]; i++) { |
184 | if (fnmatch(argv[i], name, 0) == 0) { |
185 | modinfo(tokens[0], uts.release, &env); |
186 | argv[i] = (char *) ""; |
187 | } |
188 | } |
189 | } |
190 | if (ENABLE_FEATURE_CLEAN_UP) |
191 | config_close(parser); |
192 | |
193 | no_modules_dep: |
194 | for (i = 0; argv[i]; i++) { |
195 | if (argv[i][0]) { |
196 | modinfo(argv[i], uts.release, &env); |
197 | } |
198 | } |
199 | |
200 | return 0; |
201 | } |
202 |