diff options
| author | Ron Yorston <rmy@pobox.com> | 2022-05-12 08:11:27 +0100 |
|---|---|---|
| committer | Ron Yorston <rmy@pobox.com> | 2022-05-12 08:11:27 +0100 |
| commit | 7c8c7681a9c8fac1fb8cf77f5950d32885ebb08c (patch) | |
| tree | 4e21c0c676bc424ba10e616d9f97de76bfe4409c | |
| parent | f637f37e0bd2e295936a7b4836676846693219aa (diff) | |
| parent | 1099a27696cd733041db97f99da4e22ecd2424e5 (diff) | |
| download | busybox-w32-7c8c7681a9c8fac1fb8cf77f5950d32885ebb08c.tar.gz busybox-w32-7c8c7681a9c8fac1fb8cf77f5950d32885ebb08c.tar.bz2 busybox-w32-7c8c7681a9c8fac1fb8cf77f5950d32885ebb08c.zip | |
Merge branch 'busybox' into merge
| -rw-r--r-- | Makefile | 8 | ||||
| -rw-r--r-- | Makefile.flags | 4 | ||||
| -rw-r--r-- | coreutils/tsort.c | 188 | ||||
| -rw-r--r-- | docs/posix_conformance.txt | 2 | ||||
| -rw-r--r-- | editors/vi.c | 3 | ||||
| -rw-r--r-- | examples/shutdown-1.0/script/hardshutdown.c | 2 | ||||
| -rw-r--r-- | init/init.c | 6 | ||||
| -rw-r--r-- | libbb/appletlib.c | 2 | ||||
| -rw-r--r-- | libpwdgrp/pwd_grp.c | 14 | ||||
| -rw-r--r-- | miscutils/crond.c | 23 | ||||
| -rw-r--r-- | miscutils/seedrng.c | 242 | ||||
| -rw-r--r-- | networking/httpd_indexcgi.c | 2 | ||||
| -rw-r--r-- | networking/httpd_ssi.c | 2 | ||||
| -rw-r--r-- | networking/ifplugd.c | 12 | ||||
| -rw-r--r-- | printutils/lpd.c | 4 | ||||
| -rw-r--r-- | printutils/lpr.c | 4 | ||||
| -rw-r--r-- | procps/top.c | 31 | ||||
| -rw-r--r-- | scripts/Makefile.lib | 1 | ||||
| -rw-r--r-- | shell/match.c | 2 | ||||
| -rwxr-xr-x | testsuite/tsort.tests | 110 |
20 files changed, 625 insertions, 37 deletions
| @@ -1315,14 +1315,6 @@ quiet_cmd_rmdirs = $(if $(wildcard $(rm-dirs)),CLEAN $(wildcard $(rm-dirs))) | |||
| 1315 | quiet_cmd_rmfiles = $(if $(wildcard $(rm-files)),CLEAN $(wildcard $(rm-files))) | 1315 | quiet_cmd_rmfiles = $(if $(wildcard $(rm-files)),CLEAN $(wildcard $(rm-files))) |
| 1316 | cmd_rmfiles = rm -f $(rm-files) | 1316 | cmd_rmfiles = rm -f $(rm-files) |
| 1317 | 1317 | ||
| 1318 | |||
| 1319 | a_flags = -Wp,-MD,$(depfile) $(AFLAGS) $(AFLAGS_KERNEL) \ | ||
| 1320 | $(NOSTDINC_FLAGS) $(CPPFLAGS) \ | ||
| 1321 | $(modkern_aflags) $(EXTRA_AFLAGS) $(AFLAGS_$(*F).o) | ||
| 1322 | |||
| 1323 | quiet_cmd_as_o_S = AS $@ | ||
| 1324 | cmd_as_o_S = $(CC) $(a_flags) -c -o $@ $< | ||
| 1325 | |||
| 1326 | # read all saved command lines | 1318 | # read all saved command lines |
| 1327 | 1319 | ||
| 1328 | targets := $(wildcard $(sort $(targets))) | 1320 | targets := $(wildcard $(sort $(targets))) |
diff --git a/Makefile.flags b/Makefile.flags index f202a5f54..bf77c2492 100644 --- a/Makefile.flags +++ b/Makefile.flags | |||
| @@ -88,14 +88,14 @@ endif | |||
| 88 | #CFLAGS += $(call cc-option,-Wconversion,) | 88 | #CFLAGS += $(call cc-option,-Wconversion,) |
| 89 | 89 | ||
| 90 | ifneq ($(CONFIG_DEBUG),y) | 90 | ifneq ($(CONFIG_DEBUG),y) |
| 91 | CFLAGS += $(call cc-option,-Os,$(call cc-option,-O2,)) | 91 | CFLAGS += $(call cc-option,-Oz,$(call cc-option,-Os,$(call cc-option,-O2,))) |
| 92 | else | 92 | else |
| 93 | CFLAGS += $(call cc-option,-g,) | 93 | CFLAGS += $(call cc-option,-g,) |
| 94 | #CFLAGS += "-D_FORTIFY_SOURCE=2" | 94 | #CFLAGS += "-D_FORTIFY_SOURCE=2" |
| 95 | ifeq ($(CONFIG_DEBUG_PESSIMIZE),y) | 95 | ifeq ($(CONFIG_DEBUG_PESSIMIZE),y) |
| 96 | CFLAGS += $(call cc-option,-O0,) | 96 | CFLAGS += $(call cc-option,-O0,) |
| 97 | else | 97 | else |
| 98 | CFLAGS += $(call cc-option,-Os,$(call cc-option,-O2,)) | 98 | CFLAGS += $(call cc-option,-Oz,$(call cc-option,-Os,$(call cc-option,-O2,))) |
| 99 | endif | 99 | endif |
| 100 | endif | 100 | endif |
| 101 | ifeq ($(CONFIG_DEBUG_SANITIZE),y) | 101 | ifeq ($(CONFIG_DEBUG_SANITIZE),y) |
diff --git a/coreutils/tsort.c b/coreutils/tsort.c new file mode 100644 index 000000000..dedb65b15 --- /dev/null +++ b/coreutils/tsort.c | |||
| @@ -0,0 +1,188 @@ | |||
| 1 | /* vi: set sw=4 ts=4: */ | ||
| 2 | /* | ||
| 3 | * tsort implementation for busybox | ||
| 4 | * | ||
| 5 | * public domain -- David Leonard, 2022 | ||
| 6 | */ | ||
| 7 | //config:config TSORT | ||
| 8 | //config: bool "tsort (0.7 kb)" | ||
| 9 | //config: default y | ||
| 10 | //config: help | ||
| 11 | //config: tsort performs a topological sort. | ||
| 12 | |||
| 13 | //applet:IF_TSORT(APPLET(tsort, BB_DIR_USR_BIN, BB_SUID_DROP)) | ||
| 14 | |||
| 15 | //kbuild:lib-$(CONFIG_TSORT) += tsort.o | ||
| 16 | |||
| 17 | /* BB_AUDIT SUSv3 compliant */ | ||
| 18 | /* http://www.opengroup.org/onlinepubs/007904975/utilities/tsort.html */ | ||
| 19 | |||
| 20 | //usage:#define tsort_trivial_usage | ||
| 21 | //usage: "[FILE]" | ||
| 22 | //usage:#define tsort_full_usage "\n\n" | ||
| 23 | //usage: "Topological sort" | ||
| 24 | //usage:#define tsort_example_usage | ||
| 25 | //usage: "$ echo -e \"a b\\nb c\" | tsort\n" | ||
| 26 | //usage: "a\n" | ||
| 27 | //usage: "b\n" | ||
| 28 | //usage: "c\n" | ||
| 29 | |||
| 30 | #include "libbb.h" | ||
| 31 | #include "common_bufsiz.h" | ||
| 32 | |||
| 33 | struct node { | ||
| 34 | unsigned in_count; | ||
| 35 | unsigned out_count; | ||
| 36 | struct node **out; | ||
| 37 | char name[1]; | ||
| 38 | }; | ||
| 39 | |||
| 40 | struct globals { | ||
| 41 | struct node **nodes; | ||
| 42 | unsigned nodes_len; | ||
| 43 | }; | ||
| 44 | #define G (*(struct globals*)bb_common_bufsiz1) | ||
| 45 | #define INIT_G() do { \ | ||
| 46 | setup_common_bufsiz(); \ | ||
| 47 | BUILD_BUG_ON(sizeof(G) > COMMON_BUFSIZE); \ | ||
| 48 | G.nodes = NULL; \ | ||
| 49 | G.nodes_len = 0; \ | ||
| 50 | } while (0) | ||
| 51 | |||
| 52 | static struct node * | ||
| 53 | get_node(const char *name) | ||
| 54 | { | ||
| 55 | struct node *n; | ||
| 56 | unsigned a = 0; | ||
| 57 | unsigned b = G.nodes_len; | ||
| 58 | |||
| 59 | /* Binary search for name */ | ||
| 60 | while (a != b) { | ||
| 61 | unsigned m = (a + b) / 2; | ||
| 62 | int cmp = strcmp(name, G.nodes[m]->name); | ||
| 63 | if (cmp == 0) | ||
| 64 | return G.nodes[m]; /* found */ | ||
| 65 | if (cmp < 0) { | ||
| 66 | b = m; | ||
| 67 | } else { | ||
| 68 | a = m + 1; | ||
| 69 | } | ||
| 70 | } | ||
| 71 | |||
| 72 | /* Allocate new node */ | ||
| 73 | n = xzalloc(sizeof(*n) + strlen(name)); | ||
| 74 | //n->in_count = 0; | ||
| 75 | //n->out_count = 0; | ||
| 76 | //n->out = NULL; | ||
| 77 | strcpy(n->name, name); | ||
| 78 | |||
| 79 | /* Insert to maintain sort */ | ||
| 80 | G.nodes = xrealloc(G.nodes, (G.nodes_len + 1) * sizeof(*G.nodes)); | ||
| 81 | memmove(&G.nodes[a + 1], &G.nodes[a], | ||
| 82 | (G.nodes_len - a) * sizeof(*G.nodes)); | ||
| 83 | G.nodes[a] = n; | ||
| 84 | G.nodes_len++; | ||
| 85 | return n; | ||
| 86 | } | ||
| 87 | |||
| 88 | static void | ||
| 89 | add_edge(struct node *a, struct node *b) | ||
| 90 | { | ||
| 91 | a->out = xrealloc_vector(a->out, 6, a->out_count); | ||
| 92 | a->out[a->out_count++] = b; | ||
| 93 | b->in_count++; | ||
| 94 | } | ||
| 95 | |||
| 96 | int tsort_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | ||
| 97 | int tsort_main(int argc UNUSED_PARAM, char **argv) | ||
| 98 | { | ||
| 99 | char *line; | ||
| 100 | size_t linesz; | ||
| 101 | ssize_t len; | ||
| 102 | struct node *a; | ||
| 103 | int cycles; | ||
| 104 | |||
| 105 | INIT_G(); | ||
| 106 | |||
| 107 | if (argv[1]) { | ||
| 108 | if (argv[2]) | ||
| 109 | bb_show_usage(); | ||
| 110 | if (NOT_LONE_DASH(argv[1])) { | ||
| 111 | close(STDIN_FILENO); /* == 0 */ | ||
| 112 | xopen(argv[1], O_RDONLY); /* fd will be 0 */ | ||
| 113 | } | ||
| 114 | } | ||
| 115 | |||
| 116 | /* Read in words separated by <blank>s */ | ||
| 117 | a = NULL; | ||
| 118 | line = NULL; | ||
| 119 | linesz = 0; | ||
| 120 | while ((len = getline(&line, &linesz, stdin)) != -1) { | ||
| 121 | char *s = line; | ||
| 122 | while (*(s = skip_whitespace(s)) != '\0') { | ||
| 123 | struct node *b; | ||
| 124 | char *word; | ||
| 125 | |||
| 126 | word = s; | ||
| 127 | s = skip_non_whitespace(s); | ||
| 128 | if (*s) | ||
| 129 | *s++ = '\0'; | ||
| 130 | |||
| 131 | /* Create nodes and edges for each word pair */ | ||
| 132 | b = get_node(word); | ||
| 133 | if (!a) { | ||
| 134 | a = b; | ||
| 135 | } else { | ||
| 136 | if (a != b) | ||
| 137 | add_edge(a, b); | ||
| 138 | a = NULL; | ||
| 139 | } | ||
| 140 | } | ||
| 141 | } | ||
| 142 | // Most other tools do not check for input read error (treat them as EOF) | ||
| 143 | // die_if_ferror(in, input_filename); | ||
| 144 | if (a) | ||
| 145 | bb_simple_error_msg_and_die("odd input"); | ||
| 146 | free(line); | ||
| 147 | |||
| 148 | /* | ||
| 149 | * Kahn's algorithm: | ||
| 150 | * - find a node that has no incoming edges, print and remove it | ||
| 151 | * - repeat until the graph is empty | ||
| 152 | * - if any nodes are left, they form cycles. | ||
| 153 | */ | ||
| 154 | cycles = 0; | ||
| 155 | while (G.nodes_len) { | ||
| 156 | struct node *n; | ||
| 157 | unsigned i; | ||
| 158 | |||
| 159 | /* Search for first node with no incoming edges */ | ||
| 160 | for (i = 0; i < G.nodes_len; i++) { | ||
| 161 | if (!G.nodes[i]->in_count) | ||
| 162 | break; | ||
| 163 | } | ||
| 164 | if (i == G.nodes_len) { | ||
| 165 | /* Must be a cycle; arbitraily break it at node 0 */ | ||
| 166 | cycles++; | ||
| 167 | i = 0; | ||
| 168 | #ifndef TINY | ||
| 169 | bb_error_msg("cycle at %s", G.nodes[i]->name); | ||
| 170 | #endif | ||
| 171 | } | ||
| 172 | |||
| 173 | /* Remove the node (need no longer maintain sort) */ | ||
| 174 | n = G.nodes[i]; | ||
| 175 | G.nodes[i] = G.nodes[--G.nodes_len]; | ||
| 176 | |||
| 177 | /* And remove its outgoing edges */ | ||
| 178 | for (i = 0; i < n->out_count; i++) | ||
| 179 | n->out[i]->in_count--; | ||
| 180 | free(n->out); | ||
| 181 | |||
| 182 | puts(n->name); | ||
| 183 | free(n); | ||
| 184 | } | ||
| 185 | free(G.nodes); | ||
| 186 | |||
| 187 | fflush_stdout_and_exit(cycles ? 1 : 0); | ||
| 188 | } | ||
diff --git a/docs/posix_conformance.txt b/docs/posix_conformance.txt index 5e107d74d..8edbe3e15 100644 --- a/docs/posix_conformance.txt +++ b/docs/posix_conformance.txt | |||
| @@ -24,7 +24,7 @@ POSIX Tools not supported: | |||
| 24 | gencat, getconf, iconv, join, link, locale, localedef, lp, m4, | 24 | gencat, getconf, iconv, join, link, locale, localedef, lp, m4, |
| 25 | mailx, newgrp, nl, pathchk, pax, pr, qalter, qdel, qhold, qmove, | 25 | mailx, newgrp, nl, pathchk, pax, pr, qalter, qdel, qhold, qmove, |
| 26 | qmsg, qrerun, qrls, qselect, qsig, qstat, qsub, tabs, talk, tput, | 26 | qmsg, qrerun, qrls, qselect, qsig, qstat, qsub, tabs, talk, tput, |
| 27 | tsort, unlink, uucp, uustat, uux | 27 | unlink, uucp, uustat, uux |
| 28 | 28 | ||
| 29 | POSIX Tools not supported (DEVELOPMENT): | 29 | POSIX Tools not supported (DEVELOPMENT): |
| 30 | admin, cflow, ctags, cxref, delta, fort77, get, lex, make, nm, prs, rmdel, | 30 | admin, cflow, ctags, cxref, delta, fort77, get, lex, make, nm, prs, rmdel, |
diff --git a/editors/vi.c b/editors/vi.c index dd8dd488a..e28db3ab6 100644 --- a/editors/vi.c +++ b/editors/vi.c | |||
| @@ -1281,10 +1281,11 @@ static char *get_input_line(const char *prompt) | |||
| 1281 | break; // this is end of input | 1281 | break; // this is end of input |
| 1282 | if (isbackspace(c)) { | 1282 | if (isbackspace(c)) { |
| 1283 | // user wants to erase prev char | 1283 | // user wants to erase prev char |
| 1284 | write1("\b \b"); // erase char on screen | ||
| 1285 | buf[--i] = '\0'; | 1284 | buf[--i] = '\0'; |
| 1285 | go_bottom_and_clear_to_eol(); | ||
| 1286 | if (i <= 0) // user backs up before b-o-l, exit | 1286 | if (i <= 0) // user backs up before b-o-l, exit |
| 1287 | break; | 1287 | break; |
| 1288 | write1(buf); | ||
| 1288 | } else if (c > 0 && c < 256) { // exclude Unicode | 1289 | } else if (c > 0 && c < 256) { // exclude Unicode |
| 1289 | // (TODO: need to handle Unicode) | 1290 | // (TODO: need to handle Unicode) |
| 1290 | buf[i] = c; | 1291 | buf[i] = c; |
diff --git a/examples/shutdown-1.0/script/hardshutdown.c b/examples/shutdown-1.0/script/hardshutdown.c index c21ddad58..b4af26f0f 100644 --- a/examples/shutdown-1.0/script/hardshutdown.c +++ b/examples/shutdown-1.0/script/hardshutdown.c | |||
| @@ -102,7 +102,7 @@ enum action_t { | |||
| 102 | REBOOT | 102 | REBOOT |
| 103 | }; | 103 | }; |
| 104 | 104 | ||
| 105 | int main(int argc, char *argv[]) | 105 | int main(int argc, char **argv) |
| 106 | { | 106 | { |
| 107 | struct timespec t = {0,0}; | 107 | struct timespec t = {0,0}; |
| 108 | enum action_t action = SHUTDOWN; | 108 | enum action_t action = SHUTDOWN; |
diff --git a/init/init.c b/init/init.c index 785a3b460..1e1ce833d 100644 --- a/init/init.c +++ b/init/init.c | |||
| @@ -1105,10 +1105,14 @@ int init_main(int argc UNUSED_PARAM, char **argv) | |||
| 1105 | setsid(); | 1105 | setsid(); |
| 1106 | 1106 | ||
| 1107 | /* Make sure environs is set to something sane */ | 1107 | /* Make sure environs is set to something sane */ |
| 1108 | putenv((char *) "HOME=/"); | ||
| 1109 | putenv((char *) bb_PATH_root_path); | 1108 | putenv((char *) bb_PATH_root_path); |
| 1110 | putenv((char *) "SHELL=/bin/sh"); | 1109 | putenv((char *) "SHELL=/bin/sh"); |
| 1111 | putenv((char *) "USER=root"); /* needed? why? */ | 1110 | putenv((char *) "USER=root"); /* needed? why? */ |
| 1111 | /* Linux kernel sets HOME="/" when execing init, | ||
| 1112 | * and it can be overridden (but not unset?) on kernel's command line. | ||
| 1113 | * We used to set it to "/" here, but now we do not: | ||
| 1114 | */ | ||
| 1115 | //putenv((char *) "HOME=/"); | ||
| 1112 | 1116 | ||
| 1113 | if (argv[1]) | 1117 | if (argv[1]) |
| 1114 | xsetenv("RUNLEVEL", argv[1]); | 1118 | xsetenv("RUNLEVEL", argv[1]); |
diff --git a/libbb/appletlib.c b/libbb/appletlib.c index 9e415610d..99f0cb89c 100644 --- a/libbb/appletlib.c +++ b/libbb/appletlib.c | |||
| @@ -849,7 +849,7 @@ get_script_content(unsigned n) | |||
| 849 | //usage:#define busybox_trivial_usage NOUSAGE_STR | 849 | //usage:#define busybox_trivial_usage NOUSAGE_STR |
| 850 | //usage:#define busybox_full_usage "" | 850 | //usage:#define busybox_full_usage "" |
| 851 | //applet:IF_BUSYBOX(IF_FEATURE_SH_STANDALONE(IF_FEATURE_TAB_COMPLETION(APPLET(busybox, BB_DIR_BIN, BB_SUID_MAYBE)))) | 851 | //applet:IF_BUSYBOX(IF_FEATURE_SH_STANDALONE(IF_FEATURE_TAB_COMPLETION(APPLET(busybox, BB_DIR_BIN, BB_SUID_MAYBE)))) |
| 852 | int busybox_main(int argc, char *argv[]) MAIN_EXTERNALLY_VISIBLE; | 852 | int busybox_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
| 853 | # else | 853 | # else |
| 854 | # define busybox_main(argc,argv) busybox_main(argv) | 854 | # define busybox_main(argc,argv) busybox_main(argv) |
| 855 | static | 855 | static |
diff --git a/libpwdgrp/pwd_grp.c b/libpwdgrp/pwd_grp.c index b44ada432..10debbcdb 100644 --- a/libpwdgrp/pwd_grp.c +++ b/libpwdgrp/pwd_grp.c | |||
| @@ -191,6 +191,9 @@ static char *parse_common(FILE *fp, struct passdb *db, | |||
| 191 | char *buf; | 191 | char *buf; |
| 192 | 192 | ||
| 193 | while ((buf = xmalloc_fgetline(fp)) != NULL) { | 193 | while ((buf = xmalloc_fgetline(fp)) != NULL) { |
| 194 | int n; | ||
| 195 | char *field; | ||
| 196 | |||
| 194 | /* Skip empty lines, comment lines */ | 197 | /* Skip empty lines, comment lines */ |
| 195 | if (buf[0] == '\0' || buf[0] == '#') | 198 | if (buf[0] == '\0' || buf[0] == '#') |
| 196 | goto free_and_next; | 199 | goto free_and_next; |
| @@ -204,7 +207,16 @@ static char *parse_common(FILE *fp, struct passdb *db, | |||
| 204 | /* no key specified: sequential read, return a record */ | 207 | /* no key specified: sequential read, return a record */ |
| 205 | break; | 208 | break; |
| 206 | } | 209 | } |
| 207 | if (strcmp(key, nth_string(buf, field_pos)) == 0) { | 210 | /* Can't use nth_string() here, it does not allow empty strings |
| 211 | * ("\0\0" terminates the list), and a valid passwd entry | ||
| 212 | * "user::UID:GID..." would be mishandled */ | ||
| 213 | n = field_pos; | ||
| 214 | field = buf; | ||
| 215 | while (n) { | ||
| 216 | n--; | ||
| 217 | field += strlen(field) + 1; | ||
| 218 | } | ||
| 219 | if (strcmp(key, field) == 0) { | ||
| 208 | /* record found */ | 220 | /* record found */ |
| 209 | break; | 221 | break; |
| 210 | } | 222 | } |
diff --git a/miscutils/crond.c b/miscutils/crond.c index 1965af656..bd43c6b68 100644 --- a/miscutils/crond.c +++ b/miscutils/crond.c | |||
| @@ -125,6 +125,7 @@ typedef struct CronLine { | |||
| 125 | char *cl_mailto; /* whom to mail results, may be NULL */ | 125 | char *cl_mailto; /* whom to mail results, may be NULL */ |
| 126 | #endif | 126 | #endif |
| 127 | char *cl_shell; | 127 | char *cl_shell; |
| 128 | char *cl_path; | ||
| 128 | /* ordered by size, not in natural order. makes code smaller: */ | 129 | /* ordered by size, not in natural order. makes code smaller: */ |
| 129 | char cl_Dow[7]; /* 0-6, beginning sunday */ | 130 | char cl_Dow[7]; /* 0-6, beginning sunday */ |
| 130 | char cl_Mons[12]; /* 0-11 */ | 131 | char cl_Mons[12]; /* 0-11 */ |
| @@ -421,6 +422,7 @@ static void load_crontab(const char *fileName) | |||
| 421 | char *mailTo = NULL; | 422 | char *mailTo = NULL; |
| 422 | #endif | 423 | #endif |
| 423 | char *shell = NULL; | 424 | char *shell = NULL; |
| 425 | char *path = NULL; | ||
| 424 | 426 | ||
| 425 | delete_cronfile(fileName); | 427 | delete_cronfile(fileName); |
| 426 | 428 | ||
| @@ -470,7 +472,12 @@ static void load_crontab(const char *fileName) | |||
| 470 | shell = xstrdup(&tokens[0][6]); | 472 | shell = xstrdup(&tokens[0][6]); |
| 471 | continue; | 473 | continue; |
| 472 | } | 474 | } |
| 473 | //TODO: handle HOME= too? "man crontab" says: | 475 | if (is_prefixed_with(tokens[0], "PATH=")) { |
| 476 | free(path); | ||
| 477 | path = xstrdup(&tokens[0][5]); | ||
| 478 | continue; | ||
| 479 | } | ||
| 480 | //TODO: handle HOME= too? Better yet, handle arbitrary ENVVARs? "man crontab" says: | ||
| 474 | //name = value | 481 | //name = value |
| 475 | // | 482 | // |
| 476 | //where the spaces around the equal-sign (=) are optional, and any subsequent | 483 | //where the spaces around the equal-sign (=) are optional, and any subsequent |
| @@ -480,8 +487,8 @@ static void load_crontab(const char *fileName) | |||
| 480 | // | 487 | // |
| 481 | //Several environment variables are set up automatically by the cron(8) daemon. | 488 | //Several environment variables are set up automatically by the cron(8) daemon. |
| 482 | //SHELL is set to /bin/sh, and LOGNAME and HOME are set from the /etc/passwd | 489 | //SHELL is set to /bin/sh, and LOGNAME and HOME are set from the /etc/passwd |
| 483 | //line of the crontab's owner. HOME and SHELL may be overridden by settings | 490 | //line of the crontab's owner. HOME, SHELL, and PATH may be overridden by |
| 484 | //in the crontab; LOGNAME may not. | 491 | //settings in the crontab; LOGNAME may not. |
| 485 | 492 | ||
| 486 | #if ENABLE_FEATURE_CROND_SPECIAL_TIMES | 493 | #if ENABLE_FEATURE_CROND_SPECIAL_TIMES |
| 487 | if (tokens[0][0] == '@') { | 494 | if (tokens[0][0] == '@') { |
| @@ -567,6 +574,7 @@ static void load_crontab(const char *fileName) | |||
| 567 | line->cl_mailto = xstrdup(mailTo); | 574 | line->cl_mailto = xstrdup(mailTo); |
| 568 | #endif | 575 | #endif |
| 569 | line->cl_shell = xstrdup(shell); | 576 | line->cl_shell = xstrdup(shell); |
| 577 | line->cl_path = xstrdup(path); | ||
| 570 | /* copy command */ | 578 | /* copy command */ |
| 571 | line->cl_cmd = xstrdup(tokens[5]); | 579 | line->cl_cmd = xstrdup(tokens[5]); |
| 572 | pline = &line->cl_next; | 580 | pline = &line->cl_next; |
| @@ -653,21 +661,22 @@ static void safe_setenv(char **pvar_val, const char *var, const char *val) | |||
| 653 | } | 661 | } |
| 654 | #endif | 662 | #endif |
| 655 | 663 | ||
| 656 | static void set_env_vars(struct passwd *pas, const char *shell) | 664 | static void set_env_vars(struct passwd *pas, const char *shell, const char *path) |
| 657 | { | 665 | { |
| 658 | /* POSIX requires crond to set up at least HOME, LOGNAME, PATH, SHELL. | 666 | /* POSIX requires crond to set up at least HOME, LOGNAME, PATH, SHELL. |
| 659 | * We assume crond inherited suitable PATH. | ||
| 660 | */ | 667 | */ |
| 661 | #if SETENV_LEAKS | 668 | #if SETENV_LEAKS |
| 662 | safe_setenv(&G.env_var_logname, "LOGNAME", pas->pw_name); | 669 | safe_setenv(&G.env_var_logname, "LOGNAME", pas->pw_name); |
| 663 | safe_setenv(&G.env_var_user, "USER", pas->pw_name); | 670 | safe_setenv(&G.env_var_user, "USER", pas->pw_name); |
| 664 | safe_setenv(&G.env_var_home, "HOME", pas->pw_dir); | 671 | safe_setenv(&G.env_var_home, "HOME", pas->pw_dir); |
| 665 | safe_setenv(&G.env_var_shell, "SHELL", shell); | 672 | safe_setenv(&G.env_var_shell, "SHELL", shell); |
| 673 | if (path) safe_setenv(&G.env_var_shell, "PATH", path); | ||
| 666 | #else | 674 | #else |
| 667 | xsetenv("LOGNAME", pas->pw_name); | 675 | xsetenv("LOGNAME", pas->pw_name); |
| 668 | xsetenv("USER", pas->pw_name); | 676 | xsetenv("USER", pas->pw_name); |
| 669 | xsetenv("HOME", pas->pw_dir); | 677 | xsetenv("HOME", pas->pw_dir); |
| 670 | xsetenv("SHELL", shell); | 678 | xsetenv("SHELL", shell); |
| 679 | if (path) xsetenv("PATH", path); | ||
| 671 | #endif | 680 | #endif |
| 672 | } | 681 | } |
| 673 | 682 | ||
| @@ -701,7 +710,7 @@ fork_job(const char *user, int mailFd, CronLine *line, bool run_sendmail) | |||
| 701 | shell = line->cl_shell ? line->cl_shell : G.default_shell; | 710 | shell = line->cl_shell ? line->cl_shell : G.default_shell; |
| 702 | prog = run_sendmail ? SENDMAIL : shell; | 711 | prog = run_sendmail ? SENDMAIL : shell; |
| 703 | 712 | ||
| 704 | set_env_vars(pas, shell); | 713 | set_env_vars(pas, shell, NULL); /* don't use crontab's PATH for sendmail */ |
| 705 | 714 | ||
| 706 | sv_logmode = logmode; | 715 | sv_logmode = logmode; |
| 707 | pid = vfork(); | 716 | pid = vfork(); |
| @@ -845,7 +854,7 @@ static pid_t start_one_job(const char *user, CronLine *line) | |||
| 845 | 854 | ||
| 846 | /* Prepare things before vfork */ | 855 | /* Prepare things before vfork */ |
| 847 | shell = line->cl_shell ? line->cl_shell : G.default_shell; | 856 | shell = line->cl_shell ? line->cl_shell : G.default_shell; |
| 848 | set_env_vars(pas, shell); | 857 | set_env_vars(pas, shell, line->cl_path); |
| 849 | 858 | ||
| 850 | /* Fork as the user in question and run program */ | 859 | /* Fork as the user in question and run program */ |
| 851 | pid = vfork(); | 860 | pid = vfork(); |
diff --git a/miscutils/seedrng.c b/miscutils/seedrng.c new file mode 100644 index 000000000..967741dc7 --- /dev/null +++ b/miscutils/seedrng.c | |||
| @@ -0,0 +1,242 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0 OR MIT | ||
| 2 | /* | ||
| 3 | * Copyright (C) 2022 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved. | ||
| 4 | * | ||
| 5 | * SeedRNG is a simple program made for seeding the Linux kernel random number | ||
| 6 | * generator from seed files. It is is useful in light of the fact that the | ||
| 7 | * Linux kernel RNG cannot be initialized from shell scripts, and new seeds | ||
| 8 | * cannot be safely generated from boot time shell scripts either. It should | ||
| 9 | * be run once at init time and once at shutdown time. It can be run at other | ||
| 10 | * times on a timer as well. Whenever it is run, it writes existing seed files | ||
| 11 | * into the RNG pool, and then creates a new seed file. If the RNG is | ||
| 12 | * initialized at the time of creating a new seed file, then that new seed file | ||
| 13 | * is marked as "creditable", which means it can be used to initialize the RNG. | ||
| 14 | * Otherwise, it is marked as "non-creditable", in which case it is still used | ||
| 15 | * to seed the RNG's pool, but will not initialize the RNG. In order to ensure | ||
| 16 | * that entropy only ever stays the same or increases from one seed file to the | ||
| 17 | * next, old seed values are hashed together with new seed values when writing | ||
| 18 | * new seed files. | ||
| 19 | * | ||
| 20 | * This is based on code from <https://git.zx2c4.com/seedrng/about/>. | ||
| 21 | */ | ||
| 22 | //config:config SEEDRNG | ||
| 23 | //config: bool "seedrng (1.3 kb)" | ||
| 24 | //config: default y | ||
| 25 | //config: help | ||
| 26 | //config: Seed the kernel RNG from seed files, meant to be called | ||
| 27 | //config: once during startup, once during shutdown, and optionally | ||
| 28 | //config: at some periodic interval in between. | ||
| 29 | |||
| 30 | //applet:IF_SEEDRNG(APPLET(seedrng, BB_DIR_USR_SBIN, BB_SUID_DROP)) | ||
| 31 | |||
| 32 | //kbuild:lib-$(CONFIG_SEEDRNG) += seedrng.o | ||
| 33 | |||
| 34 | //usage:#define seedrng_trivial_usage | ||
| 35 | //usage: "[-d DIR] [-n]" | ||
| 36 | //usage:#define seedrng_full_usage "\n\n" | ||
| 37 | //usage: "Seed the kernel RNG from seed files" | ||
| 38 | //usage: "\n" | ||
| 39 | //usage: "\n -d DIR Use seed files in DIR (default: /var/lib/seedrng)" | ||
| 40 | //usage: "\n -n Do not credit randomness, even if creditable" | ||
| 41 | |||
| 42 | #include "libbb.h" | ||
| 43 | |||
| 44 | #include <linux/random.h> | ||
| 45 | #include <sys/random.h> | ||
| 46 | #include <sys/file.h> | ||
| 47 | |||
| 48 | #ifndef GRND_INSECURE | ||
| 49 | #define GRND_INSECURE 0x0004 /* Apparently some headers don't ship with this yet. */ | ||
| 50 | #endif | ||
| 51 | |||
| 52 | #define DEFAULT_SEED_DIR "/var/lib/seedrng" | ||
| 53 | #define CREDITABLE_SEED_NAME "seed.credit" | ||
| 54 | #define NON_CREDITABLE_SEED_NAME "seed.no-credit" | ||
| 55 | |||
| 56 | enum { | ||
| 57 | MIN_SEED_LEN = SHA256_OUTSIZE, | ||
| 58 | /* kernels < 5.18 could return short reads from getrandom() | ||
| 59 | * if signal is pending and length is > 256. | ||
| 60 | * Let's limit our reads to 256 bytes. | ||
| 61 | */ | ||
| 62 | MAX_SEED_LEN = 256, | ||
| 63 | }; | ||
| 64 | |||
| 65 | static size_t determine_optimal_seed_len(void) | ||
| 66 | { | ||
| 67 | char poolsize_str[12]; | ||
| 68 | unsigned poolsize; | ||
| 69 | int n; | ||
| 70 | |||
| 71 | n = open_read_close("/proc/sys/kernel/random/poolsize", poolsize_str, sizeof(poolsize_str) - 1); | ||
| 72 | if (n < 0) { | ||
| 73 | bb_perror_msg("can't determine pool size, assuming %u bits", MIN_SEED_LEN * 8); | ||
| 74 | return MIN_SEED_LEN; | ||
| 75 | } | ||
| 76 | poolsize_str[n] = '\0'; | ||
| 77 | poolsize = (bb_strtou(poolsize_str, NULL, 10) + 7) / 8; | ||
| 78 | return MAX(MIN(poolsize, MAX_SEED_LEN), MIN_SEED_LEN); | ||
| 79 | } | ||
| 80 | |||
| 81 | static bool read_new_seed(uint8_t *seed, size_t len) | ||
| 82 | { | ||
| 83 | bool is_creditable; | ||
| 84 | ssize_t ret; | ||
| 85 | |||
| 86 | ret = getrandom(seed, len, GRND_NONBLOCK); | ||
| 87 | if (ret == (ssize_t)len) { | ||
| 88 | return true; | ||
| 89 | } | ||
| 90 | if (ret < 0 && errno == ENOSYS) { | ||
| 91 | int fd = xopen("/dev/random", O_RDONLY); | ||
| 92 | struct pollfd random_fd; | ||
| 93 | random_fd.fd = fd; | ||
| 94 | random_fd.events = POLLIN; | ||
| 95 | is_creditable = poll(&random_fd, 1, 0) == 1; | ||
| 96 | //This is racy. is_creditable can be set to true here, but other process | ||
| 97 | //can consume "good" random data from /dev/urandom before we do it below. | ||
| 98 | close(fd); | ||
| 99 | } else { | ||
| 100 | if (getrandom(seed, len, GRND_INSECURE) == (ssize_t)len) | ||
| 101 | return false; | ||
| 102 | is_creditable = false; | ||
| 103 | } | ||
| 104 | |||
| 105 | /* Either getrandom() is not implemented, or | ||
| 106 | * getrandom(GRND_INSECURE) did not give us LEN bytes. | ||
| 107 | * Fallback to reading /dev/urandom. | ||
| 108 | */ | ||
| 109 | errno = 0; | ||
| 110 | if (open_read_close("/dev/urandom", seed, len) != (ssize_t)len) | ||
| 111 | bb_perror_msg_and_die("can't read '%s'", "/dev/urandom"); | ||
| 112 | return is_creditable; | ||
| 113 | } | ||
| 114 | |||
| 115 | static void seed_from_file_if_exists(const char *filename, int dfd, bool credit, sha256_ctx_t *hash) | ||
| 116 | { | ||
| 117 | struct { | ||
| 118 | int entropy_count; | ||
| 119 | int buf_size; | ||
| 120 | uint8_t buf[MAX_SEED_LEN]; | ||
| 121 | } req; | ||
| 122 | ssize_t seed_len; | ||
| 123 | |||
| 124 | seed_len = open_read_close(filename, req.buf, sizeof(req.buf)); | ||
| 125 | if (seed_len < 0) { | ||
| 126 | if (errno != ENOENT) | ||
| 127 | bb_perror_msg_and_die("can't read '%s'", filename); | ||
| 128 | return; | ||
| 129 | } | ||
| 130 | xunlink(filename); | ||
| 131 | if (seed_len != 0) { | ||
| 132 | int fd; | ||
| 133 | |||
| 134 | /* We are going to use this data to seed the RNG: | ||
| 135 | * we believe it to genuinely containing entropy. | ||
| 136 | * If this just-unlinked file survives | ||
| 137 | * (if machine crashes before deletion is recorded on disk) | ||
| 138 | * and we reuse it after reboot, this assumption | ||
| 139 | * would be violated, and RNG may end up generating | ||
| 140 | * the same data. fsync the directory | ||
| 141 | * to make sure file is gone: | ||
| 142 | */ | ||
| 143 | if (fsync(dfd) != 0) | ||
| 144 | bb_simple_perror_msg_and_die("I/O error"); | ||
| 145 | |||
| 146 | //Length is not random, and taking its address spills variable to stack | ||
| 147 | // sha256_hash(hash, &seed_len, sizeof(seed_len)); | ||
| 148 | sha256_hash(hash, req.buf, seed_len); | ||
| 149 | |||
| 150 | req.buf_size = seed_len; | ||
| 151 | seed_len *= 8; | ||
| 152 | req.entropy_count = credit ? seed_len : 0; | ||
| 153 | printf("Seeding %u bits %s crediting\n", | ||
| 154 | (unsigned)seed_len, credit ? "and" : "without"); | ||
| 155 | fd = xopen("/dev/urandom", O_RDONLY); | ||
| 156 | xioctl(fd, RNDADDENTROPY, &req); | ||
| 157 | if (ENABLE_FEATURE_CLEAN_UP) | ||
| 158 | close(fd); | ||
| 159 | } | ||
| 160 | } | ||
| 161 | |||
| 162 | int seedrng_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | ||
| 163 | int seedrng_main(int argc UNUSED_PARAM, char **argv) | ||
| 164 | { | ||
| 165 | const char *seed_dir; | ||
| 166 | int fd, dfd; | ||
| 167 | int i; | ||
| 168 | unsigned opts; | ||
| 169 | uint8_t new_seed[MAX_SEED_LEN]; | ||
| 170 | size_t new_seed_len; | ||
| 171 | bool new_seed_creditable; | ||
| 172 | struct timespec timestamp[2]; | ||
| 173 | sha256_ctx_t hash; | ||
| 174 | |||
| 175 | enum { | ||
| 176 | OPT_n = (1 << 0), /* must be 1 */ | ||
| 177 | OPT_d = (1 << 1), | ||
| 178 | }; | ||
| 179 | #if ENABLE_LONG_OPTS | ||
| 180 | static const char longopts[] ALIGN1 = | ||
| 181 | "skip-credit\0" No_argument "n" | ||
| 182 | "seed-dir\0" Required_argument "d" | ||
| 183 | ; | ||
| 184 | #endif | ||
| 185 | |||
| 186 | seed_dir = DEFAULT_SEED_DIR; | ||
| 187 | opts = getopt32long(argv, "nd:", longopts, &seed_dir); | ||
| 188 | umask(0077); | ||
| 189 | if (getuid() != 0) | ||
| 190 | bb_simple_error_msg_and_die(bb_msg_you_must_be_root); | ||
| 191 | |||
| 192 | if (mkdir(seed_dir, 0700) < 0 && errno != EEXIST) | ||
| 193 | bb_perror_msg_and_die("can't create directory '%s'", seed_dir); | ||
| 194 | dfd = xopen(seed_dir, O_DIRECTORY | O_RDONLY); | ||
| 195 | xfchdir(dfd); | ||
| 196 | /* Concurrent runs of this tool might feed the same data to RNG twice. | ||
| 197 | * Avoid concurrent runs by taking a blocking lock on the directory. | ||
| 198 | * Not checking for errors. Looking at manpage, | ||
| 199 | * ENOLCK "The kernel ran out of memory for allocating lock records" | ||
| 200 | * seems to be the only one which is possible - and if that happens, | ||
| 201 | * machine is OOMing (much worse problem than inability to lock...). | ||
| 202 | * Also, typically configured Linux machines do not fail GFP_KERNEL | ||
| 203 | * allocations (they trigger memory reclaim instead). | ||
| 204 | */ | ||
| 205 | flock(dfd, LOCK_EX); /* blocks while another instance runs */ | ||
| 206 | |||
| 207 | sha256_begin(&hash); | ||
| 208 | //Hashing in a constant string doesn't add any entropy | ||
| 209 | // sha256_hash(&hash, "SeedRNG v1 Old+New Prefix", 25); | ||
| 210 | clock_gettime(CLOCK_REALTIME, ×tamp[0]); | ||
| 211 | clock_gettime(CLOCK_BOOTTIME, ×tamp[1]); | ||
| 212 | sha256_hash(&hash, timestamp, sizeof(timestamp)); | ||
| 213 | |||
| 214 | for (i = 0; i <= 1; i++) { | ||
| 215 | seed_from_file_if_exists( | ||
| 216 | i == 0 ? NON_CREDITABLE_SEED_NAME : CREDITABLE_SEED_NAME, | ||
| 217 | dfd, | ||
| 218 | /*credit?*/ (opts ^ OPT_n) & i, /* 0, then 1 unless -n */ | ||
| 219 | &hash); | ||
| 220 | } | ||
| 221 | |||
| 222 | new_seed_len = determine_optimal_seed_len(); | ||
| 223 | new_seed_creditable = read_new_seed(new_seed, new_seed_len); | ||
| 224 | //Length is not random, and taking its address spills variable to stack | ||
| 225 | // sha256_hash(&hash, &new_seed_len, sizeof(new_seed_len)); | ||
| 226 | sha256_hash(&hash, new_seed, new_seed_len); | ||
| 227 | sha256_end(&hash, new_seed + new_seed_len - SHA256_OUTSIZE); | ||
| 228 | |||
| 229 | printf("Saving %u bits of %screditable seed for next boot\n", | ||
| 230 | (unsigned)new_seed_len * 8, new_seed_creditable ? "" : "non-"); | ||
| 231 | fd = xopen3(NON_CREDITABLE_SEED_NAME, O_WRONLY | O_CREAT | O_TRUNC, 0400); | ||
| 232 | xwrite(fd, new_seed, new_seed_len); | ||
| 233 | if (new_seed_creditable) { | ||
| 234 | /* More paranoia when we create a file which we believe contains | ||
| 235 | * genuine entropy: make sure disk is not full, quota isn't exceeded, etc: | ||
| 236 | */ | ||
| 237 | if (fsync(fd) < 0) | ||
| 238 | bb_perror_msg_and_die("can't write '%s'", NON_CREDITABLE_SEED_NAME); | ||
| 239 | xrename(NON_CREDITABLE_SEED_NAME, CREDITABLE_SEED_NAME); | ||
| 240 | } | ||
| 241 | return EXIT_SUCCESS; | ||
| 242 | } | ||
diff --git a/networking/httpd_indexcgi.c b/networking/httpd_indexcgi.c index 47b1159f4..edaaad566 100644 --- a/networking/httpd_indexcgi.c +++ b/networking/httpd_indexcgi.c | |||
| @@ -211,7 +211,7 @@ static void fmt_04u(/*char *dst,*/ unsigned n) | |||
| 211 | fmt_02u(n % 100); | 211 | fmt_02u(n % 100); |
| 212 | } | 212 | } |
| 213 | 213 | ||
| 214 | int main(int argc, char *argv[]) | 214 | int main(int argc, char **argv) |
| 215 | { | 215 | { |
| 216 | dir_list_t *dir_list; | 216 | dir_list_t *dir_list; |
| 217 | dir_list_t *cdir; | 217 | dir_list_t *cdir; |
diff --git a/networking/httpd_ssi.c b/networking/httpd_ssi.c index 4bd9a6d97..620b96332 100644 --- a/networking/httpd_ssi.c +++ b/networking/httpd_ssi.c | |||
| @@ -143,7 +143,7 @@ static void process_includes(const char *filename) | |||
| 143 | fclose(fp); | 143 | fclose(fp); |
| 144 | } | 144 | } |
| 145 | 145 | ||
| 146 | int main(int argc, char *argv[]) | 146 | int main(int argc, char **argv) |
| 147 | { | 147 | { |
| 148 | if (!argv[1]) | 148 | if (!argv[1]) |
| 149 | return 1; | 149 | return 1; |
diff --git a/networking/ifplugd.c b/networking/ifplugd.c index c4b6b9584..0b55bf4e5 100644 --- a/networking/ifplugd.c +++ b/networking/ifplugd.c | |||
| @@ -28,6 +28,7 @@ | |||
| 28 | //usage: "\n -a Don't up interface at each link probe" | 28 | //usage: "\n -a Don't up interface at each link probe" |
| 29 | //usage: "\n -M Monitor creation/destruction of interface" | 29 | //usage: "\n -M Monitor creation/destruction of interface" |
| 30 | //usage: "\n (otherwise it must exist)" | 30 | //usage: "\n (otherwise it must exist)" |
| 31 | //usage: "\n -A Don't up newly appeared interface" | ||
| 31 | //usage: "\n -r PROG Script to run" | 32 | //usage: "\n -r PROG Script to run" |
| 32 | //usage: "\n -x ARG Extra argument for script" | 33 | //usage: "\n -x ARG Extra argument for script" |
| 33 | //usage: "\n -I Don't exit on nonzero exit code from script" | 34 | //usage: "\n -I Don't exit on nonzero exit code from script" |
| @@ -94,7 +95,7 @@ Netlink code then can be just dropped (1k or more?) | |||
| 94 | #define IFPLUGD_ENV_CURRENT "IFPLUGD_CURRENT" | 95 | #define IFPLUGD_ENV_CURRENT "IFPLUGD_CURRENT" |
| 95 | 96 | ||
| 96 | enum { | 97 | enum { |
| 97 | FLAG_NO_AUTO = 1 << 0, // -a, Do not enable interface automatically | 98 | FLAG_NO_AUTO = 1 << 0, // -a, Don't up interface at each link probe |
| 98 | FLAG_NO_DAEMON = 1 << 1, // -n, Do not daemonize | 99 | FLAG_NO_DAEMON = 1 << 1, // -n, Do not daemonize |
| 99 | FLAG_NO_SYSLOG = 1 << 2, // -s, Do not use syslog, use stderr instead | 100 | FLAG_NO_SYSLOG = 1 << 2, // -s, Do not use syslog, use stderr instead |
| 100 | FLAG_IGNORE_FAIL = 1 << 3, // -f, Ignore detection failure, retry instead (failure is treated as DOWN) | 101 | FLAG_IGNORE_FAIL = 1 << 3, // -f, Ignore detection failure, retry instead (failure is treated as DOWN) |
| @@ -111,14 +112,15 @@ enum { | |||
| 111 | FLAG_INITIAL_DOWN = 1 << 14, // -l, Run "down" script on startup if no cable is detected | 112 | FLAG_INITIAL_DOWN = 1 << 14, // -l, Run "down" script on startup if no cable is detected |
| 112 | FLAG_EXTRA_ARG = 1 << 15, // -x, Specify an extra argument for action script | 113 | FLAG_EXTRA_ARG = 1 << 15, // -x, Specify an extra argument for action script |
| 113 | FLAG_MONITOR = 1 << 16, // -M, Use interface monitoring | 114 | FLAG_MONITOR = 1 << 16, // -M, Use interface monitoring |
| 115 | FLAG_NO_UP_NEW_IFACE = 1 << 17, // -A, Don't up newly appeared interface | ||
| 114 | #if ENABLE_FEATURE_PIDFILE | 116 | #if ENABLE_FEATURE_PIDFILE |
| 115 | FLAG_KILL = 1 << 17, // -k, Kill a running daemon | 117 | FLAG_KILL = 1 << 18, // -k, Kill a running daemon |
| 116 | #endif | 118 | #endif |
| 117 | }; | 119 | }; |
| 118 | #if ENABLE_FEATURE_PIDFILE | 120 | #if ENABLE_FEATURE_PIDFILE |
| 119 | # define OPTION_STR "+ansfFi:r:It:+u:+d:+m:pqlx:Mk" | 121 | # define OPTION_STR "+ansfFi:r:It:+u:+d:+m:pqlx:MAk" |
| 120 | #else | 122 | #else |
| 121 | # define OPTION_STR "+ansfFi:r:It:+u:+d:+m:pqlx:M" | 123 | # define OPTION_STR "+ansfFi:r:It:+u:+d:+m:pqlx:MA" |
| 122 | #endif | 124 | #endif |
| 123 | 125 | ||
| 124 | enum { // interface status | 126 | enum { // interface status |
| @@ -387,7 +389,7 @@ static void up_iface(void) | |||
| 387 | 389 | ||
| 388 | static void maybe_up_new_iface(void) | 390 | static void maybe_up_new_iface(void) |
| 389 | { | 391 | { |
| 390 | if (!(option_mask32 & FLAG_NO_AUTO)) | 392 | if (!(option_mask32 & FLAG_NO_UP_NEW_IFACE)) |
| 391 | up_iface(); | 393 | up_iface(); |
| 392 | 394 | ||
| 393 | #if 0 /* bloat */ | 395 | #if 0 /* bloat */ |
diff --git a/printutils/lpd.c b/printutils/lpd.c index e48feef90..34e5ea209 100644 --- a/printutils/lpd.c +++ b/printutils/lpd.c | |||
| @@ -114,8 +114,8 @@ static char *xmalloc_read_stdin(void) | |||
| 114 | return xmalloc_reads(STDIN_FILENO, &max); | 114 | return xmalloc_reads(STDIN_FILENO, &max); |
| 115 | } | 115 | } |
| 116 | 116 | ||
| 117 | int lpd_main(int argc, char *argv[]) MAIN_EXTERNALLY_VISIBLE; | 117 | int lpd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
| 118 | int lpd_main(int argc UNUSED_PARAM, char *argv[]) | 118 | int lpd_main(int argc UNUSED_PARAM, char **argv) |
| 119 | { | 119 | { |
| 120 | int spooling = spooling; // for compiler | 120 | int spooling = spooling; // for compiler |
| 121 | char *s, *queue; | 121 | char *s, *queue; |
diff --git a/printutils/lpr.c b/printutils/lpr.c index 77d1a79a4..d40d0a67c 100644 --- a/printutils/lpr.c +++ b/printutils/lpr.c | |||
| @@ -78,8 +78,8 @@ static void get_response_or_say_and_die(int fd, const char *errmsg) | |||
| 78 | } | 78 | } |
| 79 | } | 79 | } |
| 80 | 80 | ||
| 81 | int lpqr_main(int argc, char *argv[]) MAIN_EXTERNALLY_VISIBLE; | 81 | int lpqr_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
| 82 | int lpqr_main(int argc UNUSED_PARAM, char *argv[]) | 82 | int lpqr_main(int argc UNUSED_PARAM, char **argv) |
| 83 | { | 83 | { |
| 84 | enum { | 84 | enum { |
| 85 | OPT_P = 1 << 0, // -P queue[@host[:port]]. If no -P is given use $PRINTER, then "lp@localhost:515" | 85 | OPT_P = 1 << 0, // -P queue[@host[:port]]. If no -P is given use $PRINTER, then "lp@localhost:515" |
diff --git a/procps/top.c b/procps/top.c index 804d6f258..744f20e9b 100644 --- a/procps/top.c +++ b/procps/top.c | |||
| @@ -689,6 +689,9 @@ static NOINLINE void display_process_list(int lines_rem, int scr_width) | |||
| 689 | lines_rem = ntop - G_scroll_ofs; | 689 | lines_rem = ntop - G_scroll_ofs; |
| 690 | s = top + G_scroll_ofs; | 690 | s = top + G_scroll_ofs; |
| 691 | while (--lines_rem >= 0) { | 691 | while (--lines_rem >= 0) { |
| 692 | int n; | ||
| 693 | char *ppu; | ||
| 694 | char ppubuf[sizeof(int)*3 * 2 + 12]; | ||
| 692 | char vsz_str_buf[8]; | 695 | char vsz_str_buf[8]; |
| 693 | unsigned col; | 696 | unsigned col; |
| 694 | 697 | ||
| @@ -699,12 +702,36 @@ static NOINLINE void display_process_list(int lines_rem, int scr_width) | |||
| 699 | 702 | ||
| 700 | smart_ulltoa5(s->vsz, vsz_str_buf, " mgtpezy"); | 703 | smart_ulltoa5(s->vsz, vsz_str_buf, " mgtpezy"); |
| 701 | /* PID PPID USER STAT VSZ %VSZ [%CPU] COMMAND */ | 704 | /* PID PPID USER STAT VSZ %VSZ [%CPU] COMMAND */ |
| 705 | n = sprintf(ppubuf, "%5u %5u %-8.8s", s->pid, s->ppid, get_cached_username(s->uid)); | ||
| 706 | ppu = ppubuf; | ||
| 707 | if (n != 6+6+8) { | ||
| 708 | /* Format PID PPID USER part into 6+6+8 chars: | ||
| 709 | * shrink PID/PPID if possible, then truncate USER | ||
| 710 | */ | ||
| 711 | char *p, *pp; | ||
| 712 | if (*ppu == ' ') { | ||
| 713 | do { | ||
| 714 | ppu++, n--; | ||
| 715 | if (n == 6+6+8) | ||
| 716 | goto shortened; | ||
| 717 | } while (*ppu == ' '); | ||
| 718 | } | ||
| 719 | pp = p = skip_non_whitespace(ppu) + 1; | ||
| 720 | if (*p == ' ') { | ||
| 721 | do | ||
| 722 | p++, n--; | ||
| 723 | while (n != 6+6+8 && *p == ' '); | ||
| 724 | overlapping_strcpy(pp, p); /* shrink PPID */ | ||
| 725 | } | ||
| 726 | ppu[6+6+8] = '\0'; /* truncate USER */ | ||
| 727 | } | ||
| 728 | shortened: | ||
| 702 | col = snprintf(line_buf, scr_width, | 729 | col = snprintf(line_buf, scr_width, |
| 703 | "\n" "%5u%6u %-8.8s %s %.5s" FMT | 730 | "\n" "%s %s %.5s" FMT |
| 704 | IF_FEATURE_TOP_SMP_PROCESS(" %3d") | 731 | IF_FEATURE_TOP_SMP_PROCESS(" %3d") |
| 705 | IF_FEATURE_TOP_CPU_USAGE_PERCENTAGE(FMT) | 732 | IF_FEATURE_TOP_CPU_USAGE_PERCENTAGE(FMT) |
| 706 | " ", | 733 | " ", |
| 707 | s->pid, s->ppid, get_cached_username(s->uid), | 734 | ppu, |
| 708 | s->state, vsz_str_buf, | 735 | s->state, vsz_str_buf, |
| 709 | SHOW_STAT(pmem) | 736 | SHOW_STAT(pmem) |
| 710 | IF_FEATURE_TOP_SMP_PROCESS(, s->last_seen_on_cpu) | 737 | IF_FEATURE_TOP_SMP_PROCESS(, s->last_seen_on_cpu) |
diff --git a/scripts/Makefile.lib b/scripts/Makefile.lib index d8d768a28..ac1ac9735 100644 --- a/scripts/Makefile.lib +++ b/scripts/Makefile.lib | |||
| @@ -113,6 +113,7 @@ c_flags = -Wp,-MD,$(depfile) $(NOSTDINC_FLAGS) $(CPPFLAGS) \ | |||
| 113 | $(basename_flags) $(modname_flags) | 113 | $(basename_flags) $(modname_flags) |
| 114 | 114 | ||
| 115 | a_flags = -Wp,-MD,$(depfile) $(NOSTDINC_FLAGS) $(CPPFLAGS) \ | 115 | a_flags = -Wp,-MD,$(depfile) $(NOSTDINC_FLAGS) $(CPPFLAGS) \ |
| 116 | $(__c_flags) \ | ||
| 116 | $(__a_flags) $(modkern_aflags) | 117 | $(__a_flags) $(modkern_aflags) |
| 117 | 118 | ||
| 118 | cpp_flags = -Wp,-MD,$(depfile) $(NOSTDINC_FLAGS) $(__cpp_flags) | 119 | cpp_flags = -Wp,-MD,$(depfile) $(NOSTDINC_FLAGS) $(__cpp_flags) |
diff --git a/shell/match.c b/shell/match.c index 90f77546d..8024f2747 100644 --- a/shell/match.c +++ b/shell/match.c | |||
| @@ -95,7 +95,7 @@ char* FAST_FUNC scan_and_match(char *string, const char *pattern, unsigned flags | |||
| 95 | } | 95 | } |
| 96 | 96 | ||
| 97 | #ifdef STANDALONE | 97 | #ifdef STANDALONE |
| 98 | int main(int argc, char *argv[]) | 98 | int main(int argc, char **argv) |
| 99 | { | 99 | { |
| 100 | char *string; | 100 | char *string; |
| 101 | char *op; | 101 | char *op; |
diff --git a/testsuite/tsort.tests b/testsuite/tsort.tests new file mode 100755 index 000000000..c6fe78272 --- /dev/null +++ b/testsuite/tsort.tests | |||
| @@ -0,0 +1,110 @@ | |||
| 1 | #!/bin/sh | ||
| 2 | |||
| 3 | # SUSv3 compliant sort tests. | ||
| 4 | # Public Domain, David Leonard 2022 | ||
| 5 | |||
| 6 | . ./testing.sh | ||
| 7 | |||
| 8 | # name cmd expected ./input stdin | ||
| 9 | testing "" "tsort" "a\n" "" "a a\n" | ||
| 10 | testing "" "tsort -" "a\n" "" "a a\n" | ||
| 11 | testing "" "tsort input" "a\n" "a a\n" "" | ||
| 12 | testing "tsort input (w/o eol)" "tsort input" "a\n" "a a" "" | ||
| 13 | testing "" "tsort /dev/null" "" "" "" | ||
| 14 | |||
| 15 | testing "tsort empty" tsort "" "" "" | ||
| 16 | testing "tsort blank" tsort "" "" "\n" | ||
| 17 | testing "tsort blanks" tsort "" "" "\n\n \t\n " | ||
| 18 | |||
| 19 | # simple inputs having exactly one solution | ||
| 20 | testing "tsort 1-edge" tsort "a\nb\n" "" "a b\n" | ||
| 21 | testing "tsort 2-edge" tsort "a\nb\nc\n" "" "a b b c\n" | ||
| 22 | |||
| 23 | |||
| 24 | # The following test helper accommodates future variable output because, as | ||
| 25 | # tsort is allowed to emit any total ordering that satisfies its input, | ||
| 26 | # should the implementation changes, these tests will remain valid. | ||
| 27 | # | ||
| 28 | # The idea is to verify that: | ||
| 29 | # - each input word is present EXACTLY ONCE in tsort's output | ||
| 30 | # - for each input pair 'a b', the occurrence of 'a' APPEARS BEFORE 'b' | ||
| 31 | # - the exit code is 0 | ||
| 32 | |||
| 33 | tsort_test () { | ||
| 34 | fail= | ||
| 35 | name="$1"; shift | ||
| 36 | args="$*" | ||
| 37 | if [ $VERBOSE ]; then | ||
| 38 | echo "============" | ||
| 39 | echo "echo \"$args\" | tsort >actual" | ||
| 40 | fi | ||
| 41 | echo "$args" | tsort >actual | ||
| 42 | ec=$? | ||
| 43 | if [ $ec -ne 0 ]; then | ||
| 44 | fail "tsort exit $ec, expected 0" | ||
| 45 | fi | ||
| 46 | while [ $# -ne 0 ]; do | ||
| 47 | a=$1; shift | ||
| 48 | b=$1; shift | ||
| 49 | aline=$(grep -nxF "$a" <actual | cut -d: -f1) | ||
| 50 | bline=$(grep -nxF "$b" <actual | cut -d: -f1) | ||
| 51 | case $aline in | ||
| 52 | "") fail "word $a missing from output ($args)";; | ||
| 53 | *" "*) fail "word $a duplicated ($args)";; | ||
| 54 | esac | ||
| 55 | case $bline in | ||
| 56 | "") fail "word $b missing from output ($args)";; | ||
| 57 | *" "*) fail "word $b duplicated ($args)";; | ||
| 58 | esac | ||
| 59 | if [ $aline -gt $bline ]; then | ||
| 60 | fail "$a appears after $b ($args)" | ||
| 61 | fi | ||
| 62 | done | ||
| 63 | if [ $fail ] && [ $VERBOSE ]; then | ||
| 64 | echo "exit $ec, actual:" | ||
| 65 | cat actual | ||
| 66 | fi | ||
| 67 | rm actual | ||
| 68 | report "$name" | ||
| 69 | } | ||
| 70 | |||
| 71 | # Test that erroneous input causes an unsuccessful exit code | ||
| 72 | # we don't test the output error message | ||
| 73 | tsort_test_err () { | ||
| 74 | fail= | ||
| 75 | name="$1"; shift | ||
| 76 | echo "$*" | tsort >/dev/null 2>/dev/null | ||
| 77 | ec=$? | ||
| 78 | if [ $ec -eq 0 ]; then | ||
| 79 | fail "$name: unexpected exit 0 ($*)" | ||
| 80 | fi | ||
| 81 | report "$name" | ||
| 82 | } | ||
| 83 | |||
| 84 | fail () { | ||
| 85 | [ $VERBOSE ] && echo "ERROR: $*" | ||
| 86 | fail=1 | ||
| 87 | } | ||
| 88 | |||
| 89 | report () { | ||
| 90 | if [ $fail ]; then | ||
| 91 | FAILCOUNT=$(($FAILCOUNT + 1)) | ||
| 92 | echo "FAIL: $*" | ||
| 93 | else | ||
| 94 | echo "PASS: $*" | ||
| 95 | fi | ||
| 96 | } | ||
| 97 | |||
| 98 | tsort_test "tsort empty2" | ||
| 99 | tsort_test "tsort singleton" a a | ||
| 100 | tsort_test "tsort simple" a b b c | ||
| 101 | tsort_test "tsort 2singleton" a a b b | ||
| 102 | tsort_test "tsort medium" a b a b b c | ||
| 103 | tsort_test "tsort std.example" a b c c d e g g f g e f h h | ||
| 104 | tsort_test "tsort prefixes" a aa aa aaa aaaa aaaaa a aaaaa | ||
| 105 | |||
| 106 | tsort_test_err "tsort odd" a | ||
| 107 | tsort_test_err "tsort odd2" a b c | ||
| 108 | tsort_test_err "tsort cycle" a b b a | ||
| 109 | |||
| 110 | exit $FAILCOUNT | ||
