build: put shared functions in static library
[mit.git] / modinfo.c
blob07199c2327215b64ae54466eb96e1d2baa44b713
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 struct param
26 struct param *next;
27 const char *name; /* Terminated by a colon */
28 const char *param;
29 const char *type;
32 static struct param *add_param(const char *name, struct param **list)
34 struct param *i;
35 unsigned int namelen = strcspn(name, ":") + 1;
37 for (i = *list; i; i = i->next)
38 if (strncmp(i->name, name, namelen) == 0)
39 return i;
40 i = malloc(sizeof(*i) + namelen+1);
41 strncpy((char *)(i + 1), name, namelen);
42 ((char *)(i + 1))[namelen] = '\0';
43 i->name = (char *)(i + 1);
44 i->param = NULL;
45 i->type = NULL;
46 i->next = *list;
47 *list = i;
48 return i;
51 static void print_tag(const char *tag, const char *info, unsigned long size,
52 const char *filename, char sep)
54 unsigned int taglen = strlen(tag);
56 if (streq(tag, "filename")) {
57 printf("%s%c", filename, sep);
58 return;
61 for (; info; info = next_string(info, &size))
62 if (strncmp(info, tag, taglen) == 0 && info[taglen] == '=')
63 printf("%s%c", info + taglen + 1, sep);
66 static void print_all(const char *info, unsigned long size,
67 const char *filename, char sep)
69 struct param *i, *params = NULL;
71 printf("%-16s%s%c", "filename:", filename, sep);
72 for (; info; info = next_string(info, &size)) {
73 char *eq, *colon;
75 /* We expect this in parm and parmtype. */
76 colon = strchr(info, ':');
78 /* We store these for handling at the end */
79 if (strstarts(info, "parm=") && colon) {
80 i = add_param(info + strlen("parm="), &params);
81 i->param = colon + 1;
82 continue;
84 if (strstarts(info, "parmtype=") && colon) {
85 i = add_param(info + strlen("parmtype="), &params);
86 i->type = colon + 1;
87 continue;
90 if (!sep) {
91 printf("%s%c", info, sep);
92 continue;
95 eq = strchr(info, '=');
96 /* Warn if no '=' maybe? */
97 if (eq) {
98 char tag[eq - info + 2];
99 strncpy(tag, info, eq - info);
100 tag[eq-info] = ':';
101 tag[eq-info+1] = '\0';
102 printf("%-16s%s%c", tag, eq+1, sep);
106 /* Now show parameters. */
107 for (i = params; i; i = i->next) {
108 if (!i->param)
109 printf("%-16s%s%s%c", "parm:", i->name, i->type, sep);
110 else if (i->type)
111 printf("%-16s%s%s (%s)%c",
112 "parm:", i->name, i->param, i->type, sep);
113 else
114 printf("%-16s%s%s%c", "parm:", i->name, i->param, sep);
118 static struct option options[] =
120 {"author", 0, 0, 'a'},
121 {"description", 0, 0, 'd'},
122 {"license", 0, 0, 'l'},
123 {"parameters", 0, 0, 'p'},
124 {"filename", 0, 0, 'n'},
125 {"version", 0, 0, 'V'},
126 {"help", 0, 0, 'h'},
127 {"null", 0, 0, '0'},
128 {"field", 1, 0, 'F'},
129 {0, 0, 0, 0}
132 /* - and _ are equivalent, and expect suffix. */
133 static int name_matches(const char *line, const char *end, const char *modname)
135 unsigned int i;
136 char *p;
138 /* Ignore comment lines */
139 if (line[strspn(line, "\t ")] == '#')
140 return 0;
142 /* Find last / before colon. */
143 p = memchr(line, ':', end - line);
144 if (!p)
145 return 0;
146 while (p > line) {
147 if (*p == '/') {
148 p++;
149 break;
151 p--;
154 for (i = 0; modname[i]; i++) {
155 /* Module names can't have colons. */
156 if (modname[i] == ':')
157 continue;
158 if (modname[i] == p[i])
159 continue;
160 if (modname[i] == '_' && p[i] == '-')
161 continue;
162 if (modname[i] == '-' && p[i] == '_')
163 continue;
164 return 0;
166 /* Must match all the way to the extension */
167 return (p[i] == '.');
170 static char *next_line(char *p, const char *end)
172 char *eol;
174 eol = memchr(p, '\n', end - p);
175 if (eol)
176 return eol + 1;
177 return (char *)end + 1;
180 static void *grab_module(const char *name, unsigned long *size, char**filename,
181 const char *kernel, const char *basedir)
183 char *data;
184 struct utsname buf;
185 char *depname, *p, *moddir;
187 if (strchr(name, '.') || strchr(name, '/')) {
188 data = grab_file(name, size);
189 if (data) {
190 *filename = strdup(name);
191 return data;
192 } else {
193 fprintf(stderr, "modinfo: could not open %s: %s\n",
194 name, strerror(errno));
195 return NULL;
199 if (kernel) {
200 if (strlen(basedir))
201 asprintf(&moddir, "%s/%s/%s",
202 basedir, MODULE_DIR, kernel);
203 else
204 asprintf(&moddir, "%s/%s",
205 MODULE_DIR, kernel);
206 } else {
207 uname(&buf);
208 if (strlen(basedir))
209 asprintf(&moddir, "%s/%s/%s",
210 basedir, MODULE_DIR, buf.release);
211 else
212 asprintf(&moddir, "%s/%s",
213 MODULE_DIR, buf.release);
215 asprintf(&depname, "%s/%s", moddir, "modules.dep");
217 /* Search for it in modules.dep. */
218 data = grab_file(depname, size);
219 if (!data) {
220 fprintf(stderr, "modinfo: could not open %s\n", depname);
221 free(depname);
222 return NULL;
224 free(depname);
226 for (p = data; p < data + *size; p = next_line(p, data + *size)) {
227 if (name_matches(p, data + *size, name)) {
228 int namelen = strcspn(p, ":");
230 if ('/' == p[0]) { /* old style deps - absolute path */
231 *filename = malloc(namelen + strlen(basedir)+2);
232 if (strlen(basedir)) {
233 sprintf(*filename, "%s/", basedir);
234 memcpy(*filename+strlen(basedir)+1,p,
235 namelen);
236 (*filename)[namelen
237 +strlen(basedir)+1] = '\0';
238 } else {
239 memcpy(*filename,p,namelen);
240 (*filename)[namelen] = '\0';
242 } else {
243 *filename = malloc(namelen + strlen(moddir)+2);
244 sprintf(*filename, "%s/", moddir);
245 memcpy(*filename+strlen(moddir)+1, p,namelen);
246 (*filename)[namelen+strlen(moddir)+1] ='\0';
248 release_file(data, *size);
249 data = grab_file(*filename, size);
250 if (!data)
251 fprintf(stderr,
252 "modinfo: could not open %s: %s\n",
253 *filename, strerror(errno));
254 return data;
257 release_file(data, *size);
258 fprintf(stderr, "modinfo: could not find module %s\n", name);
259 return NULL;
262 static void usage(const char *name)
264 fprintf(stderr, "Usage: %s [-0][-F field][-k kernelversion][-b basedir] module...\n"
265 " Prints out the information about one or more module(s).\n"
266 " If a fieldname is given, just print out that field (or nothing if not found).\n"
267 " Otherwise, print all information out in a readable form\n"
268 " If -0 is given, separate with nul, not newline.\n"
269 " If -b is given, use an image of the module tree.\n",
270 name);
273 int main(int argc, char *argv[])
275 const char *field = NULL;
276 const char *kernel = NULL;
277 char sep = '\n';
278 unsigned long infosize = 0;
279 int opt, ret = 0;
280 char *basedir = "";
282 if (native_endianness() == 0)
283 abort();
285 while ((opt = getopt_long(argc,argv,"adlpVhn0F:k:b:",options,NULL)) >= 0){
286 switch (opt) {
287 case 'a': field = "author"; break;
288 case 'd': field = "description"; break;
289 case 'l': field = "license"; break;
290 case 'p': field = "parm"; break;
291 case 'n': field = "filename"; break;
292 case 'V': printf(PACKAGE " version " VERSION "\n"); exit(0);
293 case 'F': field = optarg; break;
294 case '0': sep = '\0'; break;
295 case 'k': kernel = optarg; break;
296 case 'b': basedir = optarg; break;
297 case 'h': usage(argv[0]); exit(0); break;
298 default:
299 usage(argv[0]); exit(1);
302 if (argc < optind + 1) {
303 usage(argv[0]);
304 exit(1);
307 for (opt = optind; opt < argc; opt++) {
308 void *info, *mod;
309 unsigned long modulesize;
310 char *filename;
312 mod = grab_module(argv[opt], &modulesize, &filename,
313 kernel, basedir);
314 if (!mod) {
315 ret = 1;
316 continue;
319 info = get_section(mod, modulesize, ".modinfo", &infosize);
320 if (!info) {
321 release_file(mod, modulesize);
322 free(filename);
323 continue;
325 if (field)
326 print_tag(field, info, infosize, filename, sep);
327 else
328 print_all(info, infosize, filename, sep);
329 release_file(mod, modulesize);
330 free(filename);
332 return ret;