aboutsummaryrefslogtreecommitdiff
path: root/toys/other/modinfo.c
blob: 286570f13bbb1859ae6413d313875051d1a1088c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
/* modinfo.c - Display module info
 *
 * Copyright 2012 Andre Renaud <andre@bluewatersys.com>
 *
 * TODO: cleanup

USE_MODINFO(NEWTOY(modinfo, "<1b:k:F:0", TOYFLAG_SBIN))

config MODINFO
  bool "modinfo"
  default y
  help
    usage: modinfo [-0] [-b basedir] [-k kernel] [-F field] [module|file...]

    Display module fields for modules specified by name or .ko path.

    -F  Only show the given field
    -0  Separate fields with NUL rather than newline
    -b  Use <basedir> as root for /lib/modules/
    -k  Look in given directory under /lib/modules/
*/

#define FOR_modinfo
#include "toys.h"

GLOBALS(
  char *F, *k, *b;

  long mod;
  int count;
)

static void output_field(char *field, char *value)
{
  if (!TT.F) xprintf("%s:%*c", field, 15-(int)strlen(field), ' ');
  else if (strcmp(TT.F, field)) return;
  xprintf("%s", value);
  xputc(FLAG(0) ? 0 : '\n');
}

static void modinfo_file(char *full_name)
{
  int fd, len, i;
  char *buf = 0, *pos, *modinfo_tags[] = {
    "alias", "license", "description", "author", "firmware",
    "vermagic", "srcversion", "intree", "depends", "parm",
    "parmtype",
  };

  if (-1 != (fd = open(full_name, O_RDONLY))) {
    len = fdlength(fd);
    buf = xmmap(0, len, PROT_READ, MAP_SHARED, fd, 0);
    close(fd);
  }

  if (!buf) {
    perror_msg_raw(full_name);
    return;
  }

  TT.count++;
  output_field("filename", full_name);

  for (pos = buf; pos < buf+len; pos++) {
    if (*pos) continue;

    for (i=0; i<ARRAY_LEN(modinfo_tags); i++) {
      char *str = modinfo_tags[i];
      int len = strlen(str);

      if (!strncmp(pos+1, str, len) && pos[len+1] == '=')
        output_field(str, pos+len+2);
    }
  }

  munmap(buf, len);
}

static int check_module(struct dirtree *new)
{
  if (!dirtree_notdotdot(new)) return 0;

  if (S_ISREG(new->st.st_mode)) {
    char *s;

    for (s = toys.optargs[TT.mod]; *s; s++) {
      int len = 0;

      // The kernel treats - and _ the same, so we should too.
      for (len = 0; s[len]; len++) {
        if (s[len] == '-' && new->name[len] == '_') continue;
        if (s[len] == '_' && new->name[len] == '-') continue;
        if (s[len] != new->name[len]) break;
      }
      if (s[len] || strcmp(new->name+len, ".ko")) break;

      modinfo_file(s = dirtree_path(new, 0));
      free(s);

      return DIRTREE_ABORT;
    }
  }

  return DIRTREE_RECURSE;
}

void modinfo_main(void)
{
  struct utsname uts;

  // Android (as shipped by Google) currently only has modules on /vendor.
  // Android does not support multiple sets of modules for different kernels.
  if (CFG_TOYBOX_ON_ANDROID) {
   if (!TT.b) TT.b = "/vendor";
   if (!TT.k) TT.k = "";
  } else {
   uname(&uts);
   if (!TT.b) TT.b = "";
   if (!TT.k) TT.k = uts.release;
  }

  for (TT.mod = 0; TT.mod<toys.optc; TT.mod++) {
    char *s = strstr(toys.optargs[TT.mod], ".ko");

    if (s && !s[3]) modinfo_file(toys.optargs[TT.mod]);
    else {
      char *path = xmprintf("%s/lib/modules/%s", TT.b, TT.k);

      TT.count = 0;
      dirtree_read(path, check_module);
      if (!TT.count) error_msg("%s: not found", toys.optargs[TT.mod]);
      free(path);
    }
  }
}