aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRon Yorston <rmy@pobox.com>2020-01-08 12:30:49 +0000
committerRon Yorston <rmy@pobox.com>2020-01-08 12:30:49 +0000
commita9271a8e97e6e7be5285330d5f19352decabf807 (patch)
treebf3c4464c369a15a46454792dac167505f74769f
parentb0b7ab792bc1f45963f4b84b94faaf05054e1613 (diff)
parent9ec836c033fc6e55e80f3309b3e05acdf09bb297 (diff)
downloadbusybox-w32-a9271a8e97e6e7be5285330d5f19352decabf807.tar.gz
busybox-w32-a9271a8e97e6e7be5285330d5f19352decabf807.tar.bz2
busybox-w32-a9271a8e97e6e7be5285330d5f19352decabf807.zip
Merge branch 'busybox' into merge
-rw-r--r--Config.in5
-rw-r--r--Makefile.flags31
-rw-r--r--archival/bbunzip.c2
-rw-r--r--archival/gzip.c130
-rw-r--r--archival/libarchive/decompress_gunzip.c111
-rw-r--r--archival/libarchive/decompress_unxz.c20
-rw-r--r--archival/libarchive/unxz/xz_stream.h2
-rw-r--r--archival/tar.c2
-rw-r--r--coreutils/chgrp.c4
-rw-r--r--coreutils/date.c13
-rw-r--r--coreutils/ln.c25
-rw-r--r--coreutils/tee.c24
-rw-r--r--coreutils/test.c2
-rw-r--r--editors/vi.c2
-rw-r--r--examples/inittab47
-rwxr-xr-xexamples/udhcp/simple.script5
-rw-r--r--findutils/find.c44
-rw-r--r--findutils/grep.c10
-rw-r--r--include/libbb.h22
-rw-r--r--init/init.c295
-rw-r--r--libbb/appletlib.c2
-rw-r--r--libbb/copyfd.c13
-rw-r--r--libbb/lineedit.c2
-rw-r--r--libbb/missing_syscalls.c8
-rw-r--r--libbb/read.c12
-rw-r--r--libbb/read_key.c3
-rw-r--r--libbb/replace.c4
-rw-r--r--libbb/time.c4
-rw-r--r--miscutils/bc.c28
-rw-r--r--miscutils/hdparm.c2
-rw-r--r--networking/brctl.c614
-rw-r--r--networking/netstat.c2
-rw-r--r--networking/nslookup.c28
-rw-r--r--networking/ntpd.c10
-rw-r--r--networking/route.c1
-rw-r--r--networking/tc.c2
-rw-r--r--networking/telnet.c14
-rw-r--r--networking/tls_aesgcm.c2
-rw-r--r--networking/traceroute.c10
-rw-r--r--networking/udhcp/common.c2
-rw-r--r--networking/udhcp/d6_dhcpc.c99
-rw-r--r--networking/udhcp/dhcpc.c4
-rw-r--r--networking/udhcp/dhcpc.h4
-rw-r--r--networking/wget.c2
-rw-r--r--networking/whois.c4
-rw-r--r--procps/nmeter.c5
-rw-r--r--runit/runsv.c4
-rw-r--r--shell/ash.c14
-rw-r--r--shell/ash_test/ash-redir/redir_stdin1.right3
-rwxr-xr-xshell/ash_test/ash-redir/redir_stdin1.tests7
-rw-r--r--shell/hush.c113
-rw-r--r--shell/hush_test/hush-redir/redir_stdin1.right3
-rwxr-xr-xshell/hush_test/hush-redir/redir_stdin1.tests7
-rw-r--r--shell/math.c35
-rw-r--r--shell/shell_common.c77
-rwxr-xr-xtestsuite/bc.tests5
-rwxr-xr-xtestsuite/dc.tests35
-rw-r--r--util-linux/fdisk.c33
-rw-r--r--util-linux/fdisk_aix.c40
-rw-r--r--util-linux/fdisk_gpt.c6
-rw-r--r--util-linux/fdisk_osf.c4
-rw-r--r--util-linux/fdisk_sgi.c12
-rw-r--r--util-linux/fdisk_sun.c16
-rw-r--r--util-linux/rdate.c8
-rw-r--r--util-linux/taskset.c140
65 files changed, 1523 insertions, 716 deletions
diff --git a/Config.in b/Config.in
index c60f38a8a..df8ecc3f0 100644
--- a/Config.in
+++ b/Config.in
@@ -170,12 +170,13 @@ config FEATURE_PIDFILE
170config PID_FILE_PATH 170config PID_FILE_PATH
171 string "Directory for pidfiles" 171 string "Directory for pidfiles"
172 default "/var/run" 172 default "/var/run"
173 depends on FEATURE_PIDFILE 173 depends on FEATURE_PIDFILE || FEATURE_CROND_SPECIAL_TIMES
174 help 174 help
175 This is the default path where pidfiles are created. Applets which 175 This is the default path where pidfiles are created. Applets which
176 allow you to set the pidfile path on the command line will override 176 allow you to set the pidfile path on the command line will override
177 this value. The option has no effect on applets that require you to 177 this value. The option has no effect on applets that require you to
178 specify a pidfile path. 178 specify a pidfile path. When crond has the 'Support special times'
179 option enabled, the 'crond.reboot' file is also stored here.
179 180
180config BUSYBOX 181config BUSYBOX
181 bool "Include busybox applet" 182 bool "Include busybox applet"
diff --git a/Makefile.flags b/Makefile.flags
index 6d4b2c3aa..66f28874e 100644
--- a/Makefile.flags
+++ b/Makefile.flags
@@ -47,12 +47,28 @@ endif
47# gcc 3.x emits bogus "old style proto" warning on find.c:alloc_action() 47# gcc 3.x emits bogus "old style proto" warning on find.c:alloc_action()
48CFLAGS += $(call cc-ifversion, -ge, 0400, -Wold-style-definition) 48CFLAGS += $(call cc-ifversion, -ge, 0400, -Wold-style-definition)
49 49
50CFLAGS += $(call cc-option,-fno-builtin-strlen -finline-limit=0 -fomit-frame-pointer -ffunction-sections -fdata-sections,) 50ifneq ($(CC),clang)
51# "clang-9: warning: optimization flag '-finline-limit=0' is not supported
52CFLAGS += $(call cc-option,-finline-limit=0,)
53endif
54
55CFLAGS += $(call cc-option,-fno-builtin-strlen -fomit-frame-pointer -ffunction-sections -fdata-sections,)
51# -fno-guess-branch-probability: prohibit pseudo-random guessing 56# -fno-guess-branch-probability: prohibit pseudo-random guessing
52# of branch probabilities (hopefully makes bloatcheck more stable): 57# of branch probabilities (hopefully makes bloatcheck more stable):
53CFLAGS += $(call cc-option,-fno-guess-branch-probability,) 58CFLAGS += $(call cc-option,-fno-guess-branch-probability,)
54CFLAGS += $(call cc-option,-funsigned-char -static-libgcc,) 59CFLAGS += $(call cc-option,-funsigned-char,)
55CFLAGS += $(call cc-option,-falign-functions=1 -falign-jumps=1 -falign-labels=1 -falign-loops=1,) 60
61ifneq ($(CC),clang)
62# "clang-9: warning: argument unused during compilation: '-static-libgcc'"
63CFLAGS += $(call cc-option,-static-libgcc,)
64endif
65
66CFLAGS += $(call cc-option,-falign-functions=1,)
67ifneq ($(CC),clang)
68# "clang-9: warning: optimization flag '-falign-jumps=1' is not supported" (and same for other two)
69CFLAGS += $(call cc-option,-falign-jumps=1 -falign-labels=1 -falign-loops=1,)
70endif
71
56# Defeat .eh_frame bloat (gcc 4.6.3 x86-32 defconfig: 20% smaller busybox binary): 72# Defeat .eh_frame bloat (gcc 4.6.3 x86-32 defconfig: 20% smaller busybox binary):
57CFLAGS += $(call cc-option,-fno-unwind-tables,) 73CFLAGS += $(call cc-option,-fno-unwind-tables,)
58CFLAGS += $(call cc-option,-fno-asynchronous-unwind-tables,) 74CFLAGS += $(call cc-option,-fno-asynchronous-unwind-tables,)
@@ -60,6 +76,9 @@ CFLAGS += $(call cc-option,-fno-asynchronous-unwind-tables,)
60# (try disabling this and comparing assembly, it's instructive) 76# (try disabling this and comparing assembly, it's instructive)
61CFLAGS += $(call cc-option,-fno-builtin-printf,) 77CFLAGS += $(call cc-option,-fno-builtin-printf,)
62 78
79# clang-9 does not like "str" + N and "if (CONFIG_ITEM && cond)" constructs
80CFLAGS += $(call cc-option,-Wno-string-plus-int -Wno-constant-logical-operand)
81
63# FIXME: These warnings are at least partially to be concerned about and should 82# FIXME: These warnings are at least partially to be concerned about and should
64# be fixed.. 83# be fixed..
65#CFLAGS += $(call cc-option,-Wconversion,) 84#CFLAGS += $(call cc-option,-Wconversion,)
@@ -135,10 +154,12 @@ endif
135# fall back to using a temp file: 154# fall back to using a temp file:
136CRYPT_AVAILABLE := $(shell echo 'int main(void){return 0;}' >crypttest.c; $(CC) $(CFLAGS) -lcrypt -o /dev/null crypttest.c >/dev/null 2>&1 && echo "y"; rm crypttest.c) 155CRYPT_AVAILABLE := $(shell echo 'int main(void){return 0;}' >crypttest.c; $(CC) $(CFLAGS) -lcrypt -o /dev/null crypttest.c >/dev/null 2>&1 && echo "y"; rm crypttest.c)
137ifeq ($(CRYPT_AVAILABLE),y) 156ifeq ($(CRYPT_AVAILABLE),y)
138LDLIBS += m crypt 157LDLIBS += m rt crypt
139else 158else
140LDLIBS += m 159LDLIBS += m rt
141endif 160endif
161# libm may be needed for dc, awk, ntpd
162# librt may be needed for clock_gettime()
142 163
143# libpam may use libpthread, libdl and/or libaudit. 164# libpam may use libpthread, libdl and/or libaudit.
144# On some platforms that requires an explicit -lpthread, -ldl, -laudit. 165# On some platforms that requires an explicit -lpthread, -ldl, -laudit.
diff --git a/archival/bbunzip.c b/archival/bbunzip.c
index 24033c9f5..da6f031e3 100644
--- a/archival/bbunzip.c
+++ b/archival/bbunzip.c
@@ -538,6 +538,7 @@ int unlzma_main(int argc UNUSED_PARAM, char **argv)
538//usage: "\n -c Write to stdout" 538//usage: "\n -c Write to stdout"
539//usage: "\n -f Force" 539//usage: "\n -f Force"
540//usage: "\n -k Keep input files" 540//usage: "\n -k Keep input files"
541//usage: "\n -t Test file integrity"
541//usage: 542//usage:
542//usage:#define xz_trivial_usage 543//usage:#define xz_trivial_usage
543//usage: "-d [-cfk] [FILE]..." 544//usage: "-d [-cfk] [FILE]..."
@@ -547,6 +548,7 @@ int unlzma_main(int argc UNUSED_PARAM, char **argv)
547//usage: "\n -c Write to stdout" 548//usage: "\n -c Write to stdout"
548//usage: "\n -f Force" 549//usage: "\n -f Force"
549//usage: "\n -k Keep input files" 550//usage: "\n -k Keep input files"
551//usage: "\n -t Test file integrity"
550//usage: 552//usage:
551//usage:#define xzcat_trivial_usage 553//usage:#define xzcat_trivial_usage
552//usage: "[FILE]..." 554//usage: "[FILE]..."
diff --git a/archival/gzip.c b/archival/gzip.c
index 17341de45..d9c730f13 100644
--- a/archival/gzip.c
+++ b/archival/gzip.c
@@ -52,7 +52,7 @@ aa: 85.1% -- replaced with aa.gz
52//config: help 52//config: help
53//config: Enable support for compression levels 4-9. The default level 53//config: Enable support for compression levels 4-9. The default level
54//config: is 6. If levels 1-3 are specified, 4 is used. 54//config: is 6. If levels 1-3 are specified, 4 is used.
55//config: If this option is not selected, -N options are ignored and -9 55//config: If this option is not selected, -N options are ignored and -6
56//config: is used. 56//config: is used.
57//config: 57//config:
58//config:config FEATURE_GZIP_DECOMPRESS 58//config:config FEATURE_GZIP_DECOMPRESS
@@ -259,12 +259,13 @@ enum {
259 259
260#if !ENABLE_FEATURE_GZIP_LEVELS 260#if !ENABLE_FEATURE_GZIP_LEVELS
261 261
262 max_chain_length = 4096, 262 comp_level_minus4 = 6 - 4,
263 max_chain_length = 128,
263/* To speed up deflation, hash chains are never searched beyond this length. 264/* To speed up deflation, hash chains are never searched beyond this length.
264 * A higher limit improves compression ratio but degrades the speed. 265 * A higher limit improves compression ratio but degrades the speed.
265 */ 266 */
266 267
267 max_lazy_match = 258, 268 max_lazy_match = 16,
268/* Attempt to find a better match only when the current match is strictly 269/* Attempt to find a better match only when the current match is strictly
269 * smaller than this value. This mechanism is used only for compression 270 * smaller than this value. This mechanism is used only for compression
270 * levels >= 4. 271 * levels >= 4.
@@ -276,7 +277,7 @@ enum {
276 * max_insert_length is used only for compression levels <= 3. 277 * max_insert_length is used only for compression levels <= 3.
277 */ 278 */
278 279
279 good_match = 32, 280 good_match = 8,
280/* Use a faster search when the previous match is longer than this */ 281/* Use a faster search when the previous match is longer than this */
281 282
282/* Values for max_lazy_match, good_match and max_chain_length, depending on 283/* Values for max_lazy_match, good_match and max_chain_length, depending on
@@ -285,7 +286,7 @@ enum {
285 * found for specific files. 286 * found for specific files.
286 */ 287 */
287 288
288 nice_match = 258, /* Stop searching when current match exceeds this */ 289 nice_match = 128, /* Stop searching when current match exceeds this */
289/* Note: the deflate() code requires max_lazy >= MIN_MATCH and max_chain >= 4 290/* Note: the deflate() code requires max_lazy >= MIN_MATCH and max_chain >= 4
290 * For deflate_fast() (levels <= 3) good is ignored and lazy has a different 291 * For deflate_fast() (levels <= 3) good is ignored and lazy has a different
291 * meaning. 292 * meaning.
@@ -334,14 +335,16 @@ struct globals {
334#define head (G1.prev + WSIZE) /* hash head (see deflate.c) */ 335#define head (G1.prev + WSIZE) /* hash head (see deflate.c) */
335 336
336#if ENABLE_FEATURE_GZIP_LEVELS 337#if ENABLE_FEATURE_GZIP_LEVELS
338 unsigned comp_level_minus4; /* can be a byte */
337 unsigned max_chain_length; 339 unsigned max_chain_length;
338 unsigned max_lazy_match; 340 unsigned max_lazy_match;
339 unsigned good_match; 341 unsigned good_match;
340 unsigned nice_match; 342 unsigned nice_match;
341#define max_chain_length (G1.max_chain_length) 343#define comp_level_minus4 (G1.comp_level_minus4)
342#define max_lazy_match (G1.max_lazy_match) 344#define max_chain_length (G1.max_chain_length)
343#define good_match (G1.good_match) 345#define max_lazy_match (G1.max_lazy_match)
344#define nice_match (G1.nice_match) 346#define good_match (G1.good_match)
347#define nice_match (G1.nice_match)
345#endif 348#endif
346 349
347/* =========================================================================== */ 350/* =========================================================================== */
@@ -504,7 +507,7 @@ static ALWAYS_INLINE void flush_outbuf_if_32bit_optimized(void)
504 * pointer, then initialize the crc shift register contents instead. 507 * pointer, then initialize the crc shift register contents instead.
505 * Return the current crc in either case. 508 * Return the current crc in either case.
506 */ 509 */
507static void updcrc(uch * s, unsigned n) 510static void updcrc(uch *s, unsigned n)
508{ 511{
509 G1.crc = crc32_block_endian0(G1.crc, s, n, global_crc32_table /*G1.crc_32_tab*/); 512 G1.crc = crc32_block_endian0(G1.crc, s, n, global_crc32_table /*G1.crc_32_tab*/);
510} 513}
@@ -607,7 +610,7 @@ static void bi_windup(void)
607 * Copy a stored block to the zip file, storing first the length and its 610 * Copy a stored block to the zip file, storing first the length and its
608 * one's complement if requested. 611 * one's complement if requested.
609 */ 612 */
610static void copy_block(char *buf, unsigned len, int header) 613static void copy_block(const char *buf, unsigned len, int header)
611{ 614{
612 bi_windup(); /* align on byte boundary */ 615 bi_windup(); /* align on byte boundary */
613 616
@@ -1007,7 +1010,8 @@ struct globals2 {
1007 tree_desc d_desc; 1010 tree_desc d_desc;
1008 tree_desc bl_desc; 1011 tree_desc bl_desc;
1009 1012
1010 ush bl_count[MAX_BITS + 1]; 1013 /* was "ush", but "unsigned" results in smaller code */
1014 unsigned bl_count[MAX_BITS + 1];
1011 1015
1012/* The lengths of the bit length codes are sent in order of decreasing 1016/* The lengths of the bit length codes are sent in order of decreasing
1013 * probability, to avoid transmitting the lengths for unused bit length codes. 1017 * probability, to avoid transmitting the lengths for unused bit length codes.
@@ -1118,7 +1122,7 @@ static void init_block(void)
1118 (tree[n].Freq < tree[m].Freq \ 1122 (tree[n].Freq < tree[m].Freq \
1119 || (tree[n].Freq == tree[m].Freq && G2.depth[n] <= G2.depth[m])) 1123 || (tree[n].Freq == tree[m].Freq && G2.depth[n] <= G2.depth[m]))
1120 1124
1121static void pqdownheap(ct_data * tree, int k) 1125static void pqdownheap(const ct_data *tree, int k)
1122{ 1126{
1123 int v = G2.heap[k]; 1127 int v = G2.heap[k];
1124 int j = k << 1; /* left son of k */ 1128 int j = k << 1; /* left son of k */
@@ -1152,22 +1156,15 @@ static void pqdownheap(ct_data * tree, int k)
1152 * The length opt_len is updated; static_len is also updated if stree is 1156 * The length opt_len is updated; static_len is also updated if stree is
1153 * not null. 1157 * not null.
1154 */ 1158 */
1155static void gen_bitlen(tree_desc * desc) 1159static void gen_bitlen(const tree_desc *desc)
1156{ 1160{
1157 ct_data *tree = desc->dyn_tree; 1161#define tree desc->dyn_tree
1158 const uint8_t *extra = desc->extra_bits; 1162 int h; /* heap index */
1159 int base = desc->extra_base; 1163 int n, m; /* iterate over the tree elements */
1160 int max_code = desc->max_code; 1164 int bits; /* bit length */
1161 int max_length = desc->max_length; 1165 int overflow; /* number of elements with bit length too large */
1162 ct_data *stree = desc->static_tree; 1166
1163 int h; /* heap index */ 1167 for (bits = 0; bits < ARRAY_SIZE(G2.bl_count); bits++)
1164 int n, m; /* iterate over the tree elements */
1165 int bits; /* bit length */
1166 int xbits; /* extra bits */
1167 ush f; /* frequency */
1168 int overflow = 0; /* number of elements with bit length too large */
1169
1170 for (bits = 0; bits <= MAX_BITS; bits++)
1171 G2.bl_count[bits] = 0; 1168 G2.bl_count[bits] = 0;
1172 1169
1173 /* In a first pass, compute the optimal bit lengths (which may 1170 /* In a first pass, compute the optimal bit lengths (which may
@@ -1175,28 +1172,32 @@ static void gen_bitlen(tree_desc * desc)
1175 */ 1172 */
1176 tree[G2.heap[G2.heap_max]].Len = 0; /* root of the heap */ 1173 tree[G2.heap[G2.heap_max]].Len = 0; /* root of the heap */
1177 1174
1175 overflow = 0;
1178 for (h = G2.heap_max + 1; h < HEAP_SIZE; h++) { 1176 for (h = G2.heap_max + 1; h < HEAP_SIZE; h++) {
1177 ulg f; /* frequency */
1178 int xbits; /* extra bits */
1179
1179 n = G2.heap[h]; 1180 n = G2.heap[h];
1180 bits = tree[tree[n].Dad].Len + 1; 1181 bits = tree[tree[n].Dad].Len + 1;
1181 if (bits > max_length) { 1182 if (bits > desc->max_length) {
1182 bits = max_length; 1183 bits = desc->max_length;
1183 overflow++; 1184 overflow++;
1184 } 1185 }
1185 tree[n].Len = (ush) bits; 1186 tree[n].Len = (ush) bits;
1186 /* We overwrite tree[n].Dad which is no longer needed */ 1187 /* We overwrite tree[n].Dad which is no longer needed */
1187 1188
1188 if (n > max_code) 1189 if (n > desc->max_code)
1189 continue; /* not a leaf node */ 1190 continue; /* not a leaf node */
1190 1191
1191 G2.bl_count[bits]++; 1192 G2.bl_count[bits]++;
1192 xbits = 0; 1193 xbits = 0;
1193 if (n >= base) 1194 if (n >= desc->extra_base)
1194 xbits = extra[n - base]; 1195 xbits = desc->extra_bits[n - desc->extra_base];
1195 f = tree[n].Freq; 1196 f = tree[n].Freq;
1196 G2.opt_len += (ulg) f *(bits + xbits); 1197 G2.opt_len += f * (bits + xbits);
1197 1198
1198 if (stree) 1199 if (desc->static_tree)
1199 G2.static_len += (ulg) f * (stree[n].Len + xbits); 1200 G2.static_len += f * (desc->static_tree[n].Len + xbits);
1200 } 1201 }
1201 if (overflow == 0) 1202 if (overflow == 0)
1202 return; 1203 return;
@@ -1206,14 +1207,14 @@ static void gen_bitlen(tree_desc * desc)
1206 1207
1207 /* Find the first bit length which could increase: */ 1208 /* Find the first bit length which could increase: */
1208 do { 1209 do {
1209 bits = max_length - 1; 1210 bits = desc->max_length - 1;
1210 while (G2.bl_count[bits] == 0) 1211 while (G2.bl_count[bits] == 0)
1211 bits--; 1212 bits--;
1212 G2.bl_count[bits]--; /* move one leaf down the tree */ 1213 G2.bl_count[bits]--; /* move one leaf down the tree */
1213 G2.bl_count[bits + 1] += 2; /* move one overflow item as its brother */ 1214 G2.bl_count[bits + 1] += 2; /* move one overflow item as its brother */
1214 G2.bl_count[max_length]--; 1215 G2.bl_count[desc->max_length]--;
1215 /* The brother of the overflow item also moves one step up, 1216 /* The brother of the overflow item also moves one step up,
1216 * but this does not affect bl_count[max_length] 1217 * but this does not affect bl_count[desc->max_length]
1217 */ 1218 */
1218 overflow -= 2; 1219 overflow -= 2;
1219 } while (overflow > 0); 1220 } while (overflow > 0);
@@ -1223,11 +1224,11 @@ static void gen_bitlen(tree_desc * desc)
1223 * lengths instead of fixing only the wrong ones. This idea is taken 1224 * lengths instead of fixing only the wrong ones. This idea is taken
1224 * from 'ar' written by Haruhiko Okumura.) 1225 * from 'ar' written by Haruhiko Okumura.)
1225 */ 1226 */
1226 for (bits = max_length; bits != 0; bits--) { 1227 for (bits = desc->max_length; bits != 0; bits--) {
1227 n = G2.bl_count[bits]; 1228 n = G2.bl_count[bits];
1228 while (n != 0) { 1229 while (n != 0) {
1229 m = G2.heap[--h]; 1230 m = G2.heap[--h];
1230 if (m > max_code) 1231 if (m > desc->max_code)
1231 continue; 1232 continue;
1232 if (tree[m].Len != (unsigned) bits) { 1233 if (tree[m].Len != (unsigned) bits) {
1233 Trace((stderr, "code %d bits %d->%d\n", m, tree[m].Len, bits)); 1234 Trace((stderr, "code %d bits %d->%d\n", m, tree[m].Len, bits));
@@ -1237,6 +1238,7 @@ static void gen_bitlen(tree_desc * desc)
1237 n--; 1238 n--;
1238 } 1239 }
1239 } 1240 }
1241#undef tree
1240} 1242}
1241 1243
1242/* =========================================================================== 1244/* ===========================================================================
@@ -1247,12 +1249,13 @@ static void gen_bitlen(tree_desc * desc)
1247 * OUT assertion: the field code is set for all tree elements of non 1249 * OUT assertion: the field code is set for all tree elements of non
1248 * zero code length. 1250 * zero code length.
1249 */ 1251 */
1250static void gen_codes(ct_data * tree, int max_code) 1252static void gen_codes(ct_data *tree, int max_code)
1251{ 1253{
1252 ush next_code[MAX_BITS + 1]; /* next code value for each bit length */ 1254 /* next_code[] and code used to be "ush", but "unsigned" results in smaller code */
1253 ush code = 0; /* running code value */ 1255 unsigned next_code[MAX_BITS + 1]; /* next code value for each bit length */
1254 int bits; /* bit index */ 1256 unsigned code = 0; /* running code value */
1255 int n; /* code index */ 1257 int bits; /* bit index */
1258 int n; /* code index */
1256 1259
1257 /* The distribution counts are first used to generate the code values 1260 /* The distribution counts are first used to generate the code values
1258 * without bit reversal. 1261 * without bit reversal.
@@ -1304,7 +1307,7 @@ do { \
1304 pqdownheap(tree, SMALLEST); \ 1307 pqdownheap(tree, SMALLEST); \
1305} while (0) 1308} while (0)
1306 1309
1307static void build_tree(tree_desc * desc) 1310static void build_tree(tree_desc *desc)
1308{ 1311{
1309 ct_data *tree = desc->dyn_tree; 1312 ct_data *tree = desc->dyn_tree;
1310 ct_data *stree = desc->static_tree; 1313 ct_data *stree = desc->static_tree;
@@ -1382,10 +1385,10 @@ static void build_tree(tree_desc * desc)
1382 /* At this point, the fields freq and dad are set. We can now 1385 /* At this point, the fields freq and dad are set. We can now
1383 * generate the bit lengths. 1386 * generate the bit lengths.
1384 */ 1387 */
1385 gen_bitlen((tree_desc *) desc); 1388 gen_bitlen(desc);
1386 1389
1387 /* The field len is now set, we can generate the bit codes */ 1390 /* The field len is now set, we can generate the bit codes */
1388 gen_codes((ct_data *) tree, max_code); 1391 gen_codes(tree, max_code);
1389} 1392}
1390 1393
1391/* =========================================================================== 1394/* ===========================================================================
@@ -1394,7 +1397,7 @@ static void build_tree(tree_desc * desc)
1394 * counts. (The contribution of the bit length codes will be added later 1397 * counts. (The contribution of the bit length codes will be added later
1395 * during the construction of bl_tree.) 1398 * during the construction of bl_tree.)
1396 */ 1399 */
1397static void scan_tree(ct_data * tree, int max_code) 1400static void scan_tree(ct_data *tree, int max_code)
1398{ 1401{
1399 int n; /* iterates over all tree elements */ 1402 int n; /* iterates over all tree elements */
1400 int prevlen = -1; /* last emitted length */ 1403 int prevlen = -1; /* last emitted length */
@@ -1446,7 +1449,7 @@ static void scan_tree(ct_data * tree, int max_code)
1446 * Send a literal or distance tree in compressed form, using the codes in 1449 * Send a literal or distance tree in compressed form, using the codes in
1447 * bl_tree. 1450 * bl_tree.
1448 */ 1451 */
1449static void send_tree(ct_data * tree, int max_code) 1452static void send_tree(const ct_data *tree, int max_code)
1450{ 1453{
1451 int n; /* iterates over all tree elements */ 1454 int n; /* iterates over all tree elements */
1452 int prevlen = -1; /* last emitted length */ 1455 int prevlen = -1; /* last emitted length */
@@ -1622,7 +1625,7 @@ static int ct_tally(int dist, int lc)
1622/* =========================================================================== 1625/* ===========================================================================
1623 * Send the block data compressed using the given Huffman trees 1626 * Send the block data compressed using the given Huffman trees
1624 */ 1627 */
1625static void compress_block(ct_data * ltree, ct_data * dtree) 1628static void compress_block(const ct_data *ltree, const ct_data *dtree)
1626{ 1629{
1627 unsigned dist; /* distance of matched string */ 1630 unsigned dist; /* distance of matched string */
1628 int lc; /* match length or unmatched char (if dist == 0) */ 1631 int lc; /* match length or unmatched char (if dist == 0) */
@@ -1672,7 +1675,7 @@ static void compress_block(ct_data * ltree, ct_data * dtree)
1672 * trees or store, and output the encoded block to the zip file. This function 1675 * trees or store, and output the encoded block to the zip file. This function
1673 * returns the total compressed length for the file so far. 1676 * returns the total compressed length for the file so far.
1674 */ 1677 */
1675static void flush_block(char *buf, ulg stored_len, int eof) 1678static void flush_block(const char *buf, ulg stored_len, int eof)
1676{ 1679{
1677 ulg opt_lenb, static_lenb; /* opt_len and static_len in bytes */ 1680 ulg opt_lenb, static_lenb; /* opt_len and static_len in bytes */
1678 int max_blindex; /* index of last bit length code of non zero freq */ 1681 int max_blindex; /* index of last bit length code of non zero freq */
@@ -1919,7 +1922,7 @@ static void bi_init(void)
1919/* =========================================================================== 1922/* ===========================================================================
1920 * Initialize the "longest match" routines for a new file 1923 * Initialize the "longest match" routines for a new file
1921 */ 1924 */
1922static void lm_init(unsigned *flags16p) 1925static void lm_init(void)
1923{ 1926{
1924 unsigned j; 1927 unsigned j;
1925 1928
@@ -1927,8 +1930,6 @@ static void lm_init(unsigned *flags16p)
1927 memset(head, 0, HASH_SIZE * sizeof(*head)); 1930 memset(head, 0, HASH_SIZE * sizeof(*head));
1928 /* prev will be initialized on the fly */ 1931 /* prev will be initialized on the fly */
1929 1932
1930 /* speed options for the general purpose bit flag */
1931 *flags16p |= 2; /* FAST 4, SLOW 2 */
1932 /* ??? reduce max_chain_length for binary files */ 1933 /* ??? reduce max_chain_length for binary files */
1933 1934
1934 //G1.strstart = 0; // globals are zeroed in pack_gzip() 1935 //G1.strstart = 0; // globals are zeroed in pack_gzip()
@@ -2076,10 +2077,16 @@ static void zip(void)
2076 2077
2077 bi_init(); 2078 bi_init();
2078 ct_init(); 2079 ct_init();
2079 deflate_flags = 0; /* pkzip -es, -en or -ex equivalent */ 2080 lm_init();
2080 lm_init(&deflate_flags);
2081 2081
2082 put_16bit(deflate_flags | 0x300); /* extra flags. OS id = 3 (Unix) */ 2082 deflate_flags = 0x300; /* extra flags. OS id = 3 (Unix) */
2083#if ENABLE_FEATURE_GZIP_LEVELS
2084 /* Note that comp_level < 4 do not exist in this version of gzip */
2085 if (comp_level_minus4 == 9 - 4) {
2086 deflate_flags |= 0x02; /* SLOW flag */
2087 }
2088#endif
2089 put_16bit(deflate_flags);
2083 2090
2084 /* The above 32-bit misaligns outbuf (10 bytes are stored), flush it */ 2091 /* The above 32-bit misaligns outbuf (10 bytes are stored), flush it */
2085 flush_outbuf_if_32bit_optimized(); 2092 flush_outbuf_if_32bit_optimized();
@@ -2222,8 +2229,11 @@ int gzip_main(int argc UNUSED_PARAM, char **argv)
2222#if ENABLE_FEATURE_GZIP_LEVELS 2229#if ENABLE_FEATURE_GZIP_LEVELS
2223 opt >>= (BBUNPK_OPTSTRLEN IF_FEATURE_GZIP_DECOMPRESS(+ 2) + 1); /* drop cfkvq[dt]n bits */ 2230 opt >>= (BBUNPK_OPTSTRLEN IF_FEATURE_GZIP_DECOMPRESS(+ 2) + 1); /* drop cfkvq[dt]n bits */
2224 if (opt == 0) 2231 if (opt == 0)
2225 opt = 1 << 6; /* default: 6 */ 2232 opt = 1 << 5; /* default: 6 */
2226 opt = ffs(opt >> 4); /* Maps -1..-4 to [0], -5 to [1] ... -9 to [5] */ 2233 opt = ffs(opt >> 4); /* Maps -1..-4 to [0], -5 to [1] ... -9 to [5] */
2234
2235 comp_level_minus4 = opt;
2236
2227 max_chain_length = 1 << gzip_level_config[opt].chain_shift; 2237 max_chain_length = 1 << gzip_level_config[opt].chain_shift;
2228 good_match = gzip_level_config[opt].good; 2238 good_match = gzip_level_config[opt].good;
2229 max_lazy_match = gzip_level_config[opt].lazy2 * 2; 2239 max_lazy_match = gzip_level_config[opt].lazy2 * 2;
diff --git a/archival/libarchive/decompress_gunzip.c b/archival/libarchive/decompress_gunzip.c
index cf07f71df..c0332d414 100644
--- a/archival/libarchive/decompress_gunzip.c
+++ b/archival/libarchive/decompress_gunzip.c
@@ -39,7 +39,8 @@ typedef struct huft_t {
39 unsigned char e; /* number of extra bits or operation */ 39 unsigned char e; /* number of extra bits or operation */
40 unsigned char b; /* number of bits in this code or subcode */ 40 unsigned char b; /* number of bits in this code or subcode */
41 union { 41 union {
42 unsigned short n; /* literal, length base, or distance base */ 42 unsigned n; /* literal, length base, or distance base */
43 /* ^^^^^ was "unsigned short", but that results in larger code */
43 struct huft_t *t; /* pointer to next level of table */ 44 struct huft_t *t; /* pointer to next level of table */
44 } v; 45 } v;
45} huft_t; 46} huft_t;
@@ -184,29 +185,26 @@ static const uint16_t mask_bits[] ALIGN2 = {
184 0x01ff, 0x03ff, 0x07ff, 0x0fff, 0x1fff, 0x3fff, 0x7fff, 0xffff 185 0x01ff, 0x03ff, 0x07ff, 0x0fff, 0x1fff, 0x3fff, 0x7fff, 0xffff
185}; 186};
186 187
187/* Copy lengths for literal codes 257..285 */ 188/* Put lengths/offsets and extra bits in a struct of arrays
188static const uint16_t cplens[] ALIGN2 = { 189 * to make calls to huft_build() have one fewer parameter.
189 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, 35, 43, 51, 59, 190 */
190 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0 191struct cp_ext {
192 uint16_t cp[31];
193 uint8_t ext[31];
191}; 194};
192 195/* Copy lengths and extra bits for literal codes 257..285 */
193/* note: see note #13 above about the 258 in this list. */ 196/* note: see note #13 above about the 258 in this list. */
194/* Extra bits for literal codes 257..285 */ 197static const struct cp_ext lit = {
195static const uint8_t cplext[] ALIGN1 = { 198 /*257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 */
196 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 199 /*0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 */
197 5, 5, 5, 0, 99, 99 200 { 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0 },
198}; /* 99 == invalid */ 201 { 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 99, 99 } /* 99 == invalid */
199
200/* Copy offsets for distance codes 0..29 */
201static const uint16_t cpdist[] ALIGN2 = {
202 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513,
203 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577
204}; 202};
205 203/* Copy offsets and extra bits for distance codes 0..29 */
206/* Extra bits for distance codes */ 204static const struct cp_ext dist = {
207static const uint8_t cpdext[] ALIGN1 = { 205 /*0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 */
208 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 206 { 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577 },
209 11, 11, 12, 12, 13, 13 207 { 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13 }
210}; 208};
211 209
212/* Tables for deflate from PKZIP's appnote.txt. */ 210/* Tables for deflate from PKZIP's appnote.txt. */
@@ -277,22 +275,25 @@ static unsigned fill_bitbuffer(STATE_PARAM unsigned bitbuffer, unsigned *current
277 275
278 276
279/* Given a list of code lengths and a maximum table size, make a set of 277/* Given a list of code lengths and a maximum table size, make a set of
280 * tables to decode that set of codes. Return zero on success, one if 278 * tables to decode that set of codes.
281 * the given code set is incomplete (the tables are still built in this
282 * case), two if the input is invalid (an oversubscribed set of lengths)
283 * - in this case stores NULL in *t.
284 * 279 *
285 * b: code lengths in bits (all assumed <= BMAX) 280 * b: code lengths in bits (all assumed <= BMAX)
286 * n: number of codes (assumed <= N_MAX) 281 * n: number of codes (assumed <= N_MAX)
287 * s: number of simple-valued codes (0..s-1) 282 * s: number of simple-valued codes (0..s-1)
288 * d: list of base values for non-simple codes 283 * cp_ext->cp,ext: list of base values/extra bits for non-simple codes
289 * e: list of extra bits for non-simple codes
290 * t: result: starting table
291 * m: maximum lookup bits, returns actual 284 * m: maximum lookup bits, returns actual
285 * result: starting table
286 *
287 * On error, returns a value with lowest-bit set on error.
288 * It can be just the value of 0x1,
289 * or a valid pointer to a Huffman table, ORed with 0x1 if incompete table
290 * is given: "fixed inflate" decoder feeds us such data.
292 */ 291 */
293static int huft_build(const unsigned *b, const unsigned n, 292#define BAD_HUFT(p) ((uintptr_t)(p) & 1)
294 const unsigned s, const unsigned short *d, 293#define ERR_RET ((huft_t*)(uintptr_t)1)
295 const unsigned char *e, huft_t **t, unsigned *m) 294static huft_t* huft_build(const unsigned *b, const unsigned n,
295 const unsigned s, const struct cp_ext *cp_ext,
296 unsigned *m)
296{ 297{
297 unsigned a; /* counter for codes of length k */ 298 unsigned a; /* counter for codes of length k */
298 unsigned c[BMAX + 1]; /* bit length count table */ 299 unsigned c[BMAX + 1]; /* bit length count table */
@@ -314,12 +315,12 @@ static int huft_build(const unsigned *b, const unsigned n,
314 unsigned *xp; /* pointer into x */ 315 unsigned *xp; /* pointer into x */
315 int y; /* number of dummy codes added */ 316 int y; /* number of dummy codes added */
316 unsigned z; /* number of entries in current table */ 317 unsigned z; /* number of entries in current table */
318 huft_t *result;
319 huft_t **t;
317 320
318 /* Length of EOB code, if any */ 321 /* Length of EOB code, if any */
319 eob_len = n > 256 ? b[256] : BMAX; 322 eob_len = n > 256 ? b[256] : BMAX;
320 323
321 *t = NULL;
322
323 /* Generate counts for each bit length */ 324 /* Generate counts for each bit length */
324 memset(c, 0, sizeof(c)); 325 memset(c, 0, sizeof(c));
325 p = b; 326 p = b;
@@ -335,9 +336,8 @@ static int huft_build(const unsigned *b, const unsigned n,
335 q[1].b = 1; 336 q[1].b = 1;
336 q[2].e = 99; /* invalid code marker */ 337 q[2].e = 99; /* invalid code marker */
337 q[2].b = 1; 338 q[2].b = 1;
338 *t = q + 1;
339 *m = 1; 339 *m = 1;
340 return 0; 340 return q + 1;
341 } 341 }
342 342
343 /* Find minimum and maximum length, bound *m by those */ 343 /* Find minimum and maximum length, bound *m by those */
@@ -353,11 +353,11 @@ static int huft_build(const unsigned *b, const unsigned n,
353 for (y = 1 << j; j < i; j++, y <<= 1) { 353 for (y = 1 << j; j < i; j++, y <<= 1) {
354 y -= c[j]; 354 y -= c[j];
355 if (y < 0) 355 if (y < 0)
356 return 2; /* bad input: more codes than bits */ 356 return ERR_RET; /* bad input: more codes than bits */
357 } 357 }
358 y -= c[i]; 358 y -= c[i];
359 if (y < 0) 359 if (y < 0)
360 return 2; 360 return ERR_RET;
361 c[i] += y; 361 c[i] += y;
362 362
363 /* Generate starting offsets into the value table for each length */ 363 /* Generate starting offsets into the value table for each length */
@@ -384,6 +384,8 @@ static int huft_build(const unsigned *b, const unsigned n,
384 } while (++i < n); 384 } while (++i < n);
385 385
386 /* Generate the Huffman codes and for each, make the table entries */ 386 /* Generate the Huffman codes and for each, make the table entries */
387 result = ERR_RET;
388 t = &result;
387 x[0] = i = 0; /* first Huffman code is zero */ 389 x[0] = i = 0; /* first Huffman code is zero */
388 p = v; /* grab values in bit order */ 390 p = v; /* grab values in bit order */
389 htl = -1; /* no tables yet--level -1 */ 391 htl = -1; /* no tables yet--level -1 */
@@ -449,8 +451,8 @@ static int huft_build(const unsigned *b, const unsigned n,
449 r.e = (unsigned char) (*p < 256 ? 16 : 15); /* 256 is EOB code */ 451 r.e = (unsigned char) (*p < 256 ? 16 : 15); /* 256 is EOB code */
450 r.v.n = (unsigned short) (*p++); /* simple code is just the value */ 452 r.v.n = (unsigned short) (*p++); /* simple code is just the value */
451 } else { 453 } else {
452 r.e = (unsigned char) e[*p - s]; /* non-simple--look up in lists */ 454 r.e = (unsigned char) cp_ext->ext[*p - s]; /* non-simple--look up in lists */
453 r.v.n = d[*p++ - s]; 455 r.v.n = cp_ext->cp[*p++ - s];
454 } 456 }
455 457
456 /* fill code-like entries with r */ 458 /* fill code-like entries with r */
@@ -475,8 +477,11 @@ static int huft_build(const unsigned *b, const unsigned n,
475 /* return actual size of base table */ 477 /* return actual size of base table */
476 *m = ws[1]; 478 *m = ws[1];
477 479
478 /* Return 1 if we were given an incomplete table */ 480 if (y != 0 && g != 1) /* we were given an incomplete table */
479 return y != 0 && g != 1; 481 /* return "result" ORed with 1 */
482 return (void*)((uintptr_t)result | 1);
483
484 return result;
480} 485}
481 486
482 487
@@ -777,14 +782,17 @@ static int inflate_block(STATE_PARAM smallint *e)
777 for (; i < 288; i++) /* make a complete, but wrong code set */ 782 for (; i < 288; i++) /* make a complete, but wrong code set */
778 ll[i] = 8; 783 ll[i] = 8;
779 bl = 7; 784 bl = 7;
780 huft_build(ll, 288, 257, cplens, cplext, &inflate_codes_tl, &bl); 785 inflate_codes_tl = huft_build(ll, 288, 257, &lit, &bl);
781 /* huft_build() never return nonzero - we use known data */ 786 /* ^^^ never returns error here - we use known data */
782 787
783 /* set up distance table */ 788 /* set up distance table */
784 for (i = 0; i < 30; i++) /* make an incomplete code set */ 789 for (i = 0; i < 30; i++) /* make an incomplete code set */
785 ll[i] = 5; 790 ll[i] = 5;
786 bd = 5; 791 bd = 5;
787 huft_build(ll, 30, 0, cpdist, cpdext, &inflate_codes_td, &bd); 792 inflate_codes_td = huft_build(ll, 30, 0, &dist, &bd);
793 /* ^^^ does return error here! (lsb bit is set) - we gave it incomplete code set */
794 /* clearing error bit: */
795 inflate_codes_td = (void*)((uintptr_t)inflate_codes_td & ~(uintptr_t)1);
788 796
789 /* set up data for inflate_codes() */ 797 /* set up data for inflate_codes() */
790 inflate_codes_setup(PASS_STATE bl, bd); 798 inflate_codes_setup(PASS_STATE bl, bd);
@@ -850,9 +858,9 @@ static int inflate_block(STATE_PARAM smallint *e)
850 858
851 /* build decoding table for trees - single level, 7 bit lookup */ 859 /* build decoding table for trees - single level, 7 bit lookup */
852 bl = 7; 860 bl = 7;
853 i = huft_build(ll, 19, 19, NULL, NULL, &inflate_codes_tl, &bl); 861 inflate_codes_tl = huft_build(ll, 19, 19, NULL, &bl);
854 if (i != 0) { 862 if (BAD_HUFT(inflate_codes_tl)) {
855 abort_unzip(PASS_STATE_ONLY); //return i; /* incomplete code set */ 863 abort_unzip(PASS_STATE_ONLY); /* incomplete code set */
856 } 864 }
857 865
858 /* read in literal and distance code lengths */ 866 /* read in literal and distance code lengths */
@@ -915,14 +923,13 @@ static int inflate_block(STATE_PARAM smallint *e)
915 923
916 /* build the decoding tables for literal/length and distance codes */ 924 /* build the decoding tables for literal/length and distance codes */
917 bl = lbits; 925 bl = lbits;
918 926 inflate_codes_tl = huft_build(ll, nl, 257, &lit, &bl);
919 i = huft_build(ll, nl, 257, cplens, cplext, &inflate_codes_tl, &bl); 927 if (BAD_HUFT(inflate_codes_tl)) {
920 if (i != 0) {
921 abort_unzip(PASS_STATE_ONLY); 928 abort_unzip(PASS_STATE_ONLY);
922 } 929 }
923 bd = dbits; 930 bd = dbits;
924 i = huft_build(ll + nl, nd, 0, cpdist, cpdext, &inflate_codes_td, &bd); 931 inflate_codes_td = huft_build(ll + nl, nd, 0, &dist, &bd);
925 if (i != 0) { 932 if (BAD_HUFT(inflate_codes_td)) {
926 abort_unzip(PASS_STATE_ONLY); 933 abort_unzip(PASS_STATE_ONLY);
927 } 934 }
928 935
diff --git a/archival/libarchive/decompress_unxz.c b/archival/libarchive/decompress_unxz.c
index f03341384..3dd9bbf49 100644
--- a/archival/libarchive/decompress_unxz.c
+++ b/archival/libarchive/decompress_unxz.c
@@ -96,6 +96,24 @@ unpack_xz_stream(transformer_state_t *xstate)
96 */ 96 */
97 do { 97 do {
98 if (membuf[iobuf.in_pos] != 0) { 98 if (membuf[iobuf.in_pos] != 0) {
99 /* There is more data, but is it XZ data?
100 * Example: dpkg-deb -f busybox_1.30.1-4_amd64.deb
101 * reads control.tar.xz "control" file
102 * inside the ar archive, but tar.xz
103 * extraction code reaches end of xz data,
104 * reached this code and reads the beginning
105 * of data.tar.xz's ar header, which isn't xz data,
106 * and prints "corrupted data".
107 * The correct solution is to not read
108 * past nested archive (to simulate EOF).
109 * This is a workaround:
110 */
111 if (membuf[iobuf.in_pos] != 0xfd) {
112 /* It's definitely not a xz signature
113 * (which is 0xfd,"7zXZ",0x00).
114 */
115 goto end;
116 }
99 xz_dec_reset(state); 117 xz_dec_reset(state);
100 goto do_run; 118 goto do_run;
101 } 119 }
@@ -128,7 +146,7 @@ unpack_xz_stream(transformer_state_t *xstate)
128 break; 146 break;
129 } 147 }
130 } 148 }
131 149 end:
132 xz_dec_end(state); 150 xz_dec_end(state);
133 free(membuf); 151 free(membuf);
134 152
diff --git a/archival/libarchive/unxz/xz_stream.h b/archival/libarchive/unxz/xz_stream.h
index 66cb5a705..45056e42c 100644
--- a/archival/libarchive/unxz/xz_stream.h
+++ b/archival/libarchive/unxz/xz_stream.h
@@ -25,7 +25,7 @@
25 25
26#define STREAM_HEADER_SIZE 12 26#define STREAM_HEADER_SIZE 12
27 27
28#define HEADER_MAGIC "\3757zXZ" 28#define HEADER_MAGIC "\375""7zXZ"
29#define HEADER_MAGIC_SIZE 6 29#define HEADER_MAGIC_SIZE 6
30 30
31#define FOOTER_MAGIC "YZ" 31#define FOOTER_MAGIC "YZ"
diff --git a/archival/tar.c b/archival/tar.c
index 6618c9ecc..c57bff779 100644
--- a/archival/tar.c
+++ b/archival/tar.c
@@ -1066,7 +1066,7 @@ int tar_main(int argc UNUSED_PARAM, char **argv)
1066 IF_FEATURE_TAR_CREATE("c--tx:t--cx:x--ct") // mutually exclusive 1066 IF_FEATURE_TAR_CREATE("c--tx:t--cx:x--ct") // mutually exclusive
1067 IF_NOT_FEATURE_TAR_CREATE("t--x:x--t") // mutually exclusive 1067 IF_NOT_FEATURE_TAR_CREATE("t--x:x--t") // mutually exclusive
1068#if ENABLE_FEATURE_TAR_LONG_OPTIONS 1068#if ENABLE_FEATURE_TAR_LONG_OPTIONS
1069 ":\xf9+" // --strip-components=NUM 1069 ":\xf8+" // --strip-components=NUM
1070#endif 1070#endif
1071 LONGOPTS 1071 LONGOPTS
1072 , &base_dir // -C dir 1072 , &base_dir // -C dir
diff --git a/coreutils/chgrp.c b/coreutils/chgrp.c
index ae216aa3f..4da43c45e 100644
--- a/coreutils/chgrp.c
+++ b/coreutils/chgrp.c
@@ -21,15 +21,15 @@
21/* http://www.opengroup.org/onlinepubs/007904975/utilities/chgrp.html */ 21/* http://www.opengroup.org/onlinepubs/007904975/utilities/chgrp.html */
22 22
23//usage:#define chgrp_trivial_usage 23//usage:#define chgrp_trivial_usage
24//usage: "[-RhLHP"IF_DESKTOP("cvf")"]... GROUP FILE..." 24//usage: "[-Rh"IF_DESKTOP("LHPcvf")"]... GROUP FILE..."
25//usage:#define chgrp_full_usage "\n\n" 25//usage:#define chgrp_full_usage "\n\n"
26//usage: "Change the group membership of each FILE to GROUP\n" 26//usage: "Change the group membership of each FILE to GROUP\n"
27//usage: "\n -R Recurse" 27//usage: "\n -R Recurse"
28//usage: "\n -h Affect symlinks instead of symlink targets" 28//usage: "\n -h Affect symlinks instead of symlink targets"
29//usage: IF_DESKTOP(
29//usage: "\n -L Traverse all symlinks to directories" 30//usage: "\n -L Traverse all symlinks to directories"
30//usage: "\n -H Traverse symlinks on command line only" 31//usage: "\n -H Traverse symlinks on command line only"
31//usage: "\n -P Don't traverse symlinks (default)" 32//usage: "\n -P Don't traverse symlinks (default)"
32//usage: IF_DESKTOP(
33//usage: "\n -c List changed files" 33//usage: "\n -c List changed files"
34//usage: "\n -v Verbose" 34//usage: "\n -v Verbose"
35//usage: "\n -f Hide errors" 35//usage: "\n -f Hide errors"
diff --git a/coreutils/date.c b/coreutils/date.c
index feb400430..b9b7fd2cb 100644
--- a/coreutils/date.c
+++ b/coreutils/date.c
@@ -33,10 +33,9 @@
33//config: Enable option (-I) to output an ISO-8601 compliant 33//config: Enable option (-I) to output an ISO-8601 compliant
34//config: date/time string. 34//config: date/time string.
35//config: 35//config:
36//config:# defaults to "no": stat's nanosecond field is a bit non-portable
37//config:config FEATURE_DATE_NANO 36//config:config FEATURE_DATE_NANO
38//config: bool "Support %[num]N nanosecond format specifier" 37//config: bool "Support %[num]N nanosecond format specifier"
39//config: default n # syscall(__NR_clock_gettime) 38//config: default n # stat's nanosecond field is a bit non-portable
40//config: depends on DATE 39//config: depends on DATE
41//config: select PLATFORM_LINUX 40//config: select PLATFORM_LINUX
42//config: help 41//config: help
@@ -272,13 +271,14 @@ int date_main(int argc UNUSED_PARAM, char **argv)
272#endif 271#endif
273 } else { 272 } else {
274#if ENABLE_FEATURE_DATE_NANO 273#if ENABLE_FEATURE_DATE_NANO
275 /* libc has incredibly messy way of doing this, 274 clock_gettime(CLOCK_REALTIME, &ts);
276 * typically requiring -lrt. We just skip all this mess */
277 syscall(__NR_clock_gettime, CLOCK_REALTIME, &ts);
278#else 275#else
279 time(&ts.tv_sec); 276 time(&ts.tv_sec);
280#endif 277#endif
281 } 278 }
279#if !ENABLE_FEATURE_DATE_NANO
280 ts.tv_nsec = 0;
281#endif
282 localtime_r(&ts.tv_sec, &tm_time); 282 localtime_r(&ts.tv_sec, &tm_time);
283 283
284 /* If date string is given, update tm_time, and maybe set date */ 284 /* If date string is given, update tm_time, and maybe set date */
@@ -301,9 +301,10 @@ int date_main(int argc UNUSED_PARAM, char **argv)
301 if (date_str[0] != '@') 301 if (date_str[0] != '@')
302 tm_time.tm_isdst = -1; 302 tm_time.tm_isdst = -1;
303 ts.tv_sec = validate_tm_time(date_str, &tm_time); 303 ts.tv_sec = validate_tm_time(date_str, &tm_time);
304 ts.tv_nsec = 0;
304 305
305 /* if setting time, set it */ 306 /* if setting time, set it */
306 if ((opt & OPT_SET) && stime(&ts.tv_sec) < 0) { 307 if ((opt & OPT_SET) && clock_settime(CLOCK_REALTIME, &ts) < 0) {
307 bb_simple_perror_msg("can't set date"); 308 bb_simple_perror_msg("can't set date");
308 } 309 }
309 } 310 }
diff --git a/coreutils/ln.c b/coreutils/ln.c
index ea2d10eab..5591e8335 100644
--- a/coreutils/ln.c
+++ b/coreutils/ln.c
@@ -41,7 +41,6 @@
41 41
42/* This is a NOEXEC applet. Be very careful! */ 42/* This is a NOEXEC applet. Be very careful! */
43 43
44
45#define LN_SYMLINK (1 << 0) 44#define LN_SYMLINK (1 << 0)
46#define LN_FORCE (1 << 1) 45#define LN_FORCE (1 << 1)
47#define LN_NODEREFERENCE (1 << 2) 46#define LN_NODEREFERENCE (1 << 2)
@@ -63,7 +62,22 @@ int ln_main(int argc, char **argv)
63 int (*link_func)(const char *, const char *); 62 int (*link_func)(const char *, const char *);
64 63
65 opts = getopt32(argv, "^" "sfnbS:vT" "\0" "-1", &suffix); 64 opts = getopt32(argv, "^" "sfnbS:vT" "\0" "-1", &suffix);
66 65/*
66 -s, --symbolic make symbolic links instead of hard links
67 -f, --force remove existing destination files
68 -n, --no-dereference treat LINK_NAME as a normal file if it is a symbolic link to a directory
69 -b like --backup but does not accept an argument
70 --backup[=CONTROL] make a backup of each existing destination file
71 -S, --suffix=SUFFIX override the usual backup suffix
72 -v, --verbose
73 -T, --no-target-directory
74 -d, -F, --directory allow the superuser to attempt to hard link directories
75 -i, --interactive prompt whether to remove destinations
76 -L, --logical dereference TARGETs that are symbolic links
77 -P, --physical make hard links directly to symbolic links
78 -r, --relative create symbolic links relative to link location
79 -t, --target-directory=DIRECTORY specify the DIRECTORY in which to create the links
80 */
67 last = argv[argc - 1]; 81 last = argv[argc - 1];
68 argv += optind; 82 argv += optind;
69 argc -= optind; 83 argc -= optind;
@@ -86,8 +100,11 @@ int ln_main(int argc, char **argv)
86 src = last; 100 src = last;
87 101
88 if (is_directory(src, 102 if (is_directory(src,
89 (opts & LN_NODEREFERENCE) ^ LN_NODEREFERENCE 103 /*followlinks:*/ !(opts & (LN_NODEREFERENCE|LN_LINKFILE))
90 ) 104 /* Why LN_LINKFILE does not follow links:
105 * -T/--no-target-directory implies -n/--no-dereference
106 */
107 )
91 ) { 108 ) {
92 if (opts & LN_LINKFILE) { 109 if (opts & LN_LINKFILE) {
93 bb_error_msg_and_die("'%s' is a directory", src); 110 bb_error_msg_and_die("'%s' is a directory", src);
diff --git a/coreutils/tee.c b/coreutils/tee.c
index fe5694331..e67296d43 100644
--- a/coreutils/tee.c
+++ b/coreutils/tee.c
@@ -39,6 +39,19 @@
39//usage: "$ cat /tmp/foo\n" 39//usage: "$ cat /tmp/foo\n"
40//usage: "Hello\n" 40//usage: "Hello\n"
41 41
42// Bare "tee" with no below options does not install SIGPIPE handler - just dies on it.
43// TODO:
44// --output-error[=MODE]
45// 'warn' diagnose errors writing to any output
46// 'warn-nopipe' diagnose errors writing to any output not a pipe
47// 'exit' exit on error writing to any output
48// 'exit-nopipe' exit on error writing to any output not a pipe
49// ^^^ all of these should set SIGPIPE to SIG_IGN.
50// Because "exit" mode should print error message and exit1(1) - not die on SIGPIPE.
51// "exit-nopipe" does not exit on EPIPE and does not set exitcode to 1 too.
52// -p diagnose errors writing to non pipes
53// ^^^^ this should set SIGPIPE to SIG_IGN. EPIPE is ignored (same as "warn-nopipe")
54
42#include "libbb.h" 55#include "libbb.h"
43#include "common_bufsiz.h" 56#include "common_bufsiz.h"
44 57
@@ -66,12 +79,12 @@ int tee_main(int argc, char **argv)
66 mode += (retval & 2); /* Since 'a' is the 2nd option... */ 79 mode += (retval & 2); /* Since 'a' is the 2nd option... */
67 80
68 if (retval & 1) { 81 if (retval & 1) {
69 signal(SIGINT, SIG_IGN); /* TODO - switch to sigaction. (why?) */ 82 signal(SIGINT, SIG_IGN);
70 } 83 }
71 retval = EXIT_SUCCESS; 84 retval = EXIT_SUCCESS;
72 /* gnu tee ignores SIGPIPE in case one of the output files is a pipe 85 /* if (opt_p || opt_output_error)
73 * that doesn't consume all its input. Good idea... */ 86 signal(SIGPIPE, SIG_IGN);
74 signal(SIGPIPE, SIG_IGN); 87 */
75 88
76 /* Allocate an array of FILE *'s, with one extra for a sentinel. */ 89 /* Allocate an array of FILE *'s, with one extra for a sentinel. */
77 fp = files = xzalloc(sizeof(FILE *) * (argc + 2)); 90 fp = files = xzalloc(sizeof(FILE *) * (argc + 2));
@@ -79,6 +92,7 @@ int tee_main(int argc, char **argv)
79 92
80 files[0] = stdout; 93 files[0] = stdout;
81 goto GOT_NEW_FILE; 94 goto GOT_NEW_FILE;
95
82 do { 96 do {
83 *fp = stdout; 97 *fp = stdout;
84 if (NOT_LONE_DASH(*argv)) { 98 if (NOT_LONE_DASH(*argv)) {
@@ -102,6 +116,7 @@ int tee_main(int argc, char **argv)
102 fp = files; 116 fp = files;
103 do 117 do
104 fwrite(buf, 1, c, *fp); 118 fwrite(buf, 1, c, *fp);
119 /* if (opt_p && fwrite() != c && !EPIPE) bb_error_msg("..."); */
105 while (*++fp); 120 while (*++fp);
106 } 121 }
107 if (c < 0) { /* Make sure read errors are signaled. */ 122 if (c < 0) { /* Make sure read errors are signaled. */
@@ -113,6 +128,7 @@ int tee_main(int argc, char **argv)
113 fp = files; 128 fp = files;
114 do 129 do
115 putc(c, *fp); 130 putc(c, *fp);
131 /* if (opt_p && putc() == EOF && !EPIPE) bb_error_msg("..."); */
116 while (*++fp); 132 while (*++fp);
117 } 133 }
118#endif 134#endif
diff --git a/coreutils/test.c b/coreutils/test.c
index 868ffbecb..a08986130 100644
--- a/coreutils/test.c
+++ b/coreutils/test.c
@@ -411,7 +411,7 @@ extern struct test_statics *const test_ptr_to_statics;
411#define leaving (S.leaving ) 411#define leaving (S.leaving )
412 412
413#define INIT_S() do { \ 413#define INIT_S() do { \
414 (*(struct test_statics**)&test_ptr_to_statics) = xzalloc(sizeof(S)); \ 414 (*(struct test_statics**)not_const_pp(&test_ptr_to_statics)) = xzalloc(sizeof(S)); \
415 barrier(); \ 415 barrier(); \
416} while (0) 416} while (0)
417#define DEINIT_S() do { \ 417#define DEINIT_S() do { \
diff --git a/editors/vi.c b/editors/vi.c
index 4676db2b2..948b95ae6 100644
--- a/editors/vi.c
+++ b/editors/vi.c
@@ -3556,7 +3556,7 @@ static void do_cmd(int c)
3556 } while (--cmdcnt > 0); 3556 } while (--cmdcnt > 0);
3557 break; 3557 break;
3558 case '{': // {- move backward paragraph 3558 case '{': // {- move backward paragraph
3559 q = char_search(dot, "\n\n", (BACK << 1) | FULL); 3559 q = char_search(dot, "\n\n", ((unsigned)BACK << 1) | FULL);
3560 if (q != NULL) { // found blank line 3560 if (q != NULL) { // found blank line
3561 dot = next_line(q); // move to next blank line 3561 dot = next_line(q); // move to next blank line
3562 } 3562 }
diff --git a/examples/inittab b/examples/inittab
index 01ceaef25..4761aa19c 100644
--- a/examples/inittab
+++ b/examples/inittab
@@ -19,30 +19,43 @@
19# 19#
20# <runlevels>: The runlevels field is completely ignored. 20# <runlevels>: The runlevels field is completely ignored.
21# 21#
22# <action>: Valid actions include: sysinit, respawn, askfirst, wait, once, 22# <action>: Valid actions include: sysinit, wait, once, respawn, askfirst,
23# restart, ctrlaltdel, and shutdown. 23# shutdown, restart and ctrlaltdel.
24# 24#
25# Note: askfirst acts just like respawn, but before running the specified 25# sysinit actions are started first, and init waits for them to complete.
26# process it displays the line "Please press Enter to activate this 26# wait actions are started next, and init waits for them to complete.
27# console." and then waits for the user to press enter before starting 27# once actions are started next (and not waited for).
28# the specified process.
29# 28#
30# Note: unrecognized actions (like initdefault) will cause init to emit 29# askfirst and respawn are started next.
31# an error message, and then go along with its business. 30# For askfirst, before running the specified process, init displays
31# the line "Please press Enter to activate this console"
32# and then waits for the user to press enter before starting it.
33#
34# shutdown actions are run on halt/reboot/poweroff, or on SIGQUIT.
35# Then the machine is halted/rebooted/powered off, or for SIGQUIT,
36# restart action is exec'ed (init process is replaced by that process).
37# If no restart action specified, SIGQUIT has no effect.
38#
39# ctrlaltdel actions are run when SIGINT is received
40# (this might be initiated by Ctrl-Alt-Del key combination).
41# After they complete, normal processing of askfirst / respawn resumes.
42#
43# Note: unrecognized actions (like initdefault) will cause init to emit
44# an error message, and then go along with its business.
32# 45#
33# <process>: Specifies the process to be executed and it's command line. 46# <process>: Specifies the process to be executed and it's command line.
34# 47#
35# Note: BusyBox init works just fine without an inittab. If no inittab is 48# Note: BusyBox init works just fine without an inittab. If no inittab is
36# found, it has the following default behavior: 49# found, it has the following default behavior:
37# ::sysinit:/etc/init.d/rcS 50# ::sysinit:/etc/init.d/rcS
38# ::askfirst:/bin/sh 51# ::askfirst:/bin/sh
39# ::ctrlaltdel:/sbin/reboot 52# ::ctrlaltdel:/sbin/reboot
40# ::shutdown:/sbin/swapoff -a 53# ::shutdown:/sbin/swapoff -a
41# ::shutdown:/bin/umount -a -r 54# ::shutdown:/bin/umount -a -r
42# ::restart:/sbin/init 55# ::restart:/sbin/init
43# tty2::askfirst:/bin/sh 56# tty2::askfirst:/bin/sh
44# tty3::askfirst:/bin/sh 57# tty3::askfirst:/bin/sh
45# tty4::askfirst:/bin/sh 58# tty4::askfirst:/bin/sh
46# 59#
47# Boot-time system configuration/initialization script. 60# Boot-time system configuration/initialization script.
48# This is run first except when booting in single-user mode. 61# This is run first except when booting in single-user mode.
diff --git a/examples/udhcp/simple.script b/examples/udhcp/simple.script
index 53974e6d6..6658fbeef 100755
--- a/examples/udhcp/simple.script
+++ b/examples/udhcp/simple.script
@@ -16,9 +16,10 @@ BROADCAST="broadcast +"
16 16
17case "$1" in 17case "$1" in
18 deconfig) 18 deconfig)
19 echo "Setting IP address 0.0.0.0 on $interface" 19 echo "Clearing IP addresses on $interface, upping it"
20 if command -v ip >/dev/null; then 20 if command -v ip >/dev/null; then
21 ip addr flush dev $interface 21 ip addr flush dev $interface
22 ip link set dev $interface up
22 else 23 else
23 ifconfig $interface 0.0.0.0 24 ifconfig $interface 0.0.0.0
24 fi 25 fi
@@ -52,7 +53,6 @@ case "$1" in
52 done 53 done
53 fi 54 fi
54 55
55 echo "Recreating $RESOLV_CONF"
56 # If the file is a symlink somewhere (like /etc/resolv.conf 56 # If the file is a symlink somewhere (like /etc/resolv.conf
57 # pointing to /run/resolv.conf), make sure things work. 57 # pointing to /run/resolv.conf), make sure things work.
58 if test -L "$RESOLV_CONF"; then 58 if test -L "$RESOLV_CONF"; then
@@ -60,6 +60,7 @@ case "$1" in
60 test -e "$RESOLV_CONF" || touch "$RESOLV_CONF" 60 test -e "$RESOLV_CONF" || touch "$RESOLV_CONF"
61 fi 61 fi
62 realconf=$(readlink -f "$RESOLV_CONF" 2>/dev/null || echo "$RESOLV_CONF") 62 realconf=$(readlink -f "$RESOLV_CONF" 2>/dev/null || echo "$RESOLV_CONF")
63 echo "Recreating $realconf"
63 tmpfile="$realconf-$$" 64 tmpfile="$realconf-$$"
64 > "$tmpfile" 65 > "$tmpfile"
65 [ -n "$domain" ] && echo "search $domain" >> "$tmpfile" 66 [ -n "$domain" ] && echo "search $domain" >> "$tmpfile"
diff --git a/findutils/find.c b/findutils/find.c
index 66ad36283..121f8fd03 100644
--- a/findutils/find.c
+++ b/findutils/find.c
@@ -203,6 +203,14 @@
203//config: WARNING: This option can do much harm if used wrong. Busybox will not 203//config: WARNING: This option can do much harm if used wrong. Busybox will not
204//config: try to protect the user from doing stupid things. Use with care. 204//config: try to protect the user from doing stupid things. Use with care.
205//config: 205//config:
206//config:config FEATURE_FIND_EMPTY
207//config: bool "Enable -empty: match empty files or directories"
208//config: default y
209//config: depends on FIND
210//config: help
211//config: Support the 'find -empty' option to find empty regular files
212//config: or directories.
213//config:
206//config:config FEATURE_FIND_PATH 214//config:config FEATURE_FIND_PATH
207//config: bool "Enable -path: match pathname with shell pattern" 215//config: bool "Enable -path: match pathname with shell pattern"
208//config: default y 216//config: default y
@@ -315,6 +323,9 @@
315//usage: IF_FEATURE_FIND_CONTEXT( 323//usage: IF_FEATURE_FIND_CONTEXT(
316//usage: "\n -context CTX File has specified security context" 324//usage: "\n -context CTX File has specified security context"
317//usage: ) 325//usage: )
326//usage: IF_FEATURE_FIND_EMPTY(
327//usage: "\n -empty Match empty file/directory"
328//usage: )
318//usage: IF_FEATURE_FIND_PRUNE( 329//usage: IF_FEATURE_FIND_PRUNE(
319//usage: "\n -prune If current file is directory, don't descend into it" 330//usage: "\n -prune If current file is directory, don't descend into it"
320//usage: ) 331//usage: )
@@ -396,6 +407,7 @@ IF_FEATURE_FIND_PAREN( ACTS(paren, action ***subexpr;))
396IF_FEATURE_FIND_PRUNE( ACTS(prune)) 407IF_FEATURE_FIND_PRUNE( ACTS(prune))
397IF_FEATURE_FIND_QUIT( ACTS(quit)) 408IF_FEATURE_FIND_QUIT( ACTS(quit))
398IF_FEATURE_FIND_DELETE( ACTS(delete)) 409IF_FEATURE_FIND_DELETE( ACTS(delete))
410IF_FEATURE_FIND_EMPTY( ACTS(empty))
399IF_FEATURE_FIND_EXEC( ACTS(exec, 411IF_FEATURE_FIND_EXEC( ACTS(exec,
400 char **exec_argv; /* -exec ARGS */ 412 char **exec_argv; /* -exec ARGS */
401 unsigned *subst_count; 413 unsigned *subst_count;
@@ -824,6 +836,30 @@ ACTF(delete)
824 return TRUE; 836 return TRUE;
825} 837}
826#endif 838#endif
839#if ENABLE_FEATURE_FIND_EMPTY
840ACTF(empty)
841{
842 if (S_ISDIR(statbuf->st_mode)) {
843 DIR *dir;
844 struct dirent *dent;
845
846 dir = opendir(fileName);
847 if (!dir) {
848 bb_simple_perror_msg(fileName);
849 return FALSE;
850 }
851
852 while ((dent = readdir(dir)) != NULL
853 && DOT_OR_DOTDOT(dent->d_name)
854 ) {
855 continue;
856 }
857 closedir(dir);
858 return dent == NULL;
859 }
860 return S_ISREG(statbuf->st_mode) && statbuf->st_size == 0;
861}
862#endif
827#if ENABLE_FEATURE_FIND_CONTEXT 863#if ENABLE_FEATURE_FIND_CONTEXT
828ACTF(context) 864ACTF(context)
829{ 865{
@@ -989,6 +1025,7 @@ static action*** parse_params(char **argv)
989 IF_FEATURE_FIND_PRUNE( PARM_prune ,) 1025 IF_FEATURE_FIND_PRUNE( PARM_prune ,)
990 IF_FEATURE_FIND_QUIT( PARM_quit ,) 1026 IF_FEATURE_FIND_QUIT( PARM_quit ,)
991 IF_FEATURE_FIND_DELETE( PARM_delete ,) 1027 IF_FEATURE_FIND_DELETE( PARM_delete ,)
1028 IF_FEATURE_FIND_EMPTY( PARM_empty ,)
992 IF_FEATURE_FIND_EXEC( PARM_exec ,) 1029 IF_FEATURE_FIND_EXEC( PARM_exec ,)
993 IF_FEATURE_FIND_EXECUTABLE(PARM_executable,) 1030 IF_FEATURE_FIND_EXECUTABLE(PARM_executable,)
994 IF_FEATURE_FIND_PAREN( PARM_char_brace,) 1031 IF_FEATURE_FIND_PAREN( PARM_char_brace,)
@@ -1034,6 +1071,7 @@ static action*** parse_params(char **argv)
1034 IF_FEATURE_FIND_PRUNE( "-prune\0" ) 1071 IF_FEATURE_FIND_PRUNE( "-prune\0" )
1035 IF_FEATURE_FIND_QUIT( "-quit\0" ) 1072 IF_FEATURE_FIND_QUIT( "-quit\0" )
1036 IF_FEATURE_FIND_DELETE( "-delete\0" ) 1073 IF_FEATURE_FIND_DELETE( "-delete\0" )
1074 IF_FEATURE_FIND_EMPTY( "-empty\0" )
1037 IF_FEATURE_FIND_EXEC( "-exec\0" ) 1075 IF_FEATURE_FIND_EXEC( "-exec\0" )
1038 IF_FEATURE_FIND_EXECUTABLE("-executable\0") 1076 IF_FEATURE_FIND_EXECUTABLE("-executable\0")
1039 IF_FEATURE_FIND_PAREN( "(\0" ) 1077 IF_FEATURE_FIND_PAREN( "(\0" )
@@ -1203,6 +1241,12 @@ static action*** parse_params(char **argv)
1203 (void) ALLOC_ACTION(delete); 1241 (void) ALLOC_ACTION(delete);
1204 } 1242 }
1205#endif 1243#endif
1244#if ENABLE_FEATURE_FIND_EMPTY
1245 else if (parm == PARM_empty) {
1246 dbg("%d", __LINE__);
1247 (void) ALLOC_ACTION(empty);
1248 }
1249#endif
1206#if ENABLE_FEATURE_FIND_EXEC 1250#if ENABLE_FEATURE_FIND_EXEC
1207 else if (parm == PARM_exec) { 1251 else if (parm == PARM_exec) {
1208 int i; 1252 int i;
diff --git a/findutils/grep.c b/findutils/grep.c
index 2cbe7ea91..5b8644c36 100644
--- a/findutils/grep.c
+++ b/findutils/grep.c
@@ -60,7 +60,7 @@
60 60
61/* options */ 61/* options */
62//usage:#define grep_trivial_usage 62//usage:#define grep_trivial_usage
63//usage: "[-HhnlLoqvsriwFE" 63//usage: "[-HhnlLoqvsrRiwFE"
64//usage: IF_EXTRA_COMPAT("z") 64//usage: IF_EXTRA_COMPAT("z")
65//usage: "] [-m N] " 65//usage: "] [-m N] "
66//usage: IF_FEATURE_GREP_CONTEXT("[-A/B/C N] ") 66//usage: IF_FEATURE_GREP_CONTEXT("[-A/B/C N] ")
@@ -78,6 +78,7 @@
78//usage: "\n -v Select non-matching lines" 78//usage: "\n -v Select non-matching lines"
79//usage: "\n -s Suppress open and read errors" 79//usage: "\n -s Suppress open and read errors"
80//usage: "\n -r Recurse" 80//usage: "\n -r Recurse"
81//usage: "\n -R Recurse and dereference symlinks"
81//usage: "\n -i Ignore case" 82//usage: "\n -i Ignore case"
82//usage: "\n -w Match whole words only" 83//usage: "\n -w Match whole words only"
83//usage: "\n -x Match whole lines only" 84//usage: "\n -x Match whole lines only"
@@ -108,7 +109,7 @@
108 109
109/* -e,-f are lists; -m,-A,-B,-C have numeric param */ 110/* -e,-f are lists; -m,-A,-B,-C have numeric param */
110#define OPTSTR_GREP \ 111#define OPTSTR_GREP \
111 "lnqvscFiHhe:*f:*Lorm:+wx" \ 112 "lnqvscFiHhe:*f:*LorRm:+wx" \
112 IF_FEATURE_GREP_CONTEXT("A:+B:+C:+") \ 113 IF_FEATURE_GREP_CONTEXT("A:+B:+C:+") \
113 "E" \ 114 "E" \
114 IF_EXTRA_COMPAT("z") \ 115 IF_EXTRA_COMPAT("z") \
@@ -131,6 +132,7 @@ enum {
131 OPTBIT_L, /* list unmatched file names only */ 132 OPTBIT_L, /* list unmatched file names only */
132 OPTBIT_o, /* show only matching parts of lines */ 133 OPTBIT_o, /* show only matching parts of lines */
133 OPTBIT_r, /* recurse dirs */ 134 OPTBIT_r, /* recurse dirs */
135 OPTBIT_R, /* recurse dirs and symlinks to dirs */
134 OPTBIT_m, /* -m MAX_MATCHES */ 136 OPTBIT_m, /* -m MAX_MATCHES */
135 OPTBIT_w, /* -w whole word match */ 137 OPTBIT_w, /* -w whole word match */
136 OPTBIT_x, /* -x whole line match */ 138 OPTBIT_x, /* -x whole line match */
@@ -154,6 +156,7 @@ enum {
154 OPT_L = 1 << OPTBIT_L, 156 OPT_L = 1 << OPTBIT_L,
155 OPT_o = 1 << OPTBIT_o, 157 OPT_o = 1 << OPTBIT_o,
156 OPT_r = 1 << OPTBIT_r, 158 OPT_r = 1 << OPTBIT_r,
159 OPT_R = 1 << OPTBIT_R,
157 OPT_m = 1 << OPTBIT_m, 160 OPT_m = 1 << OPTBIT_m,
158 OPT_w = 1 << OPTBIT_w, 161 OPT_w = 1 << OPTBIT_w,
159 OPT_x = 1 << OPTBIT_x, 162 OPT_x = 1 << OPTBIT_x,
@@ -687,6 +690,7 @@ static int grep_dir(const char *dir)
687 int matched = 0; 690 int matched = 0;
688 recursive_action(dir, 691 recursive_action(dir,
689 /* recurse=yes */ ACTION_RECURSE | 692 /* recurse=yes */ ACTION_RECURSE |
693 /* followLinks=always */ ((option_mask32 & OPT_R) ? ACTION_FOLLOWLINKS : 0) |
690 /* followLinks=command line only */ ACTION_FOLLOWLINKS_L0 | 694 /* followLinks=command line only */ ACTION_FOLLOWLINKS_L0 |
691 /* depthFirst=yes */ ACTION_DEPTHFIRST, 695 /* depthFirst=yes */ ACTION_DEPTHFIRST,
692 /* fileAction= */ file_action_grep, 696 /* fileAction= */ file_action_grep,
@@ -827,7 +831,7 @@ int grep_main(int argc UNUSED_PARAM, char **argv)
827 if (!cur_file || LONE_DASH(cur_file)) { 831 if (!cur_file || LONE_DASH(cur_file)) {
828 cur_file = "(standard input)"; 832 cur_file = "(standard input)";
829 } else { 833 } else {
830 if (option_mask32 & OPT_r) { 834 if (option_mask32 & (OPT_r|OPT_R)) {
831 struct stat st; 835 struct stat st;
832 if (stat(cur_file, &st) == 0 && S_ISDIR(st.st_mode)) { 836 if (stat(cur_file, &st) == 0 && S_ISDIR(st.st_mode)) {
833 if (!(option_mask32 & OPT_h)) 837 if (!(option_mask32 & OPT_h))
diff --git a/include/libbb.h b/include/libbb.h
index 84811c4f2..0aa44eb37 100644
--- a/include/libbb.h
+++ b/include/libbb.h
@@ -2218,12 +2218,32 @@ struct globals;
2218 * Magic prevents ptr_to_globals from going into rodata. 2218 * Magic prevents ptr_to_globals from going into rodata.
2219 * If you want to assign a value, use SET_PTR_TO_GLOBALS(x) */ 2219 * If you want to assign a value, use SET_PTR_TO_GLOBALS(x) */
2220extern struct globals *const ptr_to_globals; 2220extern struct globals *const ptr_to_globals;
2221
2222#if defined(__clang_major__) && __clang_major__ >= 9
2223/* Clang/llvm drops assignment to "constant" storage. Silently.
2224 * Needs serious convincing to not eliminate the store.
2225 */
2226static ALWAYS_INLINE void* not_const_pp(const void *p)
2227{
2228 void *pp;
2229 __asm__ __volatile__(
2230 "# forget that p points to const"
2231 : /*outputs*/ "=r" (pp)
2232 : /*inputs*/ "0" (p)
2233 );
2234 return pp;
2235}
2236#else
2237static ALWAYS_INLINE void* not_const_pp(const void *p) { return (void*)p; }
2238#endif
2239
2221/* At least gcc 3.4.6 on mipsel system needs optimization barrier */ 2240/* At least gcc 3.4.6 on mipsel system needs optimization barrier */
2222#define barrier() __asm__ __volatile__("":::"memory") 2241#define barrier() __asm__ __volatile__("":::"memory")
2223#define SET_PTR_TO_GLOBALS(x) do { \ 2242#define SET_PTR_TO_GLOBALS(x) do { \
2224 (*(struct globals**)&ptr_to_globals) = (void*)(x); \ 2243 (*(struct globals**)not_const_pp(&ptr_to_globals)) = (void*)(x); \
2225 barrier(); \ 2244 barrier(); \
2226} while (0) 2245} while (0)
2246
2227#define FREE_PTR_TO_GLOBALS() do { \ 2247#define FREE_PTR_TO_GLOBALS() do { \
2228 if (ENABLE_FEATURE_CLEAN_UP) { \ 2248 if (ENABLE_FEATURE_CLEAN_UP) { \
2229 free(ptr_to_globals); \ 2249 free(ptr_to_globals); \
diff --git a/init/init.c b/init/init.c
index 0f3c5fa4d..28775a65c 100644
--- a/init/init.c
+++ b/init/init.c
@@ -145,13 +145,6 @@
145# include <sys/ucontext.h> 145# include <sys/ucontext.h>
146#endif 146#endif
147 147
148/* Used only for sanitizing purposes in set_sane_term() below. On systems where
149 * the baud rate is stored in a separate field, we can safely disable them. */
150#ifndef CBAUD
151# define CBAUD 0
152# define CBAUDEX 0
153#endif
154
155/* Was a CONFIG_xxx option. A lot of people were building 148/* Was a CONFIG_xxx option. A lot of people were building
156 * not fully functional init by switching it on! */ 149 * not fully functional init by switching it on! */
157#define DEBUG_INIT 0 150#define DEBUG_INIT 0
@@ -217,6 +210,8 @@ struct globals {
217#if !ENABLE_FEATURE_INIT_SYSLOG 210#if !ENABLE_FEATURE_INIT_SYSLOG
218 const char *log_console; 211 const char *log_console;
219#endif 212#endif
213 sigset_t delayed_sigset;
214 struct timespec zero_ts;
220} FIX_ALIASING; 215} FIX_ALIASING;
221#define G (*(struct globals*)bb_common_bufsiz1) 216#define G (*(struct globals*)bb_common_bufsiz1)
222#define INIT_G() do { \ 217#define INIT_G() do { \
@@ -347,7 +342,8 @@ static void set_sane_term(void)
347{ 342{
348 struct termios tty; 343 struct termios tty;
349 344
350 tcgetattr(STDIN_FILENO, &tty); 345 if (tcgetattr(STDIN_FILENO, &tty) != 0)
346 return;
351 347
352 /* set control chars */ 348 /* set control chars */
353 tty.c_cc[VINTR] = 3; /* C-c */ 349 tty.c_cc[VINTR] = 3; /* C-c */
@@ -365,10 +361,15 @@ static void set_sane_term(void)
365#endif 361#endif
366 362
367 /* Make it be sane */ 363 /* Make it be sane */
364/* On systems where the baud rate is stored in a separate field, we can safely disable these. */
365#ifndef CBAUD
366# define CBAUD 0
367# define CBAUDEX 0
368#endif
369/* Added CRTSCTS to fix Debian bug 528560 */
368#ifndef CRTSCTS 370#ifndef CRTSCTS
369# define CRTSCTS 0 371# define CRTSCTS 0
370#endif 372#endif
371 /* added CRTSCTS to fix Debian bug 528560 */
372 tty.c_cflag &= CBAUD | CBAUDEX | CSIZE | CSTOPB | PARENB | PARODD | CRTSCTS; 373 tty.c_cflag &= CBAUD | CBAUDEX | CSIZE | CSTOPB | PARENB | PARODD | CRTSCTS;
373 tty.c_cflag |= CREAD | HUPCL | CLOCAL; 374 tty.c_cflag |= CREAD | HUPCL | CLOCAL;
374 375
@@ -412,14 +413,8 @@ static int open_stdio_to_tty(const char* tty_name)
412static void reset_sighandlers_and_unblock_sigs(void) 413static void reset_sighandlers_and_unblock_sigs(void)
413{ 414{
414 bb_signals(0 415 bb_signals(0
415 + (1 << SIGUSR1) 416 | (1 << SIGTSTP)
416 + (1 << SIGUSR2) 417 | (1 << SIGSTOP)
417 + (1 << SIGTERM)
418 + (1 << SIGQUIT)
419 + (1 << SIGINT)
420 + (1 << SIGHUP)
421 + (1 << SIGTSTP)
422 + (1 << SIGSTOP)
423 , SIG_DFL); 418 , SIG_DFL);
424 sigprocmask_allsigs(SIG_UNBLOCK); 419 sigprocmask_allsigs(SIG_UNBLOCK);
425} 420}
@@ -485,16 +480,13 @@ static pid_t run(const struct init_action *a)
485{ 480{
486 pid_t pid; 481 pid_t pid;
487 482
488 /* Careful: don't be affected by a signal in vforked child */
489 sigprocmask_allsigs(SIG_BLOCK);
490 if (BB_MMU && (a->action_type & ASKFIRST)) 483 if (BB_MMU && (a->action_type & ASKFIRST))
491 pid = fork(); 484 pid = fork();
492 else 485 else
493 pid = vfork(); 486 pid = vfork();
494 if (pid < 0)
495 message(L_LOG | L_CONSOLE, "can't fork");
496 if (pid) { 487 if (pid) {
497 sigprocmask_allsigs(SIG_UNBLOCK); 488 if (pid < 0)
489 message(L_LOG | L_CONSOLE, "can't fork");
498 return pid; /* Parent or error */ 490 return pid; /* Parent or error */
499 } 491 }
500 492
@@ -588,9 +580,11 @@ static void waitfor(pid_t pid)
588 while (1) { 580 while (1) {
589 pid_t wpid = wait(NULL); 581 pid_t wpid = wait(NULL);
590 mark_terminated(wpid); 582 mark_terminated(wpid);
591 /* Unsafe. SIGTSTP handler might have wait'ed it already */ 583 if (wpid == pid) /* this was the process we waited for */
592 /*if (wpid == pid) break;*/ 584 break;
593 /* More reliable: */ 585 /* The above is not reliable enough: SIGTSTP handler might have
586 * wait'ed it already. Double check, exit if process is gone:
587 */
594 if (kill(pid, 0)) 588 if (kill(pid, 0))
595 break; 589 break;
596 } 590 }
@@ -799,23 +793,17 @@ static void run_shutdown_and_kill_processes(void)
799 * Delayed handlers just set a flag variable. The variable is checked 793 * Delayed handlers just set a flag variable. The variable is checked
800 * in the main loop and acted upon. 794 * in the main loop and acted upon.
801 * 795 *
802 * halt/poweroff/reboot and restart have immediate handlers.
803 * They only traverse linked list of struct action's, never modify it,
804 * this should be safe to do even in signal handler. Also they
805 * never return.
806 *
807 * SIGSTOP and SIGTSTP have immediate handlers. They just wait 796 * SIGSTOP and SIGTSTP have immediate handlers. They just wait
808 * for SIGCONT to happen. 797 * for SIGCONT to happen.
809 * 798 *
799 * halt/poweroff/reboot and restart have delayed handlers.
800 *
810 * SIGHUP has a delayed handler, because modifying linked list 801 * SIGHUP has a delayed handler, because modifying linked list
811 * of struct action's from a signal handler while it is manipulated 802 * of struct action's from a signal handler while it is manipulated
812 * by the program may be disastrous. 803 * by the program may be disastrous.
813 * 804 *
814 * Ctrl-Alt-Del has a delayed handler. Not a must, but allowing 805 * Ctrl-Alt-Del has a delayed handler. Not a must, but allowing
815 * it to happen even somewhere inside "sysinit" would be a bit awkward. 806 * it to happen even somewhere inside "sysinit" would be a bit awkward.
816 *
817 * There is a tiny probability that SIGHUP and Ctrl-Alt-Del will collide
818 * and only one will be remembered and acted upon.
819 */ 807 */
820 808
821/* The SIGPWR/SIGUSR[12]/SIGTERM handler */ 809/* The SIGPWR/SIGUSR[12]/SIGTERM handler */
@@ -899,11 +887,9 @@ static void exec_restart_action(void)
899 */ 887 */
900static void stop_handler(int sig UNUSED_PARAM) 888static void stop_handler(int sig UNUSED_PARAM)
901{ 889{
902 smallint saved_bb_got_signal; 890 int saved_errno = errno;
903 int saved_errno;
904 891
905 saved_bb_got_signal = bb_got_signal; 892 bb_got_signal = 0;
906 saved_errno = errno;
907 signal(SIGCONT, record_signo); 893 signal(SIGCONT, record_signo);
908 894
909 while (1) { 895 while (1) {
@@ -917,12 +903,12 @@ static void stop_handler(int sig UNUSED_PARAM)
917 */ 903 */
918 wpid = wait_any_nohang(NULL); 904 wpid = wait_any_nohang(NULL);
919 mark_terminated(wpid); 905 mark_terminated(wpid);
920 sleep(1); 906 if (wpid <= 0) /* no processes exited? sleep a bit */
907 sleep(1);
921 } 908 }
922 909
923 signal(SIGCONT, SIG_DFL); 910 signal(SIGCONT, SIG_DFL);
924 errno = saved_errno; 911 errno = saved_errno;
925 bb_got_signal = saved_bb_got_signal;
926} 912}
927 913
928#if ENABLE_FEATURE_USE_INITTAB 914#if ENABLE_FEATURE_USE_INITTAB
@@ -987,43 +973,39 @@ static void reload_inittab(void)
987} 973}
988#endif 974#endif
989 975
990static int check_delayed_sigs(void) 976static void check_delayed_sigs(struct timespec *ts)
991{ 977{
992 int sigs_seen = 0; 978 int sig = sigtimedwait(&G.delayed_sigset, /* siginfo_t */ NULL, ts);
979 if (sig <= 0)
980 return;
993 981
994 while (1) { 982 /* The signal "sig" was caught */
995 smallint sig = bb_got_signal;
996 983
997 if (!sig)
998 return sigs_seen;
999 bb_got_signal = 0;
1000 sigs_seen = 1;
1001#if ENABLE_FEATURE_USE_INITTAB 984#if ENABLE_FEATURE_USE_INITTAB
1002 if (sig == SIGHUP) 985 if (sig == SIGHUP)
1003 reload_inittab(); 986 reload_inittab();
1004#endif 987#endif
1005 if (sig == SIGINT) 988 if (sig == SIGINT)
1006 run_actions(CTRLALTDEL); 989 run_actions(CTRLALTDEL);
1007 if (sig == SIGQUIT) { 990 if (sig == SIGQUIT) {
1008 exec_restart_action(); 991 exec_restart_action();
1009 /* returns only if no restart action defined */ 992 /* returns only if no restart action defined */
1010 } 993 }
1011 if ((1 << sig) & (0 994 if ((1 << sig) & (0
1012#ifdef SIGPWR 995#ifdef SIGPWR
1013 + (1 << SIGPWR) 996 | (1 << SIGPWR)
1014#endif 997#endif
1015 + (1 << SIGUSR1) 998 | (1 << SIGUSR1)
1016 + (1 << SIGUSR2) 999 | (1 << SIGUSR2)
1017 + (1 << SIGTERM) 1000 | (1 << SIGTERM)
1018 )) { 1001 )) {
1019 halt_reboot_pwoff(sig); 1002 halt_reboot_pwoff(sig);
1020 }
1021 } 1003 }
1004 /* if (sig == SIGCHLD) do nothing */
1022} 1005}
1023 1006
1024#if DEBUG_SEGV_HANDLER 1007#if DEBUG_SEGV_HANDLER
1025static 1008static void handle_sigsegv(int sig, siginfo_t *info, void *ucontext)
1026void handle_sigsegv(int sig, siginfo_t *info, void *ucontext)
1027{ 1009{
1028 long ip; 1010 long ip;
1029 ucontext_t *uc; 1011 ucontext_t *uc;
@@ -1050,50 +1032,62 @@ void handle_sigsegv(int sig, siginfo_t *info, void *ucontext)
1050 1032
1051static void sleep_much(void) 1033static void sleep_much(void)
1052{ 1034{
1053 sleep(30 * 24*60*60); 1035 sleep(30 * 24*60*60);
1054} 1036}
1055 1037
1056int init_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 1038int init_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
1057int init_main(int argc UNUSED_PARAM, char **argv) 1039int init_main(int argc UNUSED_PARAM, char **argv)
1058{ 1040{
1041 struct sigaction sa;
1042
1059 INIT_G(); 1043 INIT_G();
1060 1044
1045 /* Some users send poweroff signals to init VERY early.
1046 * To handle this, mask signals early.
1047 */
1048 /* sigemptyset(&G.delayed_sigset); - done by INIT_G() */
1049 sigaddset(&G.delayed_sigset, SIGINT); /* Ctrl-Alt-Del */
1050 sigaddset(&G.delayed_sigset, SIGQUIT); /* re-exec another init */
1051#ifdef SIGPWR
1052 sigaddset(&G.delayed_sigset, SIGPWR); /* halt */
1053#endif
1054 sigaddset(&G.delayed_sigset, SIGUSR1); /* halt */
1055 sigaddset(&G.delayed_sigset, SIGTERM); /* reboot */
1056 sigaddset(&G.delayed_sigset, SIGUSR2); /* poweroff */
1057#if ENABLE_FEATURE_USE_INITTAB
1058 sigaddset(&G.delayed_sigset, SIGHUP); /* reread /etc/inittab */
1059#endif
1060 sigaddset(&G.delayed_sigset, SIGCHLD); /* make sigtimedwait() exit on SIGCHLD */
1061 sigprocmask(SIG_BLOCK, &G.delayed_sigset, NULL);
1062
1063#if DEBUG_SEGV_HANDLER
1064 memset(&sa, 0, sizeof(sa));
1065 sa.sa_sigaction = handle_sigsegv;
1066 sa.sa_flags = SA_SIGINFO;
1067 sigaction_set(SIGSEGV, &sa);
1068 sigaction_set(SIGILL, &sa);
1069 sigaction_set(SIGFPE, &sa);
1070 sigaction_set(SIGBUS, &sa);
1071#endif
1072
1061 if (argv[1] && strcmp(argv[1], "-q") == 0) { 1073 if (argv[1] && strcmp(argv[1], "-q") == 0) {
1062 return kill(1, SIGHUP); 1074 return kill(1, SIGHUP);
1063 } 1075 }
1064 1076
1065#if DEBUG_SEGV_HANDLER 1077#if !DEBUG_INIT
1066 { 1078 /* Expect to be invoked as init with PID=1 or be invoked as linuxrc */
1067 struct sigaction sa; 1079 if (getpid() != 1
1068 memset(&sa, 0, sizeof(sa)); 1080 && (!ENABLE_LINUXRC || applet_name[0] != 'l') /* not linuxrc? */
1069 sa.sa_sigaction = handle_sigsegv; 1081 ) {
1070 sa.sa_flags = SA_SIGINFO; 1082 bb_simple_error_msg_and_die("must be run as PID 1");
1071 sigaction(SIGSEGV, &sa, NULL);
1072 sigaction(SIGILL, &sa, NULL);
1073 sigaction(SIGFPE, &sa, NULL);
1074 sigaction(SIGBUS, &sa, NULL);
1075 } 1083 }
1076#endif
1077
1078 if (!DEBUG_INIT) {
1079 /* Some users send poweroff signals to init VERY early.
1080 * To handle this, mask signals early,
1081 * and unmask them only after signal handlers are installed.
1082 */
1083 sigprocmask_allsigs(SIG_BLOCK);
1084 1084
1085 /* Expect to be invoked as init with PID=1 or be invoked as linuxrc */ 1085# ifdef RB_DISABLE_CAD
1086 if (getpid() != 1 1086 /* Turn off rebooting via CTL-ALT-DEL - we get a
1087 && (!ENABLE_LINUXRC || applet_name[0] != 'l') /* not linuxrc? */ 1087 * SIGINT on CAD so we can shut things down gracefully... */
1088 ) { 1088 reboot(RB_DISABLE_CAD); /* misnomer */
1089 bb_simple_error_msg_and_die("must be run as PID 1"); 1089# endif
1090 }
1091#ifdef RB_DISABLE_CAD
1092 /* Turn off rebooting via CTL-ALT-DEL - we get a
1093 * SIGINT on CAD so we can shut things down gracefully... */
1094 reboot(RB_DISABLE_CAD); /* misnomer */
1095#endif 1090#endif
1096 }
1097 1091
1098 /* If, say, xmalloc would ever die, we don't want to oops kernel 1092 /* If, say, xmalloc would ever die, we don't want to oops kernel
1099 * by exiting. 1093 * by exiting.
@@ -1157,106 +1151,65 @@ int init_main(int argc UNUSED_PARAM, char **argv)
1157 } 1151 }
1158#endif 1152#endif
1159 1153
1160 if (ENABLE_FEATURE_INIT_MODIFY_CMDLINE) { 1154#if ENABLE_FEATURE_INIT_MODIFY_CMDLINE
1161 /* Make the command line just say "init" - that's all, nothing else */ 1155 /* Make the command line just say "init" - that's all, nothing else */
1162 strncpy(argv[0], "init", strlen(argv[0])); 1156 strncpy(argv[0], "init", strlen(argv[0]));
1163 /* Wipe argv[1]-argv[N] so they don't clutter the ps listing */ 1157 /* Wipe argv[1]-argv[N] so they don't clutter the ps listing */
1164 while (*++argv) 1158 while (*++argv)
1165 nuke_str(*argv); 1159 nuke_str(*argv);
1166 }
1167
1168 /* Set up signal handlers */
1169 if (!DEBUG_INIT) {
1170 struct sigaction sa;
1171
1172 /* Stop handler must allow only SIGCONT inside itself */
1173 memset(&sa, 0, sizeof(sa));
1174 sigfillset(&sa.sa_mask);
1175 sigdelset(&sa.sa_mask, SIGCONT);
1176 sa.sa_handler = stop_handler;
1177 /* NB: sa_flags doesn't have SA_RESTART.
1178 * It must be able to interrupt wait().
1179 */
1180 sigaction_set(SIGTSTP, &sa); /* pause */
1181 /* Does not work as intended, at least in 2.6.20.
1182 * SIGSTOP is simply ignored by init:
1183 */
1184 sigaction_set(SIGSTOP, &sa); /* pause */
1185
1186 /* These signals must interrupt wait(),
1187 * setting handler without SA_RESTART flag.
1188 */
1189 bb_signals_recursive_norestart(0
1190 + (1 << SIGINT) /* Ctrl-Alt-Del */
1191 + (1 << SIGQUIT) /* re-exec another init */
1192#ifdef SIGPWR
1193 + (1 << SIGPWR) /* halt */
1194#endif 1160#endif
1195 + (1 << SIGUSR1) /* halt */
1196 + (1 << SIGTERM) /* reboot */
1197 + (1 << SIGUSR2) /* poweroff */
1198#if ENABLE_FEATURE_USE_INITTAB
1199 + (1 << SIGHUP) /* reread /etc/inittab */
1200#endif
1201 , record_signo);
1202 1161
1203 sigprocmask_allsigs(SIG_UNBLOCK); 1162 /* Set up STOP signal handlers */
1204 } 1163 /* Stop handler must allow only SIGCONT inside itself */
1164 memset(&sa, 0, sizeof(sa));
1165 sigfillset(&sa.sa_mask);
1166 sigdelset(&sa.sa_mask, SIGCONT);
1167 sa.sa_handler = stop_handler;
1168 sa.sa_flags = SA_RESTART;
1169 sigaction_set(SIGTSTP, &sa); /* pause */
1170 /* Does not work as intended, at least in 2.6.20.
1171 * SIGSTOP is simply ignored by init
1172 * (NB: behavior might differ under strace):
1173 */
1174 sigaction_set(SIGSTOP, &sa); /* pause */
1205 1175
1206 /* Now run everything that needs to be run */ 1176 /* Now run everything that needs to be run */
1207 /* First run the sysinit command */ 1177 /* First run the sysinit command */
1208 run_actions(SYSINIT); 1178 run_actions(SYSINIT);
1209 check_delayed_sigs(); 1179 check_delayed_sigs(&G.zero_ts);
1210 /* Next run anything that wants to block */ 1180 /* Next run anything that wants to block */
1211 run_actions(WAIT); 1181 run_actions(WAIT);
1212 check_delayed_sigs(); 1182 check_delayed_sigs(&G.zero_ts);
1213 /* Next run anything to be run only once */ 1183 /* Next run anything to be run only once */
1214 run_actions(ONCE); 1184 run_actions(ONCE);
1215 1185
1216 /* Now run the looping stuff for the rest of forever. 1186 /* Now run the looping stuff for the rest of forever */
1217 */
1218 while (1) { 1187 while (1) {
1219 int maybe_WNOHANG;
1220
1221 maybe_WNOHANG = check_delayed_sigs();
1222
1223 /* (Re)run the respawn/askfirst stuff */ 1188 /* (Re)run the respawn/askfirst stuff */
1224 run_actions(RESPAWN | ASKFIRST); 1189 run_actions(RESPAWN | ASKFIRST);
1225 maybe_WNOHANG |= check_delayed_sigs();
1226 1190
1227 /* Don't consume all CPU time - sleep a bit */ 1191 /* Wait for any signal (typically it's SIGCHLD) */
1228 sleep(1); 1192 check_delayed_sigs(NULL); /* NULL timespec makes it wait */
1229 maybe_WNOHANG |= check_delayed_sigs(); 1193
1230 1194 /* Wait for any child process(es) to exit */
1231 /* Wait for any child process(es) to exit.
1232 *
1233 * If check_delayed_sigs above reported that a signal
1234 * was caught, wait will be nonblocking. This ensures
1235 * that if SIGHUP has reloaded inittab, respawn and askfirst
1236 * actions will not be delayed until next child death.
1237 */
1238 if (maybe_WNOHANG)
1239 maybe_WNOHANG = WNOHANG;
1240 while (1) { 1195 while (1) {
1241 pid_t wpid; 1196 pid_t wpid;
1242 struct init_action *a; 1197 struct init_action *a;
1243 1198
1244 /* If signals happen _in_ the wait, they interrupt it, 1199 wpid = waitpid(-1, NULL, WNOHANG);
1245 * bb_signals_recursive_norestart set them up that way
1246 */
1247 wpid = waitpid(-1, NULL, maybe_WNOHANG);
1248 if (wpid <= 0) 1200 if (wpid <= 0)
1249 break; 1201 break;
1250 1202
1251 a = mark_terminated(wpid); 1203 a = mark_terminated(wpid);
1252 if (a) { 1204 if (a) {
1253 message(L_LOG, "process '%s' (pid %d) exited. " 1205 message(L_LOG, "process '%s' (pid %u) exited. "
1254 "Scheduling for restart.", 1206 "Scheduling for restart.",
1255 a->command, wpid); 1207 a->command, (unsigned)wpid);
1256 } 1208 }
1257 /* See if anyone else is waiting to be reaped */
1258 maybe_WNOHANG = WNOHANG;
1259 } 1209 }
1210
1211 /* Don't consume all CPU time - sleep a bit */
1212 sleep(1);
1260 } /* while (1) */ 1213 } /* while (1) */
1261} 1214}
1262 1215
@@ -1269,11 +1222,17 @@ int init_main(int argc UNUSED_PARAM, char **argv)
1269//usage: "Init is the first process started during boot. It never exits." 1222//usage: "Init is the first process started during boot. It never exits."
1270//usage: IF_FEATURE_USE_INITTAB( 1223//usage: IF_FEATURE_USE_INITTAB(
1271//usage: "\n""It (re)spawns children according to /etc/inittab." 1224//usage: "\n""It (re)spawns children according to /etc/inittab."
1225//usage: "\n""Signals:"
1226//usage: "\n""HUP: reload /etc/inittab"
1272//usage: ) 1227//usage: )
1273//usage: IF_NOT_FEATURE_USE_INITTAB( 1228//usage: IF_NOT_FEATURE_USE_INITTAB(
1274//usage: "\n""This version of init doesn't use /etc/inittab," 1229//usage: "\n""This version of init doesn't use /etc/inittab,"
1275//usage: "\n""has fixed set of processed to run." 1230//usage: "\n""has fixed set of processed to run."
1231//usage: "\n""Signals:"
1276//usage: ) 1232//usage: )
1233//usage: "\n""TSTP: stop respawning until CONT"
1234//usage: "\n""QUIT: re-exec another init"
1235//usage: "\n""USR1/TERM/USR2/INT: run halt/reboot/poweroff/Ctrl-Alt-Del script"
1277//usage: 1236//usage:
1278//usage:#define init_notes_usage 1237//usage:#define init_notes_usage
1279//usage: "This version of init is designed to be run only by the kernel.\n" 1238//usage: "This version of init is designed to be run only by the kernel.\n"
diff --git a/libbb/appletlib.c b/libbb/appletlib.c
index f7971daf4..17ce463ea 100644
--- a/libbb/appletlib.c
+++ b/libbb/appletlib.c
@@ -319,7 +319,7 @@ void lbb_prepare(const char *applet
319 IF_FEATURE_INDIVIDUAL(, char **argv)) 319 IF_FEATURE_INDIVIDUAL(, char **argv))
320{ 320{
321#ifdef __GLIBC__ 321#ifdef __GLIBC__
322 (*(int **)&bb_errno) = __errno_location(); 322 (*(int **)not_const_pp(&bb_errno)) = __errno_location();
323 barrier(); 323 barrier();
324#endif 324#endif
325 applet_name = applet; 325 applet_name = applet;
diff --git a/libbb/copyfd.c b/libbb/copyfd.c
index ae5c26999..d41fd10f0 100644
--- a/libbb/copyfd.c
+++ b/libbb/copyfd.c
@@ -18,7 +18,7 @@
18 * was seen to cause largish delays when user tries to ^C a file copy. 18 * was seen to cause largish delays when user tries to ^C a file copy.
19 * Let's use a saner size. 19 * Let's use a saner size.
20 * Note: needs to be >= max(CONFIG_FEATURE_COPYBUF_KB), 20 * Note: needs to be >= max(CONFIG_FEATURE_COPYBUF_KB),
21 * or else "copy to eof" code will use neddlesly short reads. 21 * or else "copy to eof" code will use needlesly short reads.
22 */ 22 */
23#define SENDFILE_BIGBUF (16*1024*1024) 23#define SENDFILE_BIGBUF (16*1024*1024)
24 24
@@ -60,10 +60,13 @@ static off_t bb_full_fd_action(int src_fd, int dst_fd, off_t size)
60 ssize_t rd; 60 ssize_t rd;
61 61
62 if (sendfile_sz) { 62 if (sendfile_sz) {
63 rd = sendfile(dst_fd, src_fd, NULL, 63 /* dst_fd == -1 is a fake, else... */
64 size > sendfile_sz ? sendfile_sz : size); 64 if (dst_fd >= 0) {
65 if (rd >= 0) 65 rd = sendfile(dst_fd, src_fd, NULL,
66 goto read_ok; 66 size > sendfile_sz ? sendfile_sz : size);
67 if (rd >= 0)
68 goto read_ok;
69 }
67 sendfile_sz = 0; /* do not try sendfile anymore */ 70 sendfile_sz = 0; /* do not try sendfile anymore */
68 } 71 }
69#if CONFIG_FEATURE_COPYBUF_KB > 4 72#if CONFIG_FEATURE_COPYBUF_KB > 4
diff --git a/libbb/lineedit.c b/libbb/lineedit.c
index 9781b4a08..9bb3ea98b 100644
--- a/libbb/lineedit.c
+++ b/libbb/lineedit.c
@@ -203,7 +203,7 @@ extern struct lineedit_statics *const lineedit_ptr_to_statics;
203#define delbuf (S.delbuf ) 203#define delbuf (S.delbuf )
204 204
205#define INIT_S() do { \ 205#define INIT_S() do { \
206 (*(struct lineedit_statics**)&lineedit_ptr_to_statics) = xzalloc(sizeof(S)); \ 206 (*(struct lineedit_statics**)not_const_pp(&lineedit_ptr_to_statics)) = xzalloc(sizeof(S)); \
207 barrier(); \ 207 barrier(); \
208 cmdedit_termw = 80; \ 208 cmdedit_termw = 80; \
209 IF_USERNAME_OR_HOMEDIR(home_pwd_buf = (char*)null_str;) \ 209 IF_USERNAME_OR_HOMEDIR(home_pwd_buf = (char*)null_str;) \
diff --git a/libbb/missing_syscalls.c b/libbb/missing_syscalls.c
index 87cf59b3d..dc40d9155 100644
--- a/libbb/missing_syscalls.c
+++ b/libbb/missing_syscalls.c
@@ -15,14 +15,6 @@ pid_t getsid(pid_t pid)
15 return syscall(__NR_getsid, pid); 15 return syscall(__NR_getsid, pid);
16} 16}
17 17
18int stime(const time_t *t)
19{
20 struct timeval tv;
21 tv.tv_sec = *t;
22 tv.tv_usec = 0;
23 return settimeofday(&tv, NULL);
24}
25
26int sethostname(const char *name, size_t len) 18int sethostname(const char *name, size_t len)
27{ 19{
28 return syscall(__NR_sethostname, name, len); 20 return syscall(__NR_sethostname, name, len);
diff --git a/libbb/read.c b/libbb/read.c
index 5906bc225..a342506a8 100644
--- a/libbb/read.c
+++ b/libbb/read.c
@@ -12,9 +12,17 @@ ssize_t FAST_FUNC safe_read(int fd, void *buf, size_t count)
12{ 12{
13 ssize_t n; 13 ssize_t n;
14 14
15 do { 15 for (;;) {
16 n = read(fd, buf, count); 16 n = read(fd, buf, count);
17 } while (n < 0 && errno == EINTR); 17 if (n >= 0 || errno != EINTR)
18 break;
19 /* Some callers set errno=0, are upset when they see EINTR.
20 * Returning EINTR is wrong since we retry read(),
21 * the "error" was transient.
22 */
23 errno = 0;
24 /* repeat the read() */
25 }
18 26
19 return n; 27 return n;
20} 28}
diff --git a/libbb/read_key.c b/libbb/read_key.c
index 951786869..03b7da656 100644
--- a/libbb/read_key.c
+++ b/libbb/read_key.c
@@ -259,7 +259,8 @@ int64_t FAST_FUNC read_key(int fd, char *buffer, int timeout)
259 259
260 buffer[-1] = 0; 260 buffer[-1] = 0;
261 /* Pack into "1 <row15bits> <col16bits>" 32-bit sequence */ 261 /* Pack into "1 <row15bits> <col16bits>" 32-bit sequence */
262 col |= (((-1 << 15) | row) << 16); 262 row |= ((unsigned)(-1) << 15);
263 col |= (row << 16);
263 /* Return it in high-order word */ 264 /* Return it in high-order word */
264 return ((int64_t) col << 32) | (uint32_t)KEYCODE_CURSOR_POS; 265 return ((int64_t) col << 32) | (uint32_t)KEYCODE_CURSOR_POS;
265 } 266 }
diff --git a/libbb/replace.c b/libbb/replace.c
index a661d96e6..6183d3e6f 100644
--- a/libbb/replace.c
+++ b/libbb/replace.c
@@ -15,6 +15,10 @@ unsigned FAST_FUNC count_strstr(const char *str, const char *sub)
15 size_t sub_len = strlen(sub); 15 size_t sub_len = strlen(sub);
16 unsigned count = 0; 16 unsigned count = 0;
17 17
18 /* If sub is empty, avoid an infinite loop */
19 if (sub_len == 0)
20 return strlen(str) + 1;
21
18 while ((str = strstr(str, sub)) != NULL) { 22 while ((str = strstr(str, sub)) != NULL) {
19 count++; 23 count++;
20 str += sub_len; 24 str += sub_len;
diff --git a/libbb/time.c b/libbb/time.c
index cab0ad602..e66a9cba8 100644
--- a/libbb/time.c
+++ b/libbb/time.c
@@ -253,11 +253,9 @@ char* FAST_FUNC strftime_YYYYMMDDHHMMSS(char *buf, unsigned len, time_t *tp)
253#define CLOCK_MONOTONIC 1 253#define CLOCK_MONOTONIC 1
254#endif 254#endif
255 255
256/* libc has incredibly messy way of doing this,
257 * typically requiring -lrt. We just skip all this mess */
258static void get_mono(struct timespec *ts) 256static void get_mono(struct timespec *ts)
259{ 257{
260 if (syscall(__NR_clock_gettime, CLOCK_MONOTONIC, ts)) 258 if (clock_gettime(CLOCK_MONOTONIC, ts))
261 bb_simple_error_msg_and_die("clock_gettime(MONOTONIC) failed"); 259 bb_simple_error_msg_and_die("clock_gettime(MONOTONIC) failed");
262} 260}
263unsigned long long FAST_FUNC monotonic_ns(void) 261unsigned long long FAST_FUNC monotonic_ns(void)
diff --git a/miscutils/bc.c b/miscutils/bc.c
index 7ac30dd53..c7246ea1a 100644
--- a/miscutils/bc.c
+++ b/miscutils/bc.c
@@ -844,10 +844,10 @@ struct globals {
844# error Strange INT_MAX 844# error Strange INT_MAX
845#endif 845#endif
846 846
847#if UINT_MAX == 4294967295 847#if UINT_MAX == 4294967295U
848# define BC_MAX_SCALE_STR "4294967295" 848# define BC_MAX_SCALE_STR "4294967295"
849# define BC_MAX_STRING_STR "4294967294" 849# define BC_MAX_STRING_STR "4294967294"
850#elif UINT_MAX == 18446744073709551615 850#elif UINT_MAX == 18446744073709551615U
851# define BC_MAX_SCALE_STR "18446744073709551615" 851# define BC_MAX_SCALE_STR "18446744073709551615"
852# define BC_MAX_STRING_STR "18446744073709551614" 852# define BC_MAX_STRING_STR "18446744073709551614"
853#else 853#else
@@ -1465,7 +1465,10 @@ static ssize_t bc_num_cmp(BcNum *a, BcNum *b)
1465 b_int = BC_NUM_INT(b); 1465 b_int = BC_NUM_INT(b);
1466 a_int -= b_int; 1466 a_int -= b_int;
1467 1467
1468 if (a_int != 0) return (ssize_t) a_int; 1468 if (a_int != 0) {
1469 if (neg) return - (ssize_t) a_int;
1470 return (ssize_t) a_int;
1471 }
1469 1472
1470 a_max = (a->rdx > b->rdx); 1473 a_max = (a->rdx > b->rdx);
1471 if (a_max) { 1474 if (a_max) {
@@ -4973,7 +4976,9 @@ static void dc_parse_string(void)
4973 xc_parse_pushInst_and_Index(XC_INST_STR, len); 4976 xc_parse_pushInst_and_Index(XC_INST_STR, len);
4974 bc_vec_push(&G.prog.strs, &str); 4977 bc_vec_push(&G.prog.strs, &str);
4975 4978
4976 // Explanation needed here 4979 // Add an empty function so that if zdc_program_execStr ever needs to
4980 // parse the string into code (from the 'x' command) there's somewhere
4981 // to store the bytecode.
4977 xc_program_add_fn(); 4982 xc_program_add_fn();
4978 p->func = xc_program_func(p->fidx); 4983 p->func = xc_program_func(p->fidx);
4979 4984
@@ -5454,11 +5459,13 @@ static void xc_program_printString(const char *str)
5454 char *n; 5459 char *n;
5455 5460
5456 c = *str++; 5461 c = *str++;
5457 n = strchr(esc, c); // note: c can be NUL 5462 n = strchr(esc, c); // note: if c is NUL, n = \0 at end of esc
5458 if (!n) { 5463 if (!n || !c) {
5459 // Just print the backslash and following character 5464 // Just print the backslash and following character
5460 bb_putchar('\\'); 5465 bb_putchar('\\');
5461 ++G.prog.nchars; 5466 ++G.prog.nchars;
5467 // But if we're at the end of the string, stop
5468 if (!c) break;
5462 } else { 5469 } else {
5463 if (n - esc == 0) // "\n" ? 5470 if (n - esc == 0) // "\n" ?
5464 G.prog.nchars = SIZE_MAX; 5471 G.prog.nchars = SIZE_MAX;
@@ -6398,7 +6405,11 @@ static BC_STATUS zdc_program_asciify(void)
6398 str = xzalloc(2); 6405 str = xzalloc(2);
6399 str[0] = c; 6406 str[0] = c;
6400 //str[1] = '\0'; - already is 6407 //str[1] = '\0'; - already is
6401 bc_vec_push(&G.prog.strs, &str); 6408 idx = bc_vec_push(&G.prog.strs, &str);
6409 // Add an empty function so that if zdc_program_execStr ever needs to
6410 // parse the string into code (from the 'x' command) there's somewhere
6411 // to store the bytecode.
6412 xc_program_add_fn();
6402 dup: 6413 dup:
6403 res.t = XC_RESULT_STR; 6414 res.t = XC_RESULT_STR;
6404 res.d.id.idx = idx; 6415 res.d.id.idx = idx;
@@ -6521,7 +6532,7 @@ static BC_STATUS zdc_program_execStr(char *code, size_t *bgn, bool cond)
6521 if (s || !BC_PROG_STR(n)) goto exit; 6532 if (s || !BC_PROG_STR(n)) goto exit;
6522 sidx = n->rdx; 6533 sidx = n->rdx;
6523 } else 6534 } else
6524 goto exit; 6535 goto exit_nopop;
6525 } 6536 }
6526 6537
6527 fidx = sidx + BC_PROG_REQ_FUNCS; 6538 fidx = sidx + BC_PROG_REQ_FUNCS;
@@ -6561,6 +6572,7 @@ static BC_STATUS zdc_program_execStr(char *code, size_t *bgn, bool cond)
6561 RETURN_STATUS(BC_STATUS_SUCCESS); 6572 RETURN_STATUS(BC_STATUS_SUCCESS);
6562 exit: 6573 exit:
6563 bc_vec_pop(&G.prog.results); 6574 bc_vec_pop(&G.prog.results);
6575 exit_nopop:
6564 RETURN_STATUS(s); 6576 RETURN_STATUS(s);
6565} 6577}
6566#define zdc_program_execStr(...) (zdc_program_execStr(__VA_ARGS__) COMMA_SUCCESS) 6578#define zdc_program_execStr(...) (zdc_program_execStr(__VA_ARGS__) COMMA_SUCCESS)
diff --git a/miscutils/hdparm.c b/miscutils/hdparm.c
index b453efba9..beabb1ad5 100644
--- a/miscutils/hdparm.c
+++ b/miscutils/hdparm.c
@@ -996,7 +996,7 @@ static void identify(uint16_t *val)
996 /* check Endian of capacity bytes */ 996 /* check Endian of capacity bytes */
997 nn = val[LCYLS_CUR] * val[LHEADS_CUR] * val[LSECTS_CUR]; 997 nn = val[LCYLS_CUR] * val[LHEADS_CUR] * val[LSECTS_CUR];
998 oo = (uint32_t)val[CAPACITY_LSB] << 16 | val[CAPACITY_MSB]; 998 oo = (uint32_t)val[CAPACITY_LSB] << 16 | val[CAPACITY_MSB];
999 if (abs(mm - nn) > abs(oo - nn)) 999 if (abs((int)(mm - nn)) > abs((int)(oo - nn)))
1000 mm = oo; 1000 mm = oo;
1001 } 1001 }
1002 printf("\tCHS current addressable sectors:%11u\n", mm); 1002 printf("\tCHS current addressable sectors:%11u\n", mm);
diff --git a/networking/brctl.c b/networking/brctl.c
index 586ca9b0c..25640246d 100644
--- a/networking/brctl.c
+++ b/networking/brctl.c
@@ -53,7 +53,9 @@
53//usage: "\n addif BRIDGE IFACE Add IFACE to BRIDGE" 53//usage: "\n addif BRIDGE IFACE Add IFACE to BRIDGE"
54//usage: "\n delif BRIDGE IFACE Delete IFACE from BRIDGE" 54//usage: "\n delif BRIDGE IFACE Delete IFACE from BRIDGE"
55//usage: IF_FEATURE_BRCTL_FANCY( 55//usage: IF_FEATURE_BRCTL_FANCY(
56//usage: "\n stp BRIDGE 1/yes/on|0/no/off STP on/off" 56//usage: "\n showmacs BRIDGE List MAC addresses"
57//usage: "\n showstp BRIDGE Show STP info"
58//usage: "\n stp BRIDGE 1/yes/on|0/no/off Set STP on/off"
57//usage: "\n setageing BRIDGE SECONDS Set ageing time" 59//usage: "\n setageing BRIDGE SECONDS Set ageing time"
58//usage: "\n setfd BRIDGE SECONDS Set bridge forward delay" 60//usage: "\n setfd BRIDGE SECONDS Set bridge forward delay"
59//usage: "\n sethello BRIDGE SECONDS Set hello time" 61//usage: "\n sethello BRIDGE SECONDS Set hello time"
@@ -63,9 +65,7 @@
63//usage: "\n setpathcost BRIDGE IFACE COST Set path cost" 65//usage: "\n setpathcost BRIDGE IFACE COST Set path cost"
64//usage: ) 66//usage: )
65// Not yet implemented: 67// Not yet implemented:
66// hairpin BRIDGE IFACE on|off Hairpin on/off 68// hairpin BRIDGE IFACE on|off Set hairpin on/off
67// showmacs BRIDGE List mac addrs
68// showstp BRIDGE Show stp info
69 69
70#include "libbb.h" 70#include "libbb.h"
71#include "common_bufsiz.h" 71#include "common_bufsiz.h"
@@ -129,7 +129,7 @@ static int show_bridge(const char *name, int need_hdr)
129 *bridge name bridge id STP enabled interfaces 129 *bridge name bridge id STP enabled interfaces
130 *br0 8000.000000000000 no eth0 130 *br0 8000.000000000000 no eth0
131 */ 131 */
132 char pathbuf[IFNAMSIZ + sizeof("/bridge/bridge_id") + 32]; 132 char pathbuf[IFNAMSIZ + sizeof("/bridge/bridge_id") + 8];
133 int tabs; 133 int tabs;
134 DIR *ifaces; 134 DIR *ifaces;
135 struct dirent *ent; 135 struct dirent *ent;
@@ -146,8 +146,7 @@ static int show_bridge(const char *name, int need_hdr)
146 146
147 if (need_hdr) 147 if (need_hdr)
148 puts("bridge name\tbridge id\t\tSTP enabled\tinterfaces"); 148 puts("bridge name\tbridge id\t\tSTP enabled\tinterfaces");
149 printf("%s\t\t", name); 149 printf("%s\t\t%s\t", name, filedata);
150 printf("%s\t", filedata);
151 150
152 strcpy(sfx, "stp_state"); 151 strcpy(sfx, "stp_state");
153 read_file(pathbuf); 152 read_file(pathbuf);
@@ -158,7 +157,8 @@ static int show_bridge(const char *name, int need_hdr)
158 strcpy(filedata, "yes"); 157 strcpy(filedata, "yes");
159 fputs(filedata, stdout); 158 fputs(filedata, stdout);
160 159
161 strcpy(sfx - (sizeof("bridge/")-1), "brif"); 160 /* sfx points past "BR/bridge/", turn it into "BR/brif": */
161 sfx[-4] = 'f'; sfx[-3] = '\0';
162 tabs = 0; 162 tabs = 0;
163 ifaces = opendir(pathbuf); 163 ifaces = opendir(pathbuf);
164 if (ifaces) { 164 if (ifaces) {
@@ -194,7 +194,299 @@ static void write_uint(const char *name, const char *leaf, unsigned val)
194 n = sprintf(filedata, "%u\n", val); 194 n = sprintf(filedata, "%u\n", val);
195 if (write(fd, filedata, n) < 0) 195 if (write(fd, filedata, n) < 0)
196 bb_simple_perror_msg_and_die(name); 196 bb_simple_perror_msg_and_die(name);
197 close(fd); 197 /* So far all callers exit very soon after calling us.
198 * Do not bother closing fd (unless debugging):
199 */
200 if (ENABLE_FEATURE_CLEAN_UP)
201 close(fd);
202}
203
204struct fdb_entry {
205 uint8_t mac_addr[6];
206 uint8_t port_no;
207 uint8_t is_local;
208 uint32_t ageing_timer_value;
209 uint8_t port_hi;
210 uint8_t pad0;
211 uint16_t unused;
212};
213
214static int compare_fdbs(const void *_f0, const void *_f1)
215{
216 const struct fdb_entry *f0 = _f0;
217 const struct fdb_entry *f1 = _f1;
218
219 return memcmp(f0->mac_addr, f1->mac_addr, 6);
220}
221
222static size_t read_bridge_forward_db(const char *name, struct fdb_entry **_fdb)
223{
224 char pathbuf[IFNAMSIZ + sizeof("/brforward") + 8];
225 struct fdb_entry *fdb;
226 size_t nentries;
227 int fd;
228 ssize_t cc;
229
230#if IFNAMSIZ == 16
231 sprintf(pathbuf, "%.16s/brforward", name);
232#else
233 sprintf(pathbuf, "%.*s/brforward", (int)IFNAMSIZ, name);
234#endif
235 fd = open(pathbuf, O_RDONLY);
236 if (fd < 0)
237 bb_error_msg_and_die("bridge %s does not exist", name);
238
239 fdb = NULL;
240 nentries = 0;
241 for (;;) {
242 fdb = xrealloc_vector(fdb, 4, nentries);
243 cc = full_read(fd, &fdb[nentries], sizeof(*fdb));
244 if (cc == 0) {
245 break;
246 }
247 if (cc != sizeof(*fdb)) {
248 bb_perror_msg_and_die("can't read bridge %s forward db", name);
249 }
250 ++nentries;
251 }
252
253 if (ENABLE_FEATURE_CLEAN_UP)
254 close(fd);
255
256 qsort(fdb, nentries, sizeof(*fdb), compare_fdbs);
257
258 *_fdb = fdb;
259 return nentries;
260}
261
262static void show_bridge_macs(const char *name)
263{
264 struct fdb_entry *fdb;
265 size_t nentries;
266 size_t i;
267
268 nentries = read_bridge_forward_db(name, &fdb);
269
270 printf("port no\tmac addr\t\tis local?\tageing timer\n");
271 for (i = 0; i < nentries; ++i) {
272 const struct fdb_entry *f = &fdb[i];
273 unsigned tv_sec = f->ageing_timer_value / 100;
274 unsigned tv_csec = f->ageing_timer_value % 100;
275 printf("%3u\t"
276 "%.2x:%.2x:%.2x:%.2x:%.2x:%.2x\t"
277 "%s\t\t"
278 "%4u.%.2u\n",
279 f->port_no,
280 f->mac_addr[0], f->mac_addr[1], f->mac_addr[2],
281 f->mac_addr[3], f->mac_addr[4], f->mac_addr[5],
282 (f->is_local ? "yes" : "no"),
283 tv_sec, tv_csec
284 );
285 }
286
287 if (ENABLE_FEATURE_CLEAN_UP)
288 free(fdb);
289}
290
291static void show_bridge_timer(const char *msg)
292{
293 unsigned long long centisec = xstrtoull(filedata, 0);
294 unsigned tv_sec = centisec / 100;
295 unsigned tv_csec = centisec % 100;
296 printf("%s%4u.%.2u", msg, tv_sec, tv_csec);
297}
298
299static const char *show_bridge_state(unsigned state)
300{
301 /* See linux/if_bridge.h, BR_STATE_ constants */
302 static const char state_names[] ALIGN1 =
303 "disabled\0" //BR_STATE_DISABLED 0
304 "listening\0" //BR_STATE_LISTENING 1
305 "learning\0" //BR_STATE_LEARNING 2
306 "forwarding\0" //BR_STATE_FORWARDING 3
307 "blocking" //BR_STATE_BLOCKING 4
308 ;
309 if (state < 5)
310 return nth_string(state_names, state);
311 return utoa(state);
312}
313
314static void printf_xstrtou(const char *fmt)
315{
316 printf(fmt, xstrtou(filedata, 0));
317}
318
319static void show_bridge_port(const char *name)
320{
321 char pathbuf[IFNAMSIZ + sizeof("/brport/forward_delay_timer") + 8];
322 char *sfx;
323
324#if IFNAMSIZ == 16
325 sfx = pathbuf + sprintf(pathbuf, "%.16s/brport/", name);
326#else
327 sfx = pathbuf + sprintf(pathbuf, "%.*s/brport/", (int)IFNAMSIZ, name);
328#endif
329
330 strcpy(sfx, "port_no");
331 read_file(pathbuf);
332 printf("%s (%u)\n", name, xstrtou(filedata, 0));
333
334 strcpy(sfx + 5, "id"); // "port_id"
335 read_file(pathbuf);
336 printf_xstrtou(" port id\t\t%.4x");
337
338 strcpy(sfx, "state");
339 read_file(pathbuf);
340 printf("\t\t\tstate\t\t%15s\n", show_bridge_state(xstrtou(filedata, 0)));
341
342 strcpy(sfx, "designated_root");
343 read_file(pathbuf);
344 printf(" designated root\t%s", filedata);
345
346 strcpy(sfx, "path_cost");
347 read_file(pathbuf);
348 printf_xstrtou("\tpath cost\t\t%4u\n");
349
350 strcpy(sfx, "designated_bridge");
351 read_file(pathbuf);
352 printf(" designated bridge\t%s", filedata);
353
354 strcpy(sfx, "message_age_timer");
355 read_file(pathbuf);
356 show_bridge_timer("\tmessage age timer\t");
357
358 strcpy(sfx, "designated_port");
359 read_file(pathbuf);
360 printf_xstrtou("\n designated port\t%.4x");
361
362 strcpy(sfx, "forward_delay_timer");
363 read_file(pathbuf);
364 show_bridge_timer("\t\t\tforward delay timer\t");
365
366 strcpy(sfx, "designated_cost");
367 read_file(pathbuf);
368 printf_xstrtou("\n designated cost\t%4u");
369
370 strcpy(sfx, "hold_timer");
371 read_file(pathbuf);
372 show_bridge_timer("\t\t\thold timer\t\t");
373
374 printf("\n flags\t\t\t");
375
376 strcpy(sfx, "config_pending");
377 read_file(pathbuf);
378 if (!LONE_CHAR(filedata, '0'))
379 printf("CONFIG_PENDING ");
380
381 strcpy(sfx, "change_ack");
382 read_file(pathbuf);
383 if (!LONE_CHAR(filedata, '0'))
384 printf("TOPOLOGY_CHANGE_ACK ");
385
386 strcpy(sfx, "hairpin_mode");
387 read_file(pathbuf);
388 if (!LONE_CHAR(filedata, '0'))
389 printf_xstrtou("\n hairpin mode\t\t%4u");
390
391 printf("\n\n");
392}
393
394static void show_bridge_stp(const char *name)
395{
396 char pathbuf[IFNAMSIZ + sizeof("/bridge/topology_change_timer") + 8];
397 char *sfx;
398
399#if IFNAMSIZ == 16
400 sfx = pathbuf + sprintf(pathbuf, "%.16s/bridge/", name);
401#else
402 sfx = pathbuf + sprintf(pathbuf, "%.*s/bridge/", (int)IFNAMSIZ, name);
403#endif
404
405 strcpy(sfx, "bridge_id");
406 if (read_file(pathbuf) < 0)
407 bb_error_msg_and_die("bridge %s does not exist", name);
408
409 printf("%s\n"
410 " bridge id\t\t%s", name, filedata);
411
412 strcpy(sfx, "root_id");
413 read_file(pathbuf);
414 printf("\n designated root\t%s", filedata);
415
416 strcpy(sfx + 5, "port"); // "root_port"
417 read_file(pathbuf);
418 printf_xstrtou("\n root port\t\t%4u\t\t\t");
419
420 strcpy(sfx + 6, "ath_cost"); // "root_path_cost"
421 read_file(pathbuf);
422 printf_xstrtou("path cost\t\t%4u\n");
423
424 strcpy(sfx, "max_age");
425 read_file(pathbuf);
426 show_bridge_timer(" max age\t\t");
427 show_bridge_timer("\t\t\tbridge max age\t\t");
428
429 strcpy(sfx, "hello_time");
430 read_file(pathbuf);
431 show_bridge_timer("\n hello time\t\t");
432 show_bridge_timer("\t\t\tbridge hello time\t");
433
434 strcpy(sfx, "forward_delay");
435 read_file(pathbuf);
436 show_bridge_timer("\n forward delay\t\t");
437 show_bridge_timer("\t\t\tbridge forward delay\t");
438
439 strcpy(sfx, "ageing_time");
440 read_file(pathbuf);
441 show_bridge_timer("\n ageing time\t\t");
442
443 strcpy(sfx, "hello_timer");
444 read_file(pathbuf);
445 show_bridge_timer("\n hello timer\t\t");
446
447 strcpy(sfx, "tcn_timer");
448 read_file(pathbuf);
449 show_bridge_timer("\t\t\ttcn timer\t\t");
450
451 strcpy(sfx, "topology_change_timer");
452 read_file(pathbuf);
453 show_bridge_timer("\n topology change timer\t");
454
455 strcpy(sfx, "gc_timer");
456 read_file(pathbuf);
457 show_bridge_timer("\t\t\tgc timer\t\t");
458
459 printf("\n flags\t\t\t");
460
461 strcpy(sfx, "topology_change");
462 read_file(pathbuf);
463 if (!LONE_CHAR(filedata, '0'))
464 printf("TOPOLOGY_CHANGE ");
465
466 strcpy(sfx, "topology_change_detected");
467 read_file(pathbuf);
468 if (!LONE_CHAR(filedata, '0'))
469 printf("TOPOLOGY_CHANGE_DETECTED ");
470 printf("\n\n\n");
471
472 /* Show bridge ports */
473 {
474 DIR *ifaces;
475
476 /* sfx points past "BR/bridge/", turn it into "BR/brif": */
477 sfx[-4] = 'f'; sfx[-3] = '\0';
478 ifaces = opendir(pathbuf);
479 if (ifaces) {
480 struct dirent *ent;
481 while ((ent = readdir(ifaces)) != NULL) {
482 if (DOT_OR_DOTDOT(ent->d_name))
483 continue; /* . or .. */
484 show_bridge_port(ent->d_name);
485 }
486 if (ENABLE_FEATURE_CLEAN_UP)
487 closedir(ifaces);
488 }
489 }
198} 490}
199#endif 491#endif
200 492
@@ -205,20 +497,26 @@ int brctl_main(int argc UNUSED_PARAM, char **argv)
205 "addbr\0" "delbr\0" "addif\0" "delif\0" 497 "addbr\0" "delbr\0" "addif\0" "delif\0"
206 IF_FEATURE_BRCTL_FANCY( 498 IF_FEATURE_BRCTL_FANCY(
207 "stp\0" 499 "stp\0"
500 "showstp\0"
208 "setageing\0" "setfd\0" "sethello\0" "setmaxage\0" 501 "setageing\0" "setfd\0" "sethello\0" "setmaxage\0"
209 "setpathcost\0" "setportprio\0" 502 "setpathcost\0" "setportprio\0"
210 "setbridgeprio\0" 503 "setbridgeprio\0"
504 "showmacs\0"
211 ) 505 )
212 IF_FEATURE_BRCTL_SHOW("show\0"); 506 IF_FEATURE_BRCTL_SHOW("show\0");
213 enum { ARG_addbr = 0, ARG_delbr, ARG_addif, ARG_delif 507 enum { ARG_addbr = 0, ARG_delbr, ARG_addif, ARG_delif
214 IF_FEATURE_BRCTL_FANCY(, 508 IF_FEATURE_BRCTL_FANCY(,
215 ARG_stp, 509 ARG_stp,
510 ARG_showstp,
216 ARG_setageing, ARG_setfd, ARG_sethello, ARG_setmaxage, 511 ARG_setageing, ARG_setfd, ARG_sethello, ARG_setmaxage,
217 ARG_setpathcost, ARG_setportprio, 512 ARG_setpathcost, ARG_setportprio,
218 ARG_setbridgeprio 513 ARG_setbridgeprio,
514 ARG_showmacs
219 ) 515 )
220 IF_FEATURE_BRCTL_SHOW(, ARG_show) 516 IF_FEATURE_BRCTL_SHOW(, ARG_show)
221 }; 517 };
518 int key;
519 char *br;
222 520
223 argv++; 521 argv++;
224 if (!*argv) { 522 if (!*argv) {
@@ -228,185 +526,157 @@ int brctl_main(int argc UNUSED_PARAM, char **argv)
228 526
229 xchdir("/sys/class/net"); 527 xchdir("/sys/class/net");
230 528
231// while (*argv) 529 key = index_in_strings(keywords, *argv);
232 { 530 if (key == -1) /* no match found in keywords array, bail out. */
233 smallint key; 531 bb_error_msg_and_die(bb_msg_invalid_arg_to, *argv, applet_name);
234 char *br; 532 argv++;
235
236 key = index_in_strings(keywords, *argv);
237 if (key == -1) /* no match found in keywords array, bail out. */
238 bb_error_msg_and_die(bb_msg_invalid_arg_to, *argv, applet_name);
239 argv++;
240 533
241#if ENABLE_FEATURE_BRCTL_SHOW 534#if ENABLE_FEATURE_BRCTL_SHOW
242 if (key == ARG_show) { /* show [BR]... */ 535 if (key == ARG_show) { /* show [BR]... */
243 DIR *net; 536 DIR *net;
244 struct dirent *ent; 537 struct dirent *ent;
245 int need_hdr = 1; 538 int need_hdr = 1;
246 int exitcode = EXIT_SUCCESS; 539 int exitcode = EXIT_SUCCESS;
247 540
248 if (*argv) { 541 if (*argv) {
249 /* "show BR1 BR2 BR3" */ 542 /* "show BR1 BR2 BR3" */
250 do { 543 do {
251 if (show_bridge(*argv, need_hdr) >= 0) { 544 if (show_bridge(*argv, need_hdr) >= 0) {
252 need_hdr = 0; 545 need_hdr = 0;
253 } else { 546 } else {
254 bb_error_msg("bridge %s does not exist", *argv); 547 bb_error_msg("bridge %s does not exist", *argv);
255//TODO: if device exists, but is not a BR, brctl from bridge-utils 1.6 548//TODO: if device exists, but is not a BR, brctl from bridge-utils 1.6
256//says this instead: "device eth0 is not a bridge" 549//says this instead: "device eth0 is not a bridge"
257 exitcode = EXIT_FAILURE; 550 exitcode = EXIT_FAILURE;
258 } 551 }
259 } while (*++argv != NULL); 552 } while (*++argv != NULL);
260 return exitcode;
261 }
262
263 /* "show" (if no ifaces, shows nothing, not even header) */
264 net = xopendir(".");
265 while ((ent = readdir(net)) != NULL) {
266 if (DOT_OR_DOTDOT(ent->d_name))
267 continue; /* . or .. */
268 if (show_bridge(ent->d_name, need_hdr) >= 0)
269 need_hdr = 0;
270 }
271 if (ENABLE_FEATURE_CLEAN_UP)
272 closedir(net);
273 return exitcode; 553 return exitcode;
274 } 554 }
555
556 /* "show" (if no ifaces, shows nothing, not even header) */
557 net = xopendir(".");
558 while ((ent = readdir(net)) != NULL) {
559 if (DOT_OR_DOTDOT(ent->d_name))
560 continue; /* . or .. */
561 if (show_bridge(ent->d_name, need_hdr) >= 0)
562 need_hdr = 0;
563 }
564 if (ENABLE_FEATURE_CLEAN_UP)
565 closedir(net);
566 return exitcode;
567 }
275#endif 568#endif
276 569
277 if (!*argv) /* all but 'show' need at least one argument */ 570 if (!*argv) /* All of the below need at least one argument */
278 bb_show_usage(); 571 bb_show_usage();
279 572
280 br = *argv++; 573 br = *argv++;
281 574
282 if (key == ARG_addbr || key == ARG_delbr) { 575 if (key == ARG_addbr || key == ARG_delbr) {
283 /* addbr or delbr */ 576 /* brctl from bridge-utils 1.6 still uses ioctl
284 /* brctl from bridge-utils 1.6 still uses ioctl 577 * for SIOCBRADDBR / SIOCBRDELBR, not /sys accesses
285 * for SIOCBRADDBR / SIOCBRDELBR, not /sys accesses 578 */
286 */ 579 int fd = xsocket(AF_INET, SOCK_STREAM, 0);
287 int fd = xsocket(AF_INET, SOCK_STREAM, 0); 580 ioctl_or_perror_and_die(fd,
288 ioctl_or_perror_and_die(fd, 581 key == ARG_addbr ? SIOCBRADDBR : SIOCBRDELBR,
289 key == ARG_addbr ? SIOCBRADDBR : SIOCBRDELBR, 582 br, "bridge %s", br
290 br, "bridge %s", br 583 );
291 ); 584 //close(fd);
292 //close(fd); 585 //goto done;
293 //goto done; 586 /* bridge-utils 1.6 simply ignores trailing args:
294 /* bridge-utils 1.6 simply ignores trailing args: 587 * "brctl addbr BR1 ARGS" ignores ARGS
295 * "brctl addbr BR1 ARGS" ignores ARGS 588 */
296 */ 589 if (ENABLE_FEATURE_CLEAN_UP)
297 if (ENABLE_FEATURE_CLEAN_UP) 590 close(fd);
298 close(fd); 591 return EXIT_SUCCESS;
299 return EXIT_SUCCESS; 592 }
300 }
301 593
302 if (!*argv) /* all but 'addbr/delbr' need at least two arguments */ 594 if (key == ARG_showmacs) {
303 bb_show_usage(); 595 show_bridge_macs(br);
596 return EXIT_SUCCESS;
597 }
598 if (key == ARG_showstp) {
599 show_bridge_stp(br);
600 return EXIT_SUCCESS;
601 }
304 602
305#if ENABLE_FEATURE_BRCTL_FANCY 603 if (!*argv) /* All of the below need at least two arguments */
306 if (key == ARG_stp) { /* stp */ 604 bb_show_usage();
307 static const char no_yes[] ALIGN1 =
308 "0\0" "off\0" "n\0" "no\0" /* 0 .. 3 */
309 "1\0" "on\0" "y\0" "yes\0"; /* 4 .. 7 */
310 int onoff = index_in_strings(no_yes, *argv);
311 if (onoff < 0)
312 bb_error_msg_and_die(bb_msg_invalid_arg_to, *argv, applet_name);
313 onoff = (unsigned)onoff / 4;
314 write_uint(br, "bridge/stp_state", onoff);
315 //goto done_next_argv;
316 return EXIT_SUCCESS;
317 }
318 605
319 if ((unsigned)(key - ARG_setageing) < 4) { /* time related ops */ 606#if ENABLE_FEATURE_BRCTL_FANCY
320 /* setageing BR N: "N*100\n" to /sys/class/net/BR/bridge/ageing_time 607 if (key == ARG_stp) {
321 * setfd BR N: "N*100\n" to /sys/class/net/BR/bridge/forward_delay 608 static const char no_yes[] ALIGN1 =
322 * sethello BR N: "N*100\n" to /sys/class/net/BR/bridge/hello_time 609 "0\0" "off\0" "n\0" "no\0" /* 0 .. 3 */
323 * setmaxage BR N: "N*100\n" to /sys/class/net/BR/bridge/max_age 610 "1\0" "on\0" "y\0" "yes\0"; /* 4 .. 7 */
324 */ 611 int onoff = index_in_strings(no_yes, *argv);
325 write_uint(br, 612 if (onoff < 0)
326 nth_string( 613 bb_error_msg_and_die(bb_msg_invalid_arg_to, *argv, applet_name);
327 "bridge/ageing_time" "\0" /* ARG_setageing */ 614 onoff = (unsigned)onoff / 4;
328 "bridge/forward_delay""\0" /* ARG_setfd */ 615 write_uint(br, "bridge/stp_state", onoff);
329 "bridge/hello_time" "\0" /* ARG_sethello */ 616 return EXIT_SUCCESS;
330 "bridge/max_age", /* ARG_setmaxage */ 617 }
331 key - ARG_setageing
332 ),
333 str_to_jiffies(*argv)
334 );
335 //goto done_next_argv;
336 return EXIT_SUCCESS;
337 }
338 618
339 if (key == ARG_setbridgeprio) { 619 if ((unsigned)(key - ARG_setageing) < 4) { /* time related ops */
340 write_uint(br, "bridge/priority", xatoi_positive(*argv)); 620 /* setageing BR N: "N*100\n" to /sys/class/net/BR/bridge/ageing_time
341 //goto done_next_argv; 621 * setfd BR N: "N*100\n" to /sys/class/net/BR/bridge/forward_delay
342 return EXIT_SUCCESS; 622 * sethello BR N: "N*100\n" to /sys/class/net/BR/bridge/hello_time
343 } 623 * setmaxage BR N: "N*100\n" to /sys/class/net/BR/bridge/max_age
624 */
625 write_uint(br,
626 nth_string(
627 "bridge/ageing_time" "\0" /* ARG_setageing */
628 "bridge/forward_delay""\0" /* ARG_setfd */
629 "bridge/hello_time" "\0" /* ARG_sethello */
630 "bridge/max_age", /* ARG_setmaxage */
631 key - ARG_setageing
632 ),
633 str_to_jiffies(*argv)
634 );
635 return EXIT_SUCCESS;
636 }
344 637
345 if (key == ARG_setpathcost 638 if (key == ARG_setbridgeprio) {
346 || key == ARG_setportprio 639 write_uint(br, "bridge/priority", xatoi_positive(*argv));
347 ) { 640 return EXIT_SUCCESS;
348 if (!argv[1]) 641 }
349 bb_show_usage();
350 /* BR is not used (and ignored!) for these commands:
351 * "setpathcost BR PORT N" writes "N\n" to
352 * /sys/class/net/PORT/brport/path_cost
353 * "setportprio BR PORT N" writes "N\n" to
354 * /sys/class/net/PORT/brport/priority
355 */
356 write_uint(argv[0],
357 nth_string(
358 "brport/path_cost" "\0" /* ARG_setpathcost */
359 "brport/priority", /* ARG_setportprio */
360 key - ARG_setpathcost
361 ),
362 xatoi_positive(argv[1])
363 );
364 //argv++;
365 //goto done_next_argv;
366 return EXIT_SUCCESS;
367 }
368 642
369/* TODO: "showmacs BR" 643 if (key == ARG_setpathcost
370 * port no\tmac addr\t\tis local?\tageing timer 644 || key == ARG_setportprio
371 * <sp><sp>1\txx:xx:xx:xx:xx:xx\tno\t\t<sp><sp><sp>1.31 645 ) {
372 * port no mac addr is local? ageing timer 646 if (!argv[1])
373 * 1 xx:xx:xx:xx:xx:xx no 1.31 647 bb_show_usage();
374 * Read fixed-sized records from /sys/class/net/BR/brforward: 648 /* BR is not used (and ignored!) for these commands:
375 * struct __fdb_entry { 649 * "setpathcost BR PORT N" writes "N\n" to
376 * uint8_t mac_addr[ETH_ALEN]; 650 * /sys/class/net/PORT/brport/path_cost
377 * uint8_t port_no; //lsb 651 * "setportprio BR PORT N" writes "N\n" to
378 * uint8_t is_local; 652 * /sys/class/net/PORT/brport/priority
379 * uint32_t ageing_timer_value; 653 */
380 * uint8_t port_hi; 654 write_uint(argv[0],
381 * uint8_t pad0; 655 nth_string(
382 * uint16_t unused; 656 "brport/path_cost" "\0" /* ARG_setpathcost */
383 * }; 657 "brport/priority", /* ARG_setportprio */
384 */ 658 key - ARG_setpathcost
659 ),
660 xatoi_positive(argv[1])
661 );
662 return EXIT_SUCCESS;
663 }
385#endif 664#endif
386 /* always true: if (key == ARG_addif || key == ARG_delif) */ { 665 /* always true: if (key == ARG_addif || key == ARG_delif) */ {
387 /* addif or delif */ 666 struct ifreq ifr;
388 struct ifreq ifr; 667 int fd = xsocket(AF_INET, SOCK_STREAM, 0);
389 int fd = xsocket(AF_INET, SOCK_STREAM, 0); 668
390 669 strncpy_IFNAMSIZ(ifr.ifr_name, br);
391 strncpy_IFNAMSIZ(ifr.ifr_name, br); 670 ifr.ifr_ifindex = if_nametoindex(*argv);
392 ifr.ifr_ifindex = if_nametoindex(*argv); 671 if (ifr.ifr_ifindex == 0) {
393 if (ifr.ifr_ifindex == 0) { 672 bb_perror_msg_and_die("iface %s", *argv);
394 bb_perror_msg_and_die("iface %s", *argv);
395 }
396 ioctl_or_perror_and_die(fd,
397 key == ARG_addif ? SIOCBRADDIF : SIOCBRDELIF,
398 &ifr, "bridge %s", br
399 );
400 //close(fd);
401 //goto done_next_argv;
402 if (ENABLE_FEATURE_CLEAN_UP)
403 close(fd);
404 return EXIT_SUCCESS;
405 } 673 }
406 674 ioctl_or_perror_and_die(fd,
407// done_next_argv: 675 key == ARG_addif ? SIOCBRADDIF : SIOCBRDELIF,
408// argv++; 676 &ifr, "bridge %s", br
409// done: 677 );
678 if (ENABLE_FEATURE_CLEAN_UP)
679 close(fd);
410 } 680 }
411 681
412 return EXIT_SUCCESS; 682 return EXIT_SUCCESS;
diff --git a/networking/netstat.c b/networking/netstat.c
index 29b891cdc..c7934423b 100644
--- a/networking/netstat.c
+++ b/networking/netstat.c
@@ -172,7 +172,7 @@ struct prg_node {
172#define PRG_HASH_SIZE 211 172#define PRG_HASH_SIZE 211
173 173
174struct globals { 174struct globals {
175 smallint flags; 175 smalluint flags;
176#if ENABLE_FEATURE_NETSTAT_PRG 176#if ENABLE_FEATURE_NETSTAT_PRG
177 smallint prg_cache_loaded; 177 smallint prg_cache_loaded;
178 struct prg_node *prg_hash[PRG_HASH_SIZE]; 178 struct prg_node *prg_hash[PRG_HASH_SIZE];
diff --git a/networking/nslookup.c b/networking/nslookup.c
index 8adde14b8..c43e60558 100644
--- a/networking/nslookup.c
+++ b/networking/nslookup.c
@@ -283,6 +283,7 @@ static const struct {
283 { ns_t_cname, "CNAME" }, 283 { ns_t_cname, "CNAME" },
284 { ns_t_mx, "MX" }, 284 { ns_t_mx, "MX" },
285 { ns_t_txt, "TXT" }, 285 { ns_t_txt, "TXT" },
286 { ns_t_srv, "SRV" },
286 { ns_t_ptr, "PTR" }, 287 { ns_t_ptr, "PTR" },
287 { ns_t_any, "ANY" }, 288 { ns_t_any, "ANY" },
288}; 289};
@@ -435,6 +436,25 @@ static int parse_reply(const unsigned char *msg, size_t len)
435 } 436 }
436 break; 437 break;
437 438
439 case ns_t_srv:
440 if (rdlen < 6) {
441 //printf("SRV record too short\n");
442 return -1;
443 }
444
445 cp = ns_rr_rdata(rr);
446 n = ns_name_uncompress(ns_msg_base(handle), ns_msg_end(handle),
447 cp + 6, dname, sizeof(dname));
448
449 if (n < 0) {
450 //printf("Unable to uncompress domain: %s\n", strerror(errno));
451 return -1;
452 }
453
454 printf("%s\tservice = %u %u %u %s\n", ns_rr_name(rr),
455 ns_get16(cp), ns_get16(cp + 2), ns_get16(cp + 4), dname);
456 break;
457
438 case ns_t_soa: 458 case ns_t_soa:
439 if (rdlen < 20) { 459 if (rdlen < 20) {
440 dbg("SOA record too short:%d\n", rdlen); 460 dbg("SOA record too short:%d\n", rdlen);
@@ -615,9 +635,15 @@ static int send_queries(struct ns *ns)
615 G.query[qn].name, rcodes[rcode]); 635 G.query[qn].name, rcodes[rcode]);
616 G.exitcode = EXIT_FAILURE; 636 G.exitcode = EXIT_FAILURE;
617 } else { 637 } else {
618 if (parse_reply(reply, recvlen) < 0) { 638 switch (parse_reply(reply, recvlen)) {
639 case -1:
619 printf("*** Can't find %s: Parse error\n", G.query[qn].name); 640 printf("*** Can't find %s: Parse error\n", G.query[qn].name);
620 G.exitcode = EXIT_FAILURE; 641 G.exitcode = EXIT_FAILURE;
642 break;
643
644 case 0:
645 printf("*** Can't find %s: No answer\n", G.query[qn].name);
646 break;
621 } 647 }
622 } 648 }
623 bb_putchar('\n'); 649 bb_putchar('\n');
diff --git a/networking/ntpd.c b/networking/ntpd.c
index 48dc1c379..0f12409f9 100644
--- a/networking/ntpd.c
+++ b/networking/ntpd.c
@@ -164,7 +164,7 @@
164 */ 164 */
165 165
166#define INITIAL_SAMPLES 4 /* how many samples do we want for init */ 166#define INITIAL_SAMPLES 4 /* how many samples do we want for init */
167#define MIN_FREQHOLD 12 /* adjust offset, but not freq in this many first adjustments */ 167#define MIN_FREQHOLD 10 /* adjust offset, but not freq in this many first adjustments */
168#define BAD_DELAY_GROWTH 4 /* drop packet if its delay grew by more than this factor */ 168#define BAD_DELAY_GROWTH 4 /* drop packet if its delay grew by more than this factor */
169 169
170#define RETRY_INTERVAL 32 /* on send/recv error, retry in N secs (need to be power of 2) */ 170#define RETRY_INTERVAL 32 /* on send/recv error, retry in N secs (need to be power of 2) */
@@ -504,12 +504,14 @@ static ALWAYS_INLINE double MAXD(double a, double b)
504 return a; 504 return a;
505 return b; 505 return b;
506} 506}
507#if !USING_KERNEL_PLL_LOOP
507static ALWAYS_INLINE double MIND(double a, double b) 508static ALWAYS_INLINE double MIND(double a, double b)
508{ 509{
509 if (a < b) 510 if (a < b)
510 return a; 511 return a;
511 return b; 512 return b;
512} 513}
514#endif
513static NOINLINE double my_SQRT(double X) 515static NOINLINE double my_SQRT(double X)
514{ 516{
515 union { 517 union {
@@ -1874,9 +1876,11 @@ update_local_clock(peer_t *p)
1874//15:31:53.473 update from:<IP> offset:+0.000007 delay:0.158142 jitter:0.010922 clock drift:+9.343ppm tc:6 1876//15:31:53.473 update from:<IP> offset:+0.000007 delay:0.158142 jitter:0.010922 clock drift:+9.343ppm tc:6
1875//15:32:58.902 update from:<IP> offset:-0.000728 delay:0.158222 jitter:0.009454 clock drift:+9.298ppm tc:6 1877//15:32:58.902 update from:<IP> offset:-0.000728 delay:0.158222 jitter:0.009454 clock drift:+9.298ppm tc:6
1876 /* 1878 /*
1877 * This expression would choose MIN_FREQHOLD + 8 in the above example. 1879 * This expression would choose MIN_FREQHOLD + 14 in the above example
1880 * (off_032 is +1 for each 0.032768 seconds of offset).
1878 */ 1881 */
1879 G.FREQHOLD_cnt = 1 + MIN_FREQHOLD + ((unsigned)(abs(tmx.offset)) >> 16); 1882 unsigned off_032 = abs((int)(tmx.offset >> 15));
1883 G.FREQHOLD_cnt = 1 + MIN_FREQHOLD + off_032;
1880 } 1884 }
1881 G.FREQHOLD_cnt--; 1885 G.FREQHOLD_cnt--;
1882 tmx.status |= STA_FREQHOLD; 1886 tmx.status |= STA_FREQHOLD;
diff --git a/networking/route.c b/networking/route.c
index a5d8d7cb9..e785b1da6 100644
--- a/networking/route.c
+++ b/networking/route.c
@@ -628,6 +628,7 @@ static void INET6_displayroutes(void)
628 628
629 r = 0; 629 r = 0;
630 while (1) { 630 while (1) {
631 memset(&snaddr6, 0, sizeof(snaddr6));
631 inet_pton(AF_INET6, addr6x + r, 632 inet_pton(AF_INET6, addr6x + r,
632 (struct sockaddr *) &snaddr6.sin6_addr); 633 (struct sockaddr *) &snaddr6.sin6_addr);
633 snaddr6.sin6_family = AF_INET6; 634 snaddr6.sin6_family = AF_INET6;
diff --git a/networking/tc.c b/networking/tc.c
index 2e1078d31..510684443 100644
--- a/networking/tc.c
+++ b/networking/tc.c
@@ -215,8 +215,6 @@ static int prio_print_opt(struct rtattr *opt)
215 if (opt == NULL) 215 if (opt == NULL)
216 return 0; 216 return 0;
217 parse_rtattr_nested_compat(tb, TCA_PRIO_MAX, opt, qopt, sizeof(*qopt)); 217 parse_rtattr_nested_compat(tb, TCA_PRIO_MAX, opt, qopt, sizeof(*qopt));
218 if (tb == NULL)
219 return 0;
220 printf("bands %u priomap ", qopt->bands); 218 printf("bands %u priomap ", qopt->bands);
221 for (i=0; i<=TC_PRIO_MAX; i++) 219 for (i=0; i<=TC_PRIO_MAX; i++)
222 printf(" %d", qopt->priomap[i]); 220 printf(" %d", qopt->priomap[i]);
diff --git a/networking/telnet.c b/networking/telnet.c
index fa1628723..9fc85050b 100644
--- a/networking/telnet.c
+++ b/networking/telnet.c
@@ -238,6 +238,18 @@ static void handle_net_output(int len)
238 *dst = '\r'; /* Enter -> CR LF */ 238 *dst = '\r'; /* Enter -> CR LF */
239 *++dst = '\n'; 239 *++dst = '\n';
240 } 240 }
241#if 0
242/* putty's "special commands" mode does this: */
243/* Korenix 3005 switch needs at least the backspace tweak */
244 if (c == 0x08 || c == 0x7f) { /* ctrl+h || backspace */
245 *dst = IAC;
246 *++dst = EC;
247 }
248 if (c == 0x03) { /* ctrl+c */
249 *dst = IAC;
250 *++dst = IP;
251 }
252#endif
241 dst++; 253 dst++;
242 } 254 }
243 if (dst - outbuf != 0) 255 if (dst - outbuf != 0)
@@ -248,7 +260,7 @@ static void handle_net_input(int len)
248{ 260{
249 byte c; 261 byte c;
250 int i; 262 int i;
251 int cstart = cstart; /* for compiler */ 263 int cstart = 0;
252 264
253 i = 0; 265 i = 0;
254 //bb_error_msg("[%u,'%.*s']", G.telstate, len, G.buf); 266 //bb_error_msg("[%u,'%.*s']", G.telstate, len, G.buf);
diff --git a/networking/tls_aesgcm.c b/networking/tls_aesgcm.c
index a4663cd79..5ddcdd2ad 100644
--- a/networking/tls_aesgcm.c
+++ b/networking/tls_aesgcm.c
@@ -13,6 +13,7 @@ typedef uint32_t word32;
13 13
14/* from wolfssl-3.15.3/wolfcrypt/src/aes.c */ 14/* from wolfssl-3.15.3/wolfcrypt/src/aes.c */
15 15
16#ifdef UNUSED
16static ALWAYS_INLINE void FlattenSzInBits(byte* buf, word32 sz) 17static ALWAYS_INLINE void FlattenSzInBits(byte* buf, word32 sz)
17{ 18{
18 /* Multiply the sz by 8 */ 19 /* Multiply the sz by 8 */
@@ -32,6 +33,7 @@ static ALWAYS_INLINE void FlattenSzInBits(byte* buf, word32 sz)
32// buf[7] = sz & 0xff; 33// buf[7] = sz & 0xff;
33 *(uint32_t*)(buf + 4) = SWAP_BE32(sz); 34 *(uint32_t*)(buf + 4) = SWAP_BE32(sz);
34} 35}
36#endif
35 37
36static void RIGHTSHIFTX(byte* x) 38static void RIGHTSHIFTX(byte* x)
37{ 39{
diff --git a/networking/traceroute.c b/networking/traceroute.c
index 0435d6ba6..06d3f19da 100644
--- a/networking/traceroute.c
+++ b/networking/traceroute.c
@@ -546,11 +546,11 @@ pr_type(unsigned char t)
546 }; 546 };
547# if ENABLE_TRACEROUTE6 547# if ENABLE_TRACEROUTE6
548 static const char *const ttab6[] = { 548 static const char *const ttab6[] = {
549[0] "Error", "Dest Unreachable", "Packet Too Big", "Time Exceeded", 549[0] = "Error", "Dest Unreachable", "Packet Too Big", "Time Exceeded",
550[4] "Param Problem", 550[4] = "Param Problem",
551[8] "Echo Request", "Echo Reply", "Membership Query", "Membership Report", 551[8] = "Echo Request", "Echo Reply", "Membership Query", "Membership Report",
552[12] "Membership Reduction", "Router Solicit", "Router Advert", "Neighbor Solicit", 552[12] = "Membership Reduction", "Router Solicit", "Router Advert", "Neighbor Solicit",
553[16] "Neighbor Advert", "Redirect", 553[16] = "Neighbor Advert", "Redirect",
554 }; 554 };
555 555
556 if (dest_lsa->u.sa.sa_family == AF_INET6) { 556 if (dest_lsa->u.sa.sa_family == AF_INET6) {
diff --git a/networking/udhcp/common.c b/networking/udhcp/common.c
index 4a452cdb9..9ec752dfc 100644
--- a/networking/udhcp/common.c
+++ b/networking/udhcp/common.c
@@ -539,7 +539,7 @@ int FAST_FUNC udhcp_str2optset(const char *const_str, void *arg,
539 539
540 if (optflag->flags == OPTION_BIN) { 540 if (optflag->flags == OPTION_BIN) {
541 val = strtok(NULL, ""); /* do not split "'q w e'" */ 541 val = strtok(NULL, ""); /* do not split "'q w e'" */
542 trim(val); 542 if (val) trim(val);
543 } else 543 } else
544 val = strtok(NULL, ", \t"); 544 val = strtok(NULL, ", \t");
545 if (!val) 545 if (!val)
diff --git a/networking/udhcp/d6_dhcpc.c b/networking/udhcp/d6_dhcpc.c
index 9d8e17c51..85c410a7c 100644
--- a/networking/udhcp/d6_dhcpc.c
+++ b/networking/udhcp/d6_dhcpc.c
@@ -123,6 +123,7 @@ static const char udhcpc6_longopts[] ALIGN1 =
123 "request-option\0" Required_argument "O" 123 "request-option\0" Required_argument "O"
124 "no-default-options\0" No_argument "o" 124 "no-default-options\0" No_argument "o"
125 "foreground\0" No_argument "f" 125 "foreground\0" No_argument "f"
126 "stateless\0" No_argument "l"
126 USE_FOR_MMU( 127 USE_FOR_MMU(
127 "background\0" No_argument "b" 128 "background\0" No_argument "b"
128 ) 129 )
@@ -147,9 +148,10 @@ enum {
147 OPT_o = 1 << 12, 148 OPT_o = 1 << 12,
148 OPT_x = 1 << 13, 149 OPT_x = 1 << 13,
149 OPT_f = 1 << 14, 150 OPT_f = 1 << 14,
150 OPT_d = 1 << 15, 151 OPT_l = 1 << 15,
152 OPT_d = 1 << 16,
151/* The rest has variable bit positions, need to be clever */ 153/* The rest has variable bit positions, need to be clever */
152 OPTBIT_d = 15, 154 OPTBIT_d = 16,
153 USE_FOR_MMU( OPTBIT_b,) 155 USE_FOR_MMU( OPTBIT_b,)
154 ///IF_FEATURE_UDHCPC_ARPING(OPTBIT_a,) 156 ///IF_FEATURE_UDHCPC_ARPING(OPTBIT_a,)
155 IF_FEATURE_UDHCP_PORT( OPTBIT_P,) 157 IF_FEATURE_UDHCP_PORT( OPTBIT_P,)
@@ -479,15 +481,31 @@ static ALWAYS_INLINE uint32_t random_xid(void)
479/* Initialize the packet with the proper defaults */ 481/* Initialize the packet with the proper defaults */
480static uint8_t *init_d6_packet(struct d6_packet *packet, char type, uint32_t xid) 482static uint8_t *init_d6_packet(struct d6_packet *packet, char type, uint32_t xid)
481{ 483{
484 uint8_t *ptr;
482 struct d6_option *clientid; 485 struct d6_option *clientid;
486 unsigned secs;
483 487
484 memset(packet, 0, sizeof(*packet)); 488 memset(packet, 0, sizeof(*packet));
485 489
486 packet->d6_xid32 = xid; 490 packet->d6_xid32 = xid;
487 packet->d6_msg_type = type; 491 packet->d6_msg_type = type;
488 492
493 /* ELAPSED_TIME option is required to be present by the RFC,
494 * and some servers do check for its presense. [which?]
495 */
496 ptr = packet->d6_options; /* NB: it is 32-bit aligned */
497 *((uint32_t*)ptr) = htonl((D6_OPT_ELAPSED_TIME << 16) + 2);
498 ptr += 4;
499 client_data.last_secs = monotonic_sec();
500 if (client_data.first_secs == 0)
501 client_data.first_secs = client_data.last_secs;
502 secs = client_data.last_secs - client_data.first_secs;
503 *((uint16_t*)ptr) = (secs < 0xffff) ? htons(secs) : 0xffff;
504 ptr += 2;
505
506 /* add CLIENTID option */
489 clientid = (void*)client_data.clientid; 507 clientid = (void*)client_data.clientid;
490 return mempcpy(packet->d6_options, clientid, clientid->len + 2+2); 508 return mempcpy(ptr, clientid, clientid->len + 2+2);
491} 509}
492 510
493static uint8_t *add_d6_client_options(uint8_t *ptr) 511static uint8_t *add_d6_client_options(uint8_t *ptr)
@@ -544,6 +562,46 @@ static int d6_mcast_from_client_data_ifindex(struct d6_packet *packet, uint8_t *
544 ); 562 );
545} 563}
546 564
565/* RFC 3315 18.1.5. Creation and Transmission of Information-request Messages
566 *
567 * The client uses an Information-request message to obtain
568 * configuration information without having addresses assigned to it.
569 *
570 * The client sets the "msg-type" field to INFORMATION-REQUEST. The
571 * client generates a transaction ID and inserts this value in the
572 * "transaction-id" field.
573 *
574 * The client SHOULD include a Client Identifier option to identify
575 * itself to the server. If the client does not include a Client
576 * Identifier option, the server will not be able to return any client-
577 * specific options to the client, or the server may choose not to
578 * respond to the message at all. The client MUST include a Client
579 * Identifier option if the Information-Request message will be
580 * authenticated.
581 *
582 * The client MUST include an Option Request option (see section 22.7)
583 * to indicate the options the client is interested in receiving. The
584 * client MAY include options with data values as hints to the server
585 * about parameter values the client would like to have returned.
586 */
587/* NOINLINE: limit stack usage in caller */
588static NOINLINE int send_d6_info_request(uint32_t xid)
589{
590 struct d6_packet packet;
591 uint8_t *opt_ptr;
592
593 /* Fill in: msg type, client id */
594 opt_ptr = init_d6_packet(&packet, D6_MSG_INFORMATION_REQUEST, xid);
595
596 /* Add options:
597 * "param req" option according to -O, options specified with -x
598 */
599 opt_ptr = add_d6_client_options(opt_ptr);
600
601 bb_error_msg("sending %s", "info request");
602 return d6_mcast_from_client_data_ifindex(&packet, opt_ptr);
603}
604
547/* Milticast a DHCPv6 Solicit packet to the network, with an optionally requested IP. 605/* Milticast a DHCPv6 Solicit packet to the network, with an optionally requested IP.
548 * 606 *
549 * RFC 3315 17.1.1. Creation of Solicit Messages 607 * RFC 3315 17.1.1. Creation of Solicit Messages
@@ -1129,6 +1187,8 @@ static void client_background(void)
1129//usage: "\n -o Don't request any options (unless -O is given)" 1187//usage: "\n -o Don't request any options (unless -O is given)"
1130//usage: "\n -r IPv6 Request this address ('no' to not request any IP)" 1188//usage: "\n -r IPv6 Request this address ('no' to not request any IP)"
1131//usage: "\n -d Request prefix" 1189//usage: "\n -d Request prefix"
1190//usage: "\n -l Send 'information request' instead of 'solicit'"
1191//usage: "\n (used for servers which do not assign IPv6 addresses)"
1132//usage: "\n -x OPT:VAL Include option OPT in sent packets (cumulative)" 1192//usage: "\n -x OPT:VAL Include option OPT in sent packets (cumulative)"
1133//usage: "\n Examples of string, numeric, and hex byte opts:" 1193//usage: "\n Examples of string, numeric, and hex byte opts:"
1134//usage: "\n -x hostname:bbox - option 12" 1194//usage: "\n -x hostname:bbox - option 12"
@@ -1181,7 +1241,7 @@ int udhcpc6_main(int argc UNUSED_PARAM, char **argv)
1181 /* Parse command line */ 1241 /* Parse command line */
1182 opt = getopt32long(argv, "^" 1242 opt = getopt32long(argv, "^"
1183 /* O,x: list; -T,-t,-A take numeric param */ 1243 /* O,x: list; -T,-t,-A take numeric param */
1184 "i:np:qRr:s:T:+t:+SA:+O:*ox:*fd" 1244 "i:np:qRr:s:T:+t:+SA:+O:*ox:*fld"
1185 USE_FOR_MMU("b") 1245 USE_FOR_MMU("b")
1186 ///IF_FEATURE_UDHCPC_ARPING("a") 1246 ///IF_FEATURE_UDHCPC_ARPING("a")
1187 IF_FEATURE_UDHCP_PORT("P:") 1247 IF_FEATURE_UDHCP_PORT("P:")
@@ -1198,15 +1258,20 @@ int udhcpc6_main(int argc UNUSED_PARAM, char **argv)
1198 ); 1258 );
1199 requested_ipv6 = NULL; 1259 requested_ipv6 = NULL;
1200 option_mask32 |= OPT_r; 1260 option_mask32 |= OPT_r;
1201 if (opt & OPT_r) { 1261 if (opt & OPT_l) {
1262 /* for -l, do not require IPv6 assignment from server */
1263 option_mask32 &= ~OPT_r;
1264 } else if (opt & OPT_r) {
1265 /* explicit "-r ARG" given */
1202 if (strcmp(str_r, "no") == 0) { 1266 if (strcmp(str_r, "no") == 0) {
1203 option_mask32 -= OPT_r; 1267 option_mask32 &= ~OPT_r;
1204 } else { 1268 } else {
1205 if (inet_pton(AF_INET6, str_r, &ipv6_buf) <= 0) 1269 if (inet_pton(AF_INET6, str_r, &ipv6_buf) <= 0)
1206 bb_error_msg_and_die("bad IPv6 address '%s'", str_r); 1270 bb_error_msg_and_die("bad IPv6 address '%s'", str_r);
1207 requested_ipv6 = &ipv6_buf; 1271 requested_ipv6 = &ipv6_buf;
1208 } 1272 }
1209 } 1273 }
1274
1210#if ENABLE_FEATURE_UDHCP_PORT 1275#if ENABLE_FEATURE_UDHCP_PORT
1211 if (opt & OPT_P) { 1276 if (opt & OPT_P) {
1212 CLIENT_PORT6 = xatou16(str_P); 1277 CLIENT_PORT6 = xatou16(str_P);
@@ -1353,7 +1418,10 @@ int udhcpc6_main(int argc UNUSED_PARAM, char **argv)
1353 if (packet_num == 0) 1418 if (packet_num == 0)
1354 xid = random_xid(); 1419 xid = random_xid();
1355 /* multicast */ 1420 /* multicast */
1356 send_d6_discover(xid, requested_ipv6); 1421 if (opt & OPT_l)
1422 send_d6_info_request(xid);
1423 else
1424 send_d6_discover(xid, requested_ipv6);
1357 timeout = discover_timeout; 1425 timeout = discover_timeout;
1358 packet_num++; 1426 packet_num++;
1359 continue; 1427 continue;
@@ -1418,7 +1486,10 @@ int udhcpc6_main(int argc UNUSED_PARAM, char **argv)
1418 * Anyway, it does recover by eventually failing through 1486 * Anyway, it does recover by eventually failing through
1419 * into INIT_SELECTING state. 1487 * into INIT_SELECTING state.
1420 */ 1488 */
1421 send_d6_renew(xid, &srv6_buf, requested_ipv6); 1489 if (opt & OPT_l)
1490 send_d6_info_request(xid);
1491 else
1492 send_d6_renew(xid, &srv6_buf, requested_ipv6);
1422 timeout >>= 1; 1493 timeout >>= 1;
1423 continue; 1494 continue;
1424 } 1495 }
@@ -1432,8 +1503,10 @@ int udhcpc6_main(int argc UNUSED_PARAM, char **argv)
1432 /* Lease is *really* about to run out, 1503 /* Lease is *really* about to run out,
1433 * try to find DHCP server using broadcast */ 1504 * try to find DHCP server using broadcast */
1434 if (timeout > 0) { 1505 if (timeout > 0) {
1435 /* send a broadcast renew request */ 1506 if (opt & OPT_l)
1436 send_d6_renew(xid, /*server_ipv6:*/ NULL, requested_ipv6); 1507 send_d6_info_request(xid);
1508 else /* send a broadcast renew request */
1509 send_d6_renew(xid, /*server_ipv6:*/ NULL, requested_ipv6);
1437 timeout >>= 1; 1510 timeout >>= 1;
1438 continue; 1511 continue;
1439 } 1512 }
@@ -1740,6 +1813,12 @@ int udhcpc6_main(int argc UNUSED_PARAM, char **argv)
1740 prefix_timeout = address_timeout; 1813 prefix_timeout = address_timeout;
1741 /* note: "int timeout" will not overflow even with 0xffffffff inputs here: */ 1814 /* note: "int timeout" will not overflow even with 0xffffffff inputs here: */
1742 timeout = (prefix_timeout < address_timeout ? prefix_timeout : address_timeout) / 2; 1815 timeout = (prefix_timeout < address_timeout ? prefix_timeout : address_timeout) / 2;
1816 if (opt & OPT_l) {
1817 /* TODO: request OPTION_INFORMATION_REFRESH_TIME (32)
1818 * and use its value instead of the default 1 day.
1819 */
1820 timeout = 24 * 60 * 60;
1821 }
1743 /* paranoia: must not be too small */ 1822 /* paranoia: must not be too small */
1744 /* timeout > 60 - ensures at least one unicast renew attempt */ 1823 /* timeout > 60 - ensures at least one unicast renew attempt */
1745 if (timeout < 61) 1824 if (timeout < 61)
diff --git a/networking/udhcp/dhcpc.c b/networking/udhcp/dhcpc.c
index 656295ff7..5a1f8fd7a 100644
--- a/networking/udhcp/dhcpc.c
+++ b/networking/udhcp/dhcpc.c
@@ -606,7 +606,7 @@ static ALWAYS_INLINE uint32_t random_xid(void)
606/* Initialize the packet with the proper defaults */ 606/* Initialize the packet with the proper defaults */
607static void init_packet(struct dhcp_packet *packet, char type) 607static void init_packet(struct dhcp_packet *packet, char type)
608{ 608{
609 uint16_t secs; 609 unsigned secs;
610 610
611 /* Fill in: op, htype, hlen, cookie fields; message type option: */ 611 /* Fill in: op, htype, hlen, cookie fields; message type option: */
612 udhcp_init_header(packet, type); 612 udhcp_init_header(packet, type);
@@ -617,7 +617,7 @@ static void init_packet(struct dhcp_packet *packet, char type)
617 if (client_data.first_secs == 0) 617 if (client_data.first_secs == 0)
618 client_data.first_secs = client_data.last_secs; 618 client_data.first_secs = client_data.last_secs;
619 secs = client_data.last_secs - client_data.first_secs; 619 secs = client_data.last_secs - client_data.first_secs;
620 packet->secs = htons(secs); 620 packet->secs = (secs < 0xffff) ? htons(secs) : 0xffff;
621 621
622 memcpy(packet->chaddr, client_data.client_mac, 6); 622 memcpy(packet->chaddr, client_data.client_mac, 6);
623 if (client_data.clientid) 623 if (client_data.clientid)
diff --git a/networking/udhcp/dhcpc.h b/networking/udhcp/dhcpc.h
index 42fe71a36..b407a6cdb 100644
--- a/networking/udhcp/dhcpc.h
+++ b/networking/udhcp/dhcpc.h
@@ -22,8 +22,8 @@ struct client_data_t {
22 uint8_t *hostname; /* Optional hostname to use */ 22 uint8_t *hostname; /* Optional hostname to use */
23 uint8_t *fqdn; /* Optional fully qualified domain name to use */ 23 uint8_t *fqdn; /* Optional fully qualified domain name to use */
24 24
25 uint16_t first_secs; 25 unsigned first_secs;
26 uint16_t last_secs; 26 unsigned last_secs;
27 27
28 int sockfd; 28 int sockfd;
29 smallint listen_mode; 29 smallint listen_mode;
diff --git a/networking/wget.c b/networking/wget.c
index 5b85cce1f..4365c76ce 100644
--- a/networking/wget.c
+++ b/networking/wget.c
@@ -1142,7 +1142,7 @@ static void download_one_url(const char *url)
1142 * We are not sure it exists on remote side */ 1142 * We are not sure it exists on remote side */
1143 } 1143 }
1144 1144
1145 redir_limit = 5; 1145 redir_limit = 16;
1146 resolve_lsa: 1146 resolve_lsa:
1147 lsa = xhost2sockaddr(server.host, server.port); 1147 lsa = xhost2sockaddr(server.host, server.port);
1148 if (!(option_mask32 & WGET_OPT_QUIET)) { 1148 if (!(option_mask32 & WGET_OPT_QUIET)) {
diff --git a/networking/whois.c b/networking/whois.c
index 55e1de964..caa71ac51 100644
--- a/networking/whois.c
+++ b/networking/whois.c
@@ -53,7 +53,9 @@ static char *query(const char *host, int port, const char *domain)
53 fp = xfdopen_for_read(fd); 53 fp = xfdopen_for_read(fd);
54 54
55 success = 0; 55 success = 0;
56 while (fgets(linebuf, sizeof(linebuf)-1, fp)) { 56 while (bufpos < 32*1024 /* paranoia */
57 && fgets(linebuf, sizeof(linebuf)-1, fp)
58 ) {
57 unsigned len; 59 unsigned len;
58 60
59 len = strcspn(linebuf, "\r\n"); 61 len = strcspn(linebuf, "\r\n");
diff --git a/procps/nmeter.c b/procps/nmeter.c
index f0eb36740..ae16d8548 100644
--- a/procps/nmeter.c
+++ b/procps/nmeter.c
@@ -122,11 +122,6 @@ static inline void reset_outbuf(void)
122 cur_outbuf = outbuf; 122 cur_outbuf = outbuf;
123} 123}
124 124
125static inline int outbuf_count(void)
126{
127 return cur_outbuf - outbuf;
128}
129
130static void print_outbuf(void) 125static void print_outbuf(void)
131{ 126{
132 int sz = cur_outbuf - outbuf; 127 int sz = cur_outbuf - outbuf;
diff --git a/runit/runsv.c b/runit/runsv.c
index ccc762d78..36d85101e 100644
--- a/runit/runsv.c
+++ b/runit/runsv.c
@@ -51,11 +51,9 @@ ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
51#if ENABLE_MONOTONIC_SYSCALL 51#if ENABLE_MONOTONIC_SYSCALL
52#include <sys/syscall.h> 52#include <sys/syscall.h>
53 53
54/* libc has incredibly messy way of doing this,
55 * typically requiring -lrt. We just skip all this mess */
56static void gettimeofday_ns(struct timespec *ts) 54static void gettimeofday_ns(struct timespec *ts)
57{ 55{
58 syscall(__NR_clock_gettime, CLOCK_REALTIME, ts); 56 clock_gettime(CLOCK_REALTIME, ts);
59} 57}
60#else 58#else
61static void gettimeofday_ns(struct timespec *ts) 59static void gettimeofday_ns(struct timespec *ts)
diff --git a/shell/ash.c b/shell/ash.c
index 8a27d5cc3..95d0aebf0 100644
--- a/shell/ash.c
+++ b/shell/ash.c
@@ -638,7 +638,7 @@ extern struct globals_misc *BB_GLOBAL_CONST ash_ptr_to_globals_misc;
638#define random_gen (G_misc.random_gen ) 638#define random_gen (G_misc.random_gen )
639#define backgndpid (G_misc.backgndpid ) 639#define backgndpid (G_misc.backgndpid )
640#define INIT_G_misc() do { \ 640#define INIT_G_misc() do { \
641 (*(struct globals_misc**)&ash_ptr_to_globals_misc) = xzalloc(sizeof(G_misc)); \ 641 (*(struct globals_misc**)not_const_pp(&ash_ptr_to_globals_misc)) = xzalloc(sizeof(G_misc)); \
642 barrier(); \ 642 barrier(); \
643 curdir = nullstr; \ 643 curdir = nullstr; \
644 physdir = nullstr; \ 644 physdir = nullstr; \
@@ -1693,7 +1693,7 @@ extern struct globals_memstack *BB_GLOBAL_CONST ash_ptr_to_globals_memstack;
1693#define g_stacknleft (G_memstack.g_stacknleft) 1693#define g_stacknleft (G_memstack.g_stacknleft)
1694#define stackbase (G_memstack.stackbase ) 1694#define stackbase (G_memstack.stackbase )
1695#define INIT_G_memstack() do { \ 1695#define INIT_G_memstack() do { \
1696 (*(struct globals_memstack**)&ash_ptr_to_globals_memstack) = xzalloc(sizeof(G_memstack)); \ 1696 (*(struct globals_memstack**)not_const_pp(&ash_ptr_to_globals_memstack)) = xzalloc(sizeof(G_memstack)); \
1697 barrier(); \ 1697 barrier(); \
1698 g_stackp = &stackbase; \ 1698 g_stackp = &stackbase; \
1699 g_stacknxt = stackbase.space; \ 1699 g_stacknxt = stackbase.space; \
@@ -2316,7 +2316,7 @@ extern struct globals_var *BB_GLOBAL_CONST ash_ptr_to_globals_var;
2316#endif 2316#endif
2317#define INIT_G_var() do { \ 2317#define INIT_G_var() do { \
2318 unsigned i; \ 2318 unsigned i; \
2319 (*(struct globals_var**)&ash_ptr_to_globals_var) = xzalloc(sizeof(G_var)); \ 2319 (*(struct globals_var**)not_const_pp(&ash_ptr_to_globals_var)) = xzalloc(sizeof(G_var)); \
2320 barrier(); \ 2320 barrier(); \
2321 for (i = 0; i < ARRAY_SIZE(varinit_data); i++) { \ 2321 for (i = 0; i < ARRAY_SIZE(varinit_data); i++) { \
2322 varinit[i].flags = varinit_data[i].flags; \ 2322 varinit[i].flags = varinit_data[i].flags; \
@@ -13112,7 +13112,13 @@ checkend: {
13112 for (p = eofmark; STPUTC(c, out), *p; p++) { 13112 for (p = eofmark; STPUTC(c, out), *p; p++) {
13113 if (c != *p) 13113 if (c != *p)
13114 goto more_heredoc; 13114 goto more_heredoc;
13115 13115 /* FIXME: fails for backslash-newlined terminator:
13116 * cat <<EOF
13117 * ...
13118 * EO\
13119 * F
13120 * (see heredoc_bkslash_newline2.tests)
13121 */
13116 c = pgetc_without_PEOA(); 13122 c = pgetc_without_PEOA();
13117 } 13123 }
13118 13124
diff --git a/shell/ash_test/ash-redir/redir_stdin1.right b/shell/ash_test/ash-redir/redir_stdin1.right
new file mode 100644
index 000000000..1c6217e92
--- /dev/null
+++ b/shell/ash_test/ash-redir/redir_stdin1.right
@@ -0,0 +1,3 @@
1#Testing that stdin redirect is restored
2read2
3Ok:0
diff --git a/shell/ash_test/ash-redir/redir_stdin1.tests b/shell/ash_test/ash-redir/redir_stdin1.tests
new file mode 100755
index 000000000..f72253f9d
--- /dev/null
+++ b/shell/ash_test/ash-redir/redir_stdin1.tests
@@ -0,0 +1,7 @@
1#Testing that stdin redirect is restored
2echo read2 | $THIS_SH -c 'read r <redir_stdin1.tests
3echo $r
4read r
5echo $r
6'
7echo Ok:$?
diff --git a/shell/hush.c b/shell/hush.c
index 19b97e2a5..97202b953 100644
--- a/shell/hush.c
+++ b/shell/hush.c
@@ -573,7 +573,6 @@ typedef struct HFILE {
573 char *cur; 573 char *cur;
574 char *end; 574 char *end;
575 struct HFILE *next_hfile; 575 struct HFILE *next_hfile;
576 int is_stdin;
577 int fd; 576 int fd;
578 char buf[1024]; 577 char buf[1024];
579} HFILE; 578} HFILE;
@@ -973,6 +972,7 @@ struct globals {
973 unsigned execute_lineno; 972 unsigned execute_lineno;
974#endif 973#endif
975 HFILE *HFILE_list; 974 HFILE *HFILE_list;
975 HFILE *HFILE_stdin;
976 /* Which signals have non-DFL handler (even with no traps set)? 976 /* Which signals have non-DFL handler (even with no traps set)?
977 * Set at the start to: 977 * Set at the start to:
978 * (SIGQUIT + maybe SPECIAL_INTERACTIVE_SIGS + maybe SPECIAL_JOBSTOP_SIGS) 978 * (SIGQUIT + maybe SPECIAL_INTERACTIVE_SIGS + maybe SPECIAL_JOBSTOP_SIGS)
@@ -1603,7 +1603,8 @@ static HFILE *hfopen(const char *name)
1603 } 1603 }
1604 1604
1605 fp = xmalloc(sizeof(*fp)); 1605 fp = xmalloc(sizeof(*fp));
1606 fp->is_stdin = (name == NULL); 1606 if (name == NULL)
1607 G.HFILE_stdin = fp;
1607 fp->fd = fd; 1608 fp->fd = fd;
1608 fp->cur = fp->end = fp->buf; 1609 fp->cur = fp->end = fp->buf;
1609 fp->next_hfile = G.HFILE_list; 1610 fp->next_hfile = G.HFILE_list;
@@ -2666,7 +2667,7 @@ static int fgetc_interactive(struct in_str *i)
2666{ 2667{
2667 int ch; 2668 int ch;
2668 /* If it's interactive stdin, get new line. */ 2669 /* If it's interactive stdin, get new line. */
2669 if (G_interactive_fd && i->file->is_stdin) { 2670 if (G_interactive_fd && i->file == G.HFILE_stdin) {
2670 /* Returns first char (or EOF), the rest is in i->p[] */ 2671 /* Returns first char (or EOF), the rest is in i->p[] */
2671 ch = get_user_input(i); 2672 ch = get_user_input(i);
2672 G.promptmode = 1; /* PS2 */ 2673 G.promptmode = 1; /* PS2 */
@@ -3652,9 +3653,9 @@ static void debug_print_tree(struct pipe *pi, int lvl)
3652 fdprintf(2, "%*s cmd %d assignment_cnt:%d", 3653 fdprintf(2, "%*s cmd %d assignment_cnt:%d",
3653 lvl*2, "", prn, 3654 lvl*2, "", prn,
3654 command->assignment_cnt); 3655 command->assignment_cnt);
3655#if ENABLE_HUSH_LINENO_VAR 3656# if ENABLE_HUSH_LINENO_VAR
3656 fdprintf(2, " LINENO:%u", command->lineno); 3657 fdprintf(2, " LINENO:%u", command->lineno);
3657#endif 3658# endif
3658 if (command->group) { 3659 if (command->group) {
3659 fdprintf(2, " group %s: (argv=%p)%s%s\n", 3660 fdprintf(2, " group %s: (argv=%p)%s%s\n",
3660 CMDTYPE[command->cmd_type], 3661 CMDTYPE[command->cmd_type],
@@ -4770,9 +4771,9 @@ static int add_till_closing_bracket(o_string *dest, struct in_str *input, unsign
4770# endif 4771# endif
4771 end_ch &= (DOUBLE_CLOSE_CHAR_FLAG - 1); 4772 end_ch &= (DOUBLE_CLOSE_CHAR_FLAG - 1);
4772 4773
4773#if ENABLE_HUSH_INTERACTIVE 4774# if ENABLE_HUSH_INTERACTIVE
4774 G.promptmode = 1; /* PS2 */ 4775 G.promptmode = 1; /* PS2 */
4775#endif 4776# endif
4776 debug_printf_prompt("%s promptmode=%d\n", __func__, G.promptmode); 4777 debug_printf_prompt("%s promptmode=%d\n", __func__, G.promptmode);
4777 4778
4778 while (1) { 4779 while (1) {
@@ -4828,13 +4829,13 @@ static int add_till_closing_bracket(o_string *dest, struct in_str *input, unsign
4828 syntax_error_unterm_ch(end_ch); 4829 syntax_error_unterm_ch(end_ch);
4829 return 0; 4830 return 0;
4830 } 4831 }
4831#if 0 4832# if 0
4832 if (ch == '\n') { 4833 if (ch == '\n') {
4833 /* "backslash+newline", ignore both */ 4834 /* "backslash+newline", ignore both */
4834 o_delchr(dest); /* undo insertion of '\' */ 4835 o_delchr(dest); /* undo insertion of '\' */
4835 continue; 4836 continue;
4836 } 4837 }
4837#endif 4838# endif
4838 o_addchr(dest, ch); 4839 o_addchr(dest, ch);
4839 //bb_error_msg("%s:o_addchr('%c') after '\\'", __func__, ch); 4840 //bb_error_msg("%s:o_addchr('%c') after '\\'", __func__, ch);
4840 continue; 4841 continue;
@@ -4991,7 +4992,7 @@ static int parse_dollar(o_string *as_string,
4991 if (last_ch == 0) /* error? */ 4992 if (last_ch == 0) /* error? */
4992 return 0; 4993 return 0;
4993#else 4994#else
4994#error Simple code to only allow ${var} is not implemented 4995# error Simple code to only allow ${var} is not implemented
4995#endif 4996#endif
4996 if (as_string) { 4997 if (as_string) {
4997 o_addstr(as_string, dest->data + pos); 4998 o_addstr(as_string, dest->data + pos);
@@ -7605,7 +7606,9 @@ static int save_fd_on_redirect(int fd, int avoid_fd, struct squirrel **sqp)
7605 avoid_fd = 9; 7606 avoid_fd = 9;
7606 7607
7607#if ENABLE_HUSH_INTERACTIVE 7608#if ENABLE_HUSH_INTERACTIVE
7608 if (fd == G_interactive_fd) { 7609 if (fd != 0 /* don't trigger for G_interactive_fd == 0 (that's "not interactive" flag) */
7610 && fd == G_interactive_fd
7611 ) {
7609 /* Testcase: "ls -l /proc/$$/fd 255>&-" should work */ 7612 /* Testcase: "ls -l /proc/$$/fd 255>&-" should work */
7610 G_interactive_fd = xdup_CLOEXEC_and_close(G_interactive_fd, avoid_fd); 7613 G_interactive_fd = xdup_CLOEXEC_and_close(G_interactive_fd, avoid_fd);
7611 debug_printf_redir("redirect_fd %d: matches interactive_fd, moving it to %d\n", fd, G_interactive_fd); 7614 debug_printf_redir("redirect_fd %d: matches interactive_fd, moving it to %d\n", fd, G_interactive_fd);
@@ -7619,7 +7622,7 @@ static int save_fd_on_redirect(int fd, int avoid_fd, struct squirrel **sqp)
7619 /* No need to move script fds. 7622 /* No need to move script fds.
7620 * For NOMMU case, it's actively wrong: we'd change ->fd 7623 * For NOMMU case, it's actively wrong: we'd change ->fd
7621 * fields in memory for the parent, but parent's fds 7624 * fields in memory for the parent, but parent's fds
7622 * aren't be moved, it would use wrong fd! 7625 * aren't moved, it would use wrong fd!
7623 * Reproducer: "cmd 3>FILE" in script. 7626 * Reproducer: "cmd 3>FILE" in script.
7624 * If we would call move_HFILEs_on_redirect(), child would: 7627 * If we would call move_HFILEs_on_redirect(), child would:
7625 * fcntl64(3, F_DUPFD_CLOEXEC, 10) = 10 7628 * fcntl64(3, F_DUPFD_CLOEXEC, 10) = 10
@@ -7683,6 +7686,20 @@ static void restore_redirects(struct squirrel *sq)
7683 } 7686 }
7684 free(sq); 7687 free(sq);
7685 } 7688 }
7689 if (G.HFILE_stdin
7690 && G.HFILE_stdin->fd != STDIN_FILENO
7691 ) {
7692 /* Testcase: interactive "read r <FILE; echo $r; read r; echo $r".
7693 * Redirect moves ->fd to e.g. 10,
7694 * and it is not restored above (we do not restore script fds
7695 * after redirects, we just use new, "moved" fds).
7696 * However for stdin, get_user_input() -> read_line_input(),
7697 * and read builtin, depend on fd == STDIN_FILENO.
7698 */
7699 debug_printf_redir("restoring %d to stdin\n", G.HFILE_stdin->fd);
7700 xmove_fd(G.HFILE_stdin->fd, STDIN_FILENO);
7701 G.HFILE_stdin->fd = STDIN_FILENO;
7702 }
7686 7703
7687 /* If moved, G_interactive_fd stays on new fd, not restoring it */ 7704 /* If moved, G_interactive_fd stays on new fd, not restoring it */
7688} 7705}
@@ -8684,9 +8701,9 @@ static int process_wait_result(struct pipe *fg_pipe, pid_t childpid, int status)
8684 pi->cmds[i].pid = 0; 8701 pi->cmds[i].pid = 0;
8685 pi->alive_cmds--; 8702 pi->alive_cmds--;
8686 if (!pi->alive_cmds) { 8703 if (!pi->alive_cmds) {
8687#if ENABLE_HUSH_BASH_COMPAT 8704# if ENABLE_HUSH_BASH_COMPAT
8688 G.dead_job_exitcode = job_exited_or_stopped(pi); 8705 G.dead_job_exitcode = job_exited_or_stopped(pi);
8689#endif 8706# endif
8690 if (G_interactive_fd) { 8707 if (G_interactive_fd) {
8691 printf(JOB_STATUS_FORMAT, pi->jobid, 8708 printf(JOB_STATUS_FORMAT, pi->jobid,
8692 "Done", pi->cmdtext); 8709 "Done", pi->cmdtext);
@@ -9824,9 +9841,12 @@ static int set_mode(int state, char mode, const char *o_opt)
9824 IF_HUSH_MODE_X(G_x_mode = state;) 9841 IF_HUSH_MODE_X(G_x_mode = state;)
9825 IF_HUSH_MODE_X(if (G.x_mode_fd <= 0) G.x_mode_fd = dup_CLOEXEC(2, 10);) 9842 IF_HUSH_MODE_X(if (G.x_mode_fd <= 0) G.x_mode_fd = dup_CLOEXEC(2, 10);)
9826 break; 9843 break;
9844 case 'e':
9845 G.o_opt[OPT_O_ERREXIT] = state;
9846 break;
9827 case 'o': 9847 case 'o':
9828 if (!o_opt) { 9848 if (!o_opt) {
9829 /* "set -+o" without parameter. 9849 /* "set -o" or "set +o" without parameter.
9830 * in bash, set -o produces this output: 9850 * in bash, set -o produces this output:
9831 * pipefail off 9851 * pipefail off
9832 * and set +o: 9852 * and set +o:
@@ -9847,9 +9867,7 @@ static int set_mode(int state, char mode, const char *o_opt)
9847 G.o_opt[idx] = state; 9867 G.o_opt[idx] = state;
9848 break; 9868 break;
9849 } 9869 }
9850 case 'e': 9870 /* fall through to error */
9851 G.o_opt[OPT_O_ERREXIT] = state;
9852 break;
9853 default: 9871 default:
9854 return EXIT_FAILURE; 9872 return EXIT_FAILURE;
9855 } 9873 }
@@ -10213,8 +10231,6 @@ int hush_main(int argc, char **argv)
10213 G_saved_tty_pgrp = 0; 10231 G_saved_tty_pgrp = 0;
10214 } 10232 }
10215 } 10233 }
10216// TODO: track & disallow any attempts of user
10217// to (inadvertently) close/redirect G_interactive_fd
10218 } 10234 }
10219 debug_printf("interactive_fd:%d\n", G_interactive_fd); 10235 debug_printf("interactive_fd:%d\n", G_interactive_fd);
10220 if (G_interactive_fd) { 10236 if (G_interactive_fd) {
@@ -10536,10 +10552,10 @@ static int FAST_FUNC builtin_type(char **argv)
10536 if (0) {} /* make conditional compile easier below */ 10552 if (0) {} /* make conditional compile easier below */
10537 /*else if (find_alias(*argv)) 10553 /*else if (find_alias(*argv))
10538 type = "an alias";*/ 10554 type = "an alias";*/
10539#if ENABLE_HUSH_FUNCTIONS 10555# if ENABLE_HUSH_FUNCTIONS
10540 else if (find_function(*argv)) 10556 else if (find_function(*argv))
10541 type = "a function"; 10557 type = "a function";
10542#endif 10558# endif
10543 else if (find_builtin(*argv)) 10559 else if (find_builtin(*argv))
10544 type = "a shell builtin"; 10560 type = "a shell builtin";
10545 else if ((path = find_in_path(*argv)) != NULL) 10561 else if ((path = find_in_path(*argv)) != NULL)
@@ -10594,11 +10610,11 @@ static int FAST_FUNC builtin_read(char **argv)
10594 * Option string must start with "sr" to match BUILTIN_READ_xxx 10610 * Option string must start with "sr" to match BUILTIN_READ_xxx
10595 */ 10611 */
10596 params.read_flags = getopt32(argv, 10612 params.read_flags = getopt32(argv,
10597#if BASH_READ_D 10613# if BASH_READ_D
10598 "!srn:p:t:u:d:", &params.opt_n, &params.opt_p, &params.opt_t, &params.opt_u, &params.opt_d 10614 "!srn:p:t:u:d:", &params.opt_n, &params.opt_p, &params.opt_t, &params.opt_u, &params.opt_d
10599#else 10615# else
10600 "!srn:p:t:u:", &params.opt_n, &params.opt_p, &params.opt_t, &params.opt_u 10616 "!srn:p:t:u:", &params.opt_n, &params.opt_p, &params.opt_t, &params.opt_u
10601#endif 10617# endif
10602 ); 10618 );
10603 if ((uint32_t)params.read_flags == (uint32_t)-1) 10619 if ((uint32_t)params.read_flags == (uint32_t)-1)
10604 return EXIT_FAILURE; 10620 return EXIT_FAILURE;
@@ -10771,24 +10787,24 @@ static int FAST_FUNC builtin_export(char **argv)
10771{ 10787{
10772 unsigned opt_unexport; 10788 unsigned opt_unexport;
10773 10789
10774#if ENABLE_HUSH_EXPORT_N 10790# if ENABLE_HUSH_EXPORT_N
10775 /* "!": do not abort on errors */ 10791 /* "!": do not abort on errors */
10776 opt_unexport = getopt32(argv, "!n"); 10792 opt_unexport = getopt32(argv, "!n");
10777 if (opt_unexport == (uint32_t)-1) 10793 if (opt_unexport == (uint32_t)-1)
10778 return EXIT_FAILURE; 10794 return EXIT_FAILURE;
10779 argv += optind; 10795 argv += optind;
10780#else 10796# else
10781 opt_unexport = 0; 10797 opt_unexport = 0;
10782 argv++; 10798 argv++;
10783#endif 10799# endif
10784 10800
10785 if (argv[0] == NULL) { 10801 if (argv[0] == NULL) {
10786 char **e = environ; 10802 char **e = environ;
10787 if (e) { 10803 if (e) {
10788 while (*e) { 10804 while (*e) {
10789#if 0 10805# if 0
10790 puts(*e++); 10806 puts(*e++);
10791#else 10807# else
10792 /* ash emits: export VAR='VAL' 10808 /* ash emits: export VAR='VAL'
10793 * bash: declare -x VAR="VAL" 10809 * bash: declare -x VAR="VAL"
10794 * we follow ash example */ 10810 * we follow ash example */
@@ -10801,7 +10817,7 @@ static int FAST_FUNC builtin_export(char **argv)
10801 printf("export %.*s", (int)(p - s) + 1, s); 10817 printf("export %.*s", (int)(p - s) + 1, s);
10802 print_escaped(p + 1); 10818 print_escaped(p + 1);
10803 putchar('\n'); 10819 putchar('\n');
10804#endif 10820# endif
10805 } 10821 }
10806 /*fflush_all(); - done after each builtin anyway */ 10822 /*fflush_all(); - done after each builtin anyway */
10807 } 10823 }
@@ -10931,8 +10947,10 @@ static int FAST_FUNC builtin_set(char **argv)
10931 if (arg[0] != '+' && arg[0] != '-') 10947 if (arg[0] != '+' && arg[0] != '-')
10932 break; 10948 break;
10933 for (n = 1; arg[n]; ++n) { 10949 for (n = 1; arg[n]; ++n) {
10934 if (set_mode((arg[0] == '-'), arg[n], argv[1])) 10950 if (set_mode((arg[0] == '-'), arg[n], argv[1])) {
10935 goto error; 10951 bb_error_msg("%s: %s: invalid option", "set", arg);
10952 return EXIT_FAILURE;
10953 }
10936 if (arg[n] == 'o' && argv[1]) 10954 if (arg[n] == 'o' && argv[1])
10937 argv++; 10955 argv++;
10938 } 10956 }
@@ -10962,11 +10980,6 @@ static int FAST_FUNC builtin_set(char **argv)
10962 G.global_argc = 1 + string_array_len(pp + 1); 10980 G.global_argc = 1 + string_array_len(pp + 1);
10963 10981
10964 return EXIT_SUCCESS; 10982 return EXIT_SUCCESS;
10965
10966 /* Nothing known, so abort */
10967 error:
10968 bb_error_msg("%s: %s: invalid option", "set", arg);
10969 return EXIT_FAILURE;
10970} 10983}
10971#endif 10984#endif
10972 10985
@@ -11459,9 +11472,9 @@ static int FAST_FUNC builtin_kill(char **argv)
11459 11472
11460#if ENABLE_HUSH_WAIT 11473#if ENABLE_HUSH_WAIT
11461/* http://www.opengroup.org/onlinepubs/9699919799/utilities/wait.html */ 11474/* http://www.opengroup.org/onlinepubs/9699919799/utilities/wait.html */
11462#if !ENABLE_HUSH_JOB 11475# if !ENABLE_HUSH_JOB
11463# define wait_for_child_or_signal(pipe,pid) wait_for_child_or_signal(pid) 11476# define wait_for_child_or_signal(pipe,pid) wait_for_child_or_signal(pid)
11464#endif 11477# endif
11465static int wait_for_child_or_signal(struct pipe *waitfor_pipe, pid_t waitfor_pid) 11478static int wait_for_child_or_signal(struct pipe *waitfor_pipe, pid_t waitfor_pid)
11466{ 11479{
11467 int ret = 0; 11480 int ret = 0;
@@ -11493,7 +11506,7 @@ static int wait_for_child_or_signal(struct pipe *waitfor_pipe, pid_t waitfor_pid
11493/* Can't pass waitfor_pipe into checkjobs(): it won't be interruptible */ 11506/* Can't pass waitfor_pipe into checkjobs(): it won't be interruptible */
11494 ret = checkjobs(NULL, waitfor_pid); /* waitpid(WNOHANG) inside */ 11507 ret = checkjobs(NULL, waitfor_pid); /* waitpid(WNOHANG) inside */
11495 debug_printf_exec("checkjobs:%d\n", ret); 11508 debug_printf_exec("checkjobs:%d\n", ret);
11496#if ENABLE_HUSH_JOB 11509# if ENABLE_HUSH_JOB
11497 if (waitfor_pipe) { 11510 if (waitfor_pipe) {
11498 int rcode = job_exited_or_stopped(waitfor_pipe); 11511 int rcode = job_exited_or_stopped(waitfor_pipe);
11499 debug_printf_exec("job_exited_or_stopped:%d\n", rcode); 11512 debug_printf_exec("job_exited_or_stopped:%d\n", rcode);
@@ -11503,7 +11516,7 @@ static int wait_for_child_or_signal(struct pipe *waitfor_pipe, pid_t waitfor_pid
11503 break; 11516 break;
11504 } 11517 }
11505 } 11518 }
11506#endif 11519# endif
11507 /* if ECHILD, there are no children (ret is -1 or 0) */ 11520 /* if ECHILD, there are no children (ret is -1 or 0) */
11508 /* if ret == 0, no children changed state */ 11521 /* if ret == 0, no children changed state */
11509 /* if ret != 0, it's exitcode+1 of exited waitfor_pid child */ 11522 /* if ret != 0, it's exitcode+1 of exited waitfor_pid child */
@@ -11511,12 +11524,12 @@ static int wait_for_child_or_signal(struct pipe *waitfor_pipe, pid_t waitfor_pid
11511 ret--; 11524 ret--;
11512 if (ret < 0) /* if ECHILD, may need to fix "ret" */ 11525 if (ret < 0) /* if ECHILD, may need to fix "ret" */
11513 ret = 0; 11526 ret = 0;
11514#if ENABLE_HUSH_BASH_COMPAT 11527# if ENABLE_HUSH_BASH_COMPAT
11515 if (waitfor_pid == -1 && errno == ECHILD) { 11528 if (waitfor_pid == -1 && errno == ECHILD) {
11516 /* exitcode of "wait -n" with no children is 127, not 0 */ 11529 /* exitcode of "wait -n" with no children is 127, not 0 */
11517 ret = 127; 11530 ret = 127;
11518 } 11531 }
11519#endif 11532# endif
11520 sigprocmask(SIG_SETMASK, &oldset, NULL); 11533 sigprocmask(SIG_SETMASK, &oldset, NULL);
11521 break; 11534 break;
11522 } 11535 }
@@ -11545,14 +11558,14 @@ static int FAST_FUNC builtin_wait(char **argv)
11545 int status; 11558 int status;
11546 11559
11547 argv = skip_dash_dash(argv); 11560 argv = skip_dash_dash(argv);
11548#if ENABLE_HUSH_BASH_COMPAT 11561# if ENABLE_HUSH_BASH_COMPAT
11549 if (argv[0] && strcmp(argv[0], "-n") == 0) { 11562 if (argv[0] && strcmp(argv[0], "-n") == 0) {
11550 /* wait -n */ 11563 /* wait -n */
11551 /* (bash accepts "wait -n PID" too and ignores PID) */ 11564 /* (bash accepts "wait -n PID" too and ignores PID) */
11552 G.dead_job_exitcode = -1; 11565 G.dead_job_exitcode = -1;
11553 return wait_for_child_or_signal(NULL, -1 /*no job, wait for one job*/); 11566 return wait_for_child_or_signal(NULL, -1 /*no job, wait for one job*/);
11554 } 11567 }
11555#endif 11568# endif
11556 if (argv[0] == NULL) { 11569 if (argv[0] == NULL) {
11557 /* Don't care about wait results */ 11570 /* Don't care about wait results */
11558 /* Note 1: must wait until there are no more children */ 11571 /* Note 1: must wait until there are no more children */
@@ -11576,7 +11589,7 @@ static int FAST_FUNC builtin_wait(char **argv)
11576 do { 11589 do {
11577 pid_t pid = bb_strtou(*argv, NULL, 10); 11590 pid_t pid = bb_strtou(*argv, NULL, 10);
11578 if (errno || pid <= 0) { 11591 if (errno || pid <= 0) {
11579#if ENABLE_HUSH_JOB 11592# if ENABLE_HUSH_JOB
11580 if (argv[0][0] == '%') { 11593 if (argv[0][0] == '%') {
11581 struct pipe *wait_pipe; 11594 struct pipe *wait_pipe;
11582 ret = 127; /* bash compat for bad jobspecs */ 11595 ret = 127; /* bash compat for bad jobspecs */
@@ -11593,7 +11606,7 @@ static int FAST_FUNC builtin_wait(char **argv)
11593 /* else: parse_jobspec() already emitted error msg */ 11606 /* else: parse_jobspec() already emitted error msg */
11594 continue; 11607 continue;
11595 } 11608 }
11596#endif 11609# endif
11597 /* mimic bash message */ 11610 /* mimic bash message */
11598 bb_error_msg("wait: '%s': not a pid or valid job spec", *argv); 11611 bb_error_msg("wait: '%s': not a pid or valid job spec", *argv);
11599 ret = EXIT_FAILURE; 11612 ret = EXIT_FAILURE;
@@ -11615,7 +11628,7 @@ static int FAST_FUNC builtin_wait(char **argv)
11615 ret = G.last_bg_pid_exitcode; 11628 ret = G.last_bg_pid_exitcode;
11616 } else { 11629 } else {
11617 /* Example: "wait 1". mimic bash message */ 11630 /* Example: "wait 1". mimic bash message */
11618 bb_error_msg("wait: pid %d is not a child of this shell", (int)pid); 11631 bb_error_msg("wait: pid %u is not a child of this shell", (unsigned)pid);
11619 } 11632 }
11620 } else { 11633 } else {
11621 /* ??? */ 11634 /* ??? */
diff --git a/shell/hush_test/hush-redir/redir_stdin1.right b/shell/hush_test/hush-redir/redir_stdin1.right
new file mode 100644
index 000000000..1c6217e92
--- /dev/null
+++ b/shell/hush_test/hush-redir/redir_stdin1.right
@@ -0,0 +1,3 @@
1#Testing that stdin redirect is restored
2read2
3Ok:0
diff --git a/shell/hush_test/hush-redir/redir_stdin1.tests b/shell/hush_test/hush-redir/redir_stdin1.tests
new file mode 100755
index 000000000..f72253f9d
--- /dev/null
+++ b/shell/hush_test/hush-redir/redir_stdin1.tests
@@ -0,0 +1,7 @@
1#Testing that stdin redirect is restored
2echo read2 | $THIS_SH -c 'read r <redir_stdin1.tests
3echo $r
4read r
5echo $r
6'
7echo Ok:$?
diff --git a/shell/math.c b/shell/math.c
index eaf4f2453..aac5017d0 100644
--- a/shell/math.c
+++ b/shell/math.c
@@ -537,11 +537,40 @@ static arith_t strto_arith_t(const char *nptr, char **endptr)
537 base = (unsigned)n; 537 base = (unsigned)n;
538 n = 0; 538 n = 0;
539 nptr = *endptr + 1; 539 nptr = *endptr + 1;
540 /* bash allows "N#" (empty "nnnn" part) */ 540 for (;;) {
541 while (isdigit(*nptr)) { 541 unsigned digit = (unsigned)*nptr - '0';
542 if (digit >= 10 /* not 0..9 */
543 && digit <= 'z' - '0' /* needed to reject e.g. $((64#~)) */
544 ) {
545 /* in bases up to 36, case does not matter for a-z */
546 digit = (unsigned)(*nptr | 0x20) - ('a' - 10);
547 if (base > 36 && *nptr <= '_') {
548 /* otherwise, A-Z,@,_ are 36-61,62,63 */
549 if (*nptr == '_')
550 digit = 63;
551 else if (*nptr == '@')
552 digit = 62;
553 else if (digit < 36) /* A-Z */
554 digit += 36 - 10;
555 else
556 break; /* error: one of [\]^ */
557 }
558 //bb_error_msg("ch:'%c'%d digit:%u", *nptr, *nptr, digit);
559 //if (digit < 10) - example where we need this?
560 // break;
561 }
562 if (digit >= base)
563 break;
542 /* bash does not check for overflows */ 564 /* bash does not check for overflows */
543 n = n * base + (*nptr++ - '0'); 565 n = n * base + digit;
566 nptr++;
544 } 567 }
568 /* Note: we do not set errno on bad chars, we just set a pointer
569 * to the first invalid char. For example, this allows
570 * "N#" (empty "nnnn" part): 64#+1 is a valid expression,
571 * it means 64# + 1, whereas 64#~... is not, since ~ is not a valid
572 * operator.
573 */
545 *endptr = (char*)nptr; 574 *endptr = (char*)nptr;
546 return n; 575 return n;
547} 576}
diff --git a/shell/shell_common.c b/shell/shell_common.c
index 06a6b6e5f..be69ff249 100644
--- a/shell/shell_common.c
+++ b/shell/shell_common.c
@@ -371,52 +371,91 @@ shell_builtin_read(struct builtin_read_params *params)
371struct limits { 371struct limits {
372 uint8_t cmd; /* RLIMIT_xxx fit into it */ 372 uint8_t cmd; /* RLIMIT_xxx fit into it */
373 uint8_t factor_shift; /* shift by to get rlim_{cur,max} values */ 373 uint8_t factor_shift; /* shift by to get rlim_{cur,max} values */
374 const char *name;
375}; 374};
376 375
377static const struct limits limits_tbl[] = { 376static const struct limits limits_tbl[] = {
378 { RLIMIT_CORE, 9, "core file size (blocks)" }, // -c 377 { RLIMIT_CORE, 9, }, // -c
379 { RLIMIT_DATA, 10, "data seg size (kb)" }, // -d 378 { RLIMIT_DATA, 10, }, // -d
380 { RLIMIT_NICE, 0, "scheduling priority" }, // -e 379 { RLIMIT_NICE, 0, }, // -e
381 { RLIMIT_FSIZE, 9, "file size (blocks)" }, // -f 380 { RLIMIT_FSIZE, 9, }, // -f
382#define LIMIT_F_IDX 3 381#define LIMIT_F_IDX 3
383#ifdef RLIMIT_SIGPENDING 382#ifdef RLIMIT_SIGPENDING
384 { RLIMIT_SIGPENDING, 0, "pending signals" }, // -i 383 { RLIMIT_SIGPENDING, 0, }, // -i
385#endif 384#endif
386#ifdef RLIMIT_MEMLOCK 385#ifdef RLIMIT_MEMLOCK
387 { RLIMIT_MEMLOCK, 10, "max locked memory (kb)" }, // -l 386 { RLIMIT_MEMLOCK, 10, }, // -l
388#endif 387#endif
389#ifdef RLIMIT_RSS 388#ifdef RLIMIT_RSS
390 { RLIMIT_RSS, 10, "max memory size (kb)" }, // -m 389 { RLIMIT_RSS, 10, }, // -m
391#endif 390#endif
392#ifdef RLIMIT_NOFILE 391#ifdef RLIMIT_NOFILE
393 { RLIMIT_NOFILE, 0, "open files" }, // -n 392 { RLIMIT_NOFILE, 0, }, // -n
394#endif 393#endif
395#ifdef RLIMIT_MSGQUEUE 394#ifdef RLIMIT_MSGQUEUE
396 { RLIMIT_MSGQUEUE, 0, "POSIX message queues (bytes)" }, // -q 395 { RLIMIT_MSGQUEUE, 0, }, // -q
397#endif 396#endif
398#ifdef RLIMIT_RTPRIO 397#ifdef RLIMIT_RTPRIO
399 { RLIMIT_RTPRIO, 0, "real-time priority" }, // -r 398 { RLIMIT_RTPRIO, 0, }, // -r
400#endif 399#endif
401#ifdef RLIMIT_STACK 400#ifdef RLIMIT_STACK
402 { RLIMIT_STACK, 10, "stack size (kb)" }, // -s 401 { RLIMIT_STACK, 10, }, // -s
403#endif 402#endif
404#ifdef RLIMIT_CPU 403#ifdef RLIMIT_CPU
405 { RLIMIT_CPU, 0, "cpu time (seconds)" }, // -t 404 { RLIMIT_CPU, 0, }, // -t
406#endif 405#endif
407#ifdef RLIMIT_NPROC 406#ifdef RLIMIT_NPROC
408 { RLIMIT_NPROC, 0, "max user processes" }, // -u 407 { RLIMIT_NPROC, 0, }, // -u
409#endif 408#endif
410#ifdef RLIMIT_AS 409#ifdef RLIMIT_AS
411 { RLIMIT_AS, 10, "virtual memory (kb)" }, // -v 410 { RLIMIT_AS, 10, }, // -v
412#endif 411#endif
413#ifdef RLIMIT_LOCKS 412#ifdef RLIMIT_LOCKS
414 { RLIMIT_LOCKS, 0, "file locks" }, // -x 413 { RLIMIT_LOCKS, 0, }, // -x
415#endif 414#endif
416}; 415};
417// bash also shows: 416// bash also shows:
418//pipe size (512 bytes, -p) 8 417//pipe size (512 bytes, -p) 8
419 418
419static const char limits_help[] ALIGN1 =
420 "core file size (blocks)" // -c
421 "\0""data seg size (kb)" // -d
422 "\0""scheduling priority" // -e
423 "\0""file size (blocks)" // -f
424#ifdef RLIMIT_SIGPENDING
425 "\0""pending signals" // -i
426#endif
427#ifdef RLIMIT_MEMLOCK
428 "\0""max locked memory (kb)" // -l
429#endif
430#ifdef RLIMIT_RSS
431 "\0""max memory size (kb)" // -m
432#endif
433#ifdef RLIMIT_NOFILE
434 "\0""open files" // -n
435#endif
436#ifdef RLIMIT_MSGQUEUE
437 "\0""POSIX message queues (bytes)" // -q
438#endif
439#ifdef RLIMIT_RTPRIO
440 "\0""real-time priority" // -r
441#endif
442#ifdef RLIMIT_STACK
443 "\0""stack size (kb)" // -s
444#endif
445#ifdef RLIMIT_CPU
446 "\0""cpu time (seconds)" // -t
447#endif
448#ifdef RLIMIT_NPROC
449 "\0""max user processes" // -u
450#endif
451#ifdef RLIMIT_AS
452 "\0""virtual memory (kb)" // -v
453#endif
454#ifdef RLIMIT_LOCKS
455 "\0""file locks" // -x
456#endif
457;
458
420static const char limit_chars[] ALIGN1 = 459static const char limit_chars[] ALIGN1 =
421 "c" 460 "c"
422 "d" 461 "d"
@@ -607,10 +646,12 @@ shell_builtin_ulimit(char **argv)
607 if (!(opts & (OPT_hard | OPT_soft))) 646 if (!(opts & (OPT_hard | OPT_soft)))
608 opts |= (OPT_hard | OPT_soft); 647 opts |= (OPT_hard | OPT_soft);
609 if (opts & OPT_all) { 648 if (opts & OPT_all) {
649 const char *help = limits_help;
610 for (i = 0; i < ARRAY_SIZE(limits_tbl); i++) { 650 for (i = 0; i < ARRAY_SIZE(limits_tbl); i++) {
611 getrlimit(limits_tbl[i].cmd, &limit); 651 getrlimit(limits_tbl[i].cmd, &limit);
612 printf("%-32s(-%c) ", limits_tbl[i].name, limit_chars[i]); 652 printf("%-32s(-%c) ", help, limit_chars[i]);
613 printlim(opts, &limit, &limits_tbl[i]); 653 printlim(opts, &limit, &limits_tbl[i]);
654 help += strlen(help) + 1;
614 } 655 }
615 return EXIT_SUCCESS; 656 return EXIT_SUCCESS;
616 } 657 }
@@ -641,7 +682,7 @@ shell_builtin_ulimit(char **argv)
641 getrlimit(limits_tbl[i].cmd, &limit); 682 getrlimit(limits_tbl[i].cmd, &limit);
642 if (!val_str) { 683 if (!val_str) {
643 if (opt_cnt > 1) 684 if (opt_cnt > 1)
644 printf("%-32s(-%c) ", limits_tbl[i].name, limit_chars[i]); 685 printf("%-32s(-%c) ", nth_string(limits_help, i), limit_chars[i]);
645 printlim(opts, &limit, &limits_tbl[i]); 686 printlim(opts, &limit, &limits_tbl[i]);
646 } else { 687 } else {
647 rlim_t val = RLIM_INFINITY; 688 rlim_t val = RLIM_INFINITY;
diff --git a/testsuite/bc.tests b/testsuite/bc.tests
index 3fde60a2c..179d5d2a2 100755
--- a/testsuite/bc.tests
+++ b/testsuite/bc.tests
@@ -187,6 +187,11 @@ testing "bc { print 1 }" \
187 "1" \ 187 "1" \
188 "" "{ print 1 }" 188 "" "{ print 1 }"
189 189
190testing "bc comparison 1" \
191 "bc" \
192 "1\n" \
193 "" "-10 < -9"
194
190testing "bc nested loops and breaks" \ 195testing "bc nested loops and breaks" \
191 "bc" \ 196 "bc" \
192 "\ 197 "\
diff --git a/testsuite/dc.tests b/testsuite/dc.tests
index 8c3af4156..361bc8459 100755
--- a/testsuite/dc.tests
+++ b/testsuite/dc.tests
@@ -44,6 +44,41 @@ testing "dc complex without spaces (multiple args)" \
44optional FEATURE_DC_BIG 44optional FEATURE_DC_BIG
45# All tests below depend on FEATURE_DC_BIG 45# All tests below depend on FEATURE_DC_BIG
46 46
47testing "dc: x should execute strings" \
48 "dc -e'[40 2 +] x f'" \
49 "42\n" \
50 "" ""
51
52testing "dc: x should not execute or pop non-strings" \
53 "dc -e'42 x f'" \
54 "42\n" \
55 "" ""
56
57testing "dc: x should work with strings created from a" \
58 "dc -e'42 112 a x'" \
59 "42\n" \
60 "" ""
61
62testing "dc: p should print invalid escapes" \
63 "dc -e '[\q] p'" \
64 "\\q\n" \
65 "" ""
66
67testing "dc: p should print trailing backslashes" \
68 "dc -e '[q\] p'" \
69 "q\\\\\n" \
70 "" ""
71
72testing "dc: p should parse/print single backslashes" \
73 "dc -e '[\] p'" \
74 "\\\\\n" \
75 "" ""
76
77testing "dc: p should print single backslash strings" \
78 "dc -e '92 a p'" \
79 "\\\\\n" \
80 "" ""
81
47testing "dc read" \ 82testing "dc read" \
48 "dc -finput" \ 83 "dc -finput" \
49 "2\n9\n1\n" \ 84 "2\n9\n1\n" \
diff --git a/util-linux/fdisk.c b/util-linux/fdisk.c
index f28d4fdd2..e58cb0fd1 100644
--- a/util-linux/fdisk.c
+++ b/util-linux/fdisk.c
@@ -299,9 +299,6 @@ static int get_boot(enum action what);
299static int get_boot(void); 299static int get_boot(void);
300#endif 300#endif
301 301
302#define PLURAL 0
303#define SINGULAR 1
304
305static sector_t get_start_sect(const struct partition *p); 302static sector_t get_start_sect(const struct partition *p);
306static sector_t get_nr_sects(const struct partition *p); 303static sector_t get_nr_sects(const struct partition *p);
307 304
@@ -591,18 +588,18 @@ partname(const char *dev, int pno, int lth)
591 return bufp; 588 return bufp;
592} 589}
593 590
591#if ENABLE_FEATURE_SGI_LABEL || ENABLE_FEATURE_OSF_LABEL
594static ALWAYS_INLINE struct partition * 592static ALWAYS_INLINE struct partition *
595get_part_table(int i) 593get_part_table(int i)
596{ 594{
597 return ptes[i].part_table; 595 return ptes[i].part_table;
598} 596}
597#endif
599 598
600static const char * 599static ALWAYS_INLINE const char *
601str_units(int n) 600str_units(void)
602{ /* n==1: use singular */ 601{
603 if (n == 1) 602 return display_in_cyl_units ? "cylinder" : "sector";
604 return display_in_cyl_units ? "cylinder" : "sector";
605 return display_in_cyl_units ? "cylinders" : "sectors";
606} 603}
607 604
608static int 605static int
@@ -1778,8 +1775,8 @@ change_units(void)
1778{ 1775{
1779 display_in_cyl_units = !display_in_cyl_units; 1776 display_in_cyl_units = !display_in_cyl_units;
1780 update_units(); 1777 update_units();
1781 printf("Changing display/entry units to %s\n", 1778 printf("Changing display/entry units to %ss\n",
1782 str_units(PLURAL)); 1779 str_units());
1783} 1780}
1784 1781
1785static void 1782static void
@@ -2030,8 +2027,7 @@ check_consistency(const struct partition *p, int partition)
2030static void 2027static void
2031list_disk_geometry(void) 2028list_disk_geometry(void)
2032{ 2029{
2033 ullong bytes = ((ullong)total_number_of_sectors << 9); 2030 ullong xbytes = total_number_of_sectors / (1024*1024 / 512);
2034 ullong xbytes = bytes / (1024*1024);
2035 char x = 'M'; 2031 char x = 'M';
2036 2032
2037 if (xbytes >= 10000) { 2033 if (xbytes >= 10000) {
@@ -2041,11 +2037,12 @@ list_disk_geometry(void)
2041 } 2037 }
2042 printf("Disk %s: %llu %cB, %llu bytes, %"SECT_FMT"u sectors\n" 2038 printf("Disk %s: %llu %cB, %llu bytes, %"SECT_FMT"u sectors\n"
2043 "%u cylinders, %u heads, %u sectors/track\n" 2039 "%u cylinders, %u heads, %u sectors/track\n"
2044 "Units: %s of %u * %u = %u bytes\n\n", 2040 "Units: %ss of %u * %u = %u bytes\n"
2041 "\n",
2045 disk_device, xbytes, x, 2042 disk_device, xbytes, x,
2046 bytes, total_number_of_sectors, 2043 ((ullong)total_number_of_sectors * 512), total_number_of_sectors,
2047 g_cylinders, g_heads, g_sectors, 2044 g_cylinders, g_heads, g_sectors,
2048 str_units(PLURAL), 2045 str_units(),
2049 units_per_sector, sector_size, units_per_sector * sector_size 2046 units_per_sector, sector_size, units_per_sector * sector_size
2050 ); 2047 );
2051} 2048}
@@ -2486,7 +2483,7 @@ add_partition(int n, int sys)
2486 for (i = 0; i < g_partitions; i++) 2483 for (i = 0; i < g_partitions; i++)
2487 first[i] = (cround(first[i]) - 1) * units_per_sector; 2484 first[i] = (cround(first[i]) - 1) * units_per_sector;
2488 2485
2489 snprintf(mesg, sizeof(mesg), "First %s", str_units(SINGULAR)); 2486 snprintf(mesg, sizeof(mesg), "First %s", str_units());
2490 do { 2487 do {
2491 temp = start; 2488 temp = start;
2492 for (i = 0; i < g_partitions; i++) { 2489 for (i = 0; i < g_partitions; i++) {
@@ -2548,7 +2545,7 @@ add_partition(int n, int sys)
2548 } else { 2545 } else {
2549 snprintf(mesg, sizeof(mesg), 2546 snprintf(mesg, sizeof(mesg),
2550 "Last %s or +size{,K,M,G,T}", 2547 "Last %s or +size{,K,M,G,T}",
2551 str_units(SINGULAR) 2548 str_units()
2552 ); 2549 );
2553 stop = read_int(cround(start), cround(limit), cround(limit), cround(start), mesg); 2550 stop = read_int(cround(start), cround(limit), cround(limit), cround(start), mesg);
2554 if (display_in_cyl_units) { 2551 if (display_in_cyl_units) {
diff --git a/util-linux/fdisk_aix.c b/util-linux/fdisk_aix.c
index ee5df50e5..0a5e818fe 100644
--- a/util-linux/fdisk_aix.c
+++ b/util-linux/fdisk_aix.c
@@ -6,10 +6,10 @@
6 */ 6 */
7 7
8typedef struct { 8typedef struct {
9 unsigned int magic; /* expect AIX_LABEL_MAGIC */ 9 uint32_t magic; /* expect AIX_LABEL_MAGIC */
10 unsigned int fillbytes1[124]; 10 uint32_t fillbytes1[124];
11 unsigned int physical_volume_id; 11 uint32_t physical_volume_id;
12 unsigned int fillbytes2[124]; 12 uint32_t fillbytes2[124];
13} aix_partition; 13} aix_partition;
14 14
15#define AIX_LABEL_MAGIC 0xc9c2d4c1 15#define AIX_LABEL_MAGIC 0xc9c2d4c1
@@ -17,20 +17,18 @@ typedef struct {
17#define AIX_INFO_MAGIC 0x00072959 17#define AIX_INFO_MAGIC 0x00072959
18#define AIX_INFO_MAGIC_SWAPPED 0x59290700 18#define AIX_INFO_MAGIC_SWAPPED 0x59290700
19 19
20#define aixlabel ((aix_partition *)MBRbuffer)
21
22
23/* 20/*
24 Changes: 21 * Changes:
25 * 1999-03-20 Arnaldo Carvalho de Melo <acme@conectiva.com.br> 22 * 1999-03-20 Arnaldo Carvalho de Melo <acme@conectiva.com.br>
26 * Internationalization 23 * Internationalization
27 * 24 *
28 * 2003-03-20 Phillip Kesling <pkesling@sgi.com> 25 * 2003-03-20 Phillip Kesling <pkesling@sgi.com>
29 * Some fixes 26 * Some fixes
30*/ 27 */
31 28
32static smallint aix_other_endian; /* bool */ 29// Write-only vars, unfinished code?
33static smallint aix_volumes = 1; /* max 15 */ 30//static smallint aix_other_endian; /* bool */
31//static smallint aix_volumes = 1; /* max 15 */
34 32
35/* 33/*
36 * only dealing with free blocks here 34 * only dealing with free blocks here
@@ -54,18 +52,20 @@ aix_info(void)
54static int 52static int
55check_aix_label(void) 53check_aix_label(void)
56{ 54{
55 aix_partition *aixlabel = (void*)MBRbuffer;
56
57 if (aixlabel->magic != AIX_LABEL_MAGIC 57 if (aixlabel->magic != AIX_LABEL_MAGIC
58 && aixlabel->magic != AIX_LABEL_MAGIC_SWAPPED 58 && aixlabel->magic != AIX_LABEL_MAGIC_SWAPPED
59 ) { 59 ) {
60 current_label_type = 0; 60 current_label_type = LABEL_DOS;
61 aix_other_endian = 0; 61// aix_other_endian = 0;
62 return 0; 62 return 0;
63 } 63 }
64 aix_other_endian = (aixlabel->magic == AIX_LABEL_MAGIC_SWAPPED); 64// aix_other_endian = (aixlabel->magic == AIX_LABEL_MAGIC_SWAPPED);
65 update_units(); 65 update_units();
66 current_label_type = LABEL_AIX; 66 current_label_type = LABEL_AIX;
67 g_partitions = 1016; 67 g_partitions = 1016;
68 aix_volumes = 15; 68// aix_volumes = 15;
69 aix_info(); 69 aix_info();
70 /*aix_nolabel();*/ /* %% */ 70 /*aix_nolabel();*/ /* %% */
71 /*aix_label = 1;*/ /* %% */ 71 /*aix_label = 1;*/ /* %% */
diff --git a/util-linux/fdisk_gpt.c b/util-linux/fdisk_gpt.c
index dbe889f7c..e884e3dc1 100644
--- a/util-linux/fdisk_gpt.c
+++ b/util-linux/fdisk_gpt.c
@@ -161,7 +161,7 @@ check_gpt_label(void)
161 if (!valid_part_table_flag(MBRbuffer) 161 if (!valid_part_table_flag(MBRbuffer)
162 || first->sys_ind != LEGACY_GPT_TYPE 162 || first->sys_ind != LEGACY_GPT_TYPE
163 ) { 163 ) {
164 current_label_type = 0; 164 current_label_type = LABEL_DOS;
165 return 0; 165 return 0;
166 } 166 }
167 167
@@ -171,7 +171,7 @@ check_gpt_label(void)
171 gpt_hdr = (void *)pe.sectorbuffer; 171 gpt_hdr = (void *)pe.sectorbuffer;
172 172
173 if (gpt_hdr->magic != SWAP_LE64(GPT_MAGIC)) { 173 if (gpt_hdr->magic != SWAP_LE64(GPT_MAGIC)) {
174 current_label_type = 0; 174 current_label_type = LABEL_DOS;
175 return 0; 175 return 0;
176 } 176 }
177 177
@@ -194,7 +194,7 @@ check_gpt_label(void)
194 || SWAP_LE32(gpt_hdr->hdr_size) > sector_size 194 || SWAP_LE32(gpt_hdr->hdr_size) > sector_size
195 ) { 195 ) {
196 puts("\nwarning: unable to parse GPT disklabel\n"); 196 puts("\nwarning: unable to parse GPT disklabel\n");
197 current_label_type = 0; 197 current_label_type = LABEL_DOS;
198 return 0; 198 return 0;
199 } 199 }
200 200
diff --git a/util-linux/fdisk_osf.c b/util-linux/fdisk_osf.c
index 1328c1fcd..92180b2bc 100644
--- a/util-linux/fdisk_osf.c
+++ b/util-linux/fdisk_osf.c
@@ -470,7 +470,7 @@ xbsd_new_part(void)
470 end = xbsd_dlabel.d_secperunit - 1; 470 end = xbsd_dlabel.d_secperunit - 1;
471#endif 471#endif
472 472
473 snprintf(mesg, sizeof(mesg), "First %s", str_units(SINGULAR)); 473 snprintf(mesg, sizeof(mesg), "First %s", str_units());
474 begin = read_int(bsd_cround(begin), bsd_cround(begin), bsd_cround(end), 474 begin = read_int(bsd_cround(begin), bsd_cround(begin), bsd_cround(end),
475 0, mesg); 475 0, mesg);
476 476
@@ -478,7 +478,7 @@ xbsd_new_part(void)
478 begin = (begin - 1) * xbsd_dlabel.d_secpercyl; 478 begin = (begin - 1) * xbsd_dlabel.d_secpercyl;
479 479
480 snprintf(mesg, sizeof(mesg), "Last %s or +size or +sizeM or +sizeK", 480 snprintf(mesg, sizeof(mesg), "Last %s or +size or +sizeM or +sizeK",
481 str_units(SINGULAR)); 481 str_units());
482 end = read_int(bsd_cround(begin), bsd_cround(end), bsd_cround(end), 482 end = read_int(bsd_cround(begin), bsd_cround(end), bsd_cround(end),
483 bsd_cround(begin), mesg); 483 bsd_cround(begin), mesg);
484 484
diff --git a/util-linux/fdisk_sgi.c b/util-linux/fdisk_sgi.c
index 0e5491a19..c90c801e2 100644
--- a/util-linux/fdisk_sgi.c
+++ b/util-linux/fdisk_sgi.c
@@ -295,19 +295,19 @@ sgi_list_table(int xtra)
295 "%u cylinders, %u physical cylinders\n" 295 "%u cylinders, %u physical cylinders\n"
296 "%u extra sects/cyl, interleave %u:1\n" 296 "%u extra sects/cyl, interleave %u:1\n"
297 "%s\n" 297 "%s\n"
298 "Units = %s of %u * 512 bytes\n\n", 298 "Units = %ss of %u * 512 bytes\n\n",
299 disk_device, g_heads, g_sectors, g_cylinders, 299 disk_device, g_heads, g_sectors, g_cylinders,
300 SGI_SSWAP16(sgiparam.pcylcount), 300 SGI_SSWAP16(sgiparam.pcylcount),
301 SGI_SSWAP16(sgiparam.sparecyl), 301 SGI_SSWAP16(sgiparam.sparecyl),
302 SGI_SSWAP16(sgiparam.ilfact), 302 SGI_SSWAP16(sgiparam.ilfact),
303 (char *)sgilabel, 303 (char *)sgilabel,
304 str_units(PLURAL), units_per_sector); 304 str_units(), units_per_sector);
305 } else { 305 } else {
306 printf("\nDisk %s (SGI disk label): " 306 printf("\nDisk %s (SGI disk label): "
307 "%u heads, %u sectors, %u cylinders\n" 307 "%u heads, %u sectors, %u cylinders\n"
308 "Units = %s of %u * 512 bytes\n\n", 308 "Units = %ss of %u * 512 bytes\n\n",
309 disk_device, g_heads, g_sectors, g_cylinders, 309 disk_device, g_heads, g_sectors, g_cylinders,
310 str_units(PLURAL), units_per_sector ); 310 str_units(), units_per_sector );
311 } 311 }
312 312
313 w = strlen(disk_device); 313 w = strlen(disk_device);
@@ -720,7 +720,7 @@ sgi_add_partition(int n, int sys)
720 printf("You got a partition overlap on the disk. Fix it first!\n"); 720 printf("You got a partition overlap on the disk. Fix it first!\n");
721 return; 721 return;
722 } 722 }
723 snprintf(mesg, sizeof(mesg), "First %s", str_units(SINGULAR)); 723 snprintf(mesg, sizeof(mesg), "First %s", str_units());
724 while (1) { 724 while (1) {
725 if (sys == SGI_VOLUME) { 725 if (sys == SGI_VOLUME) {
726 last = sgi_get_lastblock(); 726 last = sgi_get_lastblock();
@@ -746,7 +746,7 @@ sgi_add_partition(int n, int sys)
746 printf("You will get a partition overlap on the disk. " 746 printf("You will get a partition overlap on the disk. "
747 "Fix it first!\n"); 747 "Fix it first!\n");
748 } 748 }
749 snprintf(mesg, sizeof(mesg), " Last %s", str_units(SINGULAR)); 749 snprintf(mesg, sizeof(mesg), " Last %s", str_units());
750 last = read_int(scround(first), scround(last)-1, scround(last)-1, 750 last = read_int(scround(first), scround(last)-1, scround(last)-1,
751 scround(first), mesg)+1; 751 scround(first), mesg)+1;
752 if (display_in_cyl_units) 752 if (display_in_cyl_units)
diff --git a/util-linux/fdisk_sun.c b/util-linux/fdisk_sun.c
index 3697a69b9..29d7c283a 100644
--- a/util-linux/fdisk_sun.c
+++ b/util-linux/fdisk_sun.c
@@ -491,7 +491,7 @@ add_sun_partition(int n, int sys)
491 return; 491 return;
492 } 492 }
493 } 493 }
494 snprintf(mesg, sizeof(mesg), "First %s", str_units(SINGULAR)); 494 snprintf(mesg, sizeof(mesg), "First %s", str_units());
495 while (1) { 495 while (1) {
496 if (whole_disk) 496 if (whole_disk)
497 first = read_int(0, 0, 0, 0, mesg); 497 first = read_int(0, 0, 0, 0, mesg);
@@ -546,7 +546,7 @@ and is of type 'Whole disk'\n");
546 } 546 }
547 snprintf(mesg, sizeof(mesg), 547 snprintf(mesg, sizeof(mesg),
548 "Last %s or +size or +sizeM or +sizeK", 548 "Last %s or +size or +sizeM or +sizeK",
549 str_units(SINGULAR)); 549 str_units());
550 if (whole_disk) 550 if (whole_disk)
551 last = read_int(scround(stop2), scround(stop2), scround(stop2), 551 last = read_int(scround(stop2), scround(stop2), scround(stop2),
552 0, mesg); 552 0, mesg);
@@ -567,8 +567,8 @@ and is of type 'Whole disk'\n");
567"You haven't covered the whole disk with the 3rd partition,\n" 567"You haven't covered the whole disk with the 3rd partition,\n"
568"but your value %u %s covers some other partition.\n" 568"but your value %u %s covers some other partition.\n"
569"Your entry has been changed to %u %s\n", 569"Your entry has been changed to %u %s\n",
570 scround(last), str_units(SINGULAR), 570 scround(last), str_units(),
571 scround(stop), str_units(SINGULAR)); 571 scround(stop), str_units());
572 last = stop; 572 last = stop;
573 } 573 }
574 } else if (!whole_disk && last > stop) 574 } else if (!whole_disk && last > stop)
@@ -636,20 +636,20 @@ sun_list_table(int xtra)
636 "%u cylinders, %u alternate cylinders, %u physical cylinders\n" 636 "%u cylinders, %u alternate cylinders, %u physical cylinders\n"
637 "%u extra sects/cyl, interleave %u:1\n" 637 "%u extra sects/cyl, interleave %u:1\n"
638 "%s\n" 638 "%s\n"
639 "Units = %s of %u * 512 bytes\n\n", 639 "Units = %ss of %u * 512 bytes\n\n",
640 disk_device, g_heads, g_sectors, SUN_SSWAP16(sunlabel->rspeed), 640 disk_device, g_heads, g_sectors, SUN_SSWAP16(sunlabel->rspeed),
641 g_cylinders, SUN_SSWAP16(sunlabel->nacyl), 641 g_cylinders, SUN_SSWAP16(sunlabel->nacyl),
642 SUN_SSWAP16(sunlabel->pcylcount), 642 SUN_SSWAP16(sunlabel->pcylcount),
643 SUN_SSWAP16(sunlabel->sparecyl), 643 SUN_SSWAP16(sunlabel->sparecyl),
644 SUN_SSWAP16(sunlabel->ilfact), 644 SUN_SSWAP16(sunlabel->ilfact),
645 (char *)sunlabel, 645 (char *)sunlabel,
646 str_units(PLURAL), units_per_sector); 646 str_units(), units_per_sector);
647 else 647 else
648 printf( 648 printf(
649 "\nDisk %s (Sun disk label): %u heads, %u sectors, %u cylinders\n" 649 "\nDisk %s (Sun disk label): %u heads, %u sectors, %u cylinders\n"
650 "Units = %s of %u * 512 bytes\n\n", 650 "Units = %ss of %u * 512 bytes\n\n",
651 disk_device, g_heads, g_sectors, g_cylinders, 651 disk_device, g_heads, g_sectors, g_cylinders,
652 str_units(PLURAL), units_per_sector); 652 str_units(), units_per_sector);
653 653
654 printf("%*s Flag Start End Blocks Id System\n", 654 printf("%*s Flag Start End Blocks Id System\n",
655 w + 1, "Device"); 655 w + 1, "Device");
diff --git a/util-linux/rdate.c b/util-linux/rdate.c
index 41aade5ea..bb1dc519a 100644
--- a/util-linux/rdate.c
+++ b/util-linux/rdate.c
@@ -95,9 +95,13 @@ int rdate_main(int argc UNUSED_PARAM, char **argv)
95 if (!(flags & 2)) { /* no -p (-s may be present) */ 95 if (!(flags & 2)) { /* no -p (-s may be present) */
96 if (time(NULL) == remote_time) 96 if (time(NULL) == remote_time)
97 bb_simple_error_msg("current time matches remote time"); 97 bb_simple_error_msg("current time matches remote time");
98 else 98 else {
99 if (stime(&remote_time) < 0) 99 struct timespec ts;
100 ts.tv_sec = remote_time;
101 ts.tv_nsec = 0;
102 if (clock_settime(CLOCK_REALTIME, &ts) < 0)
100 bb_simple_perror_msg_and_die("can't set time of day"); 103 bb_simple_perror_msg_and_die("can't set time of day");
104 }
101 } 105 }
102 106
103 if (flags != 1) /* not lone -s */ 107 if (flags != 1) /* not lone -s */
diff --git a/util-linux/taskset.c b/util-linux/taskset.c
index ed8878ad4..b542f8c83 100644
--- a/util-linux/taskset.c
+++ b/util-linux/taskset.c
@@ -20,6 +20,14 @@
20//config: Needed for machines with more than 32-64 CPUs: 20//config: Needed for machines with more than 32-64 CPUs:
21//config: affinity parameter 0xHHHHHHHHHHHHHHHHHHHH can be arbitrarily long 21//config: affinity parameter 0xHHHHHHHHHHHHHHHHHHHH can be arbitrarily long
22//config: in this case. Otherwise, it is limited to sizeof(long). 22//config: in this case. Otherwise, it is limited to sizeof(long).
23//config:
24//config:config FEATURE_TASKSET_CPULIST
25//config: bool "CPU list support (-c option)"
26//config: default y
27//config: depends on FEATURE_TASKSET_FANCY
28//config: help
29//config: Add support for taking/printing affinity as CPU list when '-c'
30//config: option is used. For example, it prints '0-3,7' instead of mask '8f'.
23 31
24//applet:IF_TASKSET(APPLET_NOEXEC(taskset, taskset, BB_DIR_USR_BIN, BB_SUID_DROP, taskset)) 32//applet:IF_TASKSET(APPLET_NOEXEC(taskset, taskset, BB_DIR_USR_BIN, BB_SUID_DROP, taskset))
25 33
@@ -108,26 +116,120 @@ static unsigned long *get_aff(int pid, unsigned *sz)
108 return mask; 116 return mask;
109} 117}
110 118
119#if ENABLE_FEATURE_TASKSET_CPULIST
120/*
121 * Parse the CPU list and set the mask accordingly.
122 *
123 * The list element can be either a CPU index or a range of CPU indices.
124 * Example: "1,3,5-7". Stride can be specified: "0-7:2" is "0,2,4,6".
125 * Note: leading and trailing whitespace is not allowed.
126 * util-linux 2.31 allows leading and sometimes trailing whitespace:
127 * ok: taskset -c ' 1, 2'
128 * ok: taskset -c ' 1 , 2'
129 * ok: taskset -c ' 1-7: 2 ,8'
130 * not ok: taskset -c ' 1 '
131 * not ok: taskset -c ' 1-7: 2 '
132 */
133static void parse_cpulist(ul *mask, unsigned max, char *s)
134{
135 char *aff = s;
136 for (;;) {
137 unsigned bit, end;
138 unsigned stride = 1;
139
140 bit = end = bb_strtou(s, &s, 10);
141 if (*s == '-') {
142 s++;
143 end = bb_strtou(s, &s, 10);
144 if (*s == ':') {
145 s++;
146 stride = bb_strtou(s, &s, 10);
147 }
148 }
149 if ((*s != ',' && *s != '\0')
150 || bit > end
151 || end == UINT_MAX /* bb_strtou returns this on malformed / ERANGE numbers */
152 || (stride - 1) > (UINT_MAX / 4)
153 /* disallow 0, malformed input, and too large stride prone to overflows */
154 ) {
155 bb_error_msg_and_die("bad affinity '%s'", aff);
156 }
157 while (bit <= end && bit < max) {
158 mask[bit / BITS_UL] |= (1UL << (bit & MASK_UL));
159 bit += stride;
160 }
161 if (*s == '\0')
162 break;
163 s++;
164 }
165}
166static void print_cpulist(const ul *mask, unsigned mask_size_in_bytes)
167{
168 const ul *mask_end;
169 const char *delim;
170 unsigned pos;
171 ul bit;
172
173 mask_end = mask + mask_size_in_bytes / sizeof(mask[0]);
174 delim = "";
175 pos = 0;
176 bit = 1;
177 for (;;) {
178 if (*mask & bit) {
179 unsigned onebit = pos + 1;
180 printf("%s%u", delim, pos);
181 do {
182 pos++;
183 bit <<= 1;
184 if (bit == 0) {
185 mask++;
186 if (mask >= mask_end)
187 break;
188 bit = 1;
189 }
190 } while (*mask & bit);
191 if (onebit != pos)
192 printf("-%u", pos - 1);
193 delim = ",";
194 }
195 pos++;
196 bit <<= 1;
197 if (bit == 0) {
198 mask++;
199 if (mask >= mask_end)
200 break;
201 bit = 1;
202 }
203 }
204 bb_putchar('\n');
205}
206#endif
207
111int taskset_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 208int taskset_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
112int taskset_main(int argc UNUSED_PARAM, char **argv) 209int taskset_main(int argc UNUSED_PARAM, char **argv)
113{ 210{
114 ul *mask; 211 ul *mask;
115 unsigned mask_size_in_bytes; 212 unsigned mask_size_in_bytes;
116 pid_t pid = 0; 213 pid_t pid = 0;
117 unsigned opt_p;
118 const char *current_new; 214 const char *current_new;
119 char *aff; 215 char *aff;
216 unsigned opts;
217 enum {
218 OPT_p = 1 << 0,
219 OPT_c = (1 << 1) * ENABLE_FEATURE_TASKSET_CPULIST,
220 };
120 221
121 /* NB: we mimic util-linux's taskset: -p does not take 222 /* NB: we mimic util-linux's taskset: -p does not take
122 * an argument, i.e., "-pN" is NOT valid, only "-p N"! 223 * an argument, i.e., "-pN" is NOT valid, only "-p N"!
123 * Indeed, util-linux-2.13-pre7 uses: 224 * Indeed, util-linux-2.13-pre7 uses:
124 * getopt_long(argc, argv, "+pchV", ...), not "...p:..." */ 225 * getopt_long(argc, argv, "+pchV", ...), not "...p:..." */
125 226
126 opt_p = getopt32(argv, "^+" "p" "\0" "-1" /* at least 1 arg */); 227 opts = getopt32(argv, "^+" "p"IF_FEATURE_TASKSET_CPULIST("c")
228 "\0" "-1" /* at least 1 arg */);
127 argv += optind; 229 argv += optind;
128 230
129 aff = *argv++; 231 aff = *argv++;
130 if (opt_p) { 232 if (opts & OPT_p) {
131 char *pid_str = aff; 233 char *pid_str = aff;
132 if (*argv) { /* "-p <aff> <pid> ...rest.is.ignored..." */ 234 if (*argv) { /* "-p <aff> <pid> ...rest.is.ignored..." */
133 pid_str = *argv; /* NB: *argv != NULL in this case */ 235 pid_str = *argv; /* NB: *argv != NULL in this case */
@@ -144,8 +246,14 @@ int taskset_main(int argc UNUSED_PARAM, char **argv)
144 current_new = "current"; 246 current_new = "current";
145 print_aff: 247 print_aff:
146 mask = get_aff(pid, &mask_size_in_bytes); 248 mask = get_aff(pid, &mask_size_in_bytes);
147 if (opt_p) { 249 if (opts & OPT_p) {
148 printf("pid %d's %s affinity mask: "TASKSET_PRINTF_MASK"\n", 250#if ENABLE_FEATURE_TASKSET_CPULIST
251 if (opts & OPT_c) {
252 printf("pid %d's %s affinity list: ", pid, current_new);
253 print_cpulist(mask, mask_size_in_bytes);
254 } else
255#endif
256 printf("pid %d's %s affinity mask: "TASKSET_PRINTF_MASK"\n",
149 pid, current_new, from_mask(mask, mask_size_in_bytes)); 257 pid, current_new, from_mask(mask, mask_size_in_bytes));
150 if (*argv == NULL) { 258 if (*argv == NULL) {
151 /* Either it was just "-p <pid>", 259 /* Either it was just "-p <pid>",
@@ -158,17 +266,27 @@ int taskset_main(int argc UNUSED_PARAM, char **argv)
158 } 266 }
159 memset(mask, 0, mask_size_in_bytes); 267 memset(mask, 0, mask_size_in_bytes);
160 268
161 /* Affinity was specified, translate it into mask */
162 /* it is always in hex, skip "0x" if it exists */
163 if (aff[0] == '0' && (aff[1]|0x20) == 'x')
164 aff += 2;
165
166 if (!ENABLE_FEATURE_TASKSET_FANCY) { 269 if (!ENABLE_FEATURE_TASKSET_FANCY) {
270 /* Affinity was specified, translate it into mask */
271 /* it is always in hex, skip "0x" if it exists */
272 if (aff[0] == '0' && (aff[1]|0x20) == 'x')
273 aff += 2;
167 mask[0] = xstrtoul(aff, 16); 274 mask[0] = xstrtoul(aff, 16);
168 } else { 275 }
276#if ENABLE_FEATURE_TASKSET_CPULIST
277 else if (opts & OPT_c) {
278 parse_cpulist(mask, mask_size_in_bytes * 8, aff);
279 }
280#endif
281 else {
169 unsigned i; 282 unsigned i;
170 char *last_char; 283 char *last_char;
171 284
285 /* Affinity was specified, translate it into mask */
286 /* it is always in hex, skip "0x" if it exists */
287 if (aff[0] == '0' && (aff[1]|0x20) == 'x')
288 aff += 2;
289
172 i = 0; /* bit pos in mask[] */ 290 i = 0; /* bit pos in mask[] */
173 291
174 /* aff is ASCII hex string, accept very long masks in this form. 292 /* aff is ASCII hex string, accept very long masks in this form.