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}"
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+}
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 *);