release: bump release to v3.7
[mit.git] / modinfo.c
blob2208b19976f03180a5de96c90b245780ad09b9b4
1 /* Extract module info: useful for both the curious and for scripts. */
2 #define _GNU_SOURCE /* asprintf rocks */
3 #include <elf.h>
4 #include <unistd.h>
5 #include <getopt.h>
6 #include <sys/types.h>
7 #include <dirent.h>
8 #include <errno.h>
9 #include <fcntl.h>
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <string.h>
13 #include <sys/utsname.h>
14 #include <sys/mman.h>
16 #include "util.h"
17 #include "zlibsupport.h"
18 #include "testing.h"
20 #ifndef MODULE_DIR
21 #define MODULE_DIR "/lib/modules"
22 #endif
24 static int elf_endian;
25 static int my_endian;
27 static inline void __endian(const void *src, void *dest, unsigned int size)
29 unsigned int i;
30 for (i = 0; i < size; i++)
31 ((unsigned char*)dest)[i] = ((unsigned char*)src)[size - i-1];
34 #define TO_NATIVE(x) \
35 ({ \
36 typeof(x) __x; \
37 if (elf_endian != my_endian) __endian(&(x), &(__x), sizeof(__x)); \
38 else __x = x; \
39 __x; \
42 static void *get_section32(void *file, unsigned long *size, const char *name)
44 Elf32_Ehdr *hdr = file;
45 Elf32_Shdr *sechdrs = file + TO_NATIVE(hdr->e_shoff);
46 const char *secnames;
47 unsigned int i;
49 secnames = file
50 + TO_NATIVE(sechdrs[TO_NATIVE(hdr->e_shstrndx)].sh_offset);
51 for (i = 1; i < TO_NATIVE(hdr->e_shnum); i++)
52 if (streq(secnames + TO_NATIVE(sechdrs[i].sh_name), name)) {
53 *size = TO_NATIVE(sechdrs[i].sh_size);
54 return file + TO_NATIVE(sechdrs[i].sh_offset);
56 return NULL;
59 static void *get_section64(void *file, unsigned long *size, const char *name)
61 Elf64_Ehdr *hdr = file;
62 Elf64_Shdr *sechdrs = file + TO_NATIVE(hdr->e_shoff);
63 const char *secnames;
64 unsigned int i;
66 secnames = file
67 + TO_NATIVE(sechdrs[TO_NATIVE(hdr->e_shstrndx)].sh_offset);
68 for (i = 1; i < TO_NATIVE(hdr->e_shnum); i++)
69 if (streq(secnames + TO_NATIVE(sechdrs[i].sh_name), name)) {
70 *size = TO_NATIVE(sechdrs[i].sh_size);
71 return file + TO_NATIVE(sechdrs[i].sh_offset);
73 return NULL;
76 static int elf_ident(void *mod, unsigned long size)
78 /* "\177ELF" <byte> where byte = 001 for 32-bit, 002 for 64 */
79 char *ident = mod;
81 if (size < EI_CLASS || memcmp(mod, ELFMAG, SELFMAG) != 0)
82 return ELFCLASSNONE;
83 elf_endian = ident[EI_DATA];
84 return ident[EI_CLASS];
87 static void *get_section(void *file, unsigned long filesize,
88 unsigned long *size, const char *name)
90 switch (elf_ident(file, filesize)) {
91 case ELFCLASS32:
92 return get_section32(file, size, name);
93 case ELFCLASS64:
94 return get_section64(file, size, name);
95 default:
96 return NULL;
100 struct param
102 struct param *next;
103 const char *name; /* Terminated by a colon */
104 const char *param;
105 const char *type;
108 static struct param *add_param(const char *name, struct param **list)
110 struct param *i;
111 unsigned int namelen = strcspn(name, ":") + 1;
113 for (i = *list; i; i = i->next)
114 if (strncmp(i->name, name, namelen) == 0)
115 return i;
116 i = malloc(sizeof(*i) + namelen+1);
117 strncpy((char *)(i + 1), name, namelen);
118 ((char *)(i + 1))[namelen] = '\0';
119 i->name = (char *)(i + 1);
120 i->param = NULL;
121 i->type = NULL;
122 i->next = *list;
123 *list = i;
124 return i;
127 static void print_tag(const char *tag, const char *info, unsigned long size,
128 const char *filename, char sep)
130 unsigned int taglen = strlen(tag);
132 if (streq(tag, "filename")) {
133 printf("%s%c", filename, sep);
134 return;
137 for (; info; info = next_string(info, &size))
138 if (strncmp(info, tag, taglen) == 0 && info[taglen] == '=')
139 printf("%s%c", info + taglen + 1, sep);
142 static void print_all(const char *info, unsigned long size,
143 const char *filename, char sep)
145 struct param *i, *params = NULL;
147 printf("%-16s%s%c", "filename:", filename, sep);
148 for (; info; info = next_string(info, &size)) {
149 char *eq, *colon;
151 /* We expect this in parm and parmtype. */
152 colon = strchr(info, ':');
154 /* We store these for handling at the end */
155 if (strstarts(info, "parm=") && colon) {
156 i = add_param(info + strlen("parm="), &params);
157 i->param = colon + 1;
158 continue;
160 if (strstarts(info, "parmtype=") && colon) {
161 i = add_param(info + strlen("parmtype="), &params);
162 i->type = colon + 1;
163 continue;
166 if (!sep) {
167 printf("%s%c", info, sep);
168 continue;
171 eq = strchr(info, '=');
172 /* Warn if no '=' maybe? */
173 if (eq) {
174 char tag[eq - info + 2];
175 strncpy(tag, info, eq - info);
176 tag[eq-info] = ':';
177 tag[eq-info+1] = '\0';
178 printf("%-16s%s%c", tag, eq+1, sep);
182 /* Now show parameters. */
183 for (i = params; i; i = i->next) {
184 if (!i->param)
185 printf("%-16s%s%s%c", "parm:", i->name, i->type, sep);
186 else if (i->type)
187 printf("%-16s%s%s (%s)%c",
188 "parm:", i->name, i->param, i->type, sep);
189 else
190 printf("%-16s%s%s%c", "parm:", i->name, i->param, sep);
194 static struct option options[] =
196 {"author", 0, 0, 'a'},
197 {"description", 0, 0, 'd'},
198 {"license", 0, 0, 'l'},
199 {"parameters", 0, 0, 'p'},
200 {"filename", 0, 0, 'n'},
201 {"version", 0, 0, 'V'},
202 {"help", 0, 0, 'h'},
203 {"null", 0, 0, '0'},
204 {"field", 1, 0, 'F'},
205 {0, 0, 0, 0}
208 /* - and _ are equivalent, and expect suffix. */
209 static int name_matches(const char *line, const char *end, const char *modname)
211 unsigned int i;
212 char *p;
214 /* Ignore comment lines */
215 if (line[strspn(line, "\t ")] == '#')
216 return 0;
218 /* Find last / before colon. */
219 p = memchr(line, ':', end - line);
220 if (!p)
221 return 0;
222 while (p > line) {
223 if (*p == '/') {
224 p++;
225 break;
227 p--;
230 for (i = 0; modname[i]; i++) {
231 /* Module names can't have colons. */
232 if (modname[i] == ':')
233 continue;
234 if (modname[i] == p[i])
235 continue;
236 if (modname[i] == '_' && p[i] == '-')
237 continue;
238 if (modname[i] == '-' && p[i] == '_')
239 continue;
240 return 0;
242 /* Must match all the way to the extension */
243 return (p[i] == '.');
246 static char *next_line(char *p, const char *end)
248 char *eol;
250 eol = memchr(p, '\n', end - p);
251 if (eol)
252 return eol + 1;
253 return (char *)end + 1;
256 static void *grab_module(const char *name, unsigned long *size, char**filename,
257 const char *kernel, const char *basedir)
259 char *data;
260 struct utsname buf;
261 char *depname, *p, *moddir;
263 if (strchr(name, '.') || strchr(name, '/')) {
264 data = grab_file(name, size);
265 if (data) {
266 *filename = strdup(name);
267 return data;
268 } else {
269 fprintf(stderr, "modinfo: could not open %s: %s\n",
270 name, strerror(errno));
271 return NULL;
275 if (kernel) {
276 if (strlen(basedir))
277 asprintf(&moddir, "%s/%s/%s",
278 basedir, MODULE_DIR, kernel);
279 else
280 asprintf(&moddir, "%s/%s",
281 MODULE_DIR, kernel);
282 } else {
283 uname(&buf);
284 if (strlen(basedir))
285 asprintf(&moddir, "%s/%s/%s",
286 basedir, MODULE_DIR, buf.release);
287 else
288 asprintf(&moddir, "%s/%s",
289 MODULE_DIR, buf.release);
291 asprintf(&depname, "%s/%s", moddir, "modules.dep");
293 /* Search for it in modules.dep. */
294 data = grab_file(depname, size);
295 if (!data) {
296 fprintf(stderr, "modinfo: could not open %s\n", depname);
297 free(depname);
298 return NULL;
300 free(depname);
302 for (p = data; p < data + *size; p = next_line(p, data + *size)) {
303 if (name_matches(p, data + *size, name)) {
304 int namelen = strcspn(p, ":");
306 if ('/' == p[0]) { /* old style deps - absolute path */
307 *filename = malloc(namelen + strlen(basedir)+2);
308 if (strlen(basedir)) {
309 sprintf(*filename, "%s/", basedir);
310 memcpy(*filename+strlen(basedir)+1,p,
311 namelen);
312 (*filename)[namelen
313 +strlen(basedir)+1] = '\0';
314 } else {
315 memcpy(*filename,p,namelen);
316 (*filename)[namelen] = '\0';
318 } else {
319 *filename = malloc(namelen + strlen(moddir)+2);
320 sprintf(*filename, "%s/", moddir);
321 memcpy(*filename+strlen(moddir)+1, p,namelen);
322 (*filename)[namelen+strlen(moddir)+1] ='\0';
324 release_file(data, *size);
325 data = grab_file(*filename, size);
326 if (!data)
327 fprintf(stderr,
328 "modinfo: could not open %s: %s\n",
329 *filename, strerror(errno));
330 return data;
333 release_file(data, *size);
334 fprintf(stderr, "modinfo: could not find module %s\n", name);
335 return NULL;
338 static void usage(const char *name)
340 fprintf(stderr, "Usage: %s [-0][-F field][-k kernelversion][-b basedir] module...\n"
341 " Prints out the information about one or more module(s).\n"
342 " If a fieldname is given, just print out that field (or nothing if not found).\n"
343 " Otherwise, print all information out in a readable form\n"
344 " If -0 is given, separate with nul, not newline.\n"
345 " If -b is given, use an image of the module tree.\n",
346 name);
349 int main(int argc, char *argv[])
351 union { short s; char c[2]; } endian_test;
352 const char *field = NULL;
353 const char *kernel = NULL;
354 char sep = '\n';
355 unsigned long infosize = 0;
356 int opt, ret = 0;
357 char *basedir = "";
359 endian_test.s = 1;
360 if (endian_test.c[1] == 1) my_endian = ELFDATA2MSB;
361 else if (endian_test.c[0] == 1) my_endian = ELFDATA2LSB;
362 else
363 abort();
365 while ((opt = getopt_long(argc,argv,"adlpVhn0F:k:b:",options,NULL)) >= 0){
366 switch (opt) {
367 case 'a': field = "author"; break;
368 case 'd': field = "description"; break;
369 case 'l': field = "license"; break;
370 case 'p': field = "parm"; break;
371 case 'n': field = "filename"; break;
372 case 'V': printf(PACKAGE " version " VERSION "\n"); exit(0);
373 case 'F': field = optarg; break;
374 case '0': sep = '\0'; break;
375 case 'k': kernel = optarg; break;
376 case 'b': basedir = optarg; break;
377 case 'h': usage(argv[0]); exit(0); break;
378 default:
379 usage(argv[0]); exit(1);
382 if (argc < optind + 1) {
383 usage(argv[0]);
384 exit(1);
387 for (opt = optind; opt < argc; opt++) {
388 void *info, *mod;
389 unsigned long modulesize;
390 char *filename;
392 mod = grab_module(argv[opt], &modulesize, &filename,
393 kernel, basedir);
394 if (!mod) {
395 ret = 1;
396 continue;
399 info = get_section(mod, modulesize, &infosize, ".modinfo");
400 if (!info) {
401 release_file(mod, modulesize);
402 free(filename);
403 continue;
405 if (field)
406 print_tag(field, info, infosize, filename, sep);
407 else
408 print_all(info, infosize, filename, sep);
409 release_file(mod, modulesize);
410 free(filename);
412 return ret;