master xplshn/aruu / cmd / linux / depmod.c
  1
  2
  3#include "fs.h"
  4#include "util.h"
  5#include "wexec.h"
  6
  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/stat.h>
 14#include <sys/types.h>
 15#include <sys/utsname.h>
 16#include <unistd.h>
 17
 18struct ModuleNode {
 19  char  *name;
 20  char  *path;
 21  char **deps;
 22  size_t ndeps;
 23#if FEATURE_DEPMOD_ALIAS
 24  char **aliases;
 25  size_t naliases;
 26#endif
 27#if FEATURE_DEPMOD_SYMBOLS
 28  char **symbols;
 29  size_t nsymbols;
 30#endif
 31  struct ModuleNode *next;
 32};
 33
 34static struct ModuleNode *modules_head = NULL;
 35
 36static void
 37normalize_name(char *dst, const char *src)
 38{
 39  const char *base;
 40  int         i;
 41
 42  base = strrchr(src, '/');
 43  if (base)
 44    base++;
 45  else
 46    base = src;
 47
 48  for (i = 0; i < 255 && base[i] && base[i] != '.'; i++)
 49    dst[i] = (base[i] == '-') ? '_' : base[i];
 50  dst[i] = '\0';
 51}
 52
 53static struct ModuleNode *
 54find_module(const char *name)
 55{
 56  struct ModuleNode *m;
 57
 58  for (m = modules_head; m; m = m->next) {
 59    if (strcmp(m->name, name) == 0)
 60      return m;
 61  }
 62  return NULL;
 63}
 64
 65static char *
 66get_str(const char *buf, size_t len, size_t offset, size_t maxlen)
 67{
 68  size_t i;
 69
 70  for (i = 0; i < maxlen && offset + i < len; i++) {
 71    if (buf[offset + i] == '\0') {
 72      if (i == 0)
 73        return NULL;
 74      return estrndup(buf + offset, i);
 75    }
 76    if (buf[offset + i] < 32 || buf[offset + i] > 126)
 77      return NULL;
 78  }
 79  return NULL;
 80}
 81
 82static void
 83parse_ko(struct ModuleNode *m, const char *buf, size_t len)
 84{
 85  size_t i;
 86  char  *val, *tok, *save;
 87
 88  for (i = 0; i + 8 < len; i++) {
 89    if (memcmp(buf + i, "depends=", 8) == 0) {
 90      val = get_str(buf, len, i + 8, 1024);
 91      if (val) {
 92        save = val;
 93        while ((tok = strsep(&save, ","))) {
 94          if (*tok) {
 95            m->deps           = reallocarray(m->deps, m->ndeps + 1, sizeof(char *));
 96            m->deps[m->ndeps] = estrdup(tok);
 97            normalize_name(m->deps[m->ndeps], tok);
 98            m->ndeps++;
 99          }
100        }
101        i += 8 + strlen(val);
102        free(val);
103      }
104    }
105#if FEATURE_DEPMOD_ALIAS
106    else if (memcmp(buf + i, "alias=", 6) == 0) {
107      val = get_str(buf, len, i + 6, 512);
108      if (val) {
109        m->aliases              = reallocarray(m->aliases, m->naliases + 1, sizeof(char *));
110        m->aliases[m->naliases] = val;
111        i += 6 + strlen(val);
112        m->naliases++;
113      }
114    }
115#endif
116#if FEATURE_DEPMOD_SYMBOLS
117    else if (memcmp(buf + i, "__ksymtab_", 10) == 0) {
118      val = get_str(buf, len, i + 10, 256);
119      if (val) {
120        m->symbols              = reallocarray(m->symbols, m->nsymbols + 1, sizeof(char *));
121        m->symbols[m->nsymbols] = val;
122        i += 10 + strlen(val);
123        m->nsymbols++;
124      }
125    }
126#endif
127  }
128}
129
130static char *
131read_all(FILE *fp, size_t *out_len)
132{
133  char  *buf = NULL;
134  size_t cap = 0;
135  size_t len = 0;
136  size_t n;
137
138  while (1) {
139    if (len >= cap) {
140      cap = cap ? cap * 2 : 65536;
141      buf = erealloc(buf, cap);
142    }
143    n = fread(buf + len, 1, cap - len, fp);
144    if (n == 0)
145      break;
146    len += n;
147  }
148  *out_len = len;
149  return buf;
150}
151
152static void
153scan_cb(int fd, const char *name, struct stat *st, void *data, struct recursor *r)
154{
155  struct ModuleNode *m;
156  char              *buf;
157  size_t             len;
158  const char        *ext;
159  const char        *comp;
160  FILE              *fp      = NULL;
161  int                is_pipe = 0;
162  char               cmd[PATH_MAX + 32];
163
164  (void)fd;
165  (void)data;
166  (void)r;
167
168  if (S_ISDIR(st->st_mode)) {
169    recurse(fd, name, NULL, r);
170    return;
171  }
172
173  if (!S_ISREG(st->st_mode))
174    return;
175
176  ext = strstr(r->path, ".ko");
177  if (!ext)
178    return;
179
180  comp = ext + 3;
181  if (strcmp(comp, "") == 0) {
182    fp = fopen(r->path, "r");
183  } else if (strcmp(comp, ".gz") == 0) {
184    snprintf(cmd, sizeof(cmd), "gzip -dc '%s'", r->path);
185    fp      = wpopen(cmd, NULL, "r");
186    is_pipe = 1;
187  } else if (strcmp(comp, ".xz") == 0) {
188    snprintf(cmd, sizeof(cmd), "xz -dc '%s'", r->path);
189    fp      = wpopen(cmd, NULL, "r");
190    is_pipe = 1;
191  } else if (strcmp(comp, ".zst") == 0) {
192    snprintf(cmd, sizeof(cmd), "zstd -dc '%s'", r->path);
193    fp      = wpopen(cmd, NULL, "r");
194    is_pipe = 1;
195  } else {
196    fp = fopen(r->path, "r");
197  }
198
199  if (!fp) {
200    weprintf("open %s:", r->path);
201    return;
202  }
203
204  buf = read_all(fp, &len);
205  if (is_pipe)
206    wpclose(fp);
207  else
208    fclose(fp);
209
210  if (len == 0) {
211    free(buf);
212    return;
213  }
214
215  m       = ecalloc(1, sizeof(*m));
216  m->path = estrdup(r->path);
217  if (strncmp(m->path, "./", 2) == 0)
218    memmove(m->path, m->path + 2, strlen(m->path) - 1);
219  m->name = emalloc(256);
220  normalize_name(m->name, r->path);
221
222  parse_ko(m, buf, len);
223  free(buf);
224
225  m->next      = modules_head;
226  modules_head = m;
227}
228
229static void
230resolve_deps(struct ModuleNode *m, char ***out_list, size_t *out_count)
231{
232  size_t             i, j;
233  struct ModuleNode *dep_node;
234  int                already_exists;
235
236  for (i = 0; i < m->ndeps; i++) {
237    dep_node = find_module(m->deps[i]);
238    if (!dep_node)
239      continue;
240
241    already_exists = 0;
242    for (j = 0; j < *out_count; j++) {
243      if (strcmp((*out_list)[j], dep_node->path) == 0) {
244        already_exists = 1;
245        break;
246      }
247    }
248
249    if (!already_exists) {
250      resolve_deps(dep_node, out_list, out_count);
251      *out_list               = reallocarray(*out_list, *out_count + 1, sizeof(char *));
252      (*out_list)[*out_count] = estrdup(dep_node->path);
253      (*out_count)++;
254    }
255  }
256}
257
258static void
259usage(void)
260{
261  eprintf("usage: %s [-n] [-b basedir] [version]\n", argv0);
262}
263
264// ?man depmod: generate modules.dep and map files
265// ?man arguments: version
266// ?man depmod generates modules.dep containing dependency information for
267// modprobe ?man // ?man -n: dry run print results to stdout instead of writing
268// files ?man // ?man -b basedir: use basedir as prefix for module directories
269int
270main(int argc, char *argv[])
271{
272  struct utsname     uts;
273  struct recursor    r = {.fn = scan_cb, .maxdepth = 0, .follow = 'H', .flags = DIRFIRST};
274  struct ModuleNode *m;
275  char              *basedir = "/";
276  char              *version = NULL;
277  char               path[PATH_MAX];
278  int                nflag = 0;
279  size_t             i;
280  char             **resolved;
281  size_t             nresolved;
282  FILE              *f_dep, *f_alias, *f_sym;
283
284  ARGBEGIN
285  {
286    // ?man -n: specify n option
287    case 'n':
288      nflag = 1;
289      break;
290    // ?man -b:dir: specify b option
291    case 'b':
292      basedir = EARGF(usage());
293      break;
294    default:
295      usage();
296  }
297  ARGEND;
298
299  if (argc > 1)
300    usage();
301
302  if (argc == 1) {
303    version = argv[0];
304  } else {
305    if (uname(&uts) < 0)
306      eprintf("uname:");
307    version = uts.release;
308  }
309
310  snprintf(path, sizeof(path), "%s/lib/modules/%s", basedir, version);
311  if (chdir(path) < 0)
312    eprintf("chdir %s:", path);
313
314  recurse(AT_FDCWD, ".", NULL, &r);
315
316  f_dep   = stdout;
317  f_alias = stdout;
318  f_sym   = stdout;
319
320  if (!nflag) {
321    f_dep = fopen("modules.dep", "w");
322    if (!f_dep)
323      eprintf("fopen modules.dep:");
324#if FEATURE_DEPMOD_ALIAS
325    f_alias = fopen("modules.alias", "w");
326    if (!f_alias)
327      eprintf("fopen modules.alias:");
328#endif
329#if FEATURE_DEPMOD_SYMBOLS
330    f_sym = fopen("modules.symbols", "w");
331    if (!f_sym)
332      eprintf("fopen modules.symbols:");
333#endif
334  }
335
336  /* write modules.dep */
337  for (m = modules_head; m; m = m->next) {
338    resolved  = NULL;
339    nresolved = 0;
340    resolve_deps(m, &resolved, &nresolved);
341
342    fprintf(f_dep, "%s:", m->path);
343    for (i = 0; i < nresolved; i++) {
344      fprintf(f_dep, " %s", resolved[i]);
345      free(resolved[i]);
346    }
347    free(resolved);
348    fprintf(f_dep, "\n");
349  }
350
351#if FEATURE_DEPMOD_ALIAS
352  /* write modules.alias */
353  for (m = modules_head; m; m = m->next) {
354    for (i = 0; i < m->naliases; i++)
355      fprintf(f_alias, "alias %s %s\n", m->aliases[i], m->name);
356  }
357#endif
358
359#if FEATURE_DEPMOD_SYMBOLS
360  /* write modules.symbols */
361  for (m = modules_head; m; m = m->next) {
362    for (i = 0; i < m->nsymbols; i++)
363      fprintf(f_sym, "alias symbol:%s %s\n", m->symbols[i], m->name);
364  }
365#endif
366
367  if (!nflag) {
368    fclose(f_dep);
369#if FEATURE_DEPMOD_ALIAS
370    fclose(f_alias);
371#endif
372#if FEATURE_DEPMOD_SYMBOLS
373    fclose(f_sym);
374#endif
375  }
376
377  /* free memory */
378  while (modules_head) {
379    m            = modules_head;
380    modules_head = m->next;
381    free(m->name);
382    free(m->path);
383    for (i = 0; i < m->ndeps; i++)
384      free(m->deps[i]);
385    free(m->deps);
386#if FEATURE_DEPMOD_ALIAS
387    for (i = 0; i < m->naliases; i++)
388      free(m->aliases[i]);
389    free(m->aliases);
390#endif
391#if FEATURE_DEPMOD_SYMBOLS
392    for (i = 0; i < m->nsymbols; i++)
393      free(m->symbols[i]);
394    free(m->symbols);
395#endif
396    free(m);
397  }
398
399  if (fshut(stdin, "<stdin>") | fshut(stdout, "<stdout>"))
400    return 2;
401
402  return 0;
403}