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