Move more logging to common files
[mit.git] / modinfo.c
blob7c9c276d9700c06bb44b8234fd344e5360d106cc
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>
15 #include "zlibsupport.h"
16 #include "backwards_compat.c"
18 #define streq(a,b) (strcmp((a),(b)) == 0)
19 #define strstarts(a,start) (strncmp((a),(start), strlen(start)) == 0)
21 #ifndef MODULE_DIR
22 #define MODULE_DIR "/lib/modules"
23 #endif
25 static int elf_endian;
26 static int my_endian;
28 static inline void __endian(const void *src, void *dest, unsigned int size)
30 unsigned int i;
31 for (i = 0; i < size; i++)
32 ((unsigned char*)dest)[i] = ((unsigned char*)src)[size - i-1];
35 #define TO_NATIVE(x) \
36 ({ \
37 typeof(x) __x; \
38 if (elf_endian != my_endian) __endian(&(x), &(__x), sizeof(__x)); \
39 else __x = x; \
40 __x; \
43 static void *get_section32(void *file, unsigned long *size, const char *name)
45 Elf32_Ehdr *hdr = file;
46 Elf32_Shdr *sechdrs = file + TO_NATIVE(hdr->e_shoff);
47 const char *secnames;
48 unsigned int i;
50 secnames = file
51 + TO_NATIVE(sechdrs[TO_NATIVE(hdr->e_shstrndx)].sh_offset);
52 for (i = 1; i < TO_NATIVE(hdr->e_shnum); i++)
53 if (streq(secnames + TO_NATIVE(sechdrs[i].sh_name), name)) {
54 *size = TO_NATIVE(sechdrs[i].sh_size);
55 return file + TO_NATIVE(sechdrs[i].sh_offset);
57 return NULL;
60 static void *get_section64(void *file, unsigned long *size, const char *name)
62 Elf64_Ehdr *hdr = file;
63 Elf64_Shdr *sechdrs = file + TO_NATIVE(hdr->e_shoff);
64 const char *secnames;
65 unsigned int i;
67 secnames = file
68 + TO_NATIVE(sechdrs[TO_NATIVE(hdr->e_shstrndx)].sh_offset);
69 for (i = 1; i < TO_NATIVE(hdr->e_shnum); i++)
70 if (streq(secnames + TO_NATIVE(sechdrs[i].sh_name), name)) {
71 *size = TO_NATIVE(sechdrs[i].sh_size);
72 return file + TO_NATIVE(sechdrs[i].sh_offset);
74 return NULL;
77 static int elf_ident(void *mod, unsigned long size)
79 /* "\177ELF" <byte> where byte = 001 for 32-bit, 002 for 64 */
80 char *ident = mod;
82 if (size < EI_CLASS || memcmp(mod, ELFMAG, SELFMAG) != 0)
83 return ELFCLASSNONE;
84 elf_endian = ident[EI_DATA];
85 return ident[EI_CLASS];
88 static void *get_section(void *file, unsigned long filesize,
89 unsigned long *size, const char *name)
91 switch (elf_ident(file, filesize)) {
92 case ELFCLASS32:
93 return get_section32(file, size, name);
94 case ELFCLASS64:
95 return get_section64(file, size, name);
96 default:
97 return NULL;
101 static const char *next_string(const char *string, unsigned long *secsize)
103 /* Skip non-zero chars */
104 while (string[0]) {
105 string++;
106 if ((*secsize)-- <= 1)
107 return NULL;
110 /* Skip any zero padding. */
111 while (!string[0]) {
112 string++;
113 if ((*secsize)-- <= 1)
114 return NULL;
116 return string;
119 struct param
121 struct param *next;
122 const char *name; /* Terminated by a colon */
123 const char *param;
124 const char *type;
127 static struct param *add_param(const char *name, struct param **list)
129 struct param *i;
130 unsigned int namelen = strcspn(name, ":") + 1;
132 for (i = *list; i; i = i->next)
133 if (strncmp(i->name, name, namelen) == 0)
134 return i;
135 i = malloc(sizeof(*i) + namelen+1);
136 strncpy((char *)(i + 1), name, namelen);
137 ((char *)(i + 1))[namelen] = '\0';
138 i->name = (char *)(i + 1);
139 i->param = NULL;
140 i->type = NULL;
141 i->next = *list;
142 *list = i;
143 return i;
146 static void print_tag(const char *tag, const char *info, unsigned long size,
147 const char *filename, char sep)
149 unsigned int taglen = strlen(tag);
151 if (streq(tag, "filename")) {
152 printf("%s%c", filename, sep);
153 return;
156 for (; info; info = next_string(info, &size))
157 if (strncmp(info, tag, taglen) == 0 && info[taglen] == '=')
158 printf("%s%c", info + taglen + 1, sep);
161 static void print_all(const char *info, unsigned long size,
162 const char *filename, char sep)
164 struct param *i, *params = NULL;
166 printf("%-16s%s%c", "filename:", filename, sep);
167 for (; info; info = next_string(info, &size)) {
168 char *eq, *colon;
170 /* We expect this in parm and parmtype. */
171 colon = strchr(info, ':');
173 /* We store these for handling at the end */
174 if (strstarts(info, "parm=") && colon) {
175 i = add_param(info + strlen("parm="), &params);
176 i->param = colon + 1;
177 continue;
179 if (strstarts(info, "parmtype=") && colon) {
180 i = add_param(info + strlen("parmtype="), &params);
181 i->type = colon + 1;
182 continue;
185 if (!sep) {
186 printf("%s%c", info, sep);
187 continue;
190 eq = strchr(info, '=');
191 /* Warn if no '=' maybe? */
192 if (eq) {
193 char tag[eq - info + 2];
194 strncpy(tag, info, eq - info);
195 tag[eq-info] = ':';
196 tag[eq-info+1] = '\0';
197 printf("%-16s%s%c", tag, eq+1, sep);
201 /* Now show parameters. */
202 for (i = params; i; i = i->next) {
203 if (!i->param)
204 printf("%-16s%s%s%c", "parm:", i->name, i->type, sep);
205 else if (i->type)
206 printf("%-16s%s%s (%s)%c",
207 "parm:", i->name, i->param, i->type, sep);
208 else
209 printf("%-16s%s%s%c", "parm:", i->name, i->param, sep);
213 static struct option options[] =
215 {"author", 0, 0, 'a'},
216 {"description", 0, 0, 'd'},
217 {"license", 0, 0, 'l'},
218 {"parameters", 0, 0, 'p'},
219 {"filename", 0, 0, 'n'},
220 {"version", 0, 0, 'V'},
221 {"help", 0, 0, 'h'},
222 {"null", 0, 0, '0'},
223 {"field", 1, 0, 'F'},
224 {0, 0, 0, 0}
227 /* - and _ are equivalent, and expect suffix. */
228 static int name_matches(const char *line, const char *end, const char *modname)
230 unsigned int i;
231 char *p;
233 /* Ignore comment lines */
234 if (line[strspn(line, "\t ")] == '#')
235 return 0;
237 /* Find last / before colon. */
238 p = memchr(line, ':', end - line);
239 if (!p)
240 return 0;
241 while (p > line) {
242 if (*p == '/') {
243 p++;
244 break;
246 p--;
249 for (i = 0; modname[i]; i++) {
250 /* Module names can't have colons. */
251 if (modname[i] == ':')
252 continue;
253 if (modname[i] == p[i])
254 continue;
255 if (modname[i] == '_' && p[i] == '-')
256 continue;
257 if (modname[i] == '-' && p[i] == '_')
258 continue;
259 return 0;
261 /* Must match all the way to the extension */
262 return (p[i] == '.');
265 static char *next_line(char *p, const char *end)
267 char *eol;
269 eol = memchr(p, '\n', end - p);
270 if (eol)
271 return eol + 1;
272 return (char *)end + 1;
275 static void *grab_module(const char *name, unsigned long *size, char**filename,
276 const char *kernel, const char *basedir)
278 char *data;
279 struct utsname buf;
280 char *depname, *p, *moddir;
282 if (strchr(name, '.') || strchr(name, '/')) {
283 data = grab_file(name, size);
284 if (data) {
285 *filename = strdup(name);
286 return data;
287 } else {
288 fprintf(stderr, "modinfo: could not open %s: %s\n",
289 name, strerror(errno));
290 return NULL;
294 if (kernel) {
295 if (strlen(basedir))
296 asprintf(&moddir, "%s/%s/%s",
297 basedir, MODULE_DIR, kernel);
298 else
299 asprintf(&moddir, "%s/%s",
300 MODULE_DIR, kernel);
301 } else {
302 uname(&buf);
303 if (strlen(basedir))
304 asprintf(&moddir, "%s/%s/%s",
305 basedir, MODULE_DIR, buf.release);
306 else
307 asprintf(&moddir, "%s/%s",
308 MODULE_DIR, buf.release);
310 asprintf(&depname, "%s/%s", moddir, "modules.dep");
312 /* Search for it in modules.dep. */
313 data = grab_file(depname, size);
314 if (!data) {
315 fprintf(stderr, "modinfo: could not open %s\n", depname);
316 free(depname);
317 return NULL;
319 free(depname);
321 for (p = data; p < data + *size; p = next_line(p, data + *size)) {
322 if (name_matches(p, data + *size, name)) {
323 int namelen = strcspn(p, ":");
325 if ('/' == p[0]) { /* old style deps - absolute path */
326 *filename = malloc(namelen + strlen(basedir)+2);
327 if (strlen(basedir)) {
328 sprintf(*filename, "%s/", basedir);
329 memcpy(*filename+strlen(basedir)+1,p,
330 namelen);
331 (*filename)[namelen
332 +strlen(basedir)+1] = '\0';
333 } else {
334 memcpy(*filename,p,namelen);
335 (*filename)[namelen] = '\0';
337 } else {
338 *filename = malloc(namelen + strlen(moddir)+2);
339 sprintf(*filename, "%s/", moddir);
340 memcpy(*filename+strlen(moddir)+1, p,namelen);
341 (*filename)[namelen+strlen(moddir)+1] ='\0';
343 release_file(data, *size);
344 data = grab_file(*filename, size);
345 if (!data)
346 fprintf(stderr,
347 "modinfo: could not open %s: %s\n",
348 *filename, strerror(errno));
349 return data;
352 release_file(data, *size);
353 fprintf(stderr, "modinfo: could not find module %s\n", name);
354 return NULL;
357 static void usage(const char *name)
359 fprintf(stderr, "Usage: %s [-0][-F field][-k kernelversion][-b basedir] module...\n"
360 " Prints out the information about one or more module(s).\n"
361 " If a fieldname is given, just print out that field (or nothing if not found).\n"
362 " Otherwise, print all information out in a readable form\n"
363 " If -0 is given, separate with nul, not newline.\n"
364 " If -b is given, use an image of the module tree.\n",
365 name);
368 int main(int argc, char *argv[])
370 union { short s; char c[2]; } endian_test;
371 const char *field = NULL;
372 const char *kernel = NULL;
373 char sep = '\n';
374 unsigned long infosize = 0;
375 int opt, ret = 0;
376 char *basedir = "";
378 if (!getenv("NEW_MODINFO"))
379 try_old_version("modinfo", argv);
381 endian_test.s = 1;
382 if (endian_test.c[1] == 1) my_endian = ELFDATA2MSB;
383 else if (endian_test.c[0] == 1) my_endian = ELFDATA2LSB;
384 else
385 abort();
387 while ((opt = getopt_long(argc,argv,"adlpVhn0F:k:b:",options,NULL)) >= 0){
388 switch (opt) {
389 case 'a': field = "author"; break;
390 case 'd': field = "description"; break;
391 case 'l': field = "license"; break;
392 case 'p': field = "parm"; break;
393 case 'n': field = "filename"; break;
394 case 'V': printf(PACKAGE " version " VERSION "\n"); exit(0);
395 case 'F': field = optarg; break;
396 case '0': sep = '\0'; break;
397 case 'k': kernel = optarg; break;
398 case 'b': basedir = optarg; break;
399 case 'h': usage(argv[0]); exit(0); break;
400 default:
401 usage(argv[0]); exit(1);
404 if (argc < optind + 1) {
405 usage(argv[0]);
406 exit(1);
409 for (opt = optind; opt < argc; opt++) {
410 void *info, *mod;
411 unsigned long modulesize;
412 char *filename;
414 mod = grab_module(argv[opt], &modulesize, &filename,
415 kernel, basedir);
416 if (!mod) {
417 ret = 1;
418 continue;
421 info = get_section(mod, modulesize, &infosize, ".modinfo");
422 if (!info) {
423 release_file(mod, modulesize);
424 free(filename);
425 continue;
427 if (field)
428 print_tag(field, info, infosize, filename, sep);
429 else
430 print_all(info, infosize, filename, sep);
431 release_file(mod, modulesize);
432 free(filename);
434 return ret;