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 | ||