libkmod-module: convert return value from system() to errno
[platform/upstream/kmod.git] / tools / modinfo.c
1 /*
2  * kmod-modinfo - query kernel module information using libkmod.
3  *
4  * Copyright (C) 2011-2013  ProFUSION embedded systems
5  *
6  * This program is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation, either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
18  */
19
20 #include <errno.h>
21 #include <getopt.h>
22 #include <limits.h>
23 #include <stdbool.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <sys/stat.h>
28 #include <sys/utsname.h>
29
30 #include <shared/util.h>
31
32 #include <libkmod/libkmod.h>
33
34 #include "kmod.h"
35
36 static char separator = '\n';
37 static const char *field = NULL;
38
39 struct param {
40         struct param *next;
41         const char *name;
42         const char *param;
43         const char *type;
44         int namelen;
45         int paramlen;
46         int typelen;
47 };
48
49 static struct param *add_param(const char *name, int namelen, const char *param, int paramlen, const char *type, int typelen, struct param **list)
50 {
51         struct param *it;
52
53         for (it = *list; it != NULL; it = it->next) {
54                 if (it->namelen == namelen &&
55                         memcmp(it->name, name, namelen) == 0)
56                         break;
57         }
58
59         if (it == NULL) {
60                 it = malloc(sizeof(struct param));
61                 if (it == NULL)
62                         return NULL;
63                 it->next = *list;
64                 *list = it;
65                 it->name = name;
66                 it->namelen = namelen;
67                 it->param = NULL;
68                 it->type = NULL;
69                 it->paramlen = 0;
70                 it->typelen = 0;
71         }
72
73         if (param != NULL) {
74                 it->param = param;
75                 it->paramlen = paramlen;
76         }
77
78         if (type != NULL) {
79                 it->type = type;
80                 it->typelen = typelen;
81         }
82
83         return it;
84 }
85
86 static int process_parm(const char *key, const char *value, struct param **params)
87 {
88         const char *name, *param, *type;
89         int namelen, paramlen, typelen;
90         struct param *it;
91         const char *colon = strchr(value, ':');
92         if (colon == NULL) {
93                 ERR("Found invalid \"%s=%s\": missing ':'\n",
94                     key, value);
95                 return 0;
96         }
97
98         name = value;
99         namelen = colon - value;
100         if (streq(key, "parm")) {
101                 param = colon + 1;
102                 paramlen = strlen(param);
103                 type = NULL;
104                 typelen = 0;
105         } else {
106                 param = NULL;
107                 paramlen = 0;
108                 type = colon + 1;
109                 typelen = strlen(type);
110         }
111
112         it = add_param(name, namelen, param, paramlen, type, typelen, params);
113         if (it == NULL) {
114                 ERR("Out of memory!\n");
115                 return -ENOMEM;
116         }
117
118         return 0;
119 }
120
121 static int modinfo_params_do(const struct kmod_list *list)
122 {
123         const struct kmod_list *l;
124         struct param *params = NULL;
125         int err = 0;
126
127         kmod_list_foreach(l, list) {
128                 const char *key = kmod_module_info_get_key(l);
129                 const char *value = kmod_module_info_get_value(l);
130                 if (!streq(key, "parm") && !streq(key, "parmtype"))
131                         continue;
132
133                 err = process_parm(key, value, &params);
134                 if (err < 0)
135                         goto end;
136         }
137
138         while (params != NULL) {
139                 struct param *p = params;
140                 params = p->next;
141
142                 if (p->param == NULL)
143                         printf("%.*s: (%.*s)%c",
144                                p->namelen, p->name, p->typelen, p->type,
145                                separator);
146                 else if (p->type != NULL)
147                         printf("%.*s:%.*s (%.*s)%c",
148                                p->namelen, p->name,
149                                p->paramlen, p->param,
150                                p->typelen, p->type,
151                                separator);
152                 else
153                         printf("%.*s:%.*s%c",
154                                p->namelen, p->name,
155                                p->paramlen, p->param,
156                                separator);
157
158                 free(p);
159         }
160
161 end:
162         while (params != NULL) {
163                 void *tmp = params;
164                 params = params->next;
165                 free(tmp);
166         }
167
168         return err;
169 }
170
171 static int modinfo_do(struct kmod_module *mod)
172 {
173         struct kmod_list *l, *list = NULL;
174         struct param *params = NULL;
175         int err, is_builtin;
176         const char *filename = kmod_module_get_path(mod);
177
178         is_builtin = (filename == NULL);
179
180         if (is_builtin) {
181                 printf("%-16s%s%c", "name:", kmod_module_get_name(mod), separator);
182                 filename = "(builtin)";
183         }
184
185         if (field != NULL && streq(field, "filename")) {
186                 printf("%s%c", filename, separator);
187                 return 0;
188         } else if (field == NULL) {
189                 printf("%-16s%s%c", "filename:",
190                        filename, separator);
191         }
192
193         err = kmod_module_get_info(mod, &list);
194         if (err < 0) {
195                 if (is_builtin && err == -ENOENT) {
196                         /*
197                          * This is an old kernel that does not have a file
198                          * with information about built-in modules.
199                          */
200                         return 0;
201                 }
202                 ERR("could not get modinfo from '%s': %s\n",
203                         kmod_module_get_name(mod), strerror(-err));
204                 return err;
205         }
206
207         if (field != NULL && streq(field, "parm")) {
208                 err = modinfo_params_do(list);
209                 goto end;
210         }
211
212         kmod_list_foreach(l, list) {
213                 const char *key = kmod_module_info_get_key(l);
214                 const char *value = kmod_module_info_get_value(l);
215                 int keylen;
216
217                 if (field != NULL) {
218                         if (!streq(field, key))
219                                 continue;
220                         /* filtered output contains no key, just value */
221                         printf("%s%c", value, separator);
222                         continue;
223                 }
224
225                 if (streq(key, "parm") || streq(key, "parmtype")) {
226                         err = process_parm(key, value, &params);
227                         if (err < 0)
228                                 goto end;
229                         continue;
230                 }
231
232                 if (separator == '\0') {
233                         printf("%s=%s%c", key, value, separator);
234                         continue;
235                 }
236
237                 keylen = strlen(key);
238                 printf("%s:%-*s%s%c", key, 15 - keylen, "", value, separator);
239         }
240
241         if (field != NULL)
242                 goto end;
243
244         while (params != NULL) {
245                 struct param *p = params;
246                 params = p->next;
247
248                 if (p->param == NULL)
249                         printf("%-16s%.*s:%.*s%c", "parm:",
250                                p->namelen, p->name, p->typelen, p->type,
251                                separator);
252                 else if (p->type != NULL)
253                         printf("%-16s%.*s:%.*s (%.*s)%c", "parm:",
254                                p->namelen, p->name,
255                                p->paramlen, p->param,
256                                p->typelen, p->type,
257                                separator);
258                 else
259                         printf("%-16s%.*s:%.*s%c",
260                                "parm:",
261                                p->namelen, p->name,
262                                p->paramlen, p->param,
263                                separator);
264
265                 free(p);
266         }
267
268 end:
269         while (params != NULL) {
270                 void *tmp = params;
271                 params = params->next;
272                 free(tmp);
273         }
274         kmod_module_info_free_list(list);
275
276         return err;
277 }
278
279 static int modinfo_path_do(struct kmod_ctx *ctx, const char *path)
280 {
281         struct kmod_module *mod;
282         int err = kmod_module_new_from_path(ctx, path, &mod);
283         if (err < 0) {
284                 ERR("Module file %s not found.\n", path);
285                 return err;
286         }
287         err = modinfo_do(mod);
288         kmod_module_unref(mod);
289         return err;
290 }
291
292 static int modinfo_alias_do(struct kmod_ctx *ctx, const char *alias)
293 {
294         struct kmod_list *l, *list = NULL;
295         int err = kmod_module_new_from_lookup(ctx, alias, &list);
296         if (err < 0) {
297                 ERR("Module alias %s not found.\n", alias);
298                 return err;
299         }
300
301         if (list == NULL) {
302                 ERR("Module %s not found.\n", alias);
303                 return -ENOENT;
304         }
305
306         kmod_list_foreach(l, list) {
307                 struct kmod_module *mod = kmod_module_get_module(l);
308                 int r = modinfo_do(mod);
309                 kmod_module_unref(mod);
310                 if (r < 0)
311                         err = r;
312         }
313         kmod_module_unref_list(list);
314         return err;
315 }
316
317 static const char cmdopts_s[] = "adlpn0F:k:b:Vh";
318 static const struct option cmdopts[] = {
319         {"author", no_argument, 0, 'a'},
320         {"description", no_argument, 0, 'd'},
321         {"license", no_argument, 0, 'l'},
322         {"parameters", no_argument, 0, 'p'},
323         {"filename", no_argument, 0, 'n'},
324         {"null", no_argument, 0, '0'},
325         {"field", required_argument, 0, 'F'},
326         {"set-version", required_argument, 0, 'k'},
327         {"basedir", required_argument, 0, 'b'},
328         {"version", no_argument, 0, 'V'},
329         {"help", no_argument, 0, 'h'},
330         {NULL, 0, 0, 0}
331 };
332
333 static void help(void)
334 {
335         printf("Usage:\n"
336                 "\t%s [options] filename [args]\n"
337                 "Options:\n"
338                 "\t-a, --author                Print only 'author'\n"
339                 "\t-d, --description           Print only 'description'\n"
340                 "\t-l, --license               Print only 'license'\n"
341                 "\t-p, --parameters            Print only 'parm'\n"
342                 "\t-n, --filename              Print only 'filename'\n"
343                 "\t-0, --null                  Use \\0 instead of \\n\n"
344                 "\t-F, --field=FIELD           Print only provided FIELD\n"
345                 "\t-k, --set-version=VERSION   Use VERSION instead of `uname -r`\n"
346                 "\t-b, --basedir=DIR           Use DIR as filesystem root for /lib/modules\n"
347                 "\t-V, --version               Show version\n"
348                 "\t-h, --help                  Show this help\n",
349                 program_invocation_short_name);
350 }
351
352 static bool is_module_filename(const char *name)
353 {
354         struct stat st;
355
356         if (stat(name, &st) == 0 && S_ISREG(st.st_mode) &&
357                 path_ends_with_kmod_ext(name, strlen(name)))
358                         return true;
359
360         return false;
361 }
362
363 static int do_modinfo(int argc, char *argv[])
364 {
365         struct kmod_ctx *ctx;
366         char dirname_buf[PATH_MAX];
367         const char *dirname = NULL;
368         const char *kversion = NULL;
369         const char *root = NULL;
370         const char *null_config = NULL;
371         int i, err;
372
373         for (;;) {
374                 int c, idx = 0;
375                 c = getopt_long(argc, argv, cmdopts_s, cmdopts, &idx);
376                 if (c == -1)
377                         break;
378                 switch (c) {
379                 case 'a':
380                         field = "author";
381                         break;
382                 case 'd':
383                         field = "description";
384                         break;
385                 case 'l':
386                         field = "license";
387                         break;
388                 case 'p':
389                         field = "parm";
390                         break;
391                 case 'n':
392                         field = "filename";
393                         break;
394                 case '0':
395                         separator = '\0';
396                         break;
397                 case 'F':
398                         field = optarg;
399                         break;
400                 case 'k':
401                         kversion = optarg;
402                         break;
403                 case 'b':
404                         root = optarg;
405                         break;
406                 case 'h':
407                         help();
408                         return EXIT_SUCCESS;
409                 case 'V':
410                         puts(PACKAGE " version " VERSION);
411                         puts(KMOD_FEATURES);
412                         return EXIT_SUCCESS;
413                 case '?':
414                         return EXIT_FAILURE;
415                 default:
416                         ERR("unexpected getopt_long() value '%c'.\n", c);
417                         return EXIT_FAILURE;
418                 }
419         }
420
421         if (optind >= argc) {
422                 ERR("missing module or filename.\n");
423                 return EXIT_FAILURE;
424         }
425
426         if (root != NULL || kversion != NULL) {
427                 struct utsname u;
428                 if (root == NULL)
429                         root = "";
430                 if (kversion == NULL) {
431                         if (uname(&u) < 0) {
432                                 ERR("uname() failed: %m\n");
433                                 return EXIT_FAILURE;
434                         }
435                         kversion = u.release;
436                 }
437                 snprintf(dirname_buf, sizeof(dirname_buf), "%s/lib/modules/%s",
438                          root, kversion);
439                 dirname = dirname_buf;
440         }
441
442         ctx = kmod_new(dirname, &null_config);
443         if (!ctx) {
444                 ERR("kmod_new() failed!\n");
445                 return EXIT_FAILURE;
446         }
447
448         err = 0;
449         for (i = optind; i < argc; i++) {
450                 const char *name = argv[i];
451                 int r;
452
453                 if (is_module_filename(name))
454                         r = modinfo_path_do(ctx, name);
455                 else
456                         r = modinfo_alias_do(ctx, name);
457
458                 if (r < 0)
459                         err = r;
460         }
461
462         kmod_unref(ctx);
463         return err >= 0 ? EXIT_SUCCESS : EXIT_FAILURE;
464 }
465
466 const struct kmod_cmd kmod_cmd_compat_modinfo = {
467         .name = "modinfo",
468         .cmd = do_modinfo,
469         .help = "compat modinfo command",
470 };