tizen 2.0
[external/module-init-tools.git] / modinfo.c
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
16 #include "util.h"
17 #include "logging.h"
18 #include "elfops.h"
19 #include "zlibsupport.h"
20 #include "testing.h"
21
22 #ifndef MODULE_DIR
23 #define MODULE_DIR "/lib/modules"
24 #endif
25
26 struct param
27 {
28         struct param *next;
29         const char *name;       /* Terminated by a colon */
30         const char *param;
31         const char *type;
32 };
33
34 static struct param *add_param(const char *name, struct param **list)
35 {
36         struct param *i;
37         unsigned int namelen = strcspn(name, ":") + 1;
38
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;
51 }
52
53 static void print_tag(const char *tag, struct string_table *tags,
54                       const char *filename, char sep)
55 {
56         int j;
57         unsigned int taglen = strlen(tag);
58
59         if (streq(tag, "filename")) {
60                 printf("%s%c", filename, sep);
61                 return;
62         }
63
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);
68         }
69 }
70
71 static void print_all(struct string_table *tags,
72                       const char *filename, char sep)
73 {
74         int j;
75         struct param *i, *params = NULL;
76
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;
81
82                 /* We expect this in parm and parmtype. */
83                 colon = strchr(info, ':');
84
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;
90                 }
91                 if (strstarts(info, "parmtype=") && colon) {
92                         i = add_param(info + strlen("parmtype="), &params);
93                         i->type = colon + 1;
94                         continue;
95                 }
96
97                 if (!sep) {
98                         printf("%s%c", info, sep);
99                         continue;
100                 }
101
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);
110                 }
111         }
112
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);
122         }
123 }
124
125 static struct option options[] =
126 {
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}
137 };
138
139 /* - and _ are equivalent, and expect suffix. */
140 static int name_matches(const char *line, const char *end, const char *modname)
141 {
142         unsigned int i;
143         char *p;
144
145         /* Ignore comment lines */
146         if (line[strspn(line, "\t ")] == '#')
147                 return 0;
148
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;
157                 }
158                 p--;
159         }
160
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;
172         }
173         /* Must match all the way to the extension */
174         return (p[i] == '.');
175 }
176
177 static char *next_line(char *p, const char *end)
178 {
179         char *eol;
180
181         eol = memchr(p, '\n', end - p);
182         if (eol)
183                 return eol + 1;
184         return (char *)end + 1;
185 }
186
187 static struct elf_file *grab_module(const char *name,
188                                     const char *kernel,
189                                     const char *basedir)
190 {
191         char *data;
192         unsigned long size;
193         struct utsname buf;
194         char *depname, *p, *moddir;
195         struct elf_file *module;
196
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;
203         }
204
205         if (!kernel) {
206                 uname(&buf);
207                 kernel = buf.release;
208         }
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);
213
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;
221         }
222         free(depname);
223
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;
229
230                         if ('/' == p[0])
231                                 dir = basedir; /* old style deps - abs. path */
232                         else
233                                 dir = moddir; /* new style - relative path */
234
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);
240                         }
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;
248                 }
249         }
250         release_file(data, size);
251         error("modinfo: could not find module %s\n", name);
252         return NULL;
253 }
254
255 static void usage(const char *name)
256 {
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);
264 }
265
266 int main(int argc, char *argv[])
267 {
268         const char *field = NULL;
269         const char *kernel = NULL;
270         char sep = '\n';
271         int opt, ret = 0;
272         char *basedir = "";
273
274         logging = 0; /* send messages to stderr */
275
276         if (native_endianness() == 0)
277                 abort();
278
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);
294                 }
295         }
296         if (argc < optind + 1) {
297                 usage(argv[0]);
298                 exit(1);
299         }
300
301         for (opt = optind; opt < argc; opt++) {
302                 struct string_table *tags;
303                 struct elf_file *mod;
304
305                 mod = grab_module(argv[opt], kernel, basedir);
306                 if (!mod) {
307                         ret = 1;
308                         continue;
309                 }
310                 tags = mod->ops->load_strings(mod, ".modinfo", NULL);
311                 if (!tags) {
312                         release_elf_file(mod);
313                         continue;
314                 }
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);
321         }
322         return ret;
323 }