meta: update various documentation, logs, authors, etc.
[mit.git] / modinfo.c
blobc2d733fdc6f13dc2b59887e41d90bff66673b089
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 "logging.h"
18 #include "elfops.h"
19 #include "zlibsupport.h"
20 #include "testing.h"
22 #ifndef MODULE_DIR
23 #define MODULE_DIR "/lib/modules"
24 #endif
26 struct param
28 struct param *next;
29 const char *name; /* Terminated by a colon */
30 const char *param;
31 const char *type;
34 static struct param *add_param(const char *name, struct param **list)
36 struct param *i;
37 unsigned int namelen = strcspn(name, ":") + 1;
39 for (i = *list; i; i = i->next)
40 if (strncmp(i->name, name, namelen) == 0)
41 return i;
42 i = NOFAIL(malloc(sizeof(*i) + namelen+1));
43 strncpy((char *)(i + 1), name, namelen);
44 ((char *)(i + 1))[namelen] = '\0';
45 i->name = (char *)(i + 1);
46 i->param = NULL;
47 i->type = NULL;
48 i->next = *list;
49 *list = i;
50 return i;
53 static void print_tag(const char *tag, struct string_table *tags,
54 const char *filename, char sep)
56 int j;
57 unsigned int taglen = strlen(tag);
59 if (streq(tag, "filename")) {
60 printf("%s%c", filename, sep);
61 return;
64 for (j = 0; j < tags->cnt; j++) {
65 const char *info = tags->str[j];
66 if (strncmp(info, tag, taglen) == 0 && info[taglen] == '=')
67 printf("%s%c", info + taglen + 1, sep);
71 static void print_all(struct string_table *tags,
72 const char *filename, char sep)
74 int j;
75 struct param *i, *params = NULL;
77 printf("%-16s%s%c", "filename:", filename, sep);
78 for (j = 0; j < tags->cnt; j++) {
79 const char *info = tags->str[j];
80 char *eq, *colon;
82 /* We expect this in parm and parmtype. */
83 colon = strchr(info, ':');
85 /* We store these for handling at the end */
86 if (strstarts(info, "parm=") && colon) {
87 i = add_param(info + strlen("parm="), &params);
88 i->param = colon + 1;
89 continue;
91 if (strstarts(info, "parmtype=") && colon) {
92 i = add_param(info + strlen("parmtype="), &params);
93 i->type = colon + 1;
94 continue;
97 if (!sep) {
98 printf("%s%c", info, sep);
99 continue;
102 eq = strchr(info, '=');
103 /* Warn if no '=' maybe? */
104 if (eq) {
105 char tag[eq - info + 2];
106 strncpy(tag, info, eq - info);
107 tag[eq-info] = ':';
108 tag[eq-info+1] = '\0';
109 printf("%-16s%s%c", tag, eq+1, sep);
113 /* Now show parameters. */
114 for (i = params; i; i = i->next) {
115 if (!i->param)
116 printf("%-16s%s%s%c", "parm:", i->name, i->type, sep);
117 else if (i->type)
118 printf("%-16s%s%s (%s)%c",
119 "parm:", i->name, i->param, i->type, sep);
120 else
121 printf("%-16s%s%s%c", "parm:", i->name, i->param, sep);
125 static struct option options[] =
127 {"author", 0, 0, 'a'},
128 {"description", 0, 0, 'd'},
129 {"license", 0, 0, 'l'},
130 {"parameters", 0, 0, 'p'},
131 {"filename", 0, 0, 'n'},
132 {"version", 0, 0, 'V'},
133 {"help", 0, 0, 'h'},
134 {"null", 0, 0, '0'},
135 {"field", 1, 0, 'F'},
136 {0, 0, 0, 0}
139 /* - and _ are equivalent, and expect suffix. */
140 static int name_matches(const char *line, const char *end, const char *modname)
142 unsigned int i;
143 char *p;
145 /* Ignore comment lines */
146 if (line[strspn(line, "\t ")] == '#')
147 return 0;
149 /* Find last / before colon. */
150 p = memchr(line, ':', end - line);
151 if (!p)
152 return 0;
153 while (p > line) {
154 if (*p == '/') {
155 p++;
156 break;
158 p--;
161 for (i = 0; modname[i]; i++) {
162 /* Module names can't have colons. */
163 if (modname[i] == ':')
164 continue;
165 if (modname[i] == p[i])
166 continue;
167 if (modname[i] == '_' && p[i] == '-')
168 continue;
169 if (modname[i] == '-' && p[i] == '_')
170 continue;
171 return 0;
173 /* Must match all the way to the extension */
174 return (p[i] == '.');
177 static char *next_line(char *p, const char *end)
179 char *eol;
181 eol = memchr(p, '\n', end - p);
182 if (eol)
183 return eol + 1;
184 return (char *)end + 1;
187 static struct elf_file *grab_module(const char *name,
188 const char *kernel,
189 const char *basedir)
191 char *data;
192 unsigned long size;
193 struct utsname buf;
194 char *depname, *p, *moddir;
195 struct elf_file *module;
197 if (strchr(name, '.') || strchr(name, '/')) {
198 module = grab_elf_file(name);
199 if (!module)
200 error("modinfo: could not open %s: %s\n",
201 name, strerror(errno));
202 return module;
205 if (!kernel) {
206 uname(&buf);
207 kernel = buf.release;
209 if (strlen(basedir))
210 nofail_asprintf(&moddir, "%s/%s/%s", basedir, MODULE_DIR, kernel);
211 else
212 nofail_asprintf(&moddir, "%s/%s", MODULE_DIR, kernel);
214 /* Search for it in modules.dep. */
215 nofail_asprintf(&depname, "%s/%s", moddir, "modules.dep");
216 data = grab_file(depname, &size);
217 if (!data) {
218 error("modinfo: could not open %s\n", depname);
219 free(depname);
220 return NULL;
222 free(depname);
224 for (p = data; p < data + size; p = next_line(p, data + size)) {
225 if (name_matches(p, data + size, name)) {
226 int namelen = strcspn(p, ":");
227 const char *dir;
228 char *filename;
230 if ('/' == p[0])
231 dir = basedir; /* old style deps - abs. path */
232 else
233 dir = moddir; /* new style - relative path */
235 if (strlen(dir)) {
236 nofail_asprintf(&filename, "%s/%s", dir, p);
237 filename[namelen + strlen(dir) + 1] = '\0';
238 } else {
239 filename = strndup(p, namelen);
241 release_file(data, size);
242 module = grab_elf_file(filename);
243 if (!module)
244 error("modinfo: could not open %s: %s\n",
245 filename, strerror(errno));
246 free(filename);
247 return module;
250 release_file(data, size);
251 error("modinfo: could not find module %s\n", name);
252 return NULL;
255 static void usage(const char *name)
257 fprintf(stderr, "Usage: %s [-0][-F field][-k kernelversion][-b basedir] module...\n"
258 " Prints out the information about one or more module(s).\n"
259 " If a fieldname is given, just print out that field (or nothing if not found).\n"
260 " Otherwise, print all information out in a readable form\n"
261 " If -0 is given, separate with nul, not newline.\n"
262 " If -b is given, use an image of the module tree.\n",
263 name);
266 int main(int argc, char *argv[])
268 const char *field = NULL;
269 const char *kernel = NULL;
270 char sep = '\n';
271 int opt, ret = 0;
272 char *basedir = "";
274 logging = 0; /* send messages to stderr */
276 if (native_endianness() == 0)
277 abort();
279 while ((opt = getopt_long(argc,argv,"adlpVhn0F:k:b:",options,NULL)) >= 0){
280 switch (opt) {
281 case 'a': field = "author"; break;
282 case 'd': field = "description"; break;
283 case 'l': field = "license"; break;
284 case 'p': field = "parm"; break;
285 case 'n': field = "filename"; break;
286 case 'V': printf(PACKAGE " version " VERSION "\n"); exit(0);
287 case 'F': field = optarg; break;
288 case '0': sep = '\0'; break;
289 case 'k': kernel = optarg; break;
290 case 'b': basedir = optarg; break;
291 case 'h': usage(argv[0]); exit(0); break;
292 default:
293 usage(argv[0]); exit(1);
296 if (argc < optind + 1) {
297 usage(argv[0]);
298 exit(1);
301 for (opt = optind; opt < argc; opt++) {
302 struct string_table *tags;
303 struct elf_file *mod;
305 mod = grab_module(argv[opt], kernel, basedir);
306 if (!mod) {
307 ret = 1;
308 continue;
310 tags = mod->ops->load_strings(mod, ".modinfo", NULL, error);
311 if (!tags) {
312 release_elf_file(mod);
313 continue;
315 if (field)
316 print_tag(field, tags, mod->pathname, sep);
317 else
318 print_all(tags, mod->pathname, sep);
319 strtbl_free(tags);
320 release_elf_file(mod);
322 return ret;