commit 84fdd47

xplshn  ·  2026-06-17 02:12:23 +0000 UTC
parent 5bb6c0e
tar & ip

Signed-off-by: xplshn <anto@xplshn.com.ar>
12 files changed,  +1819, -111
+1, -1
1@@ -44,7 +44,7 @@ done <<EOF
2 $(tr ';' '\n' < build.cfg)
3 EOF
4 unset _line _key _val _trimmed
5-CPPFLAGS="-Ishared -DPREFIX=\"$PREFIX\" -D_DEFAULT_SOURCE -D_GNU_SOURCE -D_NETBSD_SOURCE -D_BSD_SOURCE -D_XOPEN_SOURCE=700 -D_FILE_OFFSET_BITS=64 -DSTD_NON_POSIX$_feature_flags"
6+CPPFLAGS="-Ishared -DPREFIX=\"$PREFIX\" -D_DEFAULT_SOURCE -D_GNU_SOURCE -D_NETBSD_SOURCE -D_BSD_SOURCE -D_XOPEN_SOURCE=700 -D_FILE_OFFSET_BITS=64$_feature_flags"
7 if [ "$FEATURE_USE_BEARSSL" = "1" ]; then
8 	CPPFLAGS="$CPPFLAGS -IexternalRepos/BearSSL/inc"
9 	LDFLAGS="$LDFLAGS -LexternalRepos/BearSSL/build"
+18, -0
 1@@ -43,6 +43,12 @@ posix)
 2     FEATURE_MODPROBE_SHOW_DEPENDS=0;  FEATURE_MODPROBE_BLACKLIST=0
 3     FEATURE_MODPROBE_SYSLOG=0;        FEATURE_MODPROBE_DIR_OVERRIDE=0
 4     FEATURE_DEPMOD_ALIAS=0;           FEATURE_DEPMOD_SYMBOLS=0
 5+    FEATURE_TAR_TTY_SAFE=0;           FEATURE_TAR_NOFOLLOW=0
 6+    FEATURE_READLINK_REALPATH=0;      FEATURE_UMOUNT_OPTIONS=0
 7+    FEATURE_TAR_TO_STDOUT=0;          FEATURE_TAR_KEEP_OLD=0
 8+    FEATURE_TAR_STRIP_COMPONENTS=0;   FEATURE_TAR_FILES_FROM=0
 9+    FEATURE_TAR_EXCLUDE_FROM=0;       FEATURE_IP_ROUTE_ADD_DEL=0
10+    FEATURE_IP_LINK_SET=0;            FEATURE_IP_ADDR_FLUSH=0
11     ;;
12 common)
13     FEATURE_FIND_DELETE=1;            FEATURE_FIND_QUIT=1
14@@ -66,6 +72,12 @@ common)
15     FEATURE_MODPROBE_SHOW_DEPENDS=1;  FEATURE_MODPROBE_BLACKLIST=1
16     FEATURE_MODPROBE_SYSLOG=1;        FEATURE_MODPROBE_DIR_OVERRIDE=1
17     FEATURE_DEPMOD_ALIAS=1;           FEATURE_DEPMOD_SYMBOLS=1
18+    FEATURE_TAR_TTY_SAFE=1;           FEATURE_TAR_NOFOLLOW=1
19+    FEATURE_READLINK_REALPATH=1;      FEATURE_UMOUNT_OPTIONS=1
20+    FEATURE_TAR_TO_STDOUT=1;          FEATURE_TAR_KEEP_OLD=1
21+    FEATURE_TAR_STRIP_COMPONENTS=1;   FEATURE_TAR_FILES_FROM=1
22+    FEATURE_TAR_EXCLUDE_FROM=1;       FEATURE_IP_ROUTE_ADD_DEL=1
23+    FEATURE_IP_LINK_SET=1;            FEATURE_IP_ADDR_FLUSH=1
24     ;;
25 all)
26     # histedit now included via redline
27@@ -90,6 +102,12 @@ all)
28     FEATURE_MODPROBE_SHOW_DEPENDS=1;  FEATURE_MODPROBE_BLACKLIST=1
29     FEATURE_MODPROBE_SYSLOG=1;        FEATURE_MODPROBE_DIR_OVERRIDE=1
30     FEATURE_DEPMOD_ALIAS=1;           FEATURE_DEPMOD_SYMBOLS=1
31+    FEATURE_TAR_TTY_SAFE=1;           FEATURE_TAR_NOFOLLOW=1
32+    FEATURE_READLINK_REALPATH=1;      FEATURE_UMOUNT_OPTIONS=1
33+    FEATURE_TAR_TO_STDOUT=1;          FEATURE_TAR_KEEP_OLD=1
34+    FEATURE_TAR_STRIP_COMPONENTS=1;   FEATURE_TAR_FILES_FROM=1
35+    FEATURE_TAR_EXCLUDE_FROM=1;       FEATURE_IP_ROUTE_ADD_DEL=1
36+    FEATURE_IP_LINK_SET=1;            FEATURE_IP_ADDR_FLUSH=1
37     ;;
38 *)
39     printf 'build.cfg: unknown PRESET_FEATURES value: %s\n' "$PRESET_FEATURES" >&2
+401, -0
  1@@ -0,0 +1,401 @@
  2+/* ?man
  3+depmod: generate modules.dep and map files
  4+usage: depmod [-n] [-b basedir] [version]
  5+
  6+depmod generates modules.dep containing dependency information for modprobe
  7+
  8+// ?man -n: dry run print results to stdout instead of writing files
  9+// ?man -b basedir: use basedir as prefix for module directories
 10+*/
 11+
 12+#include "fs.h"
 13+#include "util.h"
 14+
 15+#include <dirent.h>
 16+#include <errno.h>
 17+#include <fcntl.h>
 18+#include <stdio.h>
 19+#include <stdlib.h>
 20+#include <string.h>
 21+#include <sys/stat.h>
 22+#include <sys/types.h>
 23+#include <sys/utsname.h>
 24+#include <unistd.h>
 25+
 26+struct ModuleNode {
 27+	char *name;
 28+	char *path;
 29+	char **deps;
 30+	size_t ndeps;
 31+#if FEATURE_DEPMOD_ALIAS
 32+	char **aliases;
 33+	size_t naliases;
 34+#endif
 35+#if FEATURE_DEPMOD_SYMBOLS
 36+	char **symbols;
 37+	size_t nsymbols;
 38+#endif
 39+	struct ModuleNode *next;
 40+};
 41+
 42+static struct ModuleNode *modules_head = NULL;
 43+
 44+static void
 45+normalize_name(char *dst, const char *src)
 46+{
 47+	const char *base;
 48+	int i;
 49+
 50+	base = strrchr(src, '/');
 51+	if (base)
 52+		base++;
 53+	else
 54+		base = src;
 55+
 56+	for (i = 0; i < 255 && base[i] && base[i] != '.'; i++)
 57+		dst[i] = (base[i] == '-') ? '_' : base[i];
 58+	dst[i] = '\0';
 59+}
 60+
 61+static struct ModuleNode *
 62+find_module(const char *name)
 63+{
 64+	struct ModuleNode *m;
 65+
 66+	for (m = modules_head; m; m = m->next) {
 67+		if (strcmp(m->name, name) == 0)
 68+			return m;
 69+	}
 70+	return NULL;
 71+}
 72+
 73+static char *
 74+get_str(const char *buf, size_t len, size_t offset, size_t maxlen)
 75+{
 76+	size_t i;
 77+
 78+	for (i = 0; i < maxlen && offset + i < len; i++) {
 79+		if (buf[offset + i] == '\0') {
 80+			if (i == 0)
 81+				return NULL;
 82+			return estrndup(buf + offset, i);
 83+		}
 84+		if (buf[offset + i] < 32 || buf[offset + i] > 126)
 85+			return NULL;
 86+	}
 87+	return NULL;
 88+}
 89+
 90+static void
 91+parse_ko(struct ModuleNode *m, const char *buf, size_t len)
 92+{
 93+	size_t i;
 94+	char *val, *tok, *save;
 95+
 96+	for (i = 0; i + 8 < len; i++) {
 97+		if (memcmp(buf + i, "depends=", 8) == 0) {
 98+			val = get_str(buf, len, i + 8, 1024);
 99+			if (val) {
100+				save = val;
101+				while ((tok = strsep(&save, ","))) {
102+					if (*tok) {
103+						m->deps = reallocarray(m->deps, m->ndeps + 1, sizeof(char *));
104+						m->deps[m->ndeps] = estrdup(tok);
105+						normalize_name(m->deps[m->ndeps], tok);
106+						m->ndeps++;
107+					}
108+				}
109+				i += 8 + strlen(val);
110+				free(val);
111+			}
112+		}
113+#if FEATURE_DEPMOD_ALIAS
114+		else if (memcmp(buf + i, "alias=", 6) == 0) {
115+			val = get_str(buf, len, i + 6, 512);
116+			if (val) {
117+				m->aliases = reallocarray(m->aliases, m->naliases + 1, sizeof(char *));
118+				m->aliases[m->naliases] = val;
119+				i += 6 + strlen(val);
120+				m->naliases++;
121+			}
122+		}
123+#endif
124+#if FEATURE_DEPMOD_SYMBOLS
125+		else if (memcmp(buf + i, "__ksymtab_", 10) == 0) {
126+			val = get_str(buf, len, i + 10, 256);
127+			if (val) {
128+				m->symbols = reallocarray(m->symbols, m->nsymbols + 1, sizeof(char *));
129+				m->symbols[m->nsymbols] = val;
130+				i += 10 + strlen(val);
131+				m->nsymbols++;
132+			}
133+		}
134+#endif
135+	}
136+}
137+
138+static char *
139+read_all(FILE *fp, size_t *out_len)
140+{
141+	char *buf = NULL;
142+	size_t cap = 0;
143+	size_t len = 0;
144+	size_t n;
145+
146+	while (1) {
147+		if (len >= cap) {
148+			cap = cap ? cap * 2 : 65536;
149+			buf = erealloc(buf, cap);
150+		}
151+		n = fread(buf + len, 1, cap - len, fp);
152+		if (n == 0)
153+			break;
154+		len += n;
155+	}
156+	*out_len = len;
157+	return buf;
158+}
159+
160+static void
161+scan_cb(int fd, const char *name, struct stat *st, void *data, struct recursor *r)
162+{
163+	struct ModuleNode *m;
164+	char *buf;
165+	size_t len;
166+	const char *ext;
167+	const char *comp;
168+	FILE *fp = NULL;
169+	int is_pipe = 0;
170+	char cmd[PATH_MAX + 32];
171+
172+	(void)fd;
173+	(void)data;
174+	(void)r;
175+
176+	if (S_ISDIR(st->st_mode)) {
177+		recurse(fd, name, NULL, r);
178+		return;
179+	}
180+
181+	if (!S_ISREG(st->st_mode))
182+		return;
183+
184+	ext = strstr(r->path, ".ko");
185+	if (!ext)
186+		return;
187+
188+	comp = ext + 3;
189+	if (strcmp(comp, "") == 0) {
190+		fp = fopen(r->path, "r");
191+	} else if (strcmp(comp, ".gz") == 0) {
192+		snprintf(cmd, sizeof(cmd), "gzip -dc '%s'", r->path);
193+		fp = popen(cmd, "r");
194+		is_pipe = 1;
195+	} else if (strcmp(comp, ".xz") == 0) {
196+		snprintf(cmd, sizeof(cmd), "xz -dc '%s'", r->path);
197+		fp = popen(cmd, "r");
198+		is_pipe = 1;
199+	} else if (strcmp(comp, ".zst") == 0) {
200+		snprintf(cmd, sizeof(cmd), "zstd -dc '%s'", r->path);
201+		fp = popen(cmd, "r");
202+		is_pipe = 1;
203+	} else {
204+		fp = fopen(r->path, "r");
205+	}
206+
207+	if (!fp) {
208+		weprintf("open %s:", r->path);
209+		return;
210+	}
211+
212+	buf = read_all(fp, &len);
213+	if (is_pipe)
214+		pclose(fp);
215+	else
216+		fclose(fp);
217+
218+	if (len == 0) {
219+		free(buf);
220+		return;
221+	}
222+
223+	m = ecalloc(1, sizeof(*m));
224+	m->path = estrdup(r->path);
225+	if (strncmp(m->path, "./", 2) == 0)
226+		memmove(m->path, m->path + 2, strlen(m->path) - 1);
227+	m->name = emalloc(256);
228+	normalize_name(m->name, r->path);
229+
230+	parse_ko(m, buf, len);
231+	free(buf);
232+
233+	m->next = modules_head;
234+	modules_head = m;
235+}
236+
237+static void
238+resolve_deps(struct ModuleNode *m, char ***out_list, size_t *out_count)
239+{
240+	size_t i, j;
241+	struct ModuleNode *dep_node;
242+	int already_exists;
243+
244+	for (i = 0; i < m->ndeps; i++) {
245+		dep_node = find_module(m->deps[i]);
246+		if (!dep_node)
247+			continue;
248+
249+		already_exists = 0;
250+		for (j = 0; j < *out_count; j++) {
251+			if (strcmp((*out_list)[j], dep_node->path) == 0) {
252+				already_exists = 1;
253+				break;
254+			}
255+		}
256+
257+		if (!already_exists) {
258+			resolve_deps(dep_node, out_list, out_count);
259+			*out_list = reallocarray(*out_list, *out_count + 1, sizeof(char *));
260+			(*out_list)[*out_count] = estrdup(dep_node->path);
261+			(*out_count)++;
262+		}
263+	}
264+}
265+
266+static void
267+usage(void)
268+{
269+	eprintf("usage: %s [-n] [-b basedir] [version]\n", argv0);
270+}
271+
272+int
273+main(int argc, char *argv[])
274+{
275+	struct utsname uts;
276+	struct recursor r = { .fn = scan_cb, .maxdepth = 0, .follow = 'H', .flags = DIRFIRST };
277+	struct ModuleNode *m;
278+	char *basedir = "/";
279+	char *version = NULL;
280+	char path[PATH_MAX];
281+	int nflag = 0;
282+	size_t i;
283+	char **resolved;
284+	size_t nresolved;
285+	FILE *f_dep, *f_alias, *f_sym;
286+
287+	ARGBEGIN {
288+	case 'n':
289+		nflag = 1;
290+		break;
291+	case 'b':
292+		basedir = EARGF(usage());
293+		break;
294+	default:
295+		usage();
296+	} ARGEND;
297+
298+	if (argc > 1)
299+		usage();
300+
301+	if (argc == 1) {
302+		version = argv[0];
303+	} else {
304+		if (uname(&uts) < 0)
305+			eprintf("uname:");
306+		version = uts.release;
307+	}
308+
309+	snprintf(path, sizeof(path), "%s/lib/modules/%s", basedir, version);
310+	if (chdir(path) < 0)
311+		eprintf("chdir %s:", path);
312+
313+	recurse(AT_FDCWD, ".", NULL, &r);
314+
315+	f_dep = stdout;
316+	f_alias = stdout;
317+	f_sym = stdout;
318+
319+	if (!nflag) {
320+		f_dep = fopen("modules.dep", "w");
321+		if (!f_dep)
322+			eprintf("fopen modules.dep:");
323+#if FEATURE_DEPMOD_ALIAS
324+		f_alias = fopen("modules.alias", "w");
325+		if (!f_alias)
326+			eprintf("fopen modules.alias:");
327+#endif
328+#if FEATURE_DEPMOD_SYMBOLS
329+		f_sym = fopen("modules.symbols", "w");
330+		if (!f_sym)
331+			eprintf("fopen modules.symbols:");
332+#endif
333+	}
334+
335+	/* write modules.dep */
336+	for (m = modules_head; m; m = m->next) {
337+		resolved = NULL;
338+		nresolved = 0;
339+		resolve_deps(m, &resolved, &nresolved);
340+
341+		fprintf(f_dep, "%s:", m->path);
342+		for (i = 0; i < nresolved; i++) {
343+			fprintf(f_dep, " %s", resolved[i]);
344+			free(resolved[i]);
345+		}
346+		free(resolved);
347+		fprintf(f_dep, "\n");
348+	}
349+
350+#if FEATURE_DEPMOD_ALIAS
351+	/* write modules.alias */
352+	for (m = modules_head; m; m = m->next) {
353+		for (i = 0; i < m->naliases; i++)
354+			fprintf(f_alias, "alias %s %s\n", m->aliases[i], m->name);
355+	}
356+#endif
357+
358+#if FEATURE_DEPMOD_SYMBOLS
359+	/* write modules.symbols */
360+	for (m = modules_head; m; m = m->next) {
361+		for (i = 0; i < m->nsymbols; i++)
362+			fprintf(f_sym, "alias symbol:%s %s\n", m->symbols[i], m->name);
363+	}
364+#endif
365+
366+	if (!nflag) {
367+		fclose(f_dep);
368+#if FEATURE_DEPMOD_ALIAS
369+		fclose(f_alias);
370+#endif
371+#if FEATURE_DEPMOD_SYMBOLS
372+		fclose(f_sym);
373+#endif
374+	}
375+
376+	/* free memory */
377+	while (modules_head) {
378+		m = modules_head;
379+		modules_head = m->next;
380+		free(m->name);
381+		free(m->path);
382+		for (i = 0; i < m->ndeps; i++)
383+			free(m->deps[i]);
384+		free(m->deps);
385+#if FEATURE_DEPMOD_ALIAS
386+		for (i = 0; i < m->naliases; i++)
387+			free(m->aliases[i]);
388+		free(m->aliases);
389+#endif
390+#if FEATURE_DEPMOD_SYMBOLS
391+		for (i = 0; i < m->nsymbols; i++)
392+			free(m->symbols[i]);
393+		free(m->symbols);
394+#endif
395+		free(m);
396+	}
397+
398+	if (fshut(stdin, "<stdin>") | fshut(stdout, "<stdout>"))
399+		return 2;
400+
401+	return 0;
402+}
+690, -0
  1@@ -0,0 +1,690 @@
  2+/* ?man
  3+modprobe: add or remove modules from the Linux kernel
  4+usage: modprobe [-alqrv] [module [symbol=value ...]]
  5+
  6+modprobe loads or removes kernel modules from the running system.
  7+It reads modules.dep, modules.alias, and modules.symbols from the appropriate
  8+/lib/modules/release directory to resolve module names and dependencies,
  9+loading prerequisites first.
 10+
 11+Without -r, modprobe loads the named module (and any required dependencies)
 12+into the kernel.
 13+*/
 14+#include "arg.h"
 15+#include "fs.h"
 16+#include "util.h"
 17+
 18+#include <ctype.h>
 19+#include <dirent.h>
 20+#include <errno.h>
 21+#include <fcntl.h>
 22+#include <fnmatch.h>
 23+#include <stdarg.h>
 24+#include <stdio.h>
 25+#include <stdlib.h>
 26+#include <string.h>
 27+#include <sys/stat.h>
 28+#include <sys/syscall.h>
 29+#include <sys/types.h>
 30+#include <sys/utsname.h>
 31+#include <unistd.h>
 32+
 33+#if FEATURE_MODPROBE_SYSLOG
 34+#include <syslog.h>
 35+#endif
 36+
 37+#define HASH_SIZE 256
 38+#ifndef LINE_MAX
 39+#define LINE_MAX 4096
 40+#endif
 41+
 42+enum ModFlags {
 43+	MOD_LOADED      = 1 << 0,
 44+	MOD_BLACKLISTED = 1 << 1,
 45+	MOD_QUEUED      = 1 << 2,
 46+	MOD_SEEN_DEP    = 1 << 3,
 47+};
 48+
 49+struct StrNode {
 50+	char *str;
 51+	struct StrNode *next;
 52+};
 53+
 54+struct Module {
 55+	char *name;
 56+	char *path;
 57+	char *options;
 58+	struct StrNode *deps;
 59+	struct StrNode *aliases;
 60+	int flags;
 61+	struct Module *next;
 62+};
 63+
 64+static struct Module *mod_db[HASH_SIZE];
 65+static struct StrNode *probes = NULL;
 66+static struct StrNode *moddirs = NULL;
 67+static char *cmdopts = NULL;
 68+
 69+static int aflag = 0;
 70+static int rflag = 0;
 71+static int qflag = 0;
 72+static int vflag = 0;
 73+static int lflag = 0;
 74+
 75+#if FEATURE_MODPROBE_SHOW_DEPENDS
 76+static int Dflag = 0;
 77+#endif
 78+
 79+#if FEATURE_MODPROBE_BLACKLIST
 80+static int bflag = 0;
 81+#endif
 82+
 83+#if FEATURE_MODPROBE_SYSLOG
 84+static int sflag = 0;
 85+#endif
 86+
 87+/* logging wrappers: handles syslog delegation and quiet mode suppression */
 88+
 89+static void
 90+pr_warn(const char *fmt, ...)
 91+{
 92+	va_list ap;
 93+	int saved_errno = errno;
 94+#if FEATURE_MODPROBE_SYSLOG
 95+	char buf[1024];
 96+#endif
 97+
 98+	if (qflag)
 99+		return;
100+
101+	va_start(ap, fmt);
102+#if FEATURE_MODPROBE_SYSLOG
103+	if (sflag) {
104+		vsnprintf(buf, sizeof(buf), fmt, ap);
105+		if (fmt[0] && fmt[strlen(fmt)-1] == ':')
106+			syslog(LOG_ERR, "%s %s", buf, strerror(saved_errno));
107+		else
108+			syslog(LOG_ERR, "%s", buf);
109+		va_end(ap);
110+		return;
111+	}
112+#endif
113+	fprintf(stderr, "%s: ", argv0);
114+	vfprintf(stderr, fmt, ap);
115+	if (fmt[0] && fmt[strlen(fmt)-1] == ':')
116+		fprintf(stderr, " %s\n", strerror(saved_errno));
117+	else
118+		fputc('\n', stderr);
119+	va_end(ap);
120+}
121+
122+static void
123+pr_info(const char *fmt, ...)
124+{
125+	va_list ap;
126+
127+	if (qflag)
128+		return;
129+
130+	va_start(ap, fmt);
131+#if FEATURE_MODPROBE_SYSLOG
132+	if (sflag) {
133+		vsyslog(LOG_INFO, fmt, ap);
134+		va_end(ap);
135+		return;
136+	}
137+#endif
138+	vprintf(fmt, ap);
139+	putchar('\n');
140+	va_end(ap);
141+}
142+
143+/* string structures: singly linked lists used for directories, deps, and aliases */
144+
145+static void
146+strlist_append(struct StrNode **list, const char *str)
147+{
148+	struct StrNode *n, *tail;
149+
150+	n = ecalloc(1, sizeof(*n));
151+	n->str = estrdup(str);
152+
153+	if (!*list) {
154+		*list = n;
155+		return;
156+	}
157+	for (tail = *list; tail->next; tail = tail->next)
158+		;
159+	tail->next = n;
160+}
161+
162+static char *
163+append_opts(char *opts, const char *add)
164+{
165+	size_t olen, alen;
166+	char *newopts;
167+
168+	if (!add)
169+		return opts;
170+	if (!opts)
171+		return estrdup(add);
172+
173+	olen = strlen(opts);
174+	alen = strlen(add);
175+	newopts = ecalloc(1, olen + alen + 2);
176+	memcpy(newopts, opts, olen);
177+	newopts[olen] = ' ';
178+	memcpy(newopts + olen + 1, add, alen);
179+
180+	return newopts;
181+}
182+
183+/* module database: hash table allows fast realname queries for aliases */
184+
185+static unsigned int
186+hash(const char *s)
187+{
188+	unsigned int h = 5381;
189+
190+	while (*s)
191+		h = ((h << 5) + h) + *s++;
192+	return h % HASH_SIZE;
193+}
194+
195+static void
196+normalize_name(char *dst, const char *src)
197+{
198+	const char *base;
199+	int i;
200+
201+	base = strrchr(src, '/');
202+	if (base)
203+		base++;
204+	else
205+		base = src;
206+
207+	for (i = 0; i < 255 && base[i] && base[i] != '.'; i++)
208+		dst[i] = (base[i] == '-') ? '_' : base[i];
209+	dst[i] = '\0';
210+}
211+
212+static struct Module *
213+get_module(const char *path_or_name, int create)
214+{
215+	char name[256];
216+	unsigned int h;
217+	struct Module *m;
218+
219+	normalize_name(name, path_or_name);
220+	h = hash(name);
221+
222+	for (m = mod_db[h]; m; m = m->next) {
223+		if (strcmp(m->name, name) == 0)
224+			return m;
225+	}
226+
227+	if (!create)
228+		return NULL;
229+
230+	m = ecalloc(1, sizeof(*m));
231+	m->name = estrdup(name);
232+	m->next = mod_db[h];
233+	mod_db[h] = m;
234+
235+	return m;
236+}
237+
238+/* config parsing: recursively loads aliases and module options from dir tree */
239+
240+static void
241+parse_config_file(const char *path)
242+{
243+	FILE *fp;
244+	char line[LINE_MAX];
245+	char *p, *cmd, *arg1, *arg2;
246+	struct Module *m;
247+
248+	if (!(fp = fopen(path, "r")))
249+		return;
250+
251+	while (fgets(line, sizeof(line), fp)) {
252+		p = strchr(line, '#');
253+		if (p)
254+			*p = '\0';
255+
256+		cmd = strtok(line, " \t\n");
257+		if (!cmd)
258+			continue;
259+
260+		arg1 = strtok(NULL, " \t\n");
261+		if (!arg1)
262+			continue;
263+
264+		if (strcmp(cmd, "alias") == 0) {
265+			arg2 = strtok(NULL, " \t\n");
266+			if (!arg2)
267+				continue;
268+			m = get_module(arg1, 1);
269+			strlist_append(&m->aliases, arg2);
270+		} else if (strcmp(cmd, "options") == 0) {
271+			arg2 = strtok(NULL, "\n");
272+			if (!arg2)
273+				continue;
274+			while (*arg2 == ' ' || *arg2 == '\t')
275+				arg2++;
276+			m = get_module(arg1, 1);
277+			m->options = append_opts(m->options, arg2);
278+		}
279+#if FEATURE_MODPROBE_BLACKLIST
280+		else if (strcmp(cmd, "blacklist") == 0) {
281+			m = get_module(arg1, 1);
282+			m->flags |= MOD_BLACKLISTED;
283+		}
284+#endif
285+	}
286+	fclose(fp);
287+}
288+
289+static void
290+config_cb(int fd, const char *path, struct stat *st, void *data, struct recursor *r)
291+{
292+	size_t len;
293+
294+	(void)fd;
295+	(void)data;
296+	(void)r;
297+
298+	if (S_ISREG(st->st_mode)) {
299+		len = strlen(path);
300+		if (len > 5 && strcmp(path + len - 5, ".conf") == 0)
301+			parse_config_file(path);
302+	}
303+}
304+
305+static void
306+read_configs(void)
307+{
308+	struct recursor r = { .fn = config_cb, .maxdepth = 1, .follow = 'H', .flags = DIRFIRST };
309+	struct stat st;
310+
311+	parse_config_file("/etc/modprobe.conf");
312+
313+	if (stat("/etc/modprobe.d", &st) == 0 && S_ISDIR(st.st_mode))
314+		recurse(AT_FDCWD, "/etc/modprobe.d", NULL, &r);
315+}
316+
317+static void
318+parse_dep_file(const char *path)
319+{
320+	FILE *fp;
321+	char line[LINE_MAX];
322+	char *p, *tok;
323+	struct Module *m;
324+
325+	if (!(fp = fopen(path, "r"))) {
326+		pr_warn("fopen %s:", path);
327+		return;
328+	}
329+
330+	while (fgets(line, sizeof(line), fp)) {
331+		p = strchr(line, ':');
332+		if (!p)
333+			continue;
334+		*p = '\0';
335+		p++;
336+
337+		m = get_module(line, 1);
338+		if (!m->path)
339+			m->path = estrdup(line);
340+		m->flags |= MOD_SEEN_DEP;
341+
342+		while ((tok = strtok(p, " \t\n"))) {
343+			p = NULL;
344+			if (*tok)
345+				strlist_append(&m->deps, tok);
346+		}
347+	}
348+	fclose(fp);
349+}
350+
351+static void
352+mark_loaded(void)
353+{
354+	FILE *fp;
355+	char line[LINE_MAX];
356+	char *p;
357+	struct Module *m;
358+
359+	if (!(fp = fopen("/proc/modules", "r")))
360+		return;
361+
362+	while (fgets(line, sizeof(line), fp)) {
363+		p = strchr(line, ' ');
364+		if (p)
365+			*p = '\0';
366+		else {
367+			p = strchr(line, '\n');
368+			if (p)
369+				*p = '\0';
370+		}
371+		m = get_module(line, 1);
372+		m->flags |= MOD_LOADED;
373+	}
374+	fclose(fp);
375+}
376+
377+static void
378+list_modules(const char *pattern)
379+{
380+	FILE *fp;
381+	char line[LINE_MAX];
382+	char *p, *name;
383+
384+	if (!(fp = fopen("modules.dep", "r"))) {
385+		pr_warn("fopen modules.dep:");
386+		return;
387+	}
388+
389+	while (fgets(line, sizeof(line), fp)) {
390+		p = strchr(line, ':');
391+		if (!p)
392+			continue;
393+		*p = '\0';
394+
395+		name = strrchr(line, '/');
396+		name = name ? name + 1 : line;
397+
398+		p = strrchr(name, '.');
399+		if (p)
400+			*p = '\0';
401+
402+		if (!pattern || fnmatch(pattern, name, 0) == 0) {
403+			if (p)
404+				*p = '.';
405+			printf("%s\n", line); /* intended output, not a log */
406+		}
407+	}
408+	fclose(fp);
409+}
410+
411+/* kernel interaction */
412+
413+static int
414+load_module(const char *path, const char *opts)
415+{
416+	int fd, ret;
417+
418+	fd = open(path, O_RDONLY | O_CLOEXEC);
419+	if (fd < 0) {
420+		pr_warn("open %s:", path);
421+		return -1;
422+	}
423+	ret = syscall(__NR_finit_module, fd, opts ? opts : "", 0);
424+	close(fd);
425+	return ret;
426+}
427+
428+static int
429+unload_module(const char *name)
430+{
431+	return syscall(__NR_delete_module, name, O_NONBLOCK);
432+}
433+
434+/* modprobe actions: load and unload requested module graph */
435+
436+static void
437+process_module(struct Module *m)
438+{
439+	struct StrNode *dep;
440+	struct Module *dm;
441+	char *opts;
442+
443+	if (!m->path) {
444+		pr_warn("module %s not found in modules.dep", m->name);
445+		return;
446+	}
447+
448+	if (rflag) {
449+		if (m->flags & MOD_LOADED) {
450+			if (unload_module(m->name) == 0)
451+				m->flags &= ~MOD_LOADED;
452+			else
453+				pr_warn("unload %s:", m->name);
454+		}
455+		return;
456+	}
457+
458+	for (dep = m->deps; dep; dep = dep->next) {
459+		dm = get_module(dep->str, 0);
460+		if (dm && !(dm->flags & MOD_LOADED))
461+			process_module(dm);
462+	}
463+
464+	if (m->flags & MOD_LOADED) {
465+		if (vflag)
466+			pr_info("%s already loaded", m->name);
467+		return;
468+	}
469+
470+	opts = m->options;
471+	if (cmdopts && probes && strcmp(probes->str, m->name) == 0)
472+		opts = append_opts(opts, cmdopts);
473+
474+#if FEATURE_MODPROBE_SHOW_DEPENDS
475+	if (Dflag) {
476+		printf(opts ? "insmod %s %s\n" : "insmod %s\n", m->path, opts); /* output data */
477+		if (opts != m->options)
478+			free(opts);
479+		return;
480+	}
481+#endif
482+
483+	if (load_module(m->path, opts) == 0) {
484+		m->flags |= MOD_LOADED;
485+		if (vflag)
486+			pr_info("loaded %s '%s'", m->path, opts ? opts : "");
487+	} else {
488+		pr_warn("load %s:", m->path);
489+	}
490+
491+	if (opts != m->options)
492+		free(opts);
493+}
494+
495+static void
496+do_probe(const char *name)
497+{
498+	struct Module *m, *am;
499+	struct StrNode *alias;
500+	char norm[256];
501+
502+	normalize_name(norm, name);
503+	m = get_module(norm, 1);
504+
505+#if FEATURE_MODPROBE_BLACKLIST
506+	if (bflag && (m->flags & MOD_BLACKLISTED))
507+		return;
508+#endif
509+
510+	if (!m->aliases) {
511+		if (vflag)
512+			pr_info("probing %s by name", norm);
513+		process_module(m);
514+		return;
515+	}
516+
517+	for (alias = m->aliases; alias; alias = alias->next) {
518+		am = get_module(alias->str, 1);
519+#if FEATURE_MODPROBE_BLACKLIST
520+		if (am->flags & MOD_BLACKLISTED)
521+			continue;
522+#endif
523+		if (vflag)
524+			pr_info("probing alias %s -> %s", norm, alias->str);
525+		process_module(am);
526+	}
527+}
528+
529+static void
530+usage(void)
531+{
532+	eprintf("usage: %s [-alqrv"
533+#if FEATURE_MODPROBE_SHOW_DEPENDS
534+		"D"
535+#endif
536+#if FEATURE_MODPROBE_BLACKLIST
537+		"b"
538+#endif
539+#if FEATURE_MODPROBE_SYSLOG
540+		"s"
541+#endif
542+		"] "
543+#if FEATURE_MODPROBE_DIR_OVERRIDE
544+		"[-d dir] "
545+#endif
546+		"module [symbol=value ...]\n", argv0);
547+}
548+
549+int
550+main(int argc, char *argv[])
551+{
552+	struct utsname uts;
553+	struct StrNode *pn;
554+	char path[PATH_MAX];
555+	int i, ret = 0;
556+
557+	ARGBEGIN {
558+	case 'a':
559+		// ?man -a: Load all modules named on the command line (rather than stopping after the first).
560+		aflag = 1; break;
561+	case 'r':
562+		// ?man -r: Remove the named modules from the kernel.  Dependencies are not automatically removed.
563+		rflag = 1; break;
564+	case 'q':
565+		// ?man -q: Quiet mode.  Suppress error messages.
566+		qflag = 1; break;
567+	case 'v':
568+		// ?man -v: Verbose mode.  Print each action taken.
569+		vflag = 1; break;
570+	case 'l':
571+		// ?man -l: List available modules matching the optional _pattern_ (a fnmatch(3) glob).
572+		lflag = 1; break;
573+#if FEATURE_MODPROBE_SHOW_DEPENDS
574+	case 'D':
575+		// ?man -D: Print the sequence of insmod commands that would be used to load the module, without actually loading anything.
576+		Dflag = 1; break;
577+#endif
578+#if FEATURE_MODPROBE_BLACKLIST
579+	case 'b':
580+		// ?man -b: Skip modules listed as blacklist in /etc/modprobe.d/.
581+		bflag = 1; break;
582+#endif
583+#if FEATURE_MODPROBE_SYSLOG
584+	case 's':
585+		// ?man -s: Log messages to syslog(3) (facility LOG_DAEMON) instead of standard error.
586+		sflag = 1; break;
587+#endif
588+#if FEATURE_MODPROBE_DIR_OVERRIDE
589+	case 'd':
590+		// ?man -d dir: Use dir as the base directory for module files instead of /lib/modules/release.
591+		strlist_append(&moddirs, EARGF(usage())); break;
592+#endif
593+	default:
594+		usage();
595+	} ARGEND
596+
597+	if (!argc && !rflag && !lflag)
598+		usage();
599+
600+#if FEATURE_MODPROBE_SYSLOG
601+	if (sflag)
602+		openlog("modprobe", LOG_PID, LOG_DAEMON);
603+#endif
604+
605+	if (!moddirs) {
606+		if (uname(&uts) < 0)
607+			eprintf("uname:");
608+		snprintf(path, sizeof(path), "/lib/modules/%s", uts.release);
609+		strlist_append(&moddirs, path);
610+	}
611+
612+	mark_loaded();
613+	read_configs();
614+
615+	/* traverse all provided or default base directories for symbol, alias, and dependency tracking */
616+	for (pn = moddirs; pn; pn = pn->next) {
617+		if (chdir(pn->str) < 0) {
618+			pr_warn("chdir %s:", pn->str);
619+			continue;
620+		}
621+
622+		if (lflag) {
623+			list_modules(argc ? argv[0] : NULL);
624+			continue;
625+		}
626+
627+		parse_config_file("modules.symbols");
628+		parse_config_file("modules.alias");
629+		parse_dep_file("modules.dep");
630+	}
631+
632+	if (lflag)
633+		goto end;
634+
635+	if (aflag || rflag) {
636+		for (i = 0; i < argc; i++)
637+			strlist_append(&probes, argv[i]);
638+	} else if (argc > 0) {
639+		strlist_append(&probes, argv[0]);
640+		for (i = 1; i < argc; i++)
641+			cmdopts = append_opts(cmdopts, argv[i]);
642+	}
643+
644+	if (rflag && !argc) {
645+		if (syscall(__NR_delete_module, NULL, O_NONBLOCK) < 0)
646+			eprintf("delete_module:");
647+		goto end;
648+	}
649+
650+	for (pn = probes; pn; pn = pn->next) {
651+		do_probe(pn->str);
652+	}
653+
654+end:
655+#if FEATURE_MODPROBE_SYSLOG
656+	if (sflag)
657+		closelog();
658+#endif
659+	if (fshut(stdin, "<stdin>") | fshut(stdout, "<stdout>"))
660+		ret = 2;
661+
662+	/* ?man
663+	## FILES
664+
665+	`/etc/modprobe.conf`
666+	: Global configuration file.
667+
668+	Files under `/etc/modprobe.d/`
669+	: Per-module configuration snippets.
670+
671+	`/lib/modules/`_release_`/modules.dep`
672+	: Module dependency map generated by `depmod(8)`.
673+
674+	## EXIT STATUS
675+
676+	0
677+	: Success.
678+
679+	1
680+	: Module not found or kernel rejected the operation.
681+
682+	2
683+	: I/O error on stdout or stdin.
684+
685+	## SEE ALSO
686+
687+	insmod(8), rmmod(8), lsmod(8), depmod(8)
688+	*/
689+
690+	return ret;
691+}
+8, -8
 1@@ -15,7 +15,7 @@ unmount a filesystem from the directory tree
 2 
 3 #include "util.h"
 4 
 5-#ifdef STD_NON_POSIX
 6+#if FEATURE_UMOUNT_OPTIONS
 7 static int
 8 fsopt_matches(const char *opts_list, const char *opt, size_t optlen)
 9 {
10@@ -81,7 +81,7 @@ fsopts_matches(const char *opts_list, const char *reqopts_list)
11 
12 static int
13 umountall(int flags
14-#ifdef STD_NON_POSIX
15+#if FEATURE_UMOUNT_OPTIONS
16           , const char *oflag
17 #endif
18 )
19@@ -98,7 +98,7 @@ umountall(int flags
20 	while ((me = getmntent(fp))) {
21 		if (strcmp(me->mnt_type, "proc") == 0)
22 			continue;
23-#ifdef STD_NON_POSIX
24+#if FEATURE_UMOUNT_OPTIONS
25 		if (oflag && !fsopts_matches(me->mnt_opts, oflag))
26 			continue;
27 #endif
28@@ -120,7 +120,7 @@ umountall(int flags
29 static void
30 usage(void)
31 {
32-#ifdef STD_NON_POSIX
33+#if FEATURE_UMOUNT_OPTIONS
34 	weprintf("usage: %s [-lfn] [-O options] target...\n", argv0);
35 	weprintf("usage: %s -a [-lfn] [-O options]\n", argv0);
36 #else
37@@ -137,7 +137,7 @@ main(int argc, char *argv[])
38 	int aflag = 0;
39 	int flags = 0;
40 	int ret = 0;
41-#ifdef STD_NON_POSIX
42+#if FEATURE_UMOUNT_OPTIONS
43 	char *oflag = NULL;
44 #endif
45 
46@@ -157,7 +157,7 @@ main(int argc, char *argv[])
47 	// ?man -n: print line numbers or counts
48 	case 'n':
49 		break;
50-#ifdef STD_NON_POSIX
51+#if FEATURE_UMOUNT_OPTIONS
52 	// ?man -O: specify option flag
53 	case 'O':
54 		oflag = EARGF(usage());
55@@ -170,14 +170,14 @@ main(int argc, char *argv[])
56 	if (argc < 1 && aflag == 0)
57 		usage();
58 
59-#ifdef STD_NON_POSIX
60+#if FEATURE_UMOUNT_OPTIONS
61 	if (oflag && aflag == 0)
62 		usage();
63 #endif
64 
65 	if (aflag == 1)
66 		return umountall(flags
67-#ifdef STD_NON_POSIX
68+#if FEATURE_UMOUNT_OPTIONS
69 		                 , oflag
70 #endif
71 		);
+151, -9
  1@@ -4,6 +4,35 @@ ip: show or configure routing and devices
  2 usage: ip [addr | link | route] [args...]
  3 
  4 configure and view network devices, routing, and tunnels
  5+
  6+## COMMANDS
  7+
  8+### addr [show | list [dev <device>]]
  9+show interface addresses
 10+
 11+### addr add <ip>/<prefix> dev <device>
 12+add address to interface
 13+
 14+### addr del <ip>/<prefix> dev <device>
 15+delete address from interface
 16+
 17+### addr flush dev <device>
 18+flush all addresses from interface
 19+
 20+### link [show | list [dev <device>]]
 21+show interface link states
 22+
 23+### link set <interface> [up | down | mtu <mtu> | address <mac> | arp on|off | multicast on|off | allmulticast on|off | promisc on|off | name <newname> | txqueuelen <qlen>]
 24+configure interface settings
 25+
 26+### route [show | list]
 27+show routing table
 28+
 29+### route add <prefix> [via <gateway>] [dev <device>] [metric <metric>]
 30+add route to routing table
 31+
 32+### route del <prefix> [via <gateway>] [dev <device>] [metric <metric>]
 33+delete route from routing table
 34 */
 35 
 36 #include <arpa/inet.h>
 37@@ -19,14 +48,27 @@ configure and view network devices, routing, and tunnels
 38 
 39 #include "util.h"
 40 
 41-int net_get_interfaces(struct NetInterface **, int *);
 42-int net_get_stats(const char *, struct NetStats *);
 43-int net_set_flags(const char *, unsigned int, int);
 44-int net_set_mtu(const char *, int);
 45-int net_set_mac(const char *, const unsigned char *);
 46-int net_add_addr(const char *, const char *, int);
 47-int net_del_addr(const char *, const char *, int);
 48-int net_show_routes(void);
 49+#if FEATURE_IP_ROUTE_ADD_DEL
 50+static void
 51+prefix_to_mask(int prefix, char *mask_str, size_t mask_str_len)
 52+{
 53+	unsigned long mask;
 54+	struct in_addr addr;
 55+
 56+	if (prefix < 0)
 57+		prefix = 32;
 58+	if (prefix > 32)
 59+		eprintf("invalid prefix: %d\n", prefix);
 60+
 61+	if (prefix == 0) {
 62+		mask = 0;
 63+	} else {
 64+		mask = 0xffffffffUL << (32 - prefix);
 65+	}
 66+	addr.s_addr = htonl(mask);
 67+	strlcpy(mask_str, inet_ntoa(addr), mask_str_len);
 68+}
 69+#endif
 70 
 71 static void
 72 usage(void)
 73@@ -150,6 +192,23 @@ do_addr(int argc, char *argv[])
 74 		return 0;
 75 	}
 76 
 77+#if FEATURE_IP_ADDR_FLUSH
 78+	if (strcmp(cmd, "flush") == 0) {
 79+		dev = NULL;
 80+		for (i = 1; i < argc; i++) {
 81+			if (strcmp(argv[i], "dev") == 0 && i + 1 < argc) {
 82+				dev = argv[i + 1];
 83+				break;
 84+			}
 85+		}
 86+		if (!dev)
 87+			eprintf("missing dev parameter for flush\n");
 88+		if (net_flush_addrs(dev) < 0)
 89+			eprintf("net_flush_addrs:");
 90+		return 0;
 91+	}
 92+#endif
 93+
 94 	usage();
 95 	return 1;
 96 }
 97@@ -208,6 +267,46 @@ do_link(int argc, char *argv[])
 98 				if (net_set_mac(dev, mac) < 0)
 99 					eprintf("net_set_mac:");
100 				i++;
101+#if FEATURE_IP_LINK_SET
102+#ifdef IFF_NOARP
103+			} else if (strcmp(argv[i], "arp") == 0 && i + 1 < argc) {
104+				int set = (strcmp(argv[i + 1], "off") == 0);
105+				if (net_set_flags(dev, IFF_NOARP, set) < 0)
106+					eprintf("net_set_flags arp:");
107+				i++;
108+#endif
109+#ifdef IFF_MULTICAST
110+			} else if (strcmp(argv[i], "multicast") == 0 && i + 1 < argc) {
111+				int set = (strcmp(argv[i + 1], "on") == 0);
112+				if (net_set_flags(dev, IFF_MULTICAST, set) < 0)
113+					eprintf("net_set_flags multicast:");
114+				i++;
115+#endif
116+#ifdef IFF_ALLMULTI
117+			} else if (strcmp(argv[i], "allmulticast") == 0 && i + 1 < argc) {
118+				int set = (strcmp(argv[i + 1], "on") == 0);
119+				if (net_set_flags(dev, IFF_ALLMULTI, set) < 0)
120+					eprintf("net_set_flags allmulticast:");
121+				i++;
122+#endif
123+#ifdef IFF_PROMISC
124+			} else if (strcmp(argv[i], "promisc") == 0 && i + 1 < argc) {
125+				int set = (strcmp(argv[i + 1], "on") == 0);
126+				if (net_set_flags(dev, IFF_PROMISC, set) < 0)
127+					eprintf("net_set_flags promisc:");
128+				i++;
129+#endif
130+			} else if (strcmp(argv[i], "name") == 0 && i + 1 < argc) {
131+				if (net_set_name(dev, argv[i + 1]) < 0)
132+					eprintf("net_set_name:");
133+				dev = argv[i + 1];
134+				i++;
135+			} else if (strcmp(argv[i], "txqueuelen") == 0 && i + 1 < argc) {
136+				int qlen = atoi(argv[i + 1]);
137+				if (net_set_txqueuelen(dev, qlen) < 0)
138+					eprintf("net_set_txqueuelen:");
139+				i++;
140+#endif
141 			}
142 		}
143 		return 0;
144@@ -220,7 +319,10 @@ do_link(int argc, char *argv[])
145 static int
146 do_route(int argc, char *argv[])
147 {
148-	char *cmd;
149+	char *cmd, *dst, *slash;
150+	const char *gateway = NULL, *dev = NULL;
151+	int prefix_len = -1, metric = -1, i;
152+	char mask_str[32];
153 
154 	if (argc == 0)
155 		cmd = "show";
156@@ -233,6 +335,46 @@ do_route(int argc, char *argv[])
157 		return 0;
158 	}
159 
160+#if FEATURE_IP_ROUTE_ADD_DEL
161+	if (strcmp(cmd, "add") == 0 || strcmp(cmd, "del") == 0) {
162+		if (argc < 2)
163+			eprintf("usage: ip route %s <prefix> [via <gateway>] [dev <device>] [metric <metric>]\n", cmd);
164+
165+		dst = argv[1];
166+		slash = strchr(dst, '/');
167+		if (slash) {
168+			*slash = '\0';
169+			prefix_len = atoi(slash + 1);
170+		} else if (strcmp(dst, "default") == 0) {
171+			prefix_len = 0;
172+		}
173+
174+		prefix_to_mask(prefix_len, mask_str, sizeof(mask_str));
175+
176+		for (i = 2; i < argc; i++) {
177+			if (strcmp(argv[i], "via") == 0 && i + 1 < argc) {
178+				gateway = argv[i + 1];
179+				i++;
180+			} else if (strcmp(argv[i], "dev") == 0 && i + 1 < argc) {
181+				dev = argv[i + 1];
182+				i++;
183+			} else if (strcmp(argv[i], "metric") == 0 && i + 1 < argc) {
184+				metric = atoi(argv[i + 1]);
185+				i++;
186+			}
187+		}
188+
189+		if (strcmp(cmd, "add") == 0) {
190+			if (net_add_route(dst, gateway, mask_str, dev, metric) < 0)
191+				eprintf("net_add_route:");
192+		} else {
193+			if (net_del_route(dst, gateway, mask_str, dev, metric) < 0)
194+				eprintf("net_del_route:");
195+		}
196+		return 0;
197+	}
198+#endif
199+
200 	usage();
201 	return 1;
202 }
+4, -4
 1@@ -17,7 +17,7 @@ display the target of a symbolic link
 2 static void
 3 usage(void)
 4 {
 5-#ifdef STD_NON_POSIX
 6+#if FEATURE_READLINK_REALPATH
 7 	eprintf("usage: %s [-fn] path\n", argv0);
 8 #else
 9 	eprintf("usage: %s [-n] path\n", argv0);
10@@ -30,13 +30,13 @@ main(int argc, char *argv[])
11 	char buf[PATH_MAX];
12 	ssize_t n;
13 	int nflag = 0;
14-#ifdef STD_NON_POSIX
15+#if FEATURE_READLINK_REALPATH
16 	int fflag = 0;
17 #endif
18 
19 	ARGBEGIN
20 	{
21-#ifdef STD_NON_POSIX
22+#if FEATURE_READLINK_REALPATH
23 	// ?man -f: force the operation
24 	case 'f':
25 		fflag = 1;
26@@ -57,7 +57,7 @@ main(int argc, char *argv[])
27 	if (strlen(argv[0]) >= PATH_MAX)
28 		eprintf("path too long\n");
29 
30-#ifdef STD_NON_POSIX
31+#if FEATURE_READLINK_REALPATH
32 	if (fflag) {
33 		if (!realpath(argv[0], buf))
34 			eprintf("realpath %s:", argv[0]);
+333, -84
  1@@ -1,6 +1,7 @@
  2 /* ?man
  3 tar: tape archiver
  4-usage: tar [x | t | -x | -t] [-C dir] [-J | -Z | -a | -j | -z] [-m] [-p]
  5+usage: tar [x | t | -x | -t] [-C dir] [-J | -Z | -a | -j | -z] [-m] [-p] [-O] [-k] [-T file] [-X file] [--strip-components num] [-f file] [file ...]
  6+       tar [c | -c] [-C dir] [-J | -Z | -a | -j | -z] [-h] [-T file] [-X file] path ... [-f file]
  7 
  8 manipulate tape archive files
  9 */
 10@@ -26,7 +27,7 @@ manipulate tape archive files
 11 #include <sys/types.h>
 12 #include <unistd.h>
 13 
 14-#ifdef STD_NON_POSIX
 15+#if FEATURE_TAR_TTY_SAFE
 16 static void
 17 safe_puts(const char *s)
 18 {
 19@@ -140,6 +141,99 @@ static const char *filtertools[] = {
 20 	['z'] = "gzip",
 21 };
 22 
 23+#if FEATURE_TAR_TO_STDOUT
 24+static int Oflag_stdout = 0;
 25+#else
 26+#define Oflag_stdout 0
 27+#endif
 28+
 29+#if FEATURE_TAR_KEEP_OLD
 30+static int kflag_keep = 0;
 31+#else
 32+#define kflag_keep 0
 33+#endif
 34+
 35+#if FEATURE_TAR_STRIP_COMPONENTS
 36+static int strip_components_count = 0;
 37+
 38+static char *
 39+strip_components(char *path, int count)
 40+{
 41+	char *p = path;
 42+	int i;
 43+
 44+	for (i = 0; i < count; i++) {
 45+		p = strchr(p, '/');
 46+		if (!p)
 47+			return NULL;
 48+		while (*p == '/')
 49+			p++;
 50+	}
 51+	return p;
 52+}
 53+#else
 54+#define strip_components_count 0
 55+#endif
 56+
 57+#if FEATURE_TAR_FILES_FROM
 58+static char **files_from = NULL;
 59+static size_t files_from_cnt = 0;
 60+
 61+static void
 62+add_files_from(const char *path)
 63+{
 64+	files_from = ereallocarray(files_from, files_from_cnt + 1, sizeof(*files_from));
 65+	files_from[files_from_cnt++] = estrdup(path);
 66+}
 67+
 68+static void
 69+load_files_from_file(const char *path)
 70+{
 71+	FILE *fp = fopen(path, "r");
 72+	char line[PATH_MAX];
 73+
 74+	if (!fp)
 75+		eprintf("open %s:", path);
 76+	while (fgets(line, sizeof(line), fp)) {
 77+		size_t len = strlen(line);
 78+		while (len > 0 && (line[len - 1] == '\n' || line[len - 1] == '\r'))
 79+			len--;
 80+		line[len] = '\0';
 81+		if (len > 0)
 82+			add_files_from(line);
 83+	}
 84+	fclose(fp);
 85+}
 86+#else
 87+#define files_from_cnt 0
 88+#endif
 89+
 90+#if FEATURE_TAR_EXCLUDE_FROM
 91+static void
 92+load_excludes_from_file(const char *path)
 93+{
 94+	FILE *fp = fopen(path, "r");
 95+	char line[PATH_MAX];
 96+
 97+	if (!fp)
 98+		eprintf("open %s:", path);
 99+	while (fgets(line, sizeof(line), fp)) {
100+		size_t len = strlen(line);
101+		while (len > 0 && (line[len - 1] == '\n' || line[len - 1] == '\r'))
102+			len--;
103+		line[len] = '\0';
104+		if (len > 0) {
105+#if FEATURE_TAR_EXCLUDE
106+			add_exclude(line);
107+#else
108+			(void)line;
109+#endif
110+		}
111+	}
112+	fclose(fp);
113+}
114+#endif
115+
116 static void
117 pushdirtime(char *name, time_t mtime)
118 {
119@@ -351,87 +445,120 @@ archive(const char *path)
120 }
121 #endif
122 
123+static void
124+skipblk(ssize_t l)
125+{
126+	char b[BLKSIZ];
127+
128+	for (; l > 0; l -= BLKSIZ)
129+		if (!eread(tarfd, b, BLKSIZ))
130+			break;
131+}
132+
133 static int
134 unarchive(char *fname, ssize_t l, char b[BLKSIZ])
135 {
136 	struct header *h = (struct header *)b;
137 	struct timespec times[2];
138+	struct stat st;
139 	char lname[101], *tmp, *p;
140 	long mode, major, minor, type, mtime, uid, gid;
141 	int  fd = -1, lnk = h->type == SYMLINK;
142 
143+	if (kflag_keep && !Oflag_stdout) {
144+		if (lstat(fname, &st) == 0) {
145+			skipblk(l);
146+			return 0;
147+		}
148+	}
149+
150 	if (!mflag && ((mtime = strtol(h->mtime, &p, 8)) < 0 || *p != '\0'))
151 		eprintf("strtol %s: invalid mtime\n", h->mtime);
152-	if (strcmp(fname, ".") && strcmp(fname, "./") && remove(fname) < 0)
153-		if (errno != ENOENT) weprintf("remove %s:", fname);
154-
155-	tmp = estrdup(fname);
156-	mkdirp(dirname(tmp), 0777, 0777);
157-	free(tmp);
158-
159-	switch (h->type) {
160-	case REG:
161-	case AREG:
162-	case RESERVED:
163-		if ((mode = strtol(h->mode, &p, 8)) < 0 || *p != '\0')
164-			eprintf("strtol %s: invalid mode\n", h->mode);
165-#ifdef STD_NON_POSIX
166-		fd = open(fname, O_WRONLY | O_TRUNC | O_CREAT | O_NOFOLLOW, 0600);
167+
168+	if (Oflag_stdout) {
169+		if (h->type == REG || h->type == AREG || h->type == RESERVED) {
170+			fd = 1;
171+		} else {
172+			return 0;
173+		}
174+	} else {
175+		if (strcmp(fname, ".") && strcmp(fname, "./") && remove(fname) < 0)
176+			if (errno != ENOENT) weprintf("remove %s:", fname);
177+
178+		tmp = estrdup(fname);
179+		mkdirp(dirname(tmp), 0777, 0777);
180+		free(tmp);
181+
182+		switch (h->type) {
183+		case REG:
184+		case AREG:
185+		case RESERVED:
186+			if ((mode = strtol(h->mode, &p, 8)) < 0 || *p != '\0')
187+				eprintf("strtol %s: invalid mode\n", h->mode);
188+#if FEATURE_TAR_NOFOLLOW
189+			fd = open(fname, O_WRONLY | O_TRUNC | O_CREAT | O_NOFOLLOW, 0600);
190 #else
191-		fd = open(fname, O_WRONLY | O_TRUNC | O_CREAT, 0600);
192+			fd = open(fname, O_WRONLY | O_TRUNC | O_CREAT, 0600);
193 #endif
194-		if (fd < 0)
195-			eprintf("open %s:", fname);
196-		break;
197-	case HARDLINK:
198-	case SYMLINK:
199-		snprintf(lname, sizeof(lname), "%.*s", (int)sizeof(h->linkname),
200-		         h->linkname);
201-		if ((lnk ? symlink:link)(lname, fname) < 0)
202-			eprintf("%s %s -> %s:", lnk ? "symlink":"link", fname, lname);
203-		lnk++;
204-		break;
205-	case DIRECTORY:
206-		if ((mode = strtol(h->mode, &p, 8)) < 0 || *p != '\0')
207-			eprintf("strtol %s: invalid mode\n", h->mode);
208-		if (mkdir(fname, (mode_t)mode) < 0 && errno != EEXIST)
209-			eprintf("mkdir %s:", fname);
210-		pushdirtime(fname, mtime);
211-		break;
212-	case CHARDEV:
213-	case BLOCKDEV:
214-		if ((mode = strtol(h->mode, &p, 8)) < 0 || *p != '\0')
215-			eprintf("strtol %s: invalid mode\n", h->mode);
216-		if ((major = strtol(h->major, &p, 8)) < 0 || *p != '\0')
217-			eprintf("strtol %s: invalid major device\n", h->major);
218-		if ((minor = strtol(h->minor, &p, 8)) < 0 || *p != '\0')
219-			eprintf("strtol %s: invalid minor device\n", h->minor);
220-		type = (h->type == CHARDEV) ? S_IFCHR : S_IFBLK;
221-		if (mknod(fname, type | mode, makedev(major, minor)) < 0)
222-			eprintf("mknod %s:", fname);
223-		break;
224-	case FIFO:
225-		if ((mode = strtol(h->mode, &p, 8)) < 0 || *p != '\0')
226-			eprintf("strtol %s: invalid mode\n", h->mode);
227-		if (mknod(fname, S_IFIFO | mode, 0) < 0)
228-			eprintf("mknod %s:", fname);
229-		break;
230-	default:
231-		eprintf("unsupported tar-filetype %c\n", h->type);
232+			if (fd < 0)
233+				eprintf("open %s:", fname);
234+			break;
235+		case HARDLINK:
236+		case SYMLINK:
237+			snprintf(lname, sizeof(lname), "%.*s", (int)sizeof(h->linkname),
238+			         h->linkname);
239+			if ((lnk ? symlink:link)(lname, fname) < 0)
240+				eprintf("%s %s -> %s:", lnk ? "symlink":"link", fname, lname);
241+			lnk++;
242+			break;
243+		case DIRECTORY:
244+			if ((mode = strtol(h->mode, &p, 8)) < 0 || *p != '\0')
245+				eprintf("strtol %s: invalid mode\n", h->mode);
246+			if (mkdir(fname, (mode_t)mode) < 0 && errno != EEXIST)
247+				eprintf("mkdir %s:", fname);
248+			pushdirtime(fname, mtime);
249+			break;
250+		case CHARDEV:
251+		case BLOCKDEV:
252+			if ((mode = strtol(h->mode, &p, 8)) < 0 || *p != '\0')
253+				eprintf("strtol %s: invalid mode\n", h->mode);
254+			if ((major = strtol(h->major, &p, 8)) < 0 || *p != '\0')
255+				eprintf("strtol %s: invalid major device\n", h->major);
256+			if ((minor = strtol(h->minor, &p, 8)) < 0 || *p != '\0')
257+				eprintf("strtol %s: invalid minor device\n", h->minor);
258+			type = (h->type == CHARDEV) ? S_IFCHR : S_IFBLK;
259+			if (mknod(fname, type | mode, makedev(major, minor)) < 0)
260+				eprintf("mknod %s:", fname);
261+			break;
262+		case FIFO:
263+			if ((mode = strtol(h->mode, &p, 8)) < 0 || *p != '\0')
264+				eprintf("strtol %s: invalid mode\n", h->mode);
265+			if (mknod(fname, S_IFIFO | mode, 0) < 0)
266+				eprintf("mknod %s:", fname);
267+			break;
268+		default:
269+			eprintf("unsupported tar-filetype %c\n", h->type);
270+		}
271 	}
272 
273-	if ((uid = strtol(h->uid, &p, 8)) < 0 || *p != '\0')
274-		eprintf("strtol %s: invalid uid\n", h->uid);
275-	if ((gid = strtol(h->gid, &p, 8)) < 0 || *p != '\0')
276-		eprintf("strtol %s: invalid gid\n", h->gid);
277+	if (!Oflag_stdout) {
278+		if ((uid = strtol(h->uid, &p, 8)) < 0 || *p != '\0')
279+			eprintf("strtol %s: invalid uid\n", h->uid);
280+		if ((gid = strtol(h->gid, &p, 8)) < 0 || *p != '\0')
281+			eprintf("strtol %s: invalid gid\n", h->gid);
282+	}
283 
284 	if (fd != -1) {
285 		for (; l > 0; l -= BLKSIZ)
286 			if (eread(tarfd, b, BLKSIZ) > 0)
287 				ewrite(fd, b, MIN(l, (ssize_t)BLKSIZ));
288-		close(fd);
289+		if (fd != 1)
290+			close(fd);
291 	}
292 
293+	if (Oflag_stdout)
294+		return 0;
295+
296 	if (lnk == 1)
297 		return 0;
298 
299@@ -452,15 +579,6 @@ unarchive(char *fname, ssize_t l, char b[BLKSIZ])
300 	return 0;
301 }
302 
303-static void
304-skipblk(ssize_t l)
305-{
306-	char b[BLKSIZ];
307-
308-	for (; l > 0; l -= BLKSIZ)
309-		if (!eread(tarfd, b, BLKSIZ))
310-			break;
311-}
312 
313 static int
314 print(char *fname, ssize_t l, char b[BLKSIZ])
315@@ -560,12 +678,15 @@ static void
316 xt(int argc, char *argv[], int mode)
317 {
318 	long size;
319-	char b[BLKSIZ], fname[PATH_MAX + 1], *p, *q = NULL;
320-	int i, m, n;
321+	char b[BLKSIZ], fname[PATH_MAX + 1], *p, *q = NULL, *stripped;
322+	int i, m, n, match;
323 	int (*fn)(char *, ssize_t, char[BLKSIZ]) = (mode == 'x') ? unarchive : print;
324 	struct timespec times[2];
325 	struct header *h = (struct header *)b;
326 	struct dirtime *dirtime;
327+#if FEATURE_TAR_FILES_FROM
328+	size_t idx;
329+#endif
330 
331 	while (eread(tarfd, b, BLKSIZ) > 0 && (h->name[0] || h->prefix[0])) {
332 		chktar(h);
333@@ -610,20 +731,47 @@ xt(int argc, char *argv[], int mode)
334 		}
335 		q = NULL;
336 
337-		/* If argc > 0 then only extract the given files/dirs */
338-		if (argc) {
339+		/* If argc > 0 or files_from_cnt > 0 then only extract the matching files/dirs */
340+		if (argc || files_from_cnt) {
341+			match = 0;
342 			for (i = 0; i < argc; i++) {
343-				if (strncmp(argv[i], fname, n = strlen(argv[i])) == 0)
344-					if (strchr("/", fname[n]) || argv[i][n-1] == '/')
345+				if (strncmp(argv[i], fname, n = strlen(argv[i])) == 0) {
346+					if (strchr("/", fname[n]) || argv[i][n-1] == '/') {
347+						match = 1;
348 						break;
349+					}
350+				}
351+			}
352+#if FEATURE_TAR_FILES_FROM
353+			if (!match) {
354+				for (idx = 0; idx < files_from_cnt; idx++) {
355+					if (strncmp(files_from[idx], fname, n = strlen(files_from[idx])) == 0) {
356+						if (strchr("/", fname[n]) || files_from[idx][n-1] == '/') {
357+							match = 1;
358+							break;
359+						}
360+					}
361+				}
362 			}
363-			if (i == argc) {
364+#endif
365+			if (!match) {
366 				skipblk(size);
367 				continue;
368 			}
369 		}
370 
371-		fn(fname, size, b);
372+		stripped = fname;
373+#if FEATURE_TAR_STRIP_COMPONENTS
374+		if (mode == 'x' && strip_components_count > 0) {
375+			stripped = strip_components(fname, strip_components_count);
376+			if (!stripped || *stripped == '\0') {
377+				skipblk(size);
378+				continue;
379+			}
380+		}
381+#endif
382+
383+		fn(stripped, size, b);
384 		if (vflag && mode != 't')
385 			safe_puts(fname);
386 	}
387@@ -667,6 +815,7 @@ main(int argc, char *argv[])
388 	struct stat st;
389 	char *file = NULL, *dir = ".", mode = '\0';
390 	int fd;
391+	size_t i;
392 
393 	argv0 = argv[0];
394 #if FEATURE_TAR_CREATE
395@@ -725,20 +874,110 @@ main(int argc, char *argv[])
396 	// ?man -p: preserve file attributes
397 	case 'p':
398 		break;  /* do nothing as already default behaviour */
399-#if FEATURE_TAR_EXCLUDE
400+#if FEATURE_TAR_TO_STDOUT
401+	// ?man -O: extract files to stdout
402+	case 'O':
403+		Oflag_stdout = 1;
404+		break;
405+#endif
406+#if FEATURE_TAR_KEEP_OLD
407+	// ?man -k: keep existing files, do not overwrite
408+	case 'k':
409+		kflag_keep = 1;
410+		break;
411+#endif
412+#if FEATURE_TAR_FILES_FROM
413+	// ?man -T file: read filenames from file
414+	case 'T':
415+		load_files_from_file(EARGF(usage()));
416+		break;
417+#endif
418+#if FEATURE_TAR_EXCLUDE_FROM
419+	// ?man -X file: exclude patterns in file
420+	case 'X':
421+		load_excludes_from_file(EARGF(usage()));
422+		break;
423+#endif
424+#if FEATURE_TAR_STRIP_COMPONENTS
425+	// ?man -strip-components num: strip num components
426+	case 's':
427+		if (strcmp(argv[0], "strip-components") == 0) {
428+			argv[0] = "s";
429+			strip_components_count = estrtonum(EARGF(usage()), 0, INT_MAX);
430+			brk_ = 1;
431+		} else if (strncmp(argv[0], "strip-components=", 17) == 0) {
432+			strip_components_count = estrtonum(argv[0] + 17, 0, INT_MAX);
433+			brk_ = 1;
434+		} else {
435+			usage();
436+		}
437+		break;
438+#endif
439 	case '-':
440+#if FEATURE_TAR_EXCLUDE
441 		if (strncmp(argv[0], "-exclude=", 9) == 0) {
442 			add_exclude(argv[0] + 9);
443 			brk_ = 1;
444+			break;
445 		} else if (strcmp(argv[0], "-exclude") == 0) {
446 			argv[0] = "-";
447 			add_exclude(EARGF(usage()));
448 			brk_ = 1;
449-		} else {
450-			usage();
451+			break;
452+		}
453+#endif
454+#if FEATURE_TAR_EXCLUDE_FROM
455+		if (strncmp(argv[0], "-exclude-from=", 14) == 0) {
456+			load_excludes_from_file(argv[0] + 14);
457+			brk_ = 1;
458+			break;
459+		} else if (strcmp(argv[0], "-exclude-from") == 0) {
460+			argv[0] = "-";
461+			load_excludes_from_file(EARGF(usage()));
462+			brk_ = 1;
463+			break;
464 		}
465-		break;
466 #endif
467+#if FEATURE_TAR_TO_STDOUT
468+		if (strcmp(argv[0], "-to-stdout") == 0) {
469+			Oflag_stdout = 1;
470+			brk_ = 1;
471+			break;
472+		}
473+#endif
474+#if FEATURE_TAR_KEEP_OLD
475+		if (strcmp(argv[0], "-keep-old-files") == 0) {
476+			kflag_keep = 1;
477+			brk_ = 1;
478+			break;
479+		}
480+#endif
481+#if FEATURE_TAR_STRIP_COMPONENTS
482+		if (strncmp(argv[0], "-strip-components=", 18) == 0) {
483+			strip_components_count = estrtonum(argv[0] + 18, 0, INT_MAX);
484+			brk_ = 1;
485+			break;
486+		} else if (strcmp(argv[0], "-strip-components") == 0) {
487+			argv[0] = "-";
488+			strip_components_count = estrtonum(EARGF(usage()), 0, INT_MAX);
489+			brk_ = 1;
490+			break;
491+		}
492+#endif
493+#if FEATURE_TAR_FILES_FROM
494+		if (strncmp(argv[0], "-files-from=", 12) == 0) {
495+			load_files_from_file(argv[0] + 12);
496+			brk_ = 1;
497+			break;
498+		} else if (strcmp(argv[0], "-files-from") == 0) {
499+			argv[0] = "-";
500+			load_files_from_file(EARGF(usage()));
501+			brk_ = 1;
502+			break;
503+		}
504+#endif
505+		usage();
506+		break;
507 	default:
508 		usage();
509 	} ARGEND
510@@ -747,7 +986,7 @@ main(int argc, char *argv[])
511 #if FEATURE_TAR_CREATE
512 	// ?man -c: create a new archive
513 	case 'c':
514-		if (!argc)
515+		if (!argc && !files_from_cnt)
516 			usage();
517 		tarfd = 1;
518 		if (file && *file != '-') {
519@@ -767,6 +1006,10 @@ main(int argc, char *argv[])
520 			eprintf("chdir %s:", dir);
521 		for (; *argv; argc--, argv++)
522 			recurse(AT_FDCWD, *argv, NULL, &r);
523+#if FEATURE_TAR_FILES_FROM
524+		for (i = 0; i < files_from_cnt; i++)
525+			recurse(AT_FDCWD, files_from[i], NULL, &r);
526+#endif
527 		break;
528 #endif
529 	// ?man -t: list the contents of an archive
530@@ -796,11 +1039,17 @@ main(int argc, char *argv[])
531 
532 #if FEATURE_TAR_EXCLUDE
533 	if (excludes) {
534-		size_t i;
535 		for (i = 0; i < excludes_cnt; i++)
536 			free(excludes[i]);
537 		free(excludes);
538 	}
539+#endif
540+#if FEATURE_TAR_FILES_FROM
541+	if (files_from) {
542+		for (i = 0; i < files_from_cnt; i++)
543+			free(files_from[i]);
544+		free(files_from);
545+	}
546 #endif
547 	return recurse_status;
548 }
+26, -2
 1@@ -232,6 +232,18 @@ FEATURE_DEPMOD_ALIAS   = 1
 2 FEATURE_DEPMOD_SYMBOLS = 1
 3 FEATURE_USE_LIBRESSL = 0
 4 FEATURE_USE_BEARSSL = 1
 5+FEATURE_TAR_TTY_SAFE = 1
 6+FEATURE_TAR_NOFOLLOW = 1
 7+FEATURE_READLINK_REALPATH = 1
 8+FEATURE_UMOUNT_OPTIONS = 1
 9+FEATURE_TAR_TO_STDOUT = 1
10+FEATURE_TAR_KEEP_OLD = 1
11+FEATURE_TAR_STRIP_COMPONENTS = 1
12+FEATURE_TAR_FILES_FROM = 1
13+FEATURE_TAR_EXCLUDE_FROM = 1
14+FEATURE_IP_ROUTE_ADD_DEL = 1
15+FEATURE_IP_LINK_SET = 1
16+FEATURE_IP_ADDR_FLUSH = 1
17 
18 
19 CPPFLAGS =\
20@@ -243,7 +255,6 @@ CPPFLAGS =\
21 	-D_BSD_SOURCE\
22 	-D_XOPEN_SOURCE=700\
23 	-D_FILE_OFFSET_BITS=64\
24-	-DSTD_NON_POSIX\
25 	-DFEATURE_FIND_DELETE=$(FEATURE_FIND_DELETE)\
26 	-DFEATURE_FIND_QUIT=$(FEATURE_FIND_QUIT)\
27 	-DFEATURE_FIND_EMPTY=$(FEATURE_FIND_EMPTY)\
28@@ -284,7 +295,20 @@ CPPFLAGS =\
29 	-DFEATURE_DEPMOD_ALIAS=$(FEATURE_DEPMOD_ALIAS)\
30 	-DFEATURE_DEPMOD_SYMBOLS=$(FEATURE_DEPMOD_SYMBOLS)\
31 	-DFEATURE_USE_LIBRESSL=$(FEATURE_USE_LIBRESSL)\
32-	-DFEATURE_USE_BEARSSL=$(FEATURE_USE_BEARSSL) $(CPPFLAGS_TLS)
33+	-DFEATURE_USE_BEARSSL=$(FEATURE_USE_BEARSSL)\
34+	-DFEATURE_TAR_TTY_SAFE=$(FEATURE_TAR_TTY_SAFE)\
35+	-DFEATURE_TAR_NOFOLLOW=$(FEATURE_TAR_NOFOLLOW)\
36+	-DFEATURE_READLINK_REALPATH=$(FEATURE_READLINK_REALPATH)\
37+	-DFEATURE_UMOUNT_OPTIONS=$(FEATURE_UMOUNT_OPTIONS)\
38+	-DFEATURE_TAR_TO_STDOUT=$(FEATURE_TAR_TO_STDOUT)\
39+	-DFEATURE_TAR_KEEP_OLD=$(FEATURE_TAR_KEEP_OLD)\
40+	-DFEATURE_TAR_STRIP_COMPONENTS=$(FEATURE_TAR_STRIP_COMPONENTS)\
41+	-DFEATURE_TAR_FILES_FROM=$(FEATURE_TAR_FILES_FROM)\
42+	-DFEATURE_TAR_EXCLUDE_FROM=$(FEATURE_TAR_EXCLUDE_FROM)\
43+	-DFEATURE_IP_ROUTE_ADD_DEL=$(FEATURE_IP_ROUTE_ADD_DEL)\
44+	-DFEATURE_IP_LINK_SET=$(FEATURE_IP_LINK_SET)\
45+	-DFEATURE_IP_ADDR_FLUSH=$(FEATURE_IP_ADDR_FLUSH) $(CPPFLAGS_TLS)
46+
47 
48 CFLAGS   = -std=c99 -Wall -Wextra -pedantic
49 LDFLAGS  = $(LDFLAGS_TLS)
+4, -2
 1@@ -9,7 +9,7 @@
 2 set -e
 3 
 4 : "${CC:=cc}"
 5-: "${CPPFLAGS:=-Ishared -D_DEFAULT_SOURCE -D_GNU_SOURCE -D_NETBSD_SOURCE -D_BSD_SOURCE -D_XOPEN_SOURCE=700 -D_FILE_OFFSET_BITS=64 -DSTD_NON_POSIX}"
 6+: "${CPPFLAGS:=-Ishared -D_DEFAULT_SOURCE -D_GNU_SOURCE -D_NETBSD_SOURCE -D_BSD_SOURCE -D_XOPEN_SOURCE=700 -D_FILE_OFFSET_BITS=64}"
 7 : "${CFLAGS:=-std=c99 -Wall -Wextra -pedantic}"
 8 : "${LDFLAGS:=}"
 9 : "${LDLIBS:=-lcrypt -lresolv}"
10@@ -19,6 +19,7 @@ set -e
11 ROOT=$(CDPATH= cd "$(dirname "$0")/.." && pwd)
12 BDIR="${ROOT}/.box"
13 BOX="${ROOT}/aruu-box"
14+LIBREDLINE="${ROOT}/shared/libredline/libredline.a"
15 LIBUTIL="${ROOT}/shared/libutil/libutil.a"
16 LIBUTF="${ROOT}/shared/libutf/libutf.a"
17 
18@@ -30,6 +31,7 @@ die() {
19 # Clean up build directory on exit or signal termination
20 trap 'rm -rf "${BDIR}"' EXIT INT QUIT TERM
21 
22+[ -f "${LIBREDLINE}" ] || die "libredline.a not found; run 'make' first"
23 [ -f "${LIBUTIL}" ] || die "libutil.a not found; run 'make' first"
24 [ -f "${LIBUTF}" ]  || die "libutf.a not found; run 'make' first"
25 
26@@ -387,7 +389,7 @@ eval "${CC} ${CPPFLAGS} ${CFLAGS} -c \"\$DISP\" -o \"\${BDIR}/aruu-box.o\"" ||
27 OBJS="${OBJS} ${BDIR}/aruu-box.o"
28 
29 printf 'mkbox: ld aruu-box\n'
30-eval "${CC} ${LDFLAGS} -o \"\$BOX\" \$OBJS \"\$LIBUTIL\" \"\$LIBUTF\" \${LDLIBS}" ||
31+eval "${CC} ${LDFLAGS} -o \"\$BOX\" \$OBJS \"\$LIBREDLINE\" \"\$LIBUTIL\" \"\$LIBUTF\" \${LDLIBS}" ||
32 	die "link failed"
33 
34 printf 'mkbox: done => %s\n' "${BOX}"
+167, -0
  1@@ -5,6 +5,7 @@
  2 #include <errno.h>
  3 #include <ifaddrs.h>
  4 #include <net/if.h>
  5+#include <net/route.h>
  6 #include <stdio.h>
  7 #include <stdlib.h>
  8 #include <string.h>
  9@@ -401,3 +402,169 @@ net_show_routes(void)
 10 	return 0;
 11 #endif
 12 }
 13+
 14+int
 15+net_add_route(const char *dst, const char *gateway, const char *mask, const char *dev, int metric)
 16+{
 17+	struct rtentry rt;
 18+	struct sockaddr_in *sin;
 19+	int sock;
 20+
 21+	sock = socket(AF_INET, SOCK_DGRAM, 0);
 22+	if (sock < 0)
 23+		return -1;
 24+
 25+	memset(&rt, 0, sizeof(rt));
 26+
 27+	sin = (struct sockaddr_in *)&rt.rt_dst;
 28+	sin->sin_family = AF_INET;
 29+	if (strcmp(dst, "default") == 0) {
 30+		sin->sin_addr.s_addr = INADDR_ANY;
 31+	} else {
 32+		if (inet_pton(AF_INET, dst, &sin->sin_addr) <= 0) {
 33+			close(sock);
 34+			return -1;
 35+		}
 36+	}
 37+
 38+	if (gateway) {
 39+		sin = (struct sockaddr_in *)&rt.rt_gateway;
 40+		sin->sin_family = AF_INET;
 41+		if (inet_pton(AF_INET, gateway, &sin->sin_addr) <= 0) {
 42+			close(sock);
 43+			return -1;
 44+		}
 45+		rt.rt_flags |= RTF_GATEWAY;
 46+	}
 47+
 48+	if (mask) {
 49+		sin = (struct sockaddr_in *)&rt.rt_genmask;
 50+		sin->sin_family = AF_INET;
 51+		if (inet_pton(AF_INET, mask, &sin->sin_addr) <= 0) {
 52+			close(sock);
 53+			return -1;
 54+		}
 55+	}
 56+
 57+	rt.rt_flags |= RTF_UP;
 58+	if (dev)
 59+		rt.rt_dev = (char *)dev;
 60+	if (metric >= 0)
 61+		rt.rt_metric = metric + 1;
 62+
 63+	if (ioctl(sock, SIOCADDRT, &rt) < 0) {
 64+		close(sock);
 65+		return -1;
 66+	}
 67+
 68+	close(sock);
 69+	return 0;
 70+}
 71+
 72+int
 73+net_del_route(const char *dst, const char *gateway, const char *mask, const char *dev, int metric)
 74+{
 75+	struct rtentry rt;
 76+	struct sockaddr_in *sin;
 77+	int sock;
 78+
 79+	sock = socket(AF_INET, SOCK_DGRAM, 0);
 80+	if (sock < 0)
 81+		return -1;
 82+
 83+	memset(&rt, 0, sizeof(rt));
 84+
 85+	sin = (struct sockaddr_in *)&rt.rt_dst;
 86+	sin->sin_family = AF_INET;
 87+	if (strcmp(dst, "default") == 0) {
 88+		sin->sin_addr.s_addr = INADDR_ANY;
 89+	} else {
 90+		if (inet_pton(AF_INET, dst, &sin->sin_addr) <= 0) {
 91+			close(sock);
 92+			return -1;
 93+		}
 94+	}
 95+
 96+	if (gateway) {
 97+		sin = (struct sockaddr_in *)&rt.rt_gateway;
 98+		sin->sin_family = AF_INET;
 99+		if (inet_pton(AF_INET, gateway, &sin->sin_addr) <= 0) {
100+			close(sock);
101+			return -1;
102+		}
103+		rt.rt_flags |= RTF_GATEWAY;
104+	}
105+
106+	if (mask) {
107+		sin = (struct sockaddr_in *)&rt.rt_genmask;
108+		sin->sin_family = AF_INET;
109+		if (inet_pton(AF_INET, mask, &sin->sin_addr) <= 0) {
110+			close(sock);
111+			return -1;
112+		}
113+	}
114+
115+	rt.rt_flags |= RTF_UP;
116+	if (dev)
117+		rt.rt_dev = (char *)dev;
118+	if (metric >= 0)
119+		rt.rt_metric = metric + 1;
120+
121+	if (ioctl(sock, SIOCDELRT, &rt) < 0) {
122+		close(sock);
123+		return -1;
124+	}
125+
126+	close(sock);
127+	return 0;
128+}
129+
130+int
131+net_flush_addrs(const char *dev)
132+{
133+	struct NetInterface *ifaces = NULL;
134+	int count = 0, i, r = 0;
135+
136+	if (net_get_interfaces(&ifaces, &count) < 0)
137+		return -1;
138+
139+	for (i = 0; i < count; i++) {
140+		if (strcmp(ifaces[i].name, dev) == 0) {
141+			if (ifaces[i].has_ipv4) {
142+				char addr_str[16];
143+				struct sockaddr_in *sin = &ifaces[i].ipv4_addr;
144+				inet_ntop(AF_INET, &sin->sin_addr, addr_str, sizeof(addr_str));
145+				if (net_del_addr(dev, addr_str, -1) < 0)
146+					r = -1;
147+			}
148+		}
149+	}
150+	free(ifaces);
151+	return r;
152+}
153+
154+int
155+net_set_name(const char *name, const char *newname)
156+{
157+#ifdef __linux__
158+	struct ifreq ifr;
159+	int sock;
160+
161+	sock = socket(AF_INET, SOCK_DGRAM, 0);
162+	if (sock < 0)
163+		return -1;
164+	memset(&ifr, 0, sizeof(ifr));
165+	strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name));
166+	strlcpy(ifr.ifr_newname, newname, sizeof(ifr.ifr_newname));
167+	if (ioctl(sock, SIOCSIFNAME, &ifr) < 0) {
168+		close(sock);
169+		return -1;
170+	}
171+	close(sock);
172+	return 0;
173+#else
174+	(void)name;
175+	(void)newname;
176+	return -1;
177+#endif
178+}
+16, -1
 1@@ -2,9 +2,10 @@
 2 #include <sys/types.h>
 3 
 4 #include <regex.h>
 5+#include <stdarg.h>
 6 #include <stddef.h>
 7 #include <stdio.h>
 8-#include <stdarg.h>
 9+#include <string.h>
10 
11 #include "arg.h"
12 #include "compat.h"
13@@ -155,3 +156,17 @@ struct MemInfo {
14 	unsigned long long totalswap;
15 	unsigned long long freeswap;
16 };
17+
18+int net_get_interfaces(struct NetInterface **, int *);
19+int net_get_stats(const char *, struct NetStats *);
20+int net_set_flags(const char *, unsigned int, int);
21+int net_set_mtu(const char *, int);
22+int net_set_mac(const char *, const unsigned char *);
23+int net_add_addr(const char *, const char *, int);
24+int net_del_addr(const char *, const char *, int);
25+int net_show_routes(void);
26+int net_add_route(const char *, const char *, const char *, const char *, int);
27+int net_del_route(const char *, const char *, const char *, const char *, int);
28+int net_flush_addrs(const char *);
29+int net_set_txqueuelen(const char *, int);
30+int net_set_name(const char *, const char *);