aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRon Yorston <rmy@pobox.com>2012-03-22 13:15:08 +0000
committerRon Yorston <rmy@pobox.com>2012-03-22 13:15:08 +0000
commitc0d4367d6b581eb5989c02815880cf0fa2851ae8 (patch)
tree868c266e627e2d7f65ba5a4d5f98a1c421453181
parentf6bad5ef766b0447158e3de2f55c35f1f6cecb58 (diff)
parentda4441c44f6efccb6f7b7588404d9c6bfb7b6af8 (diff)
downloadbusybox-w32-c0d4367d6b581eb5989c02815880cf0fa2851ae8.tar.gz
busybox-w32-c0d4367d6b581eb5989c02815880cf0fa2851ae8.tar.bz2
busybox-w32-c0d4367d6b581eb5989c02815880cf0fa2851ae8.zip
Merge commit 'da4441c44f6efccb6f7b7588404d9c6bfb7b6af8' into merge
Conflicts: libbb/vfork_daemon_rexec.c networking/wget.c procps/ps.c
-rw-r--r--Config.in7
-rw-r--r--TEST_config_nommu1
-rw-r--r--TEST_config_noprintf1
-rw-r--r--TEST_config_rh91
-rwxr-xr-xapplets/busybox.mkll2
-rw-r--r--applets/usage_pod.c4
-rw-r--r--archival/libarchive/get_header_tar_gz.c4
-rw-r--r--archival/lzop.c2
-rw-r--r--archival/tar.c29
-rw-r--r--console-tools/reset.c4
-rw-r--r--console-tools/resize.c1
-rw-r--r--coreutils/Config.src10
-rw-r--r--coreutils/Kbuild.src2
-rw-r--r--coreutils/df.c2
-rw-r--r--coreutils/echo.c185
-rw-r--r--coreutils/id.c5
-rw-r--r--coreutils/length.c.disabled (renamed from coreutils/length.c)0
-rw-r--r--coreutils/stty.c20
-rw-r--r--debianutils/mktemp.c40
-rw-r--r--docs/nofork_noexec.txt27
-rw-r--r--e2fsprogs/old_e2fsprogs/ext2fs/ext2fs_inline.c4
-rw-r--r--editors/diff.c9
-rw-r--r--editors/ed.c6
-rw-r--r--editors/sed.c2
-rw-r--r--findutils/find.c419
-rw-r--r--include/applets.src.h2
-rw-r--r--include/archive.h20
-rw-r--r--include/libbb.h57
-rw-r--r--include/platform.h12
-rw-r--r--include/usage.src.h20
-rw-r--r--libbb/Config.src21
-rw-r--r--libbb/copy_file.c8
-rw-r--r--libbb/crc32.c2
-rw-r--r--libbb/execable.c10
-rw-r--r--libbb/find_mount_point.c2
-rw-r--r--libbb/getopt32.c10
-rw-r--r--libbb/lineedit.c12
-rw-r--r--libbb/messages.c2
-rw-r--r--libbb/platform.c11
-rw-r--r--libbb/progress.c179
-rw-r--r--libbb/systemd_support.c2
-rw-r--r--libbb/vfork_daemon_rexec.c61
-rw-r--r--loginutils/Config.src14
-rw-r--r--loginutils/getty.c177
-rw-r--r--miscutils/Config.src2
-rw-r--r--miscutils/fbsplash.c5
-rw-r--r--miscutils/timeout.c2
-rw-r--r--miscutils/ubi_attach_detach.c154
-rw-r--r--modutils/insmod.c2
-rw-r--r--modutils/modinfo.c18
-rw-r--r--modutils/modprobe.c9
-rw-r--r--modutils/modutils-24.c19
-rw-r--r--modutils/modutils.c33
-rw-r--r--modutils/modutils.h2
-rw-r--r--networking/httpd_ssi.c4
-rw-r--r--networking/libiproute/ipaddress.c3
-rw-r--r--networking/libiproute/iproute.c59
-rw-r--r--networking/libiproute/libnetlink.c3
-rw-r--r--networking/libiproute/utils.c86
-rw-r--r--networking/libiproute/utils.h6
-rw-r--r--networking/ntpd.c2
-rw-r--r--networking/ping.c12
-rw-r--r--networking/tc.c10
-rw-r--r--networking/tftp.c10
-rw-r--r--networking/udhcp/Config.src18
-rw-r--r--networking/udhcp/common.c2
-rw-r--r--networking/udhcp/dhcpc.c44
-rw-r--r--networking/udhcp/leases.c40
-rw-r--r--networking/wget.c630
-rw-r--r--procps/kill.c25
-rw-r--r--procps/ps.c44
-rw-r--r--procps/pstree.c2
-rw-r--r--runit/runsv.c24
-rw-r--r--scripts/defconfig.tig9
-rw-r--r--scripts/kconfig/mconf.c2
-rwxr-xr-xscripts/mkconfigs2
-rw-r--r--shell/Config.src9
-rw-r--r--shell/ash.c122
-rw-r--r--shell/ash_test/ash-misc/echo_write_error.right2
-rw-r--r--shell/ash_test/ash-misc/echo_write_error.tests7
-rw-r--r--shell/ash_test/ash-redir/redir.right1
-rw-r--r--shell/ash_test/ash-signals/sigint1.right1
-rwxr-xr-xshell/ash_test/ash-signals/sigint1.tests41
-rw-r--r--shell/hush.c27
-rw-r--r--shell/hush_test/hush-misc/echo_write_error.right2
-rwxr-xr-xshell/hush_test/hush-misc/echo_write_error.tests7
-rw-r--r--shell/hush_test/hush-misc/sigint1.right1
-rwxr-xr-xshell/hush_test/hush-misc/sigint1.tests41
-rw-r--r--shell/shell_common.c6
-rw-r--r--testsuite/echo/echo-prints-dash1
-rw-r--r--testsuite/echo/echo-prints-non-opts1
-rw-r--r--testsuite/echo/echo-prints-slash_000413
-rw-r--r--testsuite/echo/echo-prints-slash_00413
-rw-r--r--testsuite/echo/echo-prints-slash_0413
-rw-r--r--testsuite/echo/echo-prints-slash_413
-rwxr-xr-xtestsuite/tar.tests14
-rw-r--r--util-linux/blockdev.c24
-rw-r--r--util-linux/fdisk.c2
-rw-r--r--util-linux/fdisk_osf.c14
-rw-r--r--util-linux/flock.c2
-rw-r--r--util-linux/mount.c19
-rw-r--r--util-linux/volume_id/linux_swap.c1
-rw-r--r--win32/sys/sysmacros.h0
103 files changed, 1896 insertions, 1153 deletions
diff --git a/Config.in b/Config.in
index 6953208da..0ab94d497 100644
--- a/Config.in
+++ b/Config.in
@@ -149,7 +149,6 @@ config FEATURE_INSTALLER
149config INSTALL_NO_USR 149config INSTALL_NO_USR
150 bool "Don't use /usr" 150 bool "Don't use /usr"
151 default n 151 default n
152 depends on FEATURE_INSTALLER
153 help 152 help
154 Disable use of /usr. busybox --install and "make install" 153 Disable use of /usr. busybox --install and "make install"
155 will install applets only to /bin and /sbin, 154 will install applets only to /bin and /sbin,
@@ -490,7 +489,10 @@ config PIE
490 default n 489 default n
491 depends on !STATIC 490 depends on !STATIC
492 help 491 help
493 (TODO: what is it and why/when is it useful?) 492 Hardened code option. PIE binaries are loaded at a different
493 address at each invocation. This has some overhead,
494 particularly on x86-32 which is short on registers.
495
494 Most people will leave this set to 'N'. 496 Most people will leave this set to 'N'.
495 497
496config NOMMU 498config NOMMU
@@ -724,7 +726,6 @@ config INSTALL_APPLET_SCRIPT_WRAPPERS
724 726
725config INSTALL_APPLET_DONT 727config INSTALL_APPLET_DONT
726 bool "not installed" 728 bool "not installed"
727 depends on FEATURE_INSTALLER || FEATURE_SH_STANDALONE || FEATURE_PREFER_APPLETS
728 help 729 help
729 Do not install applet links. Useful when you plan to use 730 Do not install applet links. Useful when you plan to use
730 busybox --install for installing links, or plan to use 731 busybox --install for installing links, or plan to use
diff --git a/TEST_config_nommu b/TEST_config_nommu
index 18f448391..905f65296 100644
--- a/TEST_config_nommu
+++ b/TEST_config_nommu
@@ -189,7 +189,6 @@ CONFIG_HOSTID=y
189CONFIG_ID=y 189CONFIG_ID=y
190CONFIG_INSTALL=y 190CONFIG_INSTALL=y
191CONFIG_FEATURE_INSTALL_LONG_OPTIONS=y 191CONFIG_FEATURE_INSTALL_LONG_OPTIONS=y
192CONFIG_LENGTH=y
193CONFIG_LN=y 192CONFIG_LN=y
194CONFIG_LOGNAME=y 193CONFIG_LOGNAME=y
195CONFIG_LS=y 194CONFIG_LS=y
diff --git a/TEST_config_noprintf b/TEST_config_noprintf
index 22525ecb6..b72e12856 100644
--- a/TEST_config_noprintf
+++ b/TEST_config_noprintf
@@ -211,7 +211,6 @@ CONFIG_FALSE=y
211# CONFIG_ID is not set 211# CONFIG_ID is not set
212# CONFIG_INSTALL is not set 212# CONFIG_INSTALL is not set
213# CONFIG_FEATURE_INSTALL_LONG_OPTIONS is not set 213# CONFIG_FEATURE_INSTALL_LONG_OPTIONS is not set
214# CONFIG_LENGTH is not set
215# CONFIG_LN is not set 214# CONFIG_LN is not set
216# CONFIG_LOGNAME is not set 215# CONFIG_LOGNAME is not set
217# CONFIG_LS is not set 216# CONFIG_LS is not set
diff --git a/TEST_config_rh9 b/TEST_config_rh9
index e45608351..23094e391 100644
--- a/TEST_config_rh9
+++ b/TEST_config_rh9
@@ -200,7 +200,6 @@ CONFIG_HOSTID=y
200CONFIG_ID=y 200CONFIG_ID=y
201CONFIG_INSTALL=y 201CONFIG_INSTALL=y
202CONFIG_FEATURE_INSTALL_LONG_OPTIONS=y 202CONFIG_FEATURE_INSTALL_LONG_OPTIONS=y
203CONFIG_LENGTH=y
204CONFIG_LN=y 203CONFIG_LN=y
205CONFIG_LOGNAME=y 204CONFIG_LOGNAME=y
206CONFIG_LS=y 205CONFIG_LS=y
diff --git a/applets/busybox.mkll b/applets/busybox.mkll
index 6d61f7e82..68dbf2162 100755
--- a/applets/busybox.mkll
+++ b/applets/busybox.mkll
@@ -14,7 +14,7 @@ CONFIG_H=${1:-include/autoconf.h}
14APPLETS_H=${2:-include/applets.h} 14APPLETS_H=${2:-include/applets.h}
15$HOSTCC -E -DMAKE_LINKS -include $CONFIG_H $APPLETS_H | 15$HOSTCC -E -DMAKE_LINKS -include $CONFIG_H $APPLETS_H |
16 awk '/^[ \t]*LINK/{ 16 awk '/^[ \t]*LINK/{
17 dir=substr($2,8) 17 dir=substr($2,7)
18 gsub("_","/",dir) 18 gsub("_","/",dir)
19 if(dir=="/ROOT") dir="" 19 if(dir=="/ROOT") dir=""
20 file=$3 20 file=$3
diff --git a/applets/usage_pod.c b/applets/usage_pod.c
index da0baefc6..0b1c4aadb 100644
--- a/applets/usage_pod.c
+++ b/applets/usage_pod.c
@@ -31,8 +31,8 @@
31#include "usage.h" 31#include "usage.h"
32#define MAKE_USAGE(aname, usage) { aname, usage }, 32#define MAKE_USAGE(aname, usage) { aname, usage },
33static struct usage_data { 33static struct usage_data {
34 const char *aname; 34 const char *aname;
35 const char *usage; 35 const char *usage;
36} usage_array[] = { 36} usage_array[] = {
37#include "applets.h" 37#include "applets.h"
38}; 38};
diff --git a/archival/libarchive/get_header_tar_gz.c b/archival/libarchive/get_header_tar_gz.c
index b09f8691c..889fed0d9 100644
--- a/archival/libarchive/get_header_tar_gz.c
+++ b/archival/libarchive/get_header_tar_gz.c
@@ -9,7 +9,7 @@
9char FAST_FUNC get_header_tar_gz(archive_handle_t *archive_handle) 9char FAST_FUNC get_header_tar_gz(archive_handle_t *archive_handle)
10{ 10{
11#if BB_MMU 11#if BB_MMU
12 unsigned char magic[2]; 12 uint16_t magic;
13#endif 13#endif
14 14
15 /* Can't lseek over pipes */ 15 /* Can't lseek over pipes */
@@ -21,7 +21,7 @@ char FAST_FUNC get_header_tar_gz(archive_handle_t *archive_handle)
21#if BB_MMU 21#if BB_MMU
22 xread(archive_handle->src_fd, &magic, 2); 22 xread(archive_handle->src_fd, &magic, 2);
23 /* Can skip this check, but error message will be less clear */ 23 /* Can skip this check, but error message will be less clear */
24 if ((magic[0] != 0x1f) || (magic[1] != 0x8b)) { 24 if (magic != GZIP_MAGIC) {
25 bb_error_msg_and_die("invalid gzip magic"); 25 bb_error_msg_and_die("invalid gzip magic");
26 } 26 }
27#endif 27#endif
diff --git a/archival/lzop.c b/archival/lzop.c
index 094e78cf9..62455c313 100644
--- a/archival/lzop.c
+++ b/archival/lzop.c
@@ -401,7 +401,7 @@ struct globals {
401#define INIT_G() do { } while (0) 401#define INIT_G() do { } while (0)
402//#define G (*ptr_to_globals) 402//#define G (*ptr_to_globals)
403//#define INIT_G() do { 403//#define INIT_G() do {
404// SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); 404// SET_PTR_TO_GLOBALS(xzalloc(sizeof(G)));
405//} while (0) 405//} while (0)
406 406
407 407
diff --git a/archival/tar.c b/archival/tar.c
index 150c6f393..74d6fca91 100644
--- a/archival/tar.c
+++ b/archival/tar.c
@@ -657,7 +657,7 @@ static llist_t *append_file_list_to_list(llist_t *list)
657 llist_t *newlist = NULL; 657 llist_t *newlist = NULL;
658 658
659 while (list) { 659 while (list) {
660 src_stream = xfopen_for_read(llist_pop(&list)); 660 src_stream = xfopen_stdin(llist_pop(&list));
661 while ((line = xmalloc_fgetline(src_stream)) != NULL) { 661 while ((line = xmalloc_fgetline(src_stream)) != NULL) {
662 /* kill trailing '/' unless the string is just "/" */ 662 /* kill trailing '/' unless the string is just "/" */
663 char *cp = last_char_is(line, '/'); 663 char *cp = last_char_is(line, '/');
@@ -722,11 +722,16 @@ static void handle_SIGCHLD(int status)
722#endif 722#endif
723 723
724//usage:#define tar_trivial_usage 724//usage:#define tar_trivial_usage
725//usage: "-[" IF_FEATURE_TAR_CREATE("c") "xt" IF_FEATURE_SEAMLESS_GZ("z") 725//usage: "-[" IF_FEATURE_TAR_CREATE("c") "xt"
726//usage: IF_FEATURE_SEAMLESS_BZ2("j") IF_FEATURE_SEAMLESS_LZMA("a") 726//usage: IF_FEATURE_SEAMLESS_Z("Z")
727//usage: IF_FEATURE_SEAMLESS_Z("Z") IF_FEATURE_TAR_NOPRESERVE_TIME("m") "vO] " 727//usage: IF_FEATURE_SEAMLESS_GZ("z")
728//usage: IF_FEATURE_TAR_FROM("[-X FILE] ") 728//usage: IF_FEATURE_SEAMLESS_BZ2("j")
729//usage: "[-f TARFILE] [-C DIR] [FILE]..." 729//usage: IF_FEATURE_SEAMLESS_LZMA("a")
730//usage: IF_FEATURE_TAR_CREATE("h")
731//usage: IF_FEATURE_TAR_NOPRESERVE_TIME("m")
732//usage: "vO] "
733//usage: IF_FEATURE_TAR_FROM("[-X FILE] [-T FILE] ")
734//usage: "[-f TARFILE] [-C DIR] [FILE]..."
730//usage:#define tar_full_usage "\n\n" 735//usage:#define tar_full_usage "\n\n"
731//usage: IF_FEATURE_TAR_CREATE("Create, extract, ") 736//usage: IF_FEATURE_TAR_CREATE("Create, extract, ")
732//usage: IF_NOT_FEATURE_TAR_CREATE("Extract ") 737//usage: IF_NOT_FEATURE_TAR_CREATE("Extract ")
@@ -741,6 +746,9 @@ static void handle_SIGCHLD(int status)
741//usage: "\n f Name of TARFILE ('-' for stdin/out)" 746//usage: "\n f Name of TARFILE ('-' for stdin/out)"
742//usage: "\n C Change to DIR before operation" 747//usage: "\n C Change to DIR before operation"
743//usage: "\n v Verbose" 748//usage: "\n v Verbose"
749//usage: IF_FEATURE_SEAMLESS_Z(
750//usage: "\n Z (De)compress using compress"
751//usage: )
744//usage: IF_FEATURE_SEAMLESS_GZ( 752//usage: IF_FEATURE_SEAMLESS_GZ(
745//usage: "\n z (De)compress using gzip" 753//usage: "\n z (De)compress using gzip"
746//usage: ) 754//usage: )
@@ -750,9 +758,6 @@ static void handle_SIGCHLD(int status)
750//usage: IF_FEATURE_SEAMLESS_LZMA( 758//usage: IF_FEATURE_SEAMLESS_LZMA(
751//usage: "\n a (De)compress using lzma" 759//usage: "\n a (De)compress using lzma"
752//usage: ) 760//usage: )
753//usage: IF_FEATURE_SEAMLESS_Z(
754//usage: "\n Z (De)compress using compress"
755//usage: )
756//usage: "\n O Extract to stdout" 761//usage: "\n O Extract to stdout"
757//usage: IF_FEATURE_TAR_CREATE( 762//usage: IF_FEATURE_TAR_CREATE(
758//usage: "\n h Follow symlinks" 763//usage: "\n h Follow symlinks"
@@ -1057,8 +1062,10 @@ int tar_main(int argc UNUSED_PARAM, char **argv)
1057 tar_handle->src_fd = tar_fd; 1062 tar_handle->src_fd = tar_fd;
1058 tar_handle->seek = seek_by_read; 1063 tar_handle->seek = seek_by_read;
1059 } else { 1064 } else {
1060 if (ENABLE_FEATURE_TAR_AUTODETECT && flags == O_RDONLY) { 1065 if (ENABLE_FEATURE_TAR_AUTODETECT
1061 get_header_ptr = get_header_tar; 1066 && flags == O_RDONLY
1067 && get_header_ptr == get_header_tar
1068 ) {
1062 tar_handle->src_fd = open_zipped(tar_filename); 1069 tar_handle->src_fd = open_zipped(tar_filename);
1063 if (tar_handle->src_fd < 0) 1070 if (tar_handle->src_fd < 0)
1064 bb_perror_msg_and_die("can't open '%s'", tar_filename); 1071 bb_perror_msg_and_die("can't open '%s'", tar_filename);
diff --git a/console-tools/reset.c b/console-tools/reset.c
index 1806ce742..a23e4f408 100644
--- a/console-tools/reset.c
+++ b/console-tools/reset.c
@@ -31,12 +31,12 @@ int reset_main(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
31 if (/*isatty(STDIN_FILENO) &&*/ isatty(STDOUT_FILENO)) { 31 if (/*isatty(STDIN_FILENO) &&*/ isatty(STDOUT_FILENO)) {
32 /* See 'man 4 console_codes' for details: 32 /* See 'man 4 console_codes' for details:
33 * "ESC c" -- Reset 33 * "ESC c" -- Reset
34 * "ESC ( K" -- Select user mapping 34 * "ESC ( B" -- Select G0 Character Set (B = US)
35 * "ESC [ 0 m" -- Reset all display attributes 35 * "ESC [ 0 m" -- Reset all display attributes
36 * "ESC [ J" -- Erase to the end of screen 36 * "ESC [ J" -- Erase to the end of screen
37 * "ESC [ ? 25 h" -- Make cursor visible 37 * "ESC [ ? 25 h" -- Make cursor visible
38 */ 38 */
39 printf(ESC"c" ESC"(K" ESC"[0m" ESC"[J" ESC"[?25h"); 39 printf(ESC"c" ESC"(B" ESC"[0m" ESC"[J" ESC"[?25h");
40 /* http://bugs.busybox.net/view.php?id=1414: 40 /* http://bugs.busybox.net/view.php?id=1414:
41 * people want it to reset echo etc: */ 41 * people want it to reset echo etc: */
42#if ENABLE_STTY 42#if ENABLE_STTY
diff --git a/console-tools/resize.c b/console-tools/resize.c
index fdfe2a6a0..ee0728b71 100644
--- a/console-tools/resize.c
+++ b/console-tools/resize.c
@@ -53,6 +53,7 @@ int resize_main(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
53 */ 53 */
54 fprintf(stderr, ESC"7" ESC"[r" ESC"[999;999H" ESC"[6n"); 54 fprintf(stderr, ESC"7" ESC"[r" ESC"[999;999H" ESC"[6n");
55 alarm(3); /* Just in case terminal won't answer */ 55 alarm(3); /* Just in case terminal won't answer */
56//BUG: death by signal won't restore termios
56 scanf(ESC"[%hu;%huR", &w.ws_row, &w.ws_col); 57 scanf(ESC"[%hu;%huR", &w.ws_row, &w.ws_col);
57 fprintf(stderr, ESC"8"); 58 fprintf(stderr, ESC"8");
58 59
diff --git a/coreutils/Config.src b/coreutils/Config.src
index b599f7999..6297f43a3 100644
--- a/coreutils/Config.src
+++ b/coreutils/Config.src
@@ -295,11 +295,11 @@ config FEATURE_INSTALL_LONG_OPTIONS
295 help 295 help
296 Support long options for the install applet. 296 Support long options for the install applet.
297 297
298config LENGTH 298####config LENGTH
299 bool "length" 299#### bool "length"
300 default y 300#### default y
301 help 301#### help
302 length is used to print out the length of a specified string. 302#### length is used to print out the length of a specified string.
303 303
304config LN 304config LN
305 bool "ln" 305 bool "ln"
diff --git a/coreutils/Kbuild.src b/coreutils/Kbuild.src
index 4ea0fa50a..6a41c8318 100644
--- a/coreutils/Kbuild.src
+++ b/coreutils/Kbuild.src
@@ -39,7 +39,7 @@ lib-$(CONFIG_HEAD) += head.o
39lib-$(CONFIG_HOSTID) += hostid.o 39lib-$(CONFIG_HOSTID) += hostid.o
40lib-$(CONFIG_ID) += id.o 40lib-$(CONFIG_ID) += id.o
41lib-$(CONFIG_INSTALL) += install.o 41lib-$(CONFIG_INSTALL) += install.o
42lib-$(CONFIG_LENGTH) += length.o 42#lib-$(CONFIG_LENGTH) += length.o
43lib-$(CONFIG_LN) += ln.o 43lib-$(CONFIG_LN) += ln.o
44lib-$(CONFIG_LOGNAME) += logname.o 44lib-$(CONFIG_LOGNAME) += logname.o
45lib-$(CONFIG_LS) += ls.o 45lib-$(CONFIG_LS) += ls.o
diff --git a/coreutils/df.c b/coreutils/df.c
index af9b77b23..70fd1f4fd 100644
--- a/coreutils/df.c
+++ b/coreutils/df.c
@@ -160,7 +160,7 @@ int df_main(int argc UNUSED_PARAM, char **argv)
160 } 160 }
161 161
162 /* GNU coreutils 6.10 skips certain mounts, try to be compatible. */ 162 /* GNU coreutils 6.10 skips certain mounts, try to be compatible. */
163 if (strcmp(device, "rootfs") == 0) 163 if (ENABLE_FEATURE_SKIP_ROOTFS && strcmp(device, "rootfs") == 0)
164 continue; 164 continue;
165 165
166#ifdef WHY_WE_DO_IT_FOR_DEV_ROOT_ONLY 166#ifdef WHY_WE_DO_IT_FOR_DEV_ROOT_ONLY
diff --git a/coreutils/echo.c b/coreutils/echo.c
index 3821e594e..72e18c161 100644
--- a/coreutils/echo.c
+++ b/coreutils/echo.c
@@ -29,122 +29,135 @@
29 29
30/* NB: can be used by shell even if not enabled as applet */ 30/* NB: can be used by shell even if not enabled as applet */
31 31
32/*
33 * NB2: we don't use stdio, we need better error handing.
34 * Examples include writing into non-opened stdout and error on write.
35 *
36 * With stdio, output gets shoveled into stdout buffer, and even
37 * fflush cannot clear it out. It seems that even if libc receives
38 * EBADF on write attempts, it feels determined to output data no matter what.
39 * If echo is called by shell, it will try writing again later, and possibly
40 * will clobber future output. Not good.
41 *
42 * Solaris has fpurge which discards buffered input. glibc has __fpurge.
43 * But this function is not standard.
44 */
45
32int echo_main(int argc UNUSED_PARAM, char **argv) 46int echo_main(int argc UNUSED_PARAM, char **argv)
33{ 47{
48 char **pp;
34 const char *arg; 49 const char *arg;
50 char *out;
51 char *buffer;
52 unsigned buflen;
35#if !ENABLE_FEATURE_FANCY_ECHO 53#if !ENABLE_FEATURE_FANCY_ECHO
36 enum { 54 enum {
37 eflag = '\\', 55 eflag = '\\',
38 nflag = 1, /* 1 -- print '\n' */ 56 nflag = 1, /* 1 -- print '\n' */
39 }; 57 };
40 58
41 /* We must check that stdout is not closed. 59 argv++;
42 * The reason for this is highly non-obvious.
43 * echo_main is used from shell. Shell must correctly handle "echo foo"
44 * if stdout is closed. With stdio, output gets shoveled into
45 * stdout buffer, and even fflush cannot clear it out. It seems that
46 * even if libc receives EBADF on write attempts, it feels determined
47 * to output data no matter what. So it will try later,
48 * and possibly will clobber future output. Not good. */
49// TODO: check fcntl() & O_ACCMODE == O_WRONLY or O_RDWR?
50 if (fcntl(1, F_GETFL) == -1)
51 return 1; /* match coreutils 6.10 (sans error msg to stderr) */
52 //if (dup2(1, 1) != 1) - old way
53 // return 1;
54
55 arg = *++argv;
56 if (!arg)
57 goto newline_ret;
58#else 60#else
59 const char *p;
60 char nflag = 1; 61 char nflag = 1;
61 char eflag = 0; 62 char eflag = 0;
62 63
63 /* We must check that stdout is not closed. */ 64 while ((arg = *++argv) != NULL) {
64 if (fcntl(1, F_GETFL) == -1) 65 char n, e;
65 return 1;
66 66
67 while (1) { 67 if (arg[0] != '-')
68 arg = *++argv; 68 break; /* not an option arg, echo it */
69 if (!arg)
70 goto newline_ret;
71 if (*arg != '-')
72 break;
73 69
74 /* If it appears that we are handling options, then make sure 70 /* If it appears that we are handling options, then make sure
75 * that all of the options specified are actually valid. 71 * that all of the options specified are actually valid.
76 * Otherwise, the string should just be echoed. 72 * Otherwise, the string should just be echoed.
77 */ 73 */
78 p = arg + 1; 74 arg++;
79 if (!*p) /* A single '-', so echo it. */ 75 n = nflag;
80 goto just_echo; 76 e = eflag;
81
82 do { 77 do {
83 if (!strrchr("neE", *p)) 78 if (*arg == 'n')
79 n = 0;
80 else if (*arg == 'e')
81 e = '\\';
82 else if (*arg != 'E') {
83 /* "-ccc" arg with one of c's invalid, echo it */
84 /* arg consisting from just "-" also handled here */
84 goto just_echo; 85 goto just_echo;
85 } while (*++p); 86 }
86 87 } while (*++arg);
87 /* All of the options in this arg are valid, so handle them. */ 88 nflag = n;
88 p = arg + 1; 89 eflag = e;
89 do {
90 if (*p == 'n')
91 nflag = 0;
92 if (*p == 'e')
93 eflag = '\\';
94 } while (*++p);
95 } 90 }
96 just_echo: 91 just_echo:
97#endif 92#endif
98 while (1) { 93
99 /* arg is already == *argv and isn't NULL */ 94 buflen = 0;
95 pp = argv;
96 while ((arg = *pp) != NULL) {
97 buflen += strlen(arg) + 1;
98 pp++;
99 }
100 out = buffer = xmalloc(buflen + 1); /* +1 is needed for "no args" case */
101
102 while ((arg = *argv) != NULL) {
100 int c; 103 int c;
101 104
102 if (!eflag) { 105 if (!eflag) {
103 /* optimization for very common case */ 106 /* optimization for very common case */
104 fputs(arg, stdout); 107 out = stpcpy(out, arg);
105 } else while ((c = *arg++)) { 108 } else
106 if (c == eflag) { /* Check for escape seq. */ 109 while ((c = *arg++) != '\0') {
110 if (c == eflag) {
111 /* This is an "\x" sequence */
112
107 if (*arg == 'c') { 113 if (*arg == 'c') {
108 /* '\c' means cancel newline and 114 /* "\c" means cancel newline and
109 * ignore all subsequent chars. */ 115 * ignore all subsequent chars. */
110 goto ret; 116 goto do_write;
111 } 117 }
112#if !ENABLE_FEATURE_FANCY_ECHO 118 /* Since SUSv3 mandates a first digit of 0, 4-digit octals
113 /* SUSv3 specifies that octal escapes must begin with '0'. */ 119 * of the form \0### are accepted. */
114 if ( ((int)(unsigned char)(*arg) - '0') >= 8) /* '8' or bigger */ 120 if (*arg == '0') {
115#endif 121 if ((unsigned char)(arg[1] - '0') < 8) {
116 { 122 /* 2nd char is 0..7: skip leading '0' */
117 /* Since SUSv3 mandates a first digit of 0, 4-digit octals 123 arg++;
118 * of the form \0### are accepted. */
119 if (*arg == '0') {
120 /* NB: don't turn "...\0" into "...\" */
121 if (arg[1] && ((unsigned char)(arg[1]) - '0') < 8) {
122 arg++;
123 }
124 } 124 }
125 /* bb_process_escape_sequence handles NUL correctly 125 }
126 * ("...\" case). */ 126 /* bb_process_escape_sequence handles NUL correctly
127 c = bb_process_escape_sequence(&arg); 127 * ("...\" case). */
128 {
129 /* optimization: don't force arg to be on-stack,
130 * use another variable for that. ~30 bytes win */
131 const char *z = arg;
132 c = bb_process_escape_sequence(&z);
133 arg = z;
128 } 134 }
129 } 135 }
130 bb_putchar(c); 136 *out++ = c;
131 } 137 }
132 138
133 arg = *++argv; 139 if (!*++argv)
134 if (!arg)
135 break; 140 break;
136 bb_putchar(' '); 141 *out++ = ' ';
137 } 142 }
138 143
139 newline_ret:
140 if (nflag) { 144 if (nflag) {
141 bb_putchar('\n'); 145 *out++ = '\n';
142 } 146 }
143 ret: 147
144 return fflush_all(); 148 do_write:
149 /* Careful to error out on partial writes too (think ENOSPC!) */
150 errno = 0;
151 /*r =*/ full_write(STDOUT_FILENO, buffer, out - buffer);
152 free(buffer);
153 if (/*WRONG:r < 0*/ errno) {
154 bb_perror_msg(bb_msg_write_error);
155 return 1;
156 }
157 return 0;
145} 158}
146 159
147/*- 160/*
148 * Copyright (c) 1991, 1993 161 * Copyright (c) 1991, 1993
149 * The Regents of the University of California. All rights reserved. 162 * The Regents of the University of California. All rights reserved.
150 * 163 *
@@ -230,7 +243,7 @@ int echo_main(int argc, char **argv)
230 goto just_echo; 243 goto just_echo;
231 244
232 do { 245 do {
233 if (!strrchr("neE", *p)) 246 if (!strchr("neE", *p))
234 goto just_echo; 247 goto just_echo;
235 } while (*++p); 248 } while (*++p);
236 249
@@ -256,27 +269,23 @@ int echo_main(int argc, char **argv)
256 /* optimization for very common case */ 269 /* optimization for very common case */
257 p += strlen(arg); 270 p += strlen(arg);
258 } else while ((c = *arg++)) { 271 } else while ((c = *arg++)) {
259 if (c == eflag) { /* Check for escape seq. */ 272 if (c == eflag) {
273 /* This is an "\x" sequence */
274
260 if (*arg == 'c') { 275 if (*arg == 'c') {
261 /* '\c' means cancel newline and 276 /* "\c" means cancel newline and
262 * ignore all subsequent chars. */ 277 * ignore all subsequent chars. */
263 cur_io->iov_len = p - (char*)cur_io->iov_base; 278 cur_io->iov_len = p - (char*)cur_io->iov_base;
264 cur_io++; 279 cur_io++;
265 goto ret; 280 goto ret;
266 } 281 }
267#if !ENABLE_FEATURE_FANCY_ECHO 282 /* Since SUSv3 mandates a first digit of 0, 4-digit octals
268 /* SUSv3 specifies that octal escapes must begin with '0'. */ 283 * of the form \0### are accepted. */
269 if ( (((unsigned char)*arg) - '1') >= 7) 284 if (*arg == '0' && (unsigned char)(arg[1] - '0') < 8) {
270#endif 285 arg++;
271 {
272 /* Since SUSv3 mandates a first digit of 0, 4-digit octals
273 * of the form \0### are accepted. */
274 if (*arg == '0' && ((unsigned char)(arg[1]) - '0') < 8) {
275 arg++;
276 }
277 /* bb_process_escape_sequence can handle nul correctly */
278 c = bb_process_escape_sequence( (void*) &arg);
279 } 286 }
287 /* bb_process_escape_sequence can handle nul correctly */
288 c = bb_process_escape_sequence( (void*) &arg);
280 } 289 }
281 *p++ = c; 290 *p++ = c;
282 } 291 }
diff --git a/coreutils/id.c b/coreutils/id.c
index ed1dc862e..0639325c3 100644
--- a/coreutils/id.c
+++ b/coreutils/id.c
@@ -176,10 +176,9 @@ int id_main(int argc UNUSED_PARAM, char **argv)
176 prefix = ","; 176 prefix = ",";
177 } 177 }
178 } else if (n < 0) { /* error in get_groups() */ 178 } else if (n < 0) { /* error in get_groups() */
179 if (!ENABLE_DESKTOP) 179 if (ENABLE_DESKTOP)
180 bb_error_msg_and_die("can't get groups"); 180 bb_error_msg_and_die("can't get groups");
181 else 181 return EXIT_FAILURE;
182 return EXIT_FAILURE;
183 } 182 }
184 if (ENABLE_FEATURE_CLEAN_UP) 183 if (ENABLE_FEATURE_CLEAN_UP)
185 free(groups); 184 free(groups);
diff --git a/coreutils/length.c b/coreutils/length.c.disabled
index 7f0b48ccd..7f0b48ccd 100644
--- a/coreutils/length.c
+++ b/coreutils/length.c.disabled
diff --git a/coreutils/stty.c b/coreutils/stty.c
index 1dd01c4dd..d8184a34f 100644
--- a/coreutils/stty.c
+++ b/coreutils/stty.c
@@ -345,7 +345,7 @@ static const char mode_name[] =
345 MI_ENTRY("icrnl", input, SANE_SET | REV, ICRNL, 0 ) 345 MI_ENTRY("icrnl", input, SANE_SET | REV, ICRNL, 0 )
346 MI_ENTRY("ixon", input, REV, IXON, 0 ) 346 MI_ENTRY("ixon", input, REV, IXON, 0 )
347 MI_ENTRY("ixoff", input, SANE_UNSET | REV, IXOFF, 0 ) 347 MI_ENTRY("ixoff", input, SANE_UNSET | REV, IXOFF, 0 )
348 MI_ENTRY("tandem", input, REV | OMIT, IXOFF, 0 ) 348 MI_ENTRY("tandem", input, OMIT | REV, IXOFF, 0 )
349#if IUCLC 349#if IUCLC
350 MI_ENTRY("iuclc", input, SANE_UNSET | REV, IUCLC, 0 ) 350 MI_ENTRY("iuclc", input, SANE_UNSET | REV, IUCLC, 0 )
351#endif 351#endif
@@ -425,7 +425,7 @@ static const char mode_name[] =
425#endif 425#endif
426 MI_ENTRY("echo", local, SANE_SET | REV, ECHO, 0 ) 426 MI_ENTRY("echo", local, SANE_SET | REV, ECHO, 0 )
427 MI_ENTRY("echoe", local, SANE_SET | REV, ECHOE, 0 ) 427 MI_ENTRY("echoe", local, SANE_SET | REV, ECHOE, 0 )
428 MI_ENTRY("crterase", local, REV | OMIT, ECHOE, 0 ) 428 MI_ENTRY("crterase", local, OMIT | REV, ECHOE, 0 )
429 MI_ENTRY("echok", local, SANE_SET | REV, ECHOK, 0 ) 429 MI_ENTRY("echok", local, SANE_SET | REV, ECHOK, 0 )
430 MI_ENTRY("echonl", local, SANE_UNSET | REV, ECHONL, 0 ) 430 MI_ENTRY("echonl", local, SANE_UNSET | REV, ECHONL, 0 )
431 MI_ENTRY("noflsh", local, SANE_UNSET | REV, NOFLSH, 0 ) 431 MI_ENTRY("noflsh", local, SANE_UNSET | REV, NOFLSH, 0 )
@@ -437,15 +437,15 @@ static const char mode_name[] =
437#endif 437#endif
438#if ECHOPRT 438#if ECHOPRT
439 MI_ENTRY("echoprt", local, SANE_UNSET | REV, ECHOPRT, 0 ) 439 MI_ENTRY("echoprt", local, SANE_UNSET | REV, ECHOPRT, 0 )
440 MI_ENTRY("prterase", local, REV | OMIT, ECHOPRT, 0 ) 440 MI_ENTRY("prterase", local, OMIT | REV, ECHOPRT, 0 )
441#endif 441#endif
442#if ECHOCTL 442#if ECHOCTL
443 MI_ENTRY("echoctl", local, SANE_SET | REV, ECHOCTL, 0 ) 443 MI_ENTRY("echoctl", local, SANE_SET | REV, ECHOCTL, 0 )
444 MI_ENTRY("ctlecho", local, REV | OMIT, ECHOCTL, 0 ) 444 MI_ENTRY("ctlecho", local, OMIT | REV, ECHOCTL, 0 )
445#endif 445#endif
446#if ECHOKE 446#if ECHOKE
447 MI_ENTRY("echoke", local, SANE_SET | REV, ECHOKE, 0 ) 447 MI_ENTRY("echoke", local, SANE_SET | REV, ECHOKE, 0 )
448 MI_ENTRY("crtkill", local, REV | OMIT, ECHOKE, 0 ) 448 MI_ENTRY("crtkill", local, OMIT | REV, ECHOKE, 0 )
449#endif 449#endif
450 ; 450 ;
451 451
@@ -502,7 +502,7 @@ static const struct mode_info mode_info[] = {
502 MI_ENTRY("icrnl", input, SANE_SET | REV, ICRNL, 0 ) 502 MI_ENTRY("icrnl", input, SANE_SET | REV, ICRNL, 0 )
503 MI_ENTRY("ixon", input, REV, IXON, 0 ) 503 MI_ENTRY("ixon", input, REV, IXON, 0 )
504 MI_ENTRY("ixoff", input, SANE_UNSET | REV, IXOFF, 0 ) 504 MI_ENTRY("ixoff", input, SANE_UNSET | REV, IXOFF, 0 )
505 MI_ENTRY("tandem", input, REV | OMIT, IXOFF, 0 ) 505 MI_ENTRY("tandem", input, OMIT | REV, IXOFF, 0 )
506#if IUCLC 506#if IUCLC
507 MI_ENTRY("iuclc", input, SANE_UNSET | REV, IUCLC, 0 ) 507 MI_ENTRY("iuclc", input, SANE_UNSET | REV, IUCLC, 0 )
508#endif 508#endif
@@ -582,7 +582,7 @@ static const struct mode_info mode_info[] = {
582#endif 582#endif
583 MI_ENTRY("echo", local, SANE_SET | REV, ECHO, 0 ) 583 MI_ENTRY("echo", local, SANE_SET | REV, ECHO, 0 )
584 MI_ENTRY("echoe", local, SANE_SET | REV, ECHOE, 0 ) 584 MI_ENTRY("echoe", local, SANE_SET | REV, ECHOE, 0 )
585 MI_ENTRY("crterase", local, REV | OMIT, ECHOE, 0 ) 585 MI_ENTRY("crterase", local, OMIT | REV, ECHOE, 0 )
586 MI_ENTRY("echok", local, SANE_SET | REV, ECHOK, 0 ) 586 MI_ENTRY("echok", local, SANE_SET | REV, ECHOK, 0 )
587 MI_ENTRY("echonl", local, SANE_UNSET | REV, ECHONL, 0 ) 587 MI_ENTRY("echonl", local, SANE_UNSET | REV, ECHONL, 0 )
588 MI_ENTRY("noflsh", local, SANE_UNSET | REV, NOFLSH, 0 ) 588 MI_ENTRY("noflsh", local, SANE_UNSET | REV, NOFLSH, 0 )
@@ -594,15 +594,15 @@ static const struct mode_info mode_info[] = {
594#endif 594#endif
595#if ECHOPRT 595#if ECHOPRT
596 MI_ENTRY("echoprt", local, SANE_UNSET | REV, ECHOPRT, 0 ) 596 MI_ENTRY("echoprt", local, SANE_UNSET | REV, ECHOPRT, 0 )
597 MI_ENTRY("prterase", local, REV | OMIT, ECHOPRT, 0 ) 597 MI_ENTRY("prterase", local, OMIT | REV, ECHOPRT, 0 )
598#endif 598#endif
599#if ECHOCTL 599#if ECHOCTL
600 MI_ENTRY("echoctl", local, SANE_SET | REV, ECHOCTL, 0 ) 600 MI_ENTRY("echoctl", local, SANE_SET | REV, ECHOCTL, 0 )
601 MI_ENTRY("ctlecho", local, REV | OMIT, ECHOCTL, 0 ) 601 MI_ENTRY("ctlecho", local, OMIT | REV, ECHOCTL, 0 )
602#endif 602#endif
603#if ECHOKE 603#if ECHOKE
604 MI_ENTRY("echoke", local, SANE_SET | REV, ECHOKE, 0 ) 604 MI_ENTRY("echoke", local, SANE_SET | REV, ECHOKE, 0 )
605 MI_ENTRY("crtkill", local, REV | OMIT, ECHOKE, 0 ) 605 MI_ENTRY("crtkill", local, OMIT | REV, ECHOKE, 0 )
606#endif 606#endif
607}; 607};
608 608
diff --git a/debianutils/mktemp.c b/debianutils/mktemp.c
index 86881f86d..f4961af59 100644
--- a/debianutils/mktemp.c
+++ b/debianutils/mktemp.c
@@ -31,6 +31,25 @@
31 * -p; else /tmp [deprecated] 31 * -p; else /tmp [deprecated]
32 */ 32 */
33 33
34//usage:#define mktemp_trivial_usage
35//usage: "[-dt] [-p DIR] [TEMPLATE]"
36//usage:#define mktemp_full_usage "\n\n"
37//usage: "Create a temporary file with name based on TEMPLATE and print its name.\n"
38//usage: "TEMPLATE must end with XXXXXX (e.g. [/dir/]nameXXXXXX).\n"
39//usage: "Without TEMPLATE, -t tmp.XXXXXX is assumed.\n"
40//usage: "\nOptions:"
41//usage: "\n -d Make directory, not file"
42////usage: "\n -q Fail silently on errors" - we ignore this opt
43//usage: "\n -t Prepend base directory name to TEMPLATE"
44//usage: "\n -p DIR Use DIR as a base directory (implies -t)"
45//usage: "\n"
46//usage: "\nBase directory is: -p DIR, else $TMPDIR, else /tmp"
47//usage:
48//usage:#define mktemp_example_usage
49//usage: "$ mktemp /tmp/temp.XXXXXX\n"
50//usage: "/tmp/temp.mWiLjM\n"
51//usage: "$ ls -la /tmp/temp.mWiLjM\n"
52//usage: "-rw------- 1 andersen andersen 0 Apr 25 17:10 /tmp/temp.mWiLjM\n"
34 53
35#include "libbb.h" 54#include "libbb.h"
36 55
@@ -40,20 +59,33 @@ int mktemp_main(int argc UNUSED_PARAM, char **argv)
40 const char *path; 59 const char *path;
41 char *chp; 60 char *chp;
42 unsigned opts; 61 unsigned opts;
62 enum {
63 OPT_d = 1 << 0,
64 OPT_q = 1 << 1,
65 OPT_t = 1 << 2,
66 OPT_p = 1 << 3,
67 };
43 68
44 path = getenv("TMPDIR"); 69 path = getenv("TMPDIR");
45 if (!path || path[0] == '\0') 70 if (!path || path[0] == '\0')
46 path = "/tmp"; 71 path = "/tmp";
47 72
48 /* -q and -t are ignored */ 73 /* -q is ignored */
49 opt_complementary = "?1"; /* 1 argument max */ 74 opt_complementary = "?1"; /* 1 argument max */
50 opts = getopt32(argv, "dqtp:", &path); 75 opts = getopt32(argv, "dqtp:", &path);
51 76
52 chp = argv[optind] ? argv[optind] : xstrdup("tmp.XXXXXX"); 77 chp = argv[optind];
53 if (!strchr(chp, '/') || (opts & 8)) 78 if (!chp) {
79 /* GNU coreutils 8.4:
80 * bare "mktemp" -> "mktemp -t tmp.XXXXXX"
81 */
82 chp = xstrdup("tmp.XXXXXX");
83 opts |= OPT_t;
84 }
85 if (opts & (OPT_t|OPT_p))
54 chp = concat_path_file(path, chp); 86 chp = concat_path_file(path, chp);
55 87
56 if (opts & 1) { /* -d */ 88 if (opts & OPT_d) {
57 if (mkdtemp(chp) == NULL) 89 if (mkdtemp(chp) == NULL)
58 return EXIT_FAILURE; 90 return EXIT_FAILURE;
59 } else { 91 } else {
diff --git a/docs/nofork_noexec.txt b/docs/nofork_noexec.txt
index 06c789aff..c58f5a83f 100644
--- a/docs/nofork_noexec.txt
+++ b/docs/nofork_noexec.txt
@@ -44,9 +44,11 @@ NOEXEC trick is disabled for NOMMU build.
44 NOFORK 44 NOFORK
45 45
46NOFORK applet should work correctly if another applet simply runs 46NOFORK applet should work correctly if another applet simply runs
47<applet>_main(argc,argv) and then continues with its business (xargs, 47<applet>_main(argc,argv) and then continues with its business.
48find, shells can do it). This poses much more serious limitations 48xargs, find, shells do it (grep for "spawn_and_wait" and
49on what applet can/cannot do: 49"run_nofork_applet" to find more users).
50
51This poses much more serious limitations on what applet can do:
50 52
51* all NOEXEC limitations apply. 53* all NOEXEC limitations apply.
52* do not ever exit() or exec(). 54* do not ever exit() or exec().
@@ -56,7 +58,7 @@ on what applet can/cannot do:
56 is taken from xfunc_error_retval. 58 is taken from xfunc_error_retval.
57 - fflush_stdout_and_exit(n) is ok to use. 59 - fflush_stdout_and_exit(n) is ok to use.
58* do not use shared global data, or save/restore shared global data 60* do not use shared global data, or save/restore shared global data
59 prior to returning. (e.g. bb_common_bufsiz1 is off-limits). 61 (e.g. bb_common_bufsiz1) prior to returning.
60 - getopt32() is ok to use. You do not need to save/restore option_mask32, 62 - getopt32() is ok to use. You do not need to save/restore option_mask32,
61 it is already done by core code. 63 it is already done by core code.
62* if you allocate memory, you can use xmalloc() only on the very first 64* if you allocate memory, you can use xmalloc() only on the very first
@@ -77,3 +79,20 @@ script loops. Applets which mess with signal handlers, termios etc
77are probably not worth the effort. 79are probably not worth the effort.
78 80
79Any NOFORK applet is also a NOEXEC applet. 81Any NOFORK applet is also a NOEXEC applet.
82
83
84 Relevant CONFIG options
85
86FEATURE_PREFER_APPLETS
87 BB_EXECVP(cmd, argv) will try to exec /proc/self/exe
88 if command's name matches some applet name
89 applet tables will contain NOFORK/NOEXEC bits
90 spawn_and_wait(argv) will do NOFORK/NOEXEC tricks
91
92FEATURE_SH_STANDALONE (needs FEATURE_PREFER_APPLETS=y)
93 shells will try to exec /proc/self/exe if command's name matches
94 some applet name
95 shells will do NOEXEC trick on NOEXEC applets
96
97FEATURE_SH_NOFORK (needs FEATURE_PREFER_APPLETS=y)
98 shells will do NOFORK trick on NOFORK applets
diff --git a/e2fsprogs/old_e2fsprogs/ext2fs/ext2fs_inline.c b/e2fsprogs/old_e2fsprogs/ext2fs/ext2fs_inline.c
index b9aab440a..7d37d232d 100644
--- a/e2fsprogs/old_e2fsprogs/ext2fs/ext2fs_inline.c
+++ b/e2fsprogs/old_e2fsprogs/ext2fs/ext2fs_inline.c
@@ -155,8 +155,8 @@ int ext2fs_group_of_ino(ext2_filsys fs, ext2_ino_t ino)
155blk_t ext2fs_inode_data_blocks(ext2_filsys fs, 155blk_t ext2fs_inode_data_blocks(ext2_filsys fs,
156 struct ext2_inode *inode) 156 struct ext2_inode *inode)
157{ 157{
158 return inode->i_blocks - 158 return inode->i_blocks -
159 (inode->i_file_acl ? fs->blocksize >> 9 : 0); 159 (inode->i_file_acl ? fs->blocksize >> 9 : 0);
160} 160}
161 161
162 162
diff --git a/editors/diff.c b/editors/diff.c
index cc7ba472e..ca4a4eae7 100644
--- a/editors/diff.c
+++ b/editors/diff.c
@@ -794,7 +794,9 @@ static int FAST_FUNC skip_dir(const char *filename,
794 free(othername); 794 free(othername);
795 if (r != 0 || !S_ISDIR(osb.st_mode)) { 795 if (r != 0 || !S_ISDIR(osb.st_mode)) {
796 /* other dir doesn't have similarly named 796 /* other dir doesn't have similarly named
797 * directory, don't recurse */ 797 * directory, don't recurse; return 1 upon
798 * exit, just like diffutils' diff */
799 exit_status |= 1;
798 return SKIP; 800 return SKIP;
799 } 801 }
800 } 802 }
@@ -846,9 +848,10 @@ static void diffdir(char *p[2], const char *s_start)
846 break; 848 break;
847 pos = !dp[0] ? 1 : (!dp[1] ? -1 : strcmp(dp[0], dp[1])); 849 pos = !dp[0] ? 1 : (!dp[1] ? -1 : strcmp(dp[0], dp[1]));
848 k = pos > 0; 850 k = pos > 0;
849 if (pos && !(option_mask32 & FLAG(N))) 851 if (pos && !(option_mask32 & FLAG(N))) {
850 printf("Only in %s: %s\n", p[k], dp[k]); 852 printf("Only in %s: %s\n", p[k], dp[k]);
851 else { 853 exit_status |= 1;
854 } else {
852 char *fullpath[2], *path[2]; /* if -N */ 855 char *fullpath[2], *path[2]; /* if -N */
853 856
854 for (i = 0; i < 2; i++) { 857 for (i = 0; i < 2; i++) {
diff --git a/editors/ed.c b/editors/ed.c
index 859668406..b1b6a8d27 100644
--- a/editors/ed.c
+++ b/editors/ed.c
@@ -129,7 +129,7 @@ static void doCommands(void)
129 * 0 on ctrl-C, 129 * 0 on ctrl-C,
130 * >0 length of input string, including terminating '\n' 130 * >0 length of input string, including terminating '\n'
131 */ 131 */
132 len = read_line_input(": ", buf, sizeof(buf), NULL); 132 len = read_line_input(NULL, ": ", buf, sizeof(buf), /*timeout*/ -1);
133 if (len <= 0) 133 if (len <= 0)
134 return; 134 return;
135 endbuf = &buf[len - 1]; 135 endbuf = &buf[len - 1];
@@ -227,7 +227,7 @@ static void doCommands(void)
227 } 227 }
228 if (!dirty) 228 if (!dirty)
229 return; 229 return;
230 len = read_line_input("Really quit? ", buf, 16, NULL); 230 len = read_line_input(NULL, "Really quit? ", buf, 16, /*timeout*/ -1);
231 /* read error/EOF - no way to continue */ 231 /* read error/EOF - no way to continue */
232 if (len < 0) 232 if (len < 0)
233 return; 233 return;
@@ -541,7 +541,7 @@ static void addLines(int num)
541 * 0 on ctrl-C, 541 * 0 on ctrl-C,
542 * >0 length of input string, including terminating '\n' 542 * >0 length of input string, including terminating '\n'
543 */ 543 */
544 len = read_line_input("", buf, sizeof(buf), NULL); 544 len = read_line_input(NULL, "", buf, sizeof(buf), /*timeout*/ -1);
545 if (len <= 0) { 545 if (len <= 0) {
546 /* Previously, ctrl-C was exiting to shell. 546 /* Previously, ctrl-C was exiting to shell.
547 * Now we exit to ed prompt. Is in important? */ 547 * Now we exit to ed prompt. Is in important? */
diff --git a/editors/sed.c b/editors/sed.c
index b91acfb7f..d3555243f 100644
--- a/editors/sed.c
+++ b/editors/sed.c
@@ -124,7 +124,7 @@ struct globals {
124} FIX_ALIASING; 124} FIX_ALIASING;
125#define G (*(struct globals*)&bb_common_bufsiz1) 125#define G (*(struct globals*)&bb_common_bufsiz1)
126struct BUG_G_too_big { 126struct BUG_G_too_big {
127 char BUG_G_too_big[sizeof(G) <= COMMON_BUFSIZE ? 1 : -1]; 127 char BUG_G_too_big[sizeof(G) <= COMMON_BUFSIZE ? 1 : -1];
128}; 128};
129#define INIT_G() do { \ 129#define INIT_G() do { \
130 G.sed_cmd_tail = &G.sed_cmd_head; \ 130 G.sed_cmd_tail = &G.sed_cmd_head; \
diff --git a/findutils/find.c b/findutils/find.c
index f85381b47..9ae84fa0d 100644
--- a/findutils/find.c
+++ b/findutils/find.c
@@ -53,10 +53,6 @@
53 * diff -u /tmp/std_find /tmp/bb_find && echo Identical 53 * diff -u /tmp/std_find /tmp/bb_find && echo Identical
54 */ 54 */
55 55
56//applet:IF_FIND(APPLET_NOEXEC(find, find, BB_DIR_USR_BIN, BB_SUID_DROP, find))
57
58//kbuild:lib-$(CONFIG_FIND) += find.o
59
60//config:config FIND 56//config:config FIND
61//config: bool "find" 57//config: bool "find"
62//config: default y 58//config: default y
@@ -112,11 +108,11 @@
112//config: This option allows find to restrict searches to a single filesystem. 108//config: This option allows find to restrict searches to a single filesystem.
113//config: 109//config:
114//config:config FEATURE_FIND_MAXDEPTH 110//config:config FEATURE_FIND_MAXDEPTH
115//config: bool "Enable -maxdepth N" 111//config: bool "Enable -mindepth N and -maxdepth N"
116//config: default y 112//config: default y
117//config: depends on FIND 113//config: depends on FIND
118//config: help 114//config: help
119//config: This option enables -maxdepth N option. 115//config: This option enables -mindepth N and -maxdepth N option.
120//config: 116//config:
121//config:config FEATURE_FIND_NEWER 117//config:config FEATURE_FIND_NEWER
122//config: bool "Enable -newer: compare file modification times" 118//config: bool "Enable -newer: compare file modification times"
@@ -124,7 +120,7 @@
124//config: depends on FIND 120//config: depends on FIND
125//config: help 121//config: help
126//config: Support the 'find -newer' option for finding any files which have 122//config: Support the 'find -newer' option for finding any files which have
127//config: a modified time that is more recent than the specified FILE. 123//config: modification time that is more recent than the specified FILE.
128//config: 124//config:
129//config:config FEATURE_FIND_INUM 125//config:config FEATURE_FIND_INUM
130//config: bool "Enable -inum: inode number matching" 126//config: bool "Enable -inum: inode number matching"
@@ -230,6 +226,106 @@
230//config: help 226//config: help
231//config: Support the 'find -links' option for matching number of links. 227//config: Support the 'find -links' option for matching number of links.
232 228
229//applet:IF_FIND(APPLET_NOEXEC(find, find, BB_DIR_USR_BIN, BB_SUID_DROP, find))
230
231//kbuild:lib-$(CONFIG_FIND) += find.o
232
233//usage:#define find_trivial_usage
234//usage: "[PATH]... [OPTIONS] [ACTIONS]"
235//usage:#define find_full_usage "\n\n"
236//usage: "Search for files and perform actions on them.\n"
237//usage: "First failed action stops processing of current file.\n"
238//usage: "Defaults: PATH is current directory, action is '-print'\n"
239//usage: "\nOptions:"
240//usage: "\n -follow Follow symlinks"
241//usage: IF_FEATURE_FIND_XDEV(
242//usage: "\n -xdev Don't descend directories on other filesystems"
243//usage: )
244//usage: IF_FEATURE_FIND_MAXDEPTH(
245//usage: "\n -maxdepth N Descend at most N levels. -maxdepth 0 applies"
246//usage: "\n actions to command line arguments only"
247//usage: "\n -mindepth N Don't act on first N levels"
248//usage: )
249//usage: IF_FEATURE_FIND_DEPTH(
250//usage: "\n -depth Act on directory *after* traversing it"
251//usage: )
252//usage: "\n"
253//usage: "\nActions:"
254//usage: IF_FEATURE_FIND_PAREN(
255//usage: "\n ( ACTIONS ) Group actions for -o / -a"
256//usage: )
257//usage: IF_FEATURE_FIND_NOT(
258//usage: "\n ! ACT Invert ACT's success/failure"
259//usage: )
260//usage: "\n ACT1 [-a] ACT2 If ACT1 fails, stop, else do ACT2"
261//usage: "\n ACT1 -o ACT2 If ACT1 succeeds, stop, else do ACT2"
262//usage: "\n Note: -a has higher priority than -o"
263//usage: "\n -name PATTERN Match file name (w/o directory name) to PATTERN"
264//usage: "\n -iname PATTERN Case insensitive -name"
265//usage: IF_FEATURE_FIND_PATH(
266//usage: "\n -path PATTERN Match path to PATTERN"
267//usage: )
268//usage: IF_FEATURE_FIND_REGEX(
269//usage: "\n -regex PATTERN Match path to regex PATTERN"
270//usage: )
271//usage: IF_FEATURE_FIND_TYPE(
272//usage: "\n -type X File type is X (one of: f,d,l,b,c,...)"
273//usage: )
274//usage: IF_FEATURE_FIND_PERM(
275//usage: "\n -perm MASK At least one mask bit (+MASK), all bits (-MASK),"
276//usage: "\n or exactly MASK bits are set in file's mode"
277//usage: )
278//usage: IF_FEATURE_FIND_MTIME(
279//usage: "\n -mtime DAYS mtime is greater than (+N), less than (-N),"
280//usage: "\n or exactly N days in the past"
281//usage: )
282//usage: IF_FEATURE_FIND_MMIN(
283//usage: "\n -mmin MINS mtime is greater than (+N), less than (-N),"
284//usage: "\n or exactly N minutes in the past"
285//usage: )
286//usage: IF_FEATURE_FIND_NEWER(
287//usage: "\n -newer FILE mtime is more recent than FILE's"
288//usage: )
289//usage: IF_FEATURE_FIND_INUM(
290//usage: "\n -inum N File has inode number N"
291//usage: )
292//usage: IF_FEATURE_FIND_USER(
293//usage: "\n -user NAME/ID File is owned by given user"
294//usage: )
295//usage: IF_FEATURE_FIND_GROUP(
296//usage: "\n -group NAME/ID File is owned by given group"
297//usage: )
298//usage: IF_FEATURE_FIND_SIZE(
299//usage: "\n -size N[bck] File size is N (c:bytes,k:kbytes,b:512 bytes(def.))"
300//usage: "\n +/-N: file size is bigger/smaller than N"
301//usage: )
302//usage: IF_FEATURE_FIND_LINKS(
303//usage: "\n -links N Number of links is greater than (+N), less than (-N),"
304//usage: "\n or exactly N"
305//usage: )
306//usage: IF_FEATURE_FIND_CONTEXT(
307//usage: "\n -context CTX File has specified security context"
308//usage: )
309//usage: IF_FEATURE_FIND_PRUNE(
310//usage: "\n -prune If current file is directory, don't descend into it"
311//usage: )
312//usage: "\nIf none of the following actions is specified, -print is assumed"
313//usage: "\n -print Print file name"
314//usage: IF_FEATURE_FIND_PRINT0(
315//usage: "\n -print0 Print file name, NUL terminated"
316//usage: )
317//usage: IF_FEATURE_FIND_EXEC(
318//usage: "\n -exec CMD ARG ; Run CMD with all instances of {} replaced by"
319//usage: "\n file name. Fails if CMD exits with nonzero"
320//usage: )
321//usage: IF_FEATURE_FIND_DELETE(
322//usage: "\n -delete Delete current file/directory. Turns on -depth option"
323//usage: )
324//usage:
325//usage:#define find_example_usage
326//usage: "$ find / -name passwd\n"
327//usage: "/etc/passwd\n"
328
233#include <fnmatch.h> 329#include <fnmatch.h>
234#include "libbb.h" 330#include "libbb.h"
235#if ENABLE_FEATURE_FIND_REGEX 331#if ENABLE_FEATURE_FIND_REGEX
@@ -278,8 +374,12 @@ IF_FEATURE_FIND_LINKS( ACTS(links, char links_char; int links_count;))
278struct globals { 374struct globals {
279 IF_FEATURE_FIND_XDEV(dev_t *xdev_dev;) 375 IF_FEATURE_FIND_XDEV(dev_t *xdev_dev;)
280 IF_FEATURE_FIND_XDEV(int xdev_count;) 376 IF_FEATURE_FIND_XDEV(int xdev_count;)
377#if ENABLE_FEATURE_FIND_MAXDEPTH
378 int minmaxdepth[2];
379#endif
281 action ***actions; 380 action ***actions;
282 bool need_print; 381 smallint need_print;
382 smallint xdev_on;
283 recurse_flags_t recurse_flags; 383 recurse_flags_t recurse_flags;
284} FIX_ALIASING; 384} FIX_ALIASING;
285#define G (*(struct globals*)&bb_common_bufsiz1) 385#define G (*(struct globals*)&bb_common_bufsiz1)
@@ -288,7 +388,8 @@ struct globals {
288 char G_sizecheck[sizeof(G) > COMMON_BUFSIZE ? -1 : 1]; \ 388 char G_sizecheck[sizeof(G) > COMMON_BUFSIZE ? -1 : 1]; \
289 }; \ 389 }; \
290 /* we have to zero it out because of NOEXEC */ \ 390 /* we have to zero it out because of NOEXEC */ \
291 memset(&G, 0, offsetof(struct globals, need_print)); \ 391 memset(&G, 0, sizeof(G)); \
392 IF_FEATURE_FIND_MAXDEPTH(G.minmaxdepth[1] = INT_MAX;) \
292 G.need_print = 1; \ 393 G.need_print = 1; \
293 G.recurse_flags = ACTION_RECURSE; \ 394 G.recurse_flags = ACTION_RECURSE; \
294} while (0) 395} while (0)
@@ -586,16 +687,15 @@ ACTF(links)
586 687
587static int FAST_FUNC fileAction(const char *fileName, 688static int FAST_FUNC fileAction(const char *fileName,
588 struct stat *statbuf, 689 struct stat *statbuf,
589 void *userData IF_NOT_FEATURE_FIND_MAXDEPTH(UNUSED_PARAM), 690 void *userData UNUSED_PARAM,
590 int depth IF_NOT_FEATURE_FIND_MAXDEPTH(UNUSED_PARAM)) 691 int depth IF_NOT_FEATURE_FIND_MAXDEPTH(UNUSED_PARAM))
591{ 692{
592 int r; 693 int r;
593#if ENABLE_FEATURE_FIND_MAXDEPTH
594#define minmaxdepth ((int*)userData)
595 694
596 if (depth < minmaxdepth[0]) 695#if ENABLE_FEATURE_FIND_MAXDEPTH
696 if (depth < G.minmaxdepth[0])
597 return TRUE; /* skip this, continue recursing */ 697 return TRUE; /* skip this, continue recursing */
598 if (depth > minmaxdepth[1]) 698 if (depth > G.minmaxdepth[1])
599 return SKIP; /* stop recursing */ 699 return SKIP; /* stop recursing */
600#endif 700#endif
601 701
@@ -606,7 +706,7 @@ static int FAST_FUNC fileAction(const char *fileName,
606 706
607#if ENABLE_FEATURE_FIND_MAXDEPTH 707#if ENABLE_FEATURE_FIND_MAXDEPTH
608 if (S_ISDIR(statbuf->st_mode)) { 708 if (S_ISDIR(statbuf->st_mode)) {
609 if (depth == minmaxdepth[1]) 709 if (depth == G.minmaxdepth[1])
610 return SKIP; 710 return SKIP;
611 } 711 }
612#endif 712#endif
@@ -629,7 +729,6 @@ static int FAST_FUNC fileAction(const char *fileName,
629 /* Cannot return 0: our caller, recursive_action(), 729 /* Cannot return 0: our caller, recursive_action(),
630 * will perror() and skip dirs (if called on dir) */ 730 * will perror() and skip dirs (if called on dir) */
631 return (r & SKIP) ? SKIP : TRUE; 731 return (r & SKIP) ? SKIP : TRUE;
632#undef minmaxdepth
633} 732}
634 733
635 734
@@ -674,6 +773,9 @@ static const char* plus_minus_num(const char* str)
674static action*** parse_params(char **argv) 773static action*** parse_params(char **argv)
675{ 774{
676 enum { 775 enum {
776 OPT_FOLLOW ,
777 IF_FEATURE_FIND_XDEV( OPT_XDEV ,)
778 IF_FEATURE_FIND_DEPTH( OPT_DEPTH ,)
677 PARM_a , 779 PARM_a ,
678 PARM_o , 780 PARM_o ,
679 IF_FEATURE_FIND_NOT( PARM_char_not ,) 781 IF_FEATURE_FIND_NOT( PARM_char_not ,)
@@ -684,12 +786,11 @@ static action*** parse_params(char **argv)
684#endif 786#endif
685 PARM_print , 787 PARM_print ,
686 IF_FEATURE_FIND_PRINT0( PARM_print0 ,) 788 IF_FEATURE_FIND_PRINT0( PARM_print0 ,)
687 IF_FEATURE_FIND_DEPTH( PARM_depth ,)
688 IF_FEATURE_FIND_PRUNE( PARM_prune ,) 789 IF_FEATURE_FIND_PRUNE( PARM_prune ,)
689 IF_FEATURE_FIND_DELETE( PARM_delete ,) 790 IF_FEATURE_FIND_DELETE( PARM_delete ,)
690 IF_FEATURE_FIND_EXEC( PARM_exec ,) 791 IF_FEATURE_FIND_EXEC( PARM_exec ,)
691 IF_FEATURE_FIND_PAREN( PARM_char_brace,) 792 IF_FEATURE_FIND_PAREN( PARM_char_brace,)
692 /* All options starting from here require argument */ 793 /* All options/actions starting from here require argument */
693 PARM_name , 794 PARM_name ,
694 PARM_iname , 795 PARM_iname ,
695 IF_FEATURE_FIND_PATH( PARM_path ,) 796 IF_FEATURE_FIND_PATH( PARM_path ,)
@@ -705,25 +806,28 @@ static action*** parse_params(char **argv)
705 IF_FEATURE_FIND_SIZE( PARM_size ,) 806 IF_FEATURE_FIND_SIZE( PARM_size ,)
706 IF_FEATURE_FIND_CONTEXT(PARM_context ,) 807 IF_FEATURE_FIND_CONTEXT(PARM_context ,)
707 IF_FEATURE_FIND_LINKS( PARM_links ,) 808 IF_FEATURE_FIND_LINKS( PARM_links ,)
809 IF_FEATURE_FIND_MAXDEPTH(OPT_MINDEPTH,OPT_MAXDEPTH,)
708 }; 810 };
709 811
710 static const char params[] ALIGN1 = 812 static const char params[] ALIGN1 =
711 "-a\0" 813 "-follow\0"
712 "-o\0" 814 IF_FEATURE_FIND_XDEV( "-xdev\0" )
815 IF_FEATURE_FIND_DEPTH( "-depth\0" )
816 "-a\0"
817 "-o\0"
713 IF_FEATURE_FIND_NOT( "!\0" ) 818 IF_FEATURE_FIND_NOT( "!\0" )
714#if ENABLE_DESKTOP 819#if ENABLE_DESKTOP
715 "-and\0" 820 "-and\0"
716 "-or\0" 821 "-or\0"
717 IF_FEATURE_FIND_NOT( "-not\0" ) 822 IF_FEATURE_FIND_NOT( "-not\0" )
718#endif 823#endif
719 "-print\0" 824 "-print\0"
720 IF_FEATURE_FIND_PRINT0( "-print0\0" ) 825 IF_FEATURE_FIND_PRINT0( "-print0\0" )
721 IF_FEATURE_FIND_DEPTH( "-depth\0" )
722 IF_FEATURE_FIND_PRUNE( "-prune\0" ) 826 IF_FEATURE_FIND_PRUNE( "-prune\0" )
723 IF_FEATURE_FIND_DELETE( "-delete\0" ) 827 IF_FEATURE_FIND_DELETE( "-delete\0" )
724 IF_FEATURE_FIND_EXEC( "-exec\0" ) 828 IF_FEATURE_FIND_EXEC( "-exec\0" )
725 IF_FEATURE_FIND_PAREN( "(\0" ) 829 IF_FEATURE_FIND_PAREN( "(\0" )
726 /* All options starting from here require argument */ 830 /* All options/actions starting from here require argument */
727 "-name\0" 831 "-name\0"
728 "-iname\0" 832 "-iname\0"
729 IF_FEATURE_FIND_PATH( "-path\0" ) 833 IF_FEATURE_FIND_PATH( "-path\0" )
@@ -739,7 +843,8 @@ static action*** parse_params(char **argv)
739 IF_FEATURE_FIND_SIZE( "-size\0" ) 843 IF_FEATURE_FIND_SIZE( "-size\0" )
740 IF_FEATURE_FIND_CONTEXT("-context\0") 844 IF_FEATURE_FIND_CONTEXT("-context\0")
741 IF_FEATURE_FIND_LINKS( "-links\0" ) 845 IF_FEATURE_FIND_LINKS( "-links\0" )
742 ; 846 IF_FEATURE_FIND_MAXDEPTH("-mindepth\0""-maxdepth\0")
847 ;
743 848
744 action*** appp; 849 action*** appp;
745 unsigned cur_group = 0; 850 unsigned cur_group = 0;
@@ -766,27 +871,13 @@ static action*** parse_params(char **argv)
766 871
767 appp = xzalloc(2 * sizeof(appp[0])); /* appp[0],[1] == NULL */ 872 appp = xzalloc(2 * sizeof(appp[0])); /* appp[0],[1] == NULL */
768 873
769/* Actions have side effects and return a true or false value
770 * We implement: -print, -print0, -exec
771 *
772 * The rest are tests.
773 *
774 * Tests and actions are grouped by operators
775 * ( expr ) Force precedence
776 * ! expr True if expr is false
777 * -not expr Same as ! expr
778 * expr1 [-a[nd]] expr2 And; expr2 is not evaluated if expr1 is false
779 * expr1 -o[r] expr2 Or; expr2 is not evaluated if expr1 is true
780 * expr1 , expr2 List; both expr1 and expr2 are always evaluated
781 * We implement: (), -a, -o
782 */
783 while (*argv) { 874 while (*argv) {
784 const char *arg = argv[0]; 875 const char *arg = argv[0];
785 int parm = index_in_strings(params, arg); 876 int parm = index_in_strings(params, arg);
786 const char *arg1 = argv[1]; 877 const char *arg1 = argv[1];
787 878
788 if (parm >= PARM_name) { 879 if (parm >= PARM_name) {
789 /* All options starting from -name require argument */ 880 /* All options/actions starting from -name require argument */
790 if (!arg1) 881 if (!arg1)
791 bb_error_msg_and_die(bb_msg_requires_arg, arg); 882 bb_error_msg_and_die(bb_msg_requires_arg, arg);
792 argv++; 883 argv++;
@@ -795,8 +886,37 @@ static action*** parse_params(char **argv)
795 /* We can use big switch() here, but on i386 886 /* We can use big switch() here, but on i386
796 * it doesn't give smaller code. Other arches? */ 887 * it doesn't give smaller code. Other arches? */
797 888
798 /* --- Operators --- */ 889/* Options always return true. They always take effect
799 if (parm == PARM_a IF_DESKTOP(|| parm == PARM_and)) { 890 * rather than being processed only when their place in the
891 * expression is reached.
892 */
893 /* Options */
894#if ENABLE_FEATURE_FIND_XDEV
895 if (parm == OPT_XDEV) {
896 G.xdev_on = 1;
897 }
898#endif
899#if ENABLE_FEATURE_FIND_MAXDEPTH
900 else if (parm == OPT_MINDEPTH || parm == OPT_MINDEPTH + 1) {
901 G.minmaxdepth[parm - OPT_MINDEPTH] = xatoi_positive(arg1);
902 }
903#endif
904#if ENABLE_FEATURE_FIND_DEPTH
905 else if (parm == OPT_DEPTH) {
906 G.recurse_flags |= ACTION_DEPTHFIRST;
907 }
908#endif
909/* Actions are grouped by operators
910 * ( expr ) Force precedence
911 * ! expr True if expr is false
912 * -not expr Same as ! expr
913 * expr1 [-a[nd]] expr2 And; expr2 is not evaluated if expr1 is false
914 * expr1 -o[r] expr2 Or; expr2 is not evaluated if expr1 is true
915 * expr1 , expr2 List; both expr1 and expr2 are always evaluated
916 * We implement: (), -a, -o
917 */
918 /* Operators */
919 else if (parm == PARM_a IF_DESKTOP(|| parm == PARM_and)) {
800 /* no further special handling required */ 920 /* no further special handling required */
801 } 921 }
802 else if (parm == PARM_o IF_DESKTOP(|| parm == PARM_or)) { 922 else if (parm == PARM_o IF_DESKTOP(|| parm == PARM_or)) {
@@ -813,29 +933,19 @@ static action*** parse_params(char **argv)
813 invert_flag ^= 1; 933 invert_flag ^= 1;
814 } 934 }
815#endif 935#endif
816 936 /* Actions */
817 /* --- Tests and actions --- */
818 else if (parm == PARM_print) { 937 else if (parm == PARM_print) {
819 G.need_print = 0; 938 G.need_print = 0;
820 /* GNU find ignores '!' here: "find ! -print" */
821 IF_FEATURE_FIND_NOT( invert_flag = 0; )
822 (void) ALLOC_ACTION(print); 939 (void) ALLOC_ACTION(print);
823 } 940 }
824#if ENABLE_FEATURE_FIND_PRINT0 941#if ENABLE_FEATURE_FIND_PRINT0
825 else if (parm == PARM_print0) { 942 else if (parm == PARM_print0) {
826 G.need_print = 0; 943 G.need_print = 0;
827 IF_FEATURE_FIND_NOT( invert_flag = 0; )
828 (void) ALLOC_ACTION(print0); 944 (void) ALLOC_ACTION(print0);
829 } 945 }
830#endif 946#endif
831#if ENABLE_FEATURE_FIND_DEPTH
832 else if (parm == PARM_depth) {
833 G.recurse_flags |= ACTION_DEPTHFIRST;
834 }
835#endif
836#if ENABLE_FEATURE_FIND_PRUNE 947#if ENABLE_FEATURE_FIND_PRUNE
837 else if (parm == PARM_prune) { 948 else if (parm == PARM_prune) {
838 IF_FEATURE_FIND_NOT( invert_flag = 0; )
839 (void) ALLOC_ACTION(prune); 949 (void) ALLOC_ACTION(prune);
840 } 950 }
841#endif 951#endif
@@ -851,7 +961,6 @@ static action*** parse_params(char **argv)
851 int i; 961 int i;
852 action_exec *ap; 962 action_exec *ap;
853 G.need_print = 0; 963 G.need_print = 0;
854 IF_FEATURE_FIND_NOT( invert_flag = 0; )
855 ap = ALLOC_ACTION(exec); 964 ap = ALLOC_ACTION(exec);
856 ap->exec_argv = ++argv; /* first arg after -exec */ 965 ap->exec_argv = ++argv; /* first arg after -exec */
857 /*ap->exec_argc = 0; - ALLOC_ACTION did it */ 966 /*ap->exec_argc = 0; - ALLOC_ACTION did it */
@@ -859,9 +968,9 @@ static action*** parse_params(char **argv)
859 if (!*argv) /* did not see ';' or '+' until end */ 968 if (!*argv) /* did not see ';' or '+' until end */
860 bb_error_msg_and_die(bb_msg_requires_arg, "-exec"); 969 bb_error_msg_and_die(bb_msg_requires_arg, "-exec");
861 // find -exec echo Foo ">{}<" ";" 970 // find -exec echo Foo ">{}<" ";"
862 // executes "echo Foo <filename>", 971 // executes "echo Foo >FILENAME<",
863 // find -exec echo Foo ">{}<" "+" 972 // find -exec echo Foo ">{}<" "+"
864 // executes "echo Foo <filename1> <filename2> <filename3>...". 973 // executes "echo Foo FILENAME1 FILENAME2 FILENAME3...".
865 // TODO (so far we treat "+" just like ";") 974 // TODO (so far we treat "+" just like ";")
866 if ((argv[0][0] == ';' || argv[0][0] == '+') 975 if ((argv[0][0] == ';' || argv[0][0] == '+')
867 && argv[0][1] == '\0' 976 && argv[0][1] == '\0'
@@ -930,10 +1039,10 @@ static action*** parse_params(char **argv)
930 } 1039 }
931#endif 1040#endif
932#if ENABLE_FEATURE_FIND_PERM 1041#if ENABLE_FEATURE_FIND_PERM
933/* -perm mode File's permission bits are exactly mode (octal or symbolic). 1042/* -perm BITS File's mode bits are exactly BITS (octal or symbolic).
934 * Symbolic modes use mode 0 as a point of departure. 1043 * Symbolic modes use mode 0 as a point of departure.
935 * -perm -mode All of the permission bits mode are set for the file. 1044 * -perm -BITS All of the BITS are set in file's mode.
936 * -perm +mode Any of the permission bits mode are set for the file. 1045 * -perm +BITS At least one of the BITS is set in file's mode.
937 */ 1046 */
938 else if (parm == PARM_perm) { 1047 else if (parm == PARM_perm) {
939 action_perm *ap; 1048 action_perm *ap;
@@ -1050,193 +1159,59 @@ static action*** parse_params(char **argv)
1050#undef ALLOC_ACTION 1159#undef ALLOC_ACTION
1051} 1160}
1052 1161
1053//usage:#define find_trivial_usage
1054//usage: "[PATH]... [EXPRESSION]"
1055//usage:#define find_full_usage "\n\n"
1056//usage: "Search for files. The default PATH is the current directory,\n"
1057//usage: "default EXPRESSION is '-print'\n"
1058//usage: "\nEXPRESSION may consist of:"
1059//usage: "\n -follow Follow symlinks"
1060//usage: IF_FEATURE_FIND_XDEV(
1061//usage: "\n -xdev Don't descend directories on other filesystems"
1062//usage: )
1063//usage: IF_FEATURE_FIND_MAXDEPTH(
1064//usage: "\n -maxdepth N Descend at most N levels. -maxdepth 0 applies"
1065//usage: "\n tests/actions to command line arguments only"
1066//usage: )
1067//usage: "\n -mindepth N Don't act on first N levels"
1068//usage: "\n -name PATTERN File name (w/o directory name) matches PATTERN"
1069//usage: "\n -iname PATTERN Case insensitive -name"
1070//usage: IF_FEATURE_FIND_PATH(
1071//usage: "\n -path PATTERN Path matches PATTERN"
1072//usage: )
1073//usage: IF_FEATURE_FIND_REGEX(
1074//usage: "\n -regex PATTERN Path matches regex PATTERN"
1075//usage: )
1076//usage: IF_FEATURE_FIND_TYPE(
1077//usage: "\n -type X File type is X (X is one of: f,d,l,b,c,...)"
1078//usage: )
1079//usage: IF_FEATURE_FIND_PERM(
1080//usage: "\n -perm NNN Permissions match any of (+NNN), all of (-NNN),"
1081//usage: "\n or exactly NNN"
1082//usage: )
1083//usage: IF_FEATURE_FIND_MTIME(
1084//usage: "\n -mtime DAYS Modified time is greater than (+N), less than (-N),"
1085//usage: "\n or exactly N days"
1086//usage: )
1087//usage: IF_FEATURE_FIND_MMIN(
1088//usage: "\n -mmin MINS Modified time is greater than (+N), less than (-N),"
1089//usage: "\n or exactly N minutes"
1090//usage: )
1091//usage: IF_FEATURE_FIND_NEWER(
1092//usage: "\n -newer FILE Modified time is more recent than FILE's"
1093//usage: )
1094//usage: IF_FEATURE_FIND_INUM(
1095//usage: "\n -inum N File has inode number N"
1096//usage: )
1097//usage: IF_FEATURE_FIND_USER(
1098//usage: "\n -user NAME File is owned by user NAME (numeric user ID allowed)"
1099//usage: )
1100//usage: IF_FEATURE_FIND_GROUP(
1101//usage: "\n -group NAME File belongs to group NAME (numeric group ID allowed)"
1102//usage: )
1103//usage: IF_FEATURE_FIND_DEPTH(
1104//usage: "\n -depth Process directory name after traversing it"
1105//usage: )
1106//usage: IF_FEATURE_FIND_SIZE(
1107//usage: "\n -size N[bck] File size is N (c:bytes,k:kbytes,b:512 bytes(def.))"
1108//usage: "\n +/-N: file size is bigger/smaller than N"
1109//usage: )
1110//usage: IF_FEATURE_FIND_LINKS(
1111//usage: "\n -links N Number of links is greater than (+N), less than (-N),"
1112//usage: "\n or exactly N"
1113//usage: )
1114//usage: "\n -print Print (default and assumed)"
1115//usage: IF_FEATURE_FIND_PRINT0(
1116//usage: "\n -print0 Delimit output with null characters rather than"
1117//usage: "\n newlines"
1118//usage: )
1119//usage: IF_FEATURE_FIND_CONTEXT(
1120//usage: "\n -context File has specified security context"
1121//usage: )
1122//usage: IF_FEATURE_FIND_EXEC(
1123//usage: "\n -exec CMD ARG ; Run CMD with all instances of {} replaced by the"
1124//usage: "\n matching files"
1125//usage: )
1126//usage: IF_FEATURE_FIND_PRUNE(
1127//usage: "\n -prune Stop traversing current subtree"
1128//usage: )
1129//usage: IF_FEATURE_FIND_DELETE(
1130//usage: "\n -delete Delete files, turns on -depth option"
1131//usage: )
1132//usage: IF_FEATURE_FIND_PAREN(
1133//usage: "\n (EXPR) Group an expression"
1134//usage: )
1135//usage:
1136//usage:#define find_example_usage
1137//usage: "$ find / -name passwd\n"
1138//usage: "/etc/passwd\n"
1139
1140int find_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 1162int find_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
1141int find_main(int argc UNUSED_PARAM, char **argv) 1163int find_main(int argc UNUSED_PARAM, char **argv)
1142{ 1164{
1143 static const char options[] ALIGN1 =
1144 "-follow\0"
1145IF_FEATURE_FIND_XDEV( "-xdev\0" )
1146IF_FEATURE_FIND_MAXDEPTH("-mindepth\0""-maxdepth\0")
1147 ;
1148 enum {
1149 OPT_FOLLOW,
1150IF_FEATURE_FIND_XDEV( OPT_XDEV ,)
1151IF_FEATURE_FIND_MAXDEPTH(OPT_MINDEPTH,)
1152 };
1153
1154 char *arg;
1155 char **argp;
1156 int i, firstopt, status = EXIT_SUCCESS; 1165 int i, firstopt, status = EXIT_SUCCESS;
1157#if ENABLE_FEATURE_FIND_MAXDEPTH
1158 int minmaxdepth[2] = { 0, INT_MAX };
1159#else
1160#define minmaxdepth NULL
1161#endif
1162 1166
1163 INIT_G(); 1167 INIT_G();
1164 1168
1165 for (firstopt = 1; argv[firstopt]; firstopt++) { 1169 argv++;
1170 for (firstopt = 0; argv[firstopt]; firstopt++) {
1166 if (argv[firstopt][0] == '-') 1171 if (argv[firstopt][0] == '-')
1167 break; 1172 break;
1168 if (ENABLE_FEATURE_FIND_NOT && LONE_CHAR(argv[firstopt], '!')) 1173 if (ENABLE_FEATURE_FIND_NOT && LONE_CHAR(argv[firstopt], '!'))
1169 break; 1174 break;
1170#if ENABLE_FEATURE_FIND_PAREN 1175 if (ENABLE_FEATURE_FIND_PAREN && LONE_CHAR(argv[firstopt], '('))
1171 if (LONE_CHAR(argv[firstopt], '('))
1172 break; 1176 break;
1173#endif
1174 } 1177 }
1175 if (firstopt == 1) { 1178 if (firstopt == 0) {
1176 argv[0] = (char*)"."; 1179 *--argv = (char*)".";
1177 argv--;
1178 firstopt++; 1180 firstopt++;
1179 } 1181 }
1180 1182
1181/* All options always return true. They always take effect 1183 G.actions = parse_params(&argv[firstopt]);
1182 * rather than being processed only when their place in the 1184 argv[firstopt] = NULL;
1183 * expression is reached. 1185
1184 * We implement: -follow, -xdev, -maxdepth
1185 */
1186 /* Process options, and replace then with -a */
1187 /* (-a will be ignored by recursive parser later) */
1188 argp = &argv[firstopt];
1189 while ((arg = argp[0])) {
1190 int opt = index_in_strings(options, arg);
1191 if (opt == OPT_FOLLOW) {
1192 G.recurse_flags |= ACTION_FOLLOWLINKS | ACTION_DANGLING_OK;
1193 argp[0] = (char*)"-a";
1194 }
1195#if ENABLE_FEATURE_FIND_XDEV 1186#if ENABLE_FEATURE_FIND_XDEV
1196 if (opt == OPT_XDEV) { 1187 if (G.xdev_on) {
1197 struct stat stbuf; 1188 struct stat stbuf;
1198 if (!G.xdev_count) { 1189
1199 G.xdev_count = firstopt - 1; 1190 G.xdev_count = firstopt;
1200 G.xdev_dev = xzalloc(G.xdev_count * sizeof(G.xdev_dev[0])); 1191 G.xdev_dev = xzalloc(G.xdev_count * sizeof(G.xdev_dev[0]));
1201 for (i = 1; i < firstopt; i++) { 1192 for (i = 0; argv[i]; i++) {
1202 /* not xstat(): shouldn't bomb out on 1193 /* not xstat(): shouldn't bomb out on
1203 * "find not_exist exist -xdev" */ 1194 * "find not_exist exist -xdev" */
1204 if (stat(argv[i], &stbuf) == 0) 1195 if (stat(argv[i], &stbuf) == 0)
1205 G.xdev_dev[i-1] = stbuf.st_dev; 1196 G.xdev_dev[i] = stbuf.st_dev;
1206 /* else G.xdev_dev[i-1] stays 0 and 1197 /* else G.xdev_dev[i] stays 0 and
1207 * won't match any real device dev_t */ 1198 * won't match any real device dev_t
1208 } 1199 */
1209 }
1210 argp[0] = (char*)"-a";
1211 }
1212#endif
1213#if ENABLE_FEATURE_FIND_MAXDEPTH
1214 if (opt == OPT_MINDEPTH || opt == OPT_MINDEPTH + 1) {
1215 if (!argp[1])
1216 bb_show_usage();
1217 minmaxdepth[opt - OPT_MINDEPTH] = xatoi_positive(argp[1]);
1218 argp[0] = (char*)"-a";
1219 argp[1] = (char*)"-a";
1220 argp++;
1221 } 1200 }
1222#endif
1223 argp++;
1224 } 1201 }
1202#endif
1225 1203
1226 G.actions = parse_params(&argv[firstopt]); 1204 for (i = 0; argv[i]; i++) {
1227
1228 for (i = 1; i < firstopt; i++) {
1229 if (!recursive_action(argv[i], 1205 if (!recursive_action(argv[i],
1230 G.recurse_flags,/* flags */ 1206 G.recurse_flags,/* flags */
1231 fileAction, /* file action */ 1207 fileAction, /* file action */
1232 fileAction, /* dir action */ 1208 fileAction, /* dir action */
1233#if ENABLE_FEATURE_FIND_MAXDEPTH
1234 minmaxdepth, /* user data */
1235#else
1236 NULL, /* user data */ 1209 NULL, /* user data */
1237#endif 1210 0) /* depth */
1238 0)) /* depth */ 1211 ) {
1239 status = EXIT_FAILURE; 1212 status = EXIT_FAILURE;
1213 }
1240 } 1214 }
1215
1241 return status; 1216 return status;
1242} 1217}
diff --git a/include/applets.src.h b/include/applets.src.h
index fa7d0cc55..133f376a3 100644
--- a/include/applets.src.h
+++ b/include/applets.src.h
@@ -211,7 +211,7 @@ IF_KILLALL(APPLET_ODDNAME(killall, kill, BB_DIR_USR_BIN, BB_SUID_DROP, killall))
211IF_KILLALL5(APPLET_ODDNAME(killall5, kill, BB_DIR_USR_BIN, BB_SUID_DROP, killall5)) 211IF_KILLALL5(APPLET_ODDNAME(killall5, kill, BB_DIR_USR_BIN, BB_SUID_DROP, killall5))
212IF_KLOGD(APPLET(klogd, BB_DIR_SBIN, BB_SUID_DROP)) 212IF_KLOGD(APPLET(klogd, BB_DIR_SBIN, BB_SUID_DROP))
213IF_LAST(APPLET(last, BB_DIR_USR_BIN, BB_SUID_DROP)) 213IF_LAST(APPLET(last, BB_DIR_USR_BIN, BB_SUID_DROP))
214IF_LENGTH(APPLET_NOFORK(length, length, BB_DIR_USR_BIN, BB_SUID_DROP, length)) 214//IF_LENGTH(APPLET_NOFORK(length, length, BB_DIR_USR_BIN, BB_SUID_DROP, length))
215IF_LESS(APPLET(less, BB_DIR_USR_BIN, BB_SUID_DROP)) 215IF_LESS(APPLET(less, BB_DIR_USR_BIN, BB_SUID_DROP))
216IF_SETARCH(APPLET_ODDNAME(linux32, setarch, BB_DIR_BIN, BB_SUID_DROP, linux32)) 216IF_SETARCH(APPLET_ODDNAME(linux32, setarch, BB_DIR_BIN, BB_SUID_DROP, linux32))
217IF_SETARCH(APPLET_ODDNAME(linux64, setarch, BB_DIR_BIN, BB_SUID_DROP, linux64)) 217IF_SETARCH(APPLET_ODDNAME(linux64, setarch, BB_DIR_BIN, BB_SUID_DROP, linux64))
diff --git a/include/archive.h b/include/archive.h
index ba6d323e0..49c478728 100644
--- a/include/archive.h
+++ b/include/archive.h
@@ -8,22 +8,22 @@ enum {
8#if BB_BIG_ENDIAN 8#if BB_BIG_ENDIAN
9 COMPRESS_MAGIC = 0x1f9d, 9 COMPRESS_MAGIC = 0x1f9d,
10 GZIP_MAGIC = 0x1f8b, 10 GZIP_MAGIC = 0x1f8b,
11 BZIP2_MAGIC = 'B' * 256 + 'Z', 11 BZIP2_MAGIC = 256 * 'B' + 'Z',
12 /* .xz signature: 0xfd, '7', 'z', 'X', 'Z', 0x00 */ 12 /* .xz signature: 0xfd, '7', 'z', 'X', 'Z', 0x00 */
13 /* More info at: http://tukaani.org/xz/xz-file-format.txt */ 13 /* More info at: http://tukaani.org/xz/xz-file-format.txt */
14 XZ_MAGIC1 = 0xfd * 256 + '7', 14 XZ_MAGIC1 = 256 * 0xfd + '7',
15 XZ_MAGIC2 = (('z' * 256 + 'X') * 256 + 'Z') * 256 + 0, 15 XZ_MAGIC2 = 256 * (256 * (256 * 'z' + 'X') + 'Z') + 0,
16 /* Different form: 32 bits, then 16 bits: */ 16 /* Different form: 32 bits, then 16 bits: */
17 XZ_MAGIC1a = ((0xfd * 256 + '7') * 256 + 'z') * 256 + 'X', 17 XZ_MAGIC1a = 256 * (256 * (256 * 0xfd + '7') + 'z') + 'X',
18 XZ_MAGIC2a = 'Z' * 256 + 0, 18 XZ_MAGIC2a = 256 * 'Z' + 0,
19#else 19#else
20 COMPRESS_MAGIC = 0x9d1f, 20 COMPRESS_MAGIC = 0x9d1f,
21 GZIP_MAGIC = 0x8b1f, 21 GZIP_MAGIC = 0x8b1f,
22 BZIP2_MAGIC = 'Z' * 256 + 'B', 22 BZIP2_MAGIC = 'B' + 'Z' * 256,
23 XZ_MAGIC1 = '7' * 256 + 0xfd, 23 XZ_MAGIC1 = 0xfd + '7' * 256,
24 XZ_MAGIC2 = ((0 * 256 + 'Z') * 256 + 'X') * 256 + 'z', 24 XZ_MAGIC2 = 'z' + ('X' + ('Z' + 0 * 256) * 256) * 256,
25 XZ_MAGIC1a = (('X' * 256 + 'z') * 256 + '7') * 256 + 0xfd, 25 XZ_MAGIC1a = 0xfd + ('7' + ('z' + 'X' * 256) * 256) * 256,
26 XZ_MAGIC2a = 0 * 256 + 'Z', 26 XZ_MAGIC2a = 'Z' + 0 * 256,
27#endif 27#endif
28}; 28};
29 29
diff --git a/include/libbb.h b/include/libbb.h
index 7efa32349..6564038e9 100644
--- a/include/libbb.h
+++ b/include/libbb.h
@@ -33,10 +33,12 @@
33#include <sys/stat.h> 33#include <sys/stat.h>
34#include <sys/time.h> 34#include <sys/time.h>
35#include <sys/types.h> 35#include <sys/types.h>
36#ifndef major
37# include <sys/sysmacros.h>
38#endif
36#include <sys/wait.h> 39#include <sys/wait.h>
37#include <termios.h> 40#include <termios.h>
38#include <time.h> 41#include <time.h>
39#include <unistd.h>
40#include <sys/param.h> 42#include <sys/param.h>
41#ifdef HAVE_MNTENT_H 43#ifdef HAVE_MNTENT_H
42# include <mntent.h> 44# include <mntent.h>
@@ -875,6 +877,7 @@ void FAST_FUNC update_utmp(pid_t pid, int new_type, const char *tty_name, const
875# define update_utmp(pid, new_type, tty_name, username, hostname) ((void)0) 877# define update_utmp(pid, new_type, tty_name, username, hostname) ((void)0)
876#endif 878#endif
877 879
880
878int execable_file(const char *name) FAST_FUNC; 881int execable_file(const char *name) FAST_FUNC;
879char *find_execable(const char *filename, char **PATHp) FAST_FUNC; 882char *find_execable(const char *filename, char **PATHp) FAST_FUNC;
880int exists_execable(const char *filename) FAST_FUNC; 883int exists_execable(const char *filename) FAST_FUNC;
@@ -883,14 +886,16 @@ int exists_execable(const char *filename) FAST_FUNC;
883 * but it may exec busybox and call applet instead of searching PATH. 886 * but it may exec busybox and call applet instead of searching PATH.
884 */ 887 */
885#if ENABLE_FEATURE_PREFER_APPLETS 888#if ENABLE_FEATURE_PREFER_APPLETS
886int bb_execvp(const char *file, char *const argv[]) FAST_FUNC; 889int BB_EXECVP(const char *file, char *const argv[]) FAST_FUNC;
887#define BB_EXECVP(prog,cmd) bb_execvp(prog,cmd)
888#define BB_EXECLP(prog,cmd,...) \ 890#define BB_EXECLP(prog,cmd,...) \
889 execlp((find_applet_by_name(prog) >= 0) ? CONFIG_BUSYBOX_EXEC_PATH : prog, \ 891 do { \
890 cmd, __VA_ARGS__) 892 if (find_applet_by_name(prog) >= 0) \
893 execlp(bb_busybox_exec_path, cmd, __VA_ARGS__); \
894 execlp(prog, cmd, __VA_ARGS__); \
895 } while (0)
891#else 896#else
892#define BB_EXECVP(prog,cmd) execvp(prog,cmd) 897#define BB_EXECVP(prog,cmd) execvp(prog,cmd)
893#define BB_EXECLP(prog,cmd,...) execlp(prog,cmd, __VA_ARGS__) 898#define BB_EXECLP(prog,cmd,...) execlp(prog,cmd,__VA_ARGS__)
894#endif 899#endif
895int BB_EXECVP_or_die(char **argv) NORETURN FAST_FUNC; 900int BB_EXECVP_or_die(char **argv) NORETURN FAST_FUNC;
896 901
@@ -924,19 +929,8 @@ pid_t wait_any_nohang(int *wstat) FAST_FUNC;
924int wait4pid(pid_t pid) FAST_FUNC; 929int wait4pid(pid_t pid) FAST_FUNC;
925/* Same as wait4pid(spawn(argv)), but with NOFORK/NOEXEC if configured: */ 930/* Same as wait4pid(spawn(argv)), but with NOFORK/NOEXEC if configured: */
926int spawn_and_wait(char **argv) FAST_FUNC; 931int spawn_and_wait(char **argv) FAST_FUNC;
927struct nofork_save_area {
928 jmp_buf die_jmp;
929 const char *applet_name;
930 uint32_t option_mask32;
931 int die_sleep;
932 uint8_t xfunc_error_retval;
933 smallint saved;
934};
935void save_nofork_data(struct nofork_save_area *save) FAST_FUNC;
936void restore_nofork_data(struct nofork_save_area *save) FAST_FUNC;
937/* Does NOT check that applet is NOFORK, just blindly runs it */ 932/* Does NOT check that applet is NOFORK, just blindly runs it */
938int run_nofork_applet(int applet_no, char **argv) FAST_FUNC; 933int run_nofork_applet(int applet_no, char **argv) FAST_FUNC;
939int run_nofork_applet_prime(struct nofork_save_area *old, int applet_no, char **argv) FAST_FUNC;
940 934
941/* Helpers for daemonization. 935/* Helpers for daemonization.
942 * 936 *
@@ -1417,17 +1411,18 @@ enum {
1417}; 1411};
1418line_input_t *new_line_input_t(int flags) FAST_FUNC; 1412line_input_t *new_line_input_t(int flags) FAST_FUNC;
1419/* So far static: void free_line_input_t(line_input_t *n) FAST_FUNC; */ 1413/* So far static: void free_line_input_t(line_input_t *n) FAST_FUNC; */
1420/* maxsize must be >= 2. 1414/*
1415 * maxsize must be >= 2.
1421 * Returns: 1416 * Returns:
1422 * -1 on read errors or EOF, or on bare Ctrl-D, 1417 * -1 on read errors or EOF, or on bare Ctrl-D,
1423 * 0 on ctrl-C (the line entered is still returned in 'command'), 1418 * 0 on ctrl-C (the line entered is still returned in 'command'),
1424 * >0 length of input string, including terminating '\n' 1419 * >0 length of input string, including terminating '\n'
1425 */ 1420 */
1426int read_line_input(const char* prompt, char* command, int maxsize, line_input_t *state) FAST_FUNC; 1421int read_line_input(line_input_t *st, const char *prompt, char *command, int maxsize, int timeout) FAST_FUNC;
1427#else 1422#else
1428#define MAX_HISTORY 0 1423#define MAX_HISTORY 0
1429int read_line_input(const char* prompt, char* command, int maxsize) FAST_FUNC; 1424int read_line_input(const char* prompt, char* command, int maxsize) FAST_FUNC;
1430#define read_line_input(prompt, command, maxsize, state) \ 1425#define read_line_input(state, prompt, command, maxsize, timeout) \
1431 read_line_input(prompt, command, maxsize) 1426 read_line_input(prompt, command, maxsize)
1432#endif 1427#endif
1433 1428
@@ -1608,16 +1603,24 @@ int print_flags_separated(const int *masks, const char *labels,
1608int print_flags(const masks_labels_t *ml, int flags) FAST_FUNC; 1603int print_flags(const masks_labels_t *ml, int flags) FAST_FUNC;
1609 1604
1610typedef struct bb_progress_t { 1605typedef struct bb_progress_t {
1611 off_t lastsize; 1606 unsigned last_size;
1612 unsigned lastupdate_sec; 1607 unsigned last_update_sec;
1608 unsigned last_change_sec;
1613 unsigned start_sec; 1609 unsigned start_sec;
1614 smallint inited; 1610 const char *curfile;
1615} bb_progress_t; 1611} bb_progress_t;
1616 1612
1617void bb_progress_init(bb_progress_t *p) FAST_FUNC; 1613#define is_bb_progress_inited(p) ((p)->curfile != NULL)
1618void bb_progress_update(bb_progress_t *p, const char *curfile, 1614#define bb_progress_free(p) do { \
1619 off_t beg_range, off_t transferred, 1615 if (ENABLE_UNICODE_SUPPORT) free((char*)((p)->curfile)); \
1620 off_t totalsize) FAST_FUNC; 1616 (p)->curfile = NULL; \
1617} while (0)
1618void bb_progress_init(bb_progress_t *p, const char *curfile) FAST_FUNC;
1619void bb_progress_update(bb_progress_t *p,
1620 uoff_t beg_range,
1621 uoff_t transferred,
1622 uoff_t totalsize) FAST_FUNC;
1623
1621 1624
1622extern const char *applet_name; 1625extern const char *applet_name;
1623 1626
diff --git a/include/platform.h b/include/platform.h
index c272fbb05..395ec5d45 100644
--- a/include/platform.h
+++ b/include/platform.h
@@ -28,6 +28,7 @@
28#define HAVE_PTSNAME_R 1 28#define HAVE_PTSNAME_R 1
29#define HAVE_SETBIT 1 29#define HAVE_SETBIT 1
30#define HAVE_SIGHANDLER_T 1 30#define HAVE_SIGHANDLER_T 1
31#define HAVE_STPCPY 1
31#define HAVE_STRCASESTR 1 32#define HAVE_STRCASESTR 1
32#define HAVE_STRCHRNUL 1 33#define HAVE_STRCHRNUL 1
33#define HAVE_STRSEP 1 34#define HAVE_STRSEP 1
@@ -374,13 +375,16 @@ typedef unsigned smalluint;
374# define ADJ_TICK MOD_CLKB 375# define ADJ_TICK MOD_CLKB
375# endif 376# endif
376 377
378# undef HAVE_STPCPY
379
377#else 380#else
378 381
379# define bb_setpgrp() setpgrp() 382# define bb_setpgrp() setpgrp()
380 383
381#endif 384#endif
382 385
383#if defined(__GLIBC__) 386#include <unistd.h>
387#if (defined(_POSIX_VERSION) && _POSIX_VERSION >= 200809L) || defined(__GLIBC__)
384# define fdprintf dprintf 388# define fdprintf dprintf
385#endif 389#endif
386 390
@@ -393,6 +397,7 @@ typedef unsigned smalluint;
393# undef HAVE_MEMRCHR 397# undef HAVE_MEMRCHR
394# undef HAVE_MKDTEMP 398# undef HAVE_MKDTEMP
395# undef HAVE_SETBIT 399# undef HAVE_SETBIT
400# undef HAVE_STPCPY
396# undef HAVE_STRCASESTR 401# undef HAVE_STRCASESTR
397# undef HAVE_STRCHRNUL 402# undef HAVE_STRCHRNUL
398# undef HAVE_STRSIGNAL 403# undef HAVE_STRSIGNAL
@@ -406,6 +411,7 @@ typedef unsigned smalluint;
406# undef HAVE_MEMRCHR 411# undef HAVE_MEMRCHR
407# undef HAVE_MKDTEMP 412# undef HAVE_MKDTEMP
408# undef HAVE_SETBIT 413# undef HAVE_SETBIT
414# undef HAVE_STPCPY
409# undef HAVE_STRCASESTR 415# undef HAVE_STRCASESTR
410# undef HAVE_STRCHRNUL 416# undef HAVE_STRCHRNUL
411# undef HAVE_STRSEP 417# undef HAVE_STRSEP
@@ -444,6 +450,10 @@ extern char *mkdtemp(char *template) FAST_FUNC;
444typedef void (*sighandler_t)(int); 450typedef void (*sighandler_t)(int);
445#endif 451#endif
446 452
453#ifndef HAVE_STPCPY
454extern char *stpcpy(char *p, const char *to_add) FAST_FUNC;
455#endif
456
447#ifndef HAVE_STRCASESTR 457#ifndef HAVE_STRCASESTR
448extern char *strcasestr(const char *s, const char *pattern) FAST_FUNC; 458extern char *strcasestr(const char *s, const char *pattern) FAST_FUNC;
449#endif 459#endif
diff --git a/include/usage.src.h b/include/usage.src.h
index ebe80f8e1..c2575b561 100644
--- a/include/usage.src.h
+++ b/include/usage.src.h
@@ -2451,26 +2451,6 @@ INSERT
2451 "\nOptions:" \ 2451 "\nOptions:" \
2452 "\n -L LBL Label" \ 2452 "\n -L LBL Label" \
2453 2453
2454#define mktemp_trivial_usage \
2455 "[-dt] [-p DIR] [TEMPLATE]"
2456#define mktemp_full_usage "\n\n" \
2457 "Create a temporary file with name based on TEMPLATE and print its name.\n" \
2458 "TEMPLATE must end with XXXXXX (e.g. [/dir/]nameXXXXXX).\n" \
2459 "\nOptions:" \
2460 "\n -d Make a directory instead of a file" \
2461/* "\n -q Fail silently if an error occurs" - we ignore it */ \
2462 "\n -t Generate a path rooted in temporary directory" \
2463 "\n -p DIR Use DIR as a temporary directory (implies -t)" \
2464 "\n" \
2465 "\nFor -t or -p, directory is chosen as follows:" \
2466 "\n$TMPDIR if set, else -p DIR, else /tmp" \
2467
2468#define mktemp_example_usage \
2469 "$ mktemp /tmp/temp.XXXXXX\n" \
2470 "/tmp/temp.mWiLjM\n" \
2471 "$ ls -la /tmp/temp.mWiLjM\n" \
2472 "-rw------- 1 andersen andersen 0 Apr 25 17:10 /tmp/temp.mWiLjM\n"
2473
2474#define more_trivial_usage \ 2454#define more_trivial_usage \
2475 "[FILE]..." 2455 "[FILE]..."
2476#define more_full_usage "\n\n" \ 2456#define more_full_usage "\n\n" \
diff --git a/libbb/Config.src b/libbb/Config.src
index 85892d3fe..18bdc5151 100644
--- a/libbb/Config.src
+++ b/libbb/Config.src
@@ -156,11 +156,30 @@ config FEATURE_COPYBUF_KB
156 range 1 1024 156 range 1 1024
157 default 4 157 default 4
158 help 158 help
159 Size of buffer used by cp, mv, install etc. 159 Size of buffer used by cp, mv, install, wget etc.
160 Buffers which are 4 kb or less will be allocated on stack. 160 Buffers which are 4 kb or less will be allocated on stack.
161 Bigger buffers will be allocated with mmap, with fallback to 4 kb 161 Bigger buffers will be allocated with mmap, with fallback to 4 kb
162 stack buffer if mmap fails. 162 stack buffer if mmap fails.
163 163
164config FEATURE_SKIP_ROOTFS
165 bool "Skip rootfs in mount table"
166 default y
167 help
168 Ignore rootfs entry in mount table.
169
170 In Linux, kernel has a special filesystem, rootfs, which is initially
171 mounted on /. It contains initramfs data, if kernel is configured
172 to have one. Usually, another file system is mounted over / early
173 in boot process, and therefore most tools which manipulate
174 mount table, such as df, will skip rootfs entry.
175
176 However, some systems do not mount anything on /.
177 If you need to configure busybox for one of these systems,
178 you may find useful to turn this option off to make df show
179 initramfs statistic.
180
181 Otherwise, choose Y.
182
164config MONOTONIC_SYSCALL 183config MONOTONIC_SYSCALL
165 bool "Use clock_gettime(CLOCK_MONOTONIC) syscall" 184 bool "Use clock_gettime(CLOCK_MONOTONIC) syscall"
166 default n 185 default n
diff --git a/libbb/copy_file.c b/libbb/copy_file.c
index adbc2dea3..48aaf481d 100644
--- a/libbb/copy_file.c
+++ b/libbb/copy_file.c
@@ -78,9 +78,9 @@ int FAST_FUNC copy_file(const char *source, const char *dest, int flags)
78 /* NB: each struct stat is ~100 bytes */ 78 /* NB: each struct stat is ~100 bytes */
79 struct stat source_stat; 79 struct stat source_stat;
80 struct stat dest_stat; 80 struct stat dest_stat;
81 signed char retval = 0; 81 smallint retval = 0;
82 signed char dest_exists = 0; 82 smallint dest_exists = 0;
83 signed char ovr; 83 smallint ovr;
84 84
85/* Inverse of cp -d ("cp without -d") */ 85/* Inverse of cp -d ("cp without -d") */
86#define FLAGS_DEREF (flags & (FILEUTILS_DEREFERENCE + FILEUTILS_DEREFERENCE_L0)) 86#define FLAGS_DEREF (flags & (FILEUTILS_DEREFERENCE + FILEUTILS_DEREFERENCE_L0))
@@ -157,7 +157,6 @@ int FAST_FUNC copy_file(const char *source, const char *dest, int flags)
157 } 157 }
158#endif 158#endif
159 159
160 /* Create DEST */
161 if (dest_exists) { 160 if (dest_exists) {
162 if (!S_ISDIR(dest_stat.st_mode)) { 161 if (!S_ISDIR(dest_stat.st_mode)) {
163 bb_error_msg("target '%s' is not a directory", dest); 162 bb_error_msg("target '%s' is not a directory", dest);
@@ -166,6 +165,7 @@ int FAST_FUNC copy_file(const char *source, const char *dest, int flags)
166 /* race here: user can substitute a symlink between 165 /* race here: user can substitute a symlink between
167 * this check and actual creation of files inside dest */ 166 * this check and actual creation of files inside dest */
168 } else { 167 } else {
168 /* Create DEST */
169 mode_t mode; 169 mode_t mode;
170 saved_umask = umask(0); 170 saved_umask = umask(0);
171 171
diff --git a/libbb/crc32.c b/libbb/crc32.c
index c63bf0772..ac9836cc9 100644
--- a/libbb/crc32.c
+++ b/libbb/crc32.c
@@ -59,7 +59,7 @@ uint32_t FAST_FUNC crc32_block_endian0(uint32_t val, const void *buf, unsigned l
59 const void *end = (uint8_t*)buf + len; 59 const void *end = (uint8_t*)buf + len;
60 60
61 while (buf != end) { 61 while (buf != end) {
62 val = crc_table[(uint8_t)val ^ *(uint8_t*)buf] ^ (val >> 8); 62 val = crc_table[(uint8_t)val ^ *(uint8_t*)buf] ^ (val >> 8);
63 buf = (uint8_t*)buf + 1; 63 buf = (uint8_t*)buf + 1;
64 } 64 }
65 return val; 65 return val;
diff --git a/libbb/execable.c b/libbb/execable.c
index 3b144a730..e35dad208 100644
--- a/libbb/execable.c
+++ b/libbb/execable.c
@@ -101,12 +101,12 @@ int FAST_FUNC exists_execable(const char *filename)
101} 101}
102 102
103#if ENABLE_FEATURE_PREFER_APPLETS 103#if ENABLE_FEATURE_PREFER_APPLETS
104/* just like the real execvp, but try to launch an applet named 'file' first 104/* just like the real execvp, but try to launch an applet named 'file' first */
105 */ 105int FAST_FUNC BB_EXECVP(const char *file, char *const argv[])
106int FAST_FUNC bb_execvp(const char *file, char *const argv[])
107{ 106{
108 return execvp(find_applet_by_name(file) >= 0 ? bb_busybox_exec_path : file, 107 if (find_applet_by_name(file) >= 0)
109 argv); 108 execvp(bb_busybox_exec_path, argv);
109 return execvp(file, argv);
110} 110}
111#endif 111#endif
112 112
diff --git a/libbb/find_mount_point.c b/libbb/find_mount_point.c
index 361698a6b..56637ad92 100644
--- a/libbb/find_mount_point.c
+++ b/libbb/find_mount_point.c
@@ -43,7 +43,7 @@ struct mntent* FAST_FUNC find_mount_point(const char *name, int subdir_too)
43 /* rootfs mount in Linux 2.6 exists always, 43 /* rootfs mount in Linux 2.6 exists always,
44 * and it makes sense to always ignore it. 44 * and it makes sense to always ignore it.
45 * Otherwise people can't reference their "real" root! */ 45 * Otherwise people can't reference their "real" root! */
46 if (strcmp(mountEntry->mnt_fsname, "rootfs") == 0) 46 if (ENABLE_FEATURE_SKIP_ROOTFS && strcmp(mountEntry->mnt_fsname, "rootfs") == 0)
47 continue; 47 continue;
48 48
49 if (strcmp(name, mountEntry->mnt_dir) == 0 49 if (strcmp(name, mountEntry->mnt_dir) == 0
diff --git a/libbb/getopt32.c b/libbb/getopt32.c
index 187cb3a79..053ffc349 100644
--- a/libbb/getopt32.c
+++ b/libbb/getopt32.c
@@ -80,9 +80,9 @@ const char *applet_long_options
80 This struct allows you to define long options: 80 This struct allows you to define long options:
81 81
82 static const char applet_longopts[] ALIGN1 = 82 static const char applet_longopts[] ALIGN1 =
83 //"name\0" has_arg val 83 //"name\0" has_arg val
84 "verbose\0" No_argument "v" 84 "verbose\0" No_argument "v"
85 ; 85 ;
86 applet_long_options = applet_longopts; 86 applet_long_options = applet_longopts;
87 87
88 The last member of struct option (val) typically is set to 88 The last member of struct option (val) typically is set to
@@ -226,7 +226,7 @@ Special characters:
226 if specified together. In this case you must set 226 if specified together. In this case you must set
227 opt_complementary = "b--cf:c--bf:f--bc". If two of the 227 opt_complementary = "b--cf:c--bf:f--bc". If two of the
228 mutually exclusive options are found, getopt32 will call 228 mutually exclusive options are found, getopt32 will call
229 bb_show_usage() and die. 229 bb_show_usage() and die.
230 230
231 "x--x" Variation of the above, it means that -x option should occur 231 "x--x" Variation of the above, it means that -x option should occur
232 at most once. 232 at most once.
@@ -531,7 +531,7 @@ getopt32(char **argv, const char *applet_opts, ...)
531 531
532 /* In case getopt32 was already called: 532 /* In case getopt32 was already called:
533 * reset the libc getopt() function, which keeps internal state. 533 * reset the libc getopt() function, which keeps internal state.
534 * run_nofork_applet_prime() does this, but we might end up here 534 * run_nofork_applet() does this, but we might end up here
535 * also via gunzip_main() -> gzip_main(). Play safe. 535 * also via gunzip_main() -> gzip_main(). Play safe.
536 */ 536 */
537#if defined(__GLIBC__) || ENABLE_PLATFORM_MINGW32 537#if defined(__GLIBC__) || ENABLE_PLATFORM_MINGW32
diff --git a/libbb/lineedit.c b/libbb/lineedit.c
index f9658711a..92ecc330a 100644
--- a/libbb/lineedit.c
+++ b/libbb/lineedit.c
@@ -1817,10 +1817,9 @@ static void win_changed(int nsig)
1817 errno = sv_errno; 1817 errno = sv_errno;
1818} 1818}
1819 1819
1820static int lineedit_read_key(char *read_key_buffer) 1820static int lineedit_read_key(char *read_key_buffer, int timeout)
1821{ 1821{
1822 int64_t ic; 1822 int64_t ic;
1823 int timeout = -1;
1824#if ENABLE_UNICODE_SUPPORT 1823#if ENABLE_UNICODE_SUPPORT
1825 char unicode_buf[MB_CUR_MAX + 1]; 1824 char unicode_buf[MB_CUR_MAX + 1];
1826 int unicode_idx = 0; 1825 int unicode_idx = 0;
@@ -1925,7 +1924,7 @@ static int isrtl_str(void)
1925 * 0 on ctrl-C (the line entered is still returned in 'command'), 1924 * 0 on ctrl-C (the line entered is still returned in 'command'),
1926 * >0 length of input string, including terminating '\n' 1925 * >0 length of input string, including terminating '\n'
1927 */ 1926 */
1928int FAST_FUNC read_line_input(const char *prompt, char *command, int maxsize, line_input_t *st) 1927int FAST_FUNC read_line_input(line_input_t *st, const char *prompt, char *command, int maxsize, int timeout)
1929{ 1928{
1930 int len; 1929 int len;
1931#if ENABLE_FEATURE_TAB_COMPLETION 1930#if ENABLE_FEATURE_TAB_COMPLETION
@@ -2006,7 +2005,6 @@ int FAST_FUNC read_line_input(const char *prompt, char *command, int maxsize, li
2006 new_settings.c_cc[VINTR] = _POSIX_VDISABLE; 2005 new_settings.c_cc[VINTR] = _POSIX_VDISABLE;
2007 tcsetattr_stdin_TCSANOW(&new_settings); 2006 tcsetattr_stdin_TCSANOW(&new_settings);
2008 2007
2009 /* Now initialize things */
2010 previous_SIGWINCH_handler = signal(SIGWINCH, win_changed); 2008 previous_SIGWINCH_handler = signal(SIGWINCH, win_changed);
2011 win_changed(0); /* do initial resizing */ 2009 win_changed(0); /* do initial resizing */
2012#if ENABLE_USERNAME_OR_HOMEDIR 2010#if ENABLE_USERNAME_OR_HOMEDIR
@@ -2048,7 +2046,7 @@ int FAST_FUNC read_line_input(const char *prompt, char *command, int maxsize, li
2048 int32_t ic, ic_raw; 2046 int32_t ic, ic_raw;
2049 2047
2050 fflush_all(); 2048 fflush_all();
2051 ic = ic_raw = lineedit_read_key(read_key_buffer); 2049 ic = ic_raw = lineedit_read_key(read_key_buffer, timeout);
2052 2050
2053#if ENABLE_FEATURE_EDITING_VI 2051#if ENABLE_FEATURE_EDITING_VI
2054 newdelflag = 1; 2052 newdelflag = 1;
@@ -2209,7 +2207,7 @@ int FAST_FUNC read_line_input(const char *prompt, char *command, int maxsize, li
2209 case 'd'|VI_CMDMODE_BIT: { 2207 case 'd'|VI_CMDMODE_BIT: {
2210 int nc, sc; 2208 int nc, sc;
2211 2209
2212 ic = lineedit_read_key(read_key_buffer); 2210 ic = lineedit_read_key(read_key_buffer, timeout);
2213 if (errno) /* error */ 2211 if (errno) /* error */
2214 goto return_error_indicator; 2212 goto return_error_indicator;
2215 if (ic == ic_raw) { /* "cc", "dd" */ 2213 if (ic == ic_raw) { /* "cc", "dd" */
@@ -2273,7 +2271,7 @@ int FAST_FUNC read_line_input(const char *prompt, char *command, int maxsize, li
2273 break; 2271 break;
2274 case 'r'|VI_CMDMODE_BIT: 2272 case 'r'|VI_CMDMODE_BIT:
2275//FIXME: unicode case? 2273//FIXME: unicode case?
2276 ic = lineedit_read_key(read_key_buffer); 2274 ic = lineedit_read_key(read_key_buffer, timeout);
2277 if (errno) /* error */ 2275 if (errno) /* error */
2278 goto return_error_indicator; 2276 goto return_error_indicator;
2279 if (ic < ' ' || ic > 255) { 2277 if (ic < ' ' || ic > 255) {
diff --git a/libbb/messages.c b/libbb/messages.c
index 9e7bd0fef..ef42b30d7 100644
--- a/libbb/messages.c
+++ b/libbb/messages.c
@@ -22,7 +22,7 @@
22const char bb_banner[] ALIGN1 = BANNER; 22const char bb_banner[] ALIGN1 = BANNER;
23 23
24 24
25const char bb_msg_memory_exhausted[] ALIGN1 = "memory exhausted"; 25const char bb_msg_memory_exhausted[] ALIGN1 = "out of memory";
26const char bb_msg_invalid_date[] ALIGN1 = "invalid date '%s'"; 26const char bb_msg_invalid_date[] ALIGN1 = "invalid date '%s'";
27const char bb_msg_unknown[] ALIGN1 = "(unknown)"; 27const char bb_msg_unknown[] ALIGN1 = "(unknown)";
28const char bb_msg_can_not_create_raw_socket[] ALIGN1 = "can't create raw socket"; 28const char bb_msg_can_not_create_raw_socket[] ALIGN1 = "can't create raw socket";
diff --git a/libbb/platform.c b/libbb/platform.c
index 30d06e3a7..1441a84ac 100644
--- a/libbb/platform.c
+++ b/libbb/platform.c
@@ -147,3 +147,14 @@ char* FAST_FUNC strsep(char **stringp, const char *delim)
147 return start; 147 return start;
148} 148}
149#endif 149#endif
150
151#ifndef HAVE_STPCPY
152char* FAST_FUNC stpcpy(char *p, const char *to_add)
153{
154 while ((*p = *to_add) != '\0') {
155 p++;
156 to_add++;
157 }
158 return p;
159}
160#endif
diff --git a/libbb/progress.c b/libbb/progress.c
index 40608b047..df43dad5c 100644
--- a/libbb/progress.c
+++ b/libbb/progress.c
@@ -52,101 +52,154 @@ static unsigned int get_tty2_width(void)
52 return width; 52 return width;
53} 53}
54 54
55void FAST_FUNC bb_progress_init(bb_progress_t *p) 55void FAST_FUNC bb_progress_init(bb_progress_t *p, const char *curfile)
56{ 56{
57#if ENABLE_UNICODE_SUPPORT
58 init_unicode();
59 p->curfile = unicode_conv_to_printable_fixedwidth(/*NULL,*/ curfile, 20);
60#else
61 p->curfile = curfile;
62#endif
57 p->start_sec = monotonic_sec(); 63 p->start_sec = monotonic_sec();
58 p->lastupdate_sec = p->start_sec; 64 p->last_update_sec = p->start_sec;
59 p->lastsize = 0; 65 p->last_change_sec = p->start_sec;
60 p->inited = 1; 66 p->last_size = 0;
61} 67}
62 68
69/* File already had beg_size bytes.
70 * Then we started downloading.
71 * We downloaded "transferred" bytes so far.
72 * Download is expected to stop when total size (beg_size + transferred)
73 * will be "totalsize" bytes.
74 * If totalsize == 0, then it is unknown.
75 */
63void FAST_FUNC bb_progress_update(bb_progress_t *p, 76void FAST_FUNC bb_progress_update(bb_progress_t *p,
64 const char *curfile, 77 uoff_t beg_size,
65 off_t beg_range, 78 uoff_t transferred,
66 off_t transferred, 79 uoff_t totalsize)
67 off_t totalsize)
68{ 80{
69 uoff_t beg_and_transferred; 81 uoff_t beg_and_transferred;
70 unsigned since_last_update, elapsed; 82 unsigned since_last_update, elapsed;
71 unsigned ratio; 83 int barlength;
72 int barlength, i; 84 int kiloscale;
73 85
74 /* totalsize == 0 if it is unknown */ 86 //transferred = 1234; /* use for stall detection testing */
87 //totalsize = 0; /* use for unknown size download testing */
75 88
76 elapsed = monotonic_sec(); 89 elapsed = monotonic_sec();
77 since_last_update = elapsed - p->lastupdate_sec; 90 since_last_update = elapsed - p->last_update_sec;
78 /* Do not update on every call 91 p->last_update_sec = elapsed;
79 * (we can be called on every network read!) */
80 if (since_last_update == 0 && !totalsize)
81 return;
82 92
83 beg_and_transferred = beg_range + transferred; 93 if (totalsize != 0 && transferred >= totalsize - beg_size) {
84 ratio = 100; 94 /* Last call. Do not skip this update */
85 if (beg_and_transferred < totalsize) { 95 transferred = totalsize - beg_size; /* sanitize just in case */
86 /* Do not update on every call 96 }
87 * (we can be called on every network read!) */ 97 else if (since_last_update == 0) {
88 if (since_last_update == 0) 98 /*
89 return; 99 * Do not update on every call
90 /* long long helps to have it working even if !LFS */ 100 * (we can be called on every network read!)
91 ratio = 100ULL * beg_and_transferred / (uoff_t)totalsize; 101 */
102 return;
92 } 103 }
93 104
94#if ENABLE_UNICODE_SUPPORT 105 kiloscale = 0;
95 init_unicode(); 106 /*
96 { 107 * Scale sizes down if they are close to overflowing.
97 char *buf = unicode_conv_to_printable_fixedwidth(/*NULL,*/ curfile, 20); 108 * This allows calculations like (100 * transferred / totalsize)
98 fprintf(stderr, "\r%s%4u%% ", buf, ratio); 109 * without risking overflow: we guarantee 10 highest bits to be 0.
99 free(buf); 110 * Introduced error is less than 1 / 2^12 ~= 0.025%
111 */
112 if (ULONG_MAX > 0xffffffff || sizeof(off_t) == 4 || sizeof(off_t) != 8) {
113 /*
114 * 64-bit CPU || small off_t: in either case,
115 * >> is cheap, single-word operation.
116 * ... || strange off_t: also use this code
117 * (it is safe, just suboptimal wrt code size),
118 * because 32/64 optimized one works only for 64-bit off_t.
119 */
120 if (totalsize >= (1 << 22)) {
121 totalsize >>= 10;
122 beg_size >>= 10;
123 transferred >>= 10;
124 kiloscale = 1;
125 }
126 } else {
127 /* 32-bit CPU and 64-bit off_t.
128 * Use a 40-bit shift, it is easier to do on 32-bit CPU.
129 */
130 if (totalsize >= (uoff_t)(1ULL << 54)) {
131 totalsize = (uint32_t)(totalsize >> 32) >> 8;
132 beg_size = (uint32_t)(beg_size >> 32) >> 8;
133 transferred = (uint32_t)(transferred >> 32) >> 8;
134 kiloscale = 4;
135 }
100 } 136 }
101#else
102 fprintf(stderr, "\r%-20.20s%4u%% ", curfile, ratio);
103#endif
104 137
105 barlength = get_tty2_width() - 49; 138 if (ENABLE_UNICODE_SUPPORT)
106 if (barlength > 0) { 139 fprintf(stderr, "\r%s", p->curfile);
107 /* god bless gcc for variable arrays :) */ 140 else
108 char buf[barlength + 1]; 141 fprintf(stderr, "\r%-20.20s", p->curfile);
109 unsigned stars = (unsigned)barlength * ratio / (unsigned)100; 142
110 memset(buf, ' ', barlength); 143 beg_and_transferred = beg_size + transferred;
111 buf[barlength] = '\0'; 144
112 memset(buf, '*', stars); 145 if (totalsize != 0) {
113 fprintf(stderr, "|%s|", buf); 146 unsigned ratio = 100 * beg_and_transferred / totalsize;
147 fprintf(stderr, "%4u%%", ratio);
148
149 barlength = get_tty2_width() - 49;
150 if (barlength > 0) {
151 /* god bless gcc for variable arrays :) */
152 char buf[barlength + 1];
153 unsigned stars = (unsigned)barlength * beg_and_transferred / totalsize;
154 memset(buf, ' ', barlength);
155 buf[barlength] = '\0';
156 memset(buf, '*', stars);
157 fprintf(stderr, " |%s|", buf);
158 }
114 } 159 }
115 160
116 i = 0;
117 while (beg_and_transferred >= 100000) { 161 while (beg_and_transferred >= 100000) {
118 i++;
119 beg_and_transferred >>= 10; 162 beg_and_transferred >>= 10;
163 kiloscale++;
120 } 164 }
121 /* see http://en.wikipedia.org/wiki/Tera */ 165 /* see http://en.wikipedia.org/wiki/Tera */
122 fprintf(stderr, "%6u%c ", (unsigned)beg_and_transferred, " kMGTPEZY"[i]); 166 fprintf(stderr, "%6u%c", (unsigned)beg_and_transferred, " kMGTPEZY"[kiloscale]);
123#define beg_and_transferred dont_use_beg_and_transferred_below 167#define beg_and_transferred dont_use_beg_and_transferred_below()
124 168
125 if (transferred > p->lastsize) { 169 since_last_update = elapsed - p->last_change_sec;
126 p->lastupdate_sec = elapsed; 170 if ((unsigned)transferred != p->last_size) {
127 p->lastsize = transferred; 171 p->last_change_sec = elapsed;
172 p->last_size = (unsigned)transferred;
128 if (since_last_update >= STALLTIME) { 173 if (since_last_update >= STALLTIME) {
129 /* We "cut off" these seconds from elapsed time 174 /* We "cut out" these seconds from elapsed time
130 * by adjusting start time */ 175 * by adjusting start time */
131 p->start_sec += since_last_update; 176 p->start_sec += since_last_update;
132 } 177 }
133 since_last_update = 0; /* we are un-stalled now */ 178 since_last_update = 0; /* we are un-stalled now */
134 } 179 }
180
135 elapsed -= p->start_sec; /* now it's "elapsed since start" */ 181 elapsed -= p->start_sec; /* now it's "elapsed since start" */
136 182
137 if (since_last_update >= STALLTIME) { 183 if (since_last_update >= STALLTIME) {
138 fprintf(stderr, " - stalled -"); 184 fprintf(stderr, " - stalled -");
185 } else if (!totalsize || !transferred || (int)elapsed < 0) {
186 fprintf(stderr, " --:--:-- ETA");
139 } else { 187 } else {
140 off_t to_download = totalsize - beg_range; 188 unsigned eta, secs, hours;
141 if (!totalsize || transferred <= 0 || (int)elapsed <= 0 || transferred > to_download) { 189
142 fprintf(stderr, "--:--:-- ETA"); 190 totalsize -= beg_size; /* now it's "total to upload" */
143 } else { 191
144 /* to_download / (transferred/elapsed) - elapsed: */ 192 /* Estimated remaining time =
145 /* (long long helps to have working ETA even if !LFS) */ 193 * estimated_sec_to_dl_totalsize_bytes - elapsed_sec =
146 unsigned eta = (unsigned long long)to_download*elapsed/(uoff_t)transferred - elapsed; 194 * totalsize / average_bytes_sec_so_far - elapsed =
147 unsigned secs = eta % 3600; 195 * totalsize / (transferred/elapsed) - elapsed =
148 unsigned hours = eta / 3600; 196 * totalsize * elapsed / transferred - elapsed
149 fprintf(stderr, "%02u:%02u:%02u ETA", hours, secs / 60, secs % 60); 197 */
150 } 198 eta = totalsize * elapsed / transferred - elapsed;
199 if (eta >= 1000*60*60)
200 eta = 1000*60*60 - 1;
201 secs = eta % 3600;
202 hours = eta / 3600;
203 fprintf(stderr, "%3u:%02u:%02u ETA", hours, secs / 60, secs % 60);
151 } 204 }
152} 205}
diff --git a/libbb/systemd_support.c b/libbb/systemd_support.c
index 981296dbb..542a3efff 100644
--- a/libbb/systemd_support.c
+++ b/libbb/systemd_support.c
@@ -33,7 +33,7 @@
33//config: If you plan to use busybox daemons on a system where daemons 33//config: If you plan to use busybox daemons on a system where daemons
34//config: are controlled by systemd, enable this option. 34//config: are controlled by systemd, enable this option.
35//config: If you don't use systemd, it is still safe to enable it, 35//config: If you don't use systemd, it is still safe to enable it,
36//config: but yhe downside is increased code size. 36//config: but the downside is increased code size.
37 37
38//kbuild:lib-$(CONFIG_FEATURE_SYSTEMD) += systemd_support.o 38//kbuild:lib-$(CONFIG_FEATURE_SYSTEMD) += systemd_support.o
39 39
diff --git a/libbb/vfork_daemon_rexec.c b/libbb/vfork_daemon_rexec.c
index 1fbb1bba9..5d8056bd6 100644
--- a/libbb/vfork_daemon_rexec.c
+++ b/libbb/vfork_daemon_rexec.c
@@ -55,6 +55,7 @@ pid_t FAST_FUNC spawn(char **argv)
55 * Interested party can wait on pid and learn exit code. 55 * Interested party can wait on pid and learn exit code.
56 * If 111 - then it (most probably) failed to exec */ 56 * If 111 - then it (most probably) failed to exec */
57 if (failed) { 57 if (failed) {
58 safe_waitpid(pid, NULL, 0); /* prevent zombie */
58 errno = failed; 59 errno = failed;
59 return -1; 60 return -1;
60 } 61 }
@@ -71,17 +72,22 @@ pid_t FAST_FUNC xspawn(char **argv)
71} 72}
72 73
73#if ENABLE_FEATURE_PREFER_APPLETS 74#if ENABLE_FEATURE_PREFER_APPLETS
74void FAST_FUNC save_nofork_data(struct nofork_save_area *save) 75struct nofork_save_area {
76 jmp_buf die_jmp;
77 const char *applet_name;
78 uint32_t option_mask32;
79 int die_sleep;
80 uint8_t xfunc_error_retval;
81};
82static void save_nofork_data(struct nofork_save_area *save)
75{ 83{
76 memcpy(&save->die_jmp, &die_jmp, sizeof(die_jmp)); 84 memcpy(&save->die_jmp, &die_jmp, sizeof(die_jmp));
77 save->applet_name = applet_name; 85 save->applet_name = applet_name;
78 save->xfunc_error_retval = xfunc_error_retval; 86 save->xfunc_error_retval = xfunc_error_retval;
79 save->option_mask32 = option_mask32; 87 save->option_mask32 = option_mask32;
80 save->die_sleep = die_sleep; 88 save->die_sleep = die_sleep;
81 save->saved = 1;
82} 89}
83 90static void restore_nofork_data(struct nofork_save_area *save)
84void FAST_FUNC restore_nofork_data(struct nofork_save_area *save)
85{ 91{
86 memcpy(&die_jmp, &save->die_jmp, sizeof(die_jmp)); 92 memcpy(&die_jmp, &save->die_jmp, sizeof(die_jmp));
87 applet_name = save->applet_name; 93 applet_name = save->applet_name;
@@ -90,19 +96,17 @@ void FAST_FUNC restore_nofork_data(struct nofork_save_area *save)
90 die_sleep = save->die_sleep; 96 die_sleep = save->die_sleep;
91} 97}
92 98
93int FAST_FUNC run_nofork_applet_prime(struct nofork_save_area *old, int applet_no, char **argv) 99int FAST_FUNC run_nofork_applet(int applet_no, char **argv)
94{ 100{
95 int rc, argc; 101 int rc, argc;
102 struct nofork_save_area old;
103
104 save_nofork_data(&old);
96 105
97 applet_name = APPLET_NAME(applet_no); 106 applet_name = APPLET_NAME(applet_no);
98 107
99 xfunc_error_retval = EXIT_FAILURE; 108 xfunc_error_retval = EXIT_FAILURE;
100 109
101 /* Special flag for xfunc_die(). If xfunc will "die"
102 * in NOFORK applet, xfunc_die() sees negative
103 * die_sleep and longjmp here instead. */
104 die_sleep = -1;
105
106 /* In case getopt() or getopt32() was already called: 110 /* In case getopt() or getopt32() was already called:
107 * reset the libc getopt() function, which keeps internal state. 111 * reset the libc getopt() function, which keeps internal state.
108 * 112 *
@@ -132,6 +136,11 @@ int FAST_FUNC run_nofork_applet_prime(struct nofork_save_area *old, int applet_n
132 while (argv[argc]) 136 while (argv[argc])
133 argc++; 137 argc++;
134 138
139 /* Special flag for xfunc_die(). If xfunc will "die"
140 * in NOFORK applet, xfunc_die() sees negative
141 * die_sleep and longjmp here instead. */
142 die_sleep = -1;
143
135 rc = setjmp(die_jmp); 144 rc = setjmp(die_jmp);
136 if (!rc) { 145 if (!rc) {
137 /* Some callers (xargs) 146 /* Some callers (xargs)
@@ -140,15 +149,6 @@ int FAST_FUNC run_nofork_applet_prime(struct nofork_save_area *old, int applet_n
140 memcpy(tmp_argv, argv, (argc+1) * sizeof(tmp_argv[0])); 149 memcpy(tmp_argv, argv, (argc+1) * sizeof(tmp_argv[0]));
141 /* Finally we can call NOFORK applet's main() */ 150 /* Finally we can call NOFORK applet's main() */
142 rc = applet_main[applet_no](argc, tmp_argv); 151 rc = applet_main[applet_no](argc, tmp_argv);
143
144 /* The whole reason behind nofork_save_area is that <applet>_main
145 * may exit non-locally! For example, in hush Ctrl-Z tries
146 * (modulo bugs) to dynamically create a child (backgrounded task)
147 * if it detects that Ctrl-Z was pressed when a NOFORK was running.
148 * Testcase: interactive "rm -i".
149 * Don't fool yourself into thinking "and <applet>_main() returns
150 * quickly here" and removing "useless" nofork_save_area code. */
151
152 } else { /* xfunc died in NOFORK applet */ 152 } else { /* xfunc died in NOFORK applet */
153 /* in case they meant to return 0... */ 153 /* in case they meant to return 0... */
154 if (rc == -2222) 154 if (rc == -2222)
@@ -156,7 +156,7 @@ int FAST_FUNC run_nofork_applet_prime(struct nofork_save_area *old, int applet_n
156 } 156 }
157 157
158 /* Restoring some globals */ 158 /* Restoring some globals */
159 restore_nofork_data(old); 159 restore_nofork_data(&old);
160 160
161 /* Other globals can be simply reset to defaults */ 161 /* Other globals can be simply reset to defaults */
162#ifdef __GLIBC__ 162#ifdef __GLIBC__
@@ -167,15 +167,6 @@ int FAST_FUNC run_nofork_applet_prime(struct nofork_save_area *old, int applet_n
167 167
168 return rc & 0xff; /* don't confuse people with "exitcodes" >255 */ 168 return rc & 0xff; /* don't confuse people with "exitcodes" >255 */
169} 169}
170
171int FAST_FUNC run_nofork_applet(int applet_no, char **argv)
172{
173 struct nofork_save_area old;
174
175 /* Saving globals */
176 save_nofork_data(&old);
177 return run_nofork_applet_prime(&old, applet_no, argv);
178}
179#endif /* FEATURE_PREFER_APPLETS */ 170#endif /* FEATURE_PREFER_APPLETS */
180 171
181int FAST_FUNC spawn_and_wait(char **argv) 172int FAST_FUNC spawn_and_wait(char **argv)
@@ -185,17 +176,17 @@ int FAST_FUNC spawn_and_wait(char **argv)
185 int a = find_applet_by_name(argv[0]); 176 int a = find_applet_by_name(argv[0]);
186 177
187 if (a >= 0 && (APPLET_IS_NOFORK(a) 178 if (a >= 0 && (APPLET_IS_NOFORK(a)
188#if BB_MMU 179# if BB_MMU
189 || APPLET_IS_NOEXEC(a) /* NOEXEC trick needs fork() */ 180 || APPLET_IS_NOEXEC(a) /* NOEXEC trick needs fork() */
190#endif 181# endif
191 )) { 182 )) {
192#if BB_MMU 183# if BB_MMU
193 if (APPLET_IS_NOFORK(a)) 184 if (APPLET_IS_NOFORK(a))
194#endif 185# endif
195 { 186 {
196 return run_nofork_applet(a, argv); 187 return run_nofork_applet(a, argv);
197 } 188 }
198#if BB_MMU && !ENABLE_PLATFORM_MINGW32 189# if BB_MMU && !ENABLE_PLATFORM_MINGW32
199 /* MMU only */ 190 /* MMU only */
200 /* a->noexec is true */ 191 /* a->noexec is true */
201 rc = fork(); 192 rc = fork();
@@ -204,7 +195,7 @@ int FAST_FUNC spawn_and_wait(char **argv)
204 /* child */ 195 /* child */
205 xfunc_error_retval = EXIT_FAILURE; 196 xfunc_error_retval = EXIT_FAILURE;
206 run_applet_no_and_exit(a, argv); 197 run_applet_no_and_exit(a, argv);
207#endif 198# endif
208 } 199 }
209#endif /* FEATURE_PREFER_APPLETS */ 200#endif /* FEATURE_PREFER_APPLETS */
210 rc = spawn(argv); 201 rc = spawn(argv);
diff --git a/loginutils/Config.src b/loginutils/Config.src
index 4c771bbc4..0d7f50cf1 100644
--- a/loginutils/Config.src
+++ b/loginutils/Config.src
@@ -181,7 +181,19 @@ config GETTY
181 default y 181 default y
182 select FEATURE_SYSLOG 182 select FEATURE_SYSLOG
183 help 183 help
184 getty lets you log in on a tty, it is normally invoked by init. 184 getty lets you log in on a tty. It is normally invoked by init.
185
186 Note that you can save a few bytes by disabling it and
187 using login applet directly.
188 If you need to reset tty attributes before calling login,
189 this script approximates getty:
190
191 exec </dev/$1 >/dev/$1 2>&1 || exit 1
192 reset
193 stty sane; stty ispeed 38400; stty ospeed 38400
194 printf "%s login: " "`hostname`"
195 read -r login
196 exec /bin/login "$login"
185 197
186config LOGIN 198config LOGIN
187 bool "login" 199 bool "login"
diff --git a/loginutils/getty.c b/loginutils/getty.c
index b71d68a1f..3f20c8e81 100644
--- a/loginutils/getty.c
+++ b/loginutils/getty.c
@@ -106,15 +106,15 @@ struct globals {
106//usage:#define getty_full_usage "\n\n" 106//usage:#define getty_full_usage "\n\n"
107//usage: "Open a tty, prompt for a login name, then invoke /bin/login\n" 107//usage: "Open a tty, prompt for a login name, then invoke /bin/login\n"
108//usage: "\nOptions:" 108//usage: "\nOptions:"
109//usage: "\n -h Enable hardware (RTS/CTS) flow control" 109//usage: "\n -h Enable hardware RTS/CTS flow control"
110//usage: "\n -i Don't display /etc/issue" 110//usage: "\n -L Set CLOCAL (ignore Carrier Detect state)"
111//usage: "\n -L Local line, set CLOCAL on it"
112//usage: "\n -m Get baud rate from modem's CONNECT status message" 111//usage: "\n -m Get baud rate from modem's CONNECT status message"
112//usage: "\n -n Don't prompt for login name"
113//usage: "\n -w Wait for CR or LF before sending /etc/issue" 113//usage: "\n -w Wait for CR or LF before sending /etc/issue"
114//usage: "\n -n Don't prompt for a login name" 114//usage: "\n -i Don't display /etc/issue"
115//usage: "\n -f ISSUE_FILE Display ISSUE_FILE instead of /etc/issue" 115//usage: "\n -f ISSUE_FILE Display ISSUE_FILE instead of /etc/issue"
116//usage: "\n -l LOGIN Invoke LOGIN instead of /bin/login" 116//usage: "\n -l LOGIN Invoke LOGIN instead of /bin/login"
117//usage: "\n -t SEC Terminate after SEC if no username is read" 117//usage: "\n -t SEC Terminate after SEC if no login name is read"
118//usage: "\n -I INITSTR Send INITSTR before anything else" 118//usage: "\n -I INITSTR Send INITSTR before anything else"
119//usage: "\n -H HOST Log HOST into the utmp file as the hostname" 119//usage: "\n -H HOST Log HOST into the utmp file as the hostname"
120//usage: "\n" 120//usage: "\n"
@@ -224,6 +224,12 @@ static void open_tty(void)
224 } 224 }
225} 225}
226 226
227static void set_termios(void)
228{
229 if (tcsetattr_stdin_TCSANOW(&G.termios) < 0)
230 bb_perror_msg_and_die("tcsetattr");
231}
232
227/* We manipulate termios this way: 233/* We manipulate termios this way:
228 * - first, we read existing termios settings 234 * - first, we read existing termios settings
229 * - termios_init modifies some parts and sets it 235 * - termios_init modifies some parts and sets it
@@ -233,53 +239,128 @@ static void open_tty(void)
233 */ 239 */
234static void termios_init(int speed) 240static void termios_init(int speed)
235{ 241{
236 /* Flush input and output queues, important for modems! 242 /* Try to drain output buffer, with 5 sec timeout.
237 * Users report losing previously queued output chars, and I hesitate 243 * Added on request from users of ~600 baud serial interface
238 * to use tcdrain here instead of tcflush - I imagine it can block. 244 * with biggish buffer on a 90MHz CPU.
239 * Using small sleep instead. 245 * They were losing hundreds of bytes of buffered output
246 * on tcflush.
240 */ 247 */
241 usleep(100*1000); /* 0.1 sec */ 248 signal_no_SA_RESTART_empty_mask(SIGALRM, record_signo);
249 alarm(5);
250 tcdrain(STDIN_FILENO);
251 alarm(0);
252 signal(SIGALRM, SIG_DFL); /* do not break -t TIMEOUT! */
253
254 /* Flush input and output queues, important for modems! */
242 tcflush(STDIN_FILENO, TCIOFLUSH); 255 tcflush(STDIN_FILENO, TCIOFLUSH);
243 256
244 /* Set speed if it wasn't specified as "0" on command line */ 257 /* Set speed if it wasn't specified as "0" on command line */
245 if (speed != B0) 258 if (speed != B0)
246 cfsetspeed(&G.termios, speed); 259 cfsetspeed(&G.termios, speed);
247 260
248 /* 261 /* Initial termios settings: 8-bit characters, raw mode, blocking i/o.
249 * Initial termios settings: 8-bit characters, raw-mode, blocking i/o.
250 * Special characters are set after we have read the login name; all 262 * Special characters are set after we have read the login name; all
251 * reads will be done in raw mode anyway. Errors will be dealt with 263 * reads will be done in raw mode anyway.
252 * later on.
253 */ 264 */
254 G.termios.c_cflag = CS8 | HUPCL | CREAD; 265 /* Clear all bits except: */
255 if (option_mask32 & F_LOCAL) 266 G.termios.c_cflag &= (0
267 /* 2 stop bits (1 otherwise)
268 * Enable parity bit (both on input and output)
269 * Odd parity (else even)
270 */
271 | CSTOPB | PARENB | PARODD
272#ifdef CMSPAR
273 | CMSPAR /* mark or space parity */
274#endif
275 | CBAUD /* (output) baud rate */
276#ifdef CBAUDEX
277 | CBAUDEX /* (output) baud rate */
278#endif
279#ifdef CIBAUD
280 | CIBAUD /* input baud rate */
281#endif
282 );
283 /* Set: 8 bits; hang up (drop DTR) on last close; enable receive */
284 G.termios.c_cflag |= CS8 | HUPCL | CREAD;
285 if (option_mask32 & F_LOCAL) {
286 /* ignore Carrier Detect pin:
287 * opens don't block when CD is low,
288 * losing CD doesn't hang up processes whose ctty is this tty
289 */
256 G.termios.c_cflag |= CLOCAL; 290 G.termios.c_cflag |= CLOCAL;
291 }
292#ifdef CRTSCTS
293 if (option_mask32 & F_RTSCTS)
294 G.termios.c_cflag |= CRTSCTS; /* flow control using RTS/CTS pins */
295#endif
257 G.termios.c_iflag = 0; 296 G.termios.c_iflag = 0;
258 G.termios.c_lflag = 0; 297 G.termios.c_lflag = 0;
298 /* non-raw output; add CR to each NL */
259 G.termios.c_oflag = OPOST | ONLCR; 299 G.termios.c_oflag = OPOST | ONLCR;
260 G.termios.c_cc[VMIN] = 1; 300
261 G.termios.c_cc[VTIME] = 0; 301 G.termios.c_cc[VMIN] = 1; /* block reads if < 1 char is available */
302 G.termios.c_cc[VTIME] = 0; /* no timeout (reads block forever) */
262#ifdef __linux__ 303#ifdef __linux__
263 G.termios.c_line = 0; 304 G.termios.c_line = 0;
264#endif 305#endif
265#ifdef CRTSCTS
266 if (option_mask32 & F_RTSCTS)
267 G.termios.c_cflag |= CRTSCTS;
268#endif
269 306
270 tcsetattr_stdin_TCSANOW(&G.termios); 307 set_termios();
271 308
272 debug("term_io 2\n"); 309 debug("term_io 2\n");
273} 310}
274 311
275static void termios_final(void) 312static void termios_final(void)
276{ 313{
277 /* General terminal-independent stuff */ 314 /* software flow control on output (stop sending if XOFF is recvd);
278 G.termios.c_iflag |= IXON | IXOFF; /* 2-way flow control */ 315 * and on input (send XOFF when buffer is full)
316 */
317 G.termios.c_iflag |= IXON | IXOFF;
318 if (G.eol == '\r') {
319 G.termios.c_iflag |= ICRNL; /* map CR on input to NL */
320 }
321 /* Other bits in c_iflag:
322 * IXANY Any recvd char enables output (any char is also a XON)
323 * INPCK Enable parity check
324 * IGNPAR Ignore parity errors (drop bad bytes)
325 * PARMRK Mark parity errors with 0xff, 0x00 prefix
326 * (else bad byte is received as 0x00)
327 * ISTRIP Strip parity bit
328 * IGNBRK Ignore break condition
329 * BRKINT Send SIGINT on break - maybe set this?
330 * INLCR Map NL to CR
331 * IGNCR Ignore CR
332 * ICRNL Map CR to NL
333 * IUCLC Map uppercase to lowercase
334 * IMAXBEL Echo BEL on input line too long
335 * IUTF8 Appears to affect tty's idea of char widths,
336 * observed to improve backspacing through Unicode chars
337 */
338
339 /* line buffered input (NL or EOL or EOF chars end a line);
340 * recognize INT/QUIT/SUSP chars;
341 * echo input chars;
342 * echo BS-SP-BS on erase character;
343 * echo kill char specially, not as ^c (ECHOKE controls how exactly);
344 * erase all input via BS-SP-BS on kill char (else go to next line)
345 */
279 G.termios.c_lflag |= ICANON | ISIG | ECHO | ECHOE | ECHOK | ECHOKE; 346 G.termios.c_lflag |= ICANON | ISIG | ECHO | ECHOE | ECHOK | ECHOKE;
280 /* no longer in lflag: | ECHOCTL | ECHOPRT */ 347 /* Other bits in c_lflag:
281 G.termios.c_oflag |= OPOST; 348 * XCASE Map uppercase to \lowercase [tried, doesn't work]
282 /* G.termios.c_cflag = 0; */ 349 * ECHONL Echo NL even if ECHO is not set
350 * ECHOCTL Echo ctrl chars as ^c (else don't echo) - maybe set this?
351 * ECHOPRT On erase, echo erased chars
352 * [qwe<BS><BS><BS> input looks like "qwe\ewq/" on screen]
353 * NOFLSH Don't flush input buffer after interrupt or quit chars
354 * IEXTEN Enable extended functions (??)
355 * [glibc says it enables c_cc[LNEXT] "enter literal char"
356 * and c_cc[VDISCARD] "toggle discard buffered output" chars]
357 * FLUSHO Output being flushed (c_cc[VDISCARD] is in effect)
358 * PENDIN Retype pending input at next read or input char
359 * (c_cc[VREPRINT] is being processed)
360 * TOSTOP Send SIGTTOU for background output
361 * (why "stty sane" unsets this bit?)
362 */
363
283 G.termios.c_cc[VINTR] = DEF_INTR; 364 G.termios.c_cc[VINTR] = DEF_INTR;
284 G.termios.c_cc[VQUIT] = DEF_QUIT; 365 G.termios.c_cc[VQUIT] = DEF_QUIT;
285 G.termios.c_cc[VEOF] = DEF_EOF; 366 G.termios.c_cc[VEOF] = DEF_EOF;
@@ -290,24 +371,17 @@ static void termios_final(void)
290#ifdef VSWTCH 371#ifdef VSWTCH
291 G.termios.c_cc[VSWTCH] = DEF_SWITCH; 372 G.termios.c_cc[VSWTCH] = DEF_SWITCH;
292#endif 373#endif
293
294 /* Account for special characters seen in input */
295 if (G.eol == '\r') {
296 G.termios.c_iflag |= ICRNL; /* map CR in input to NL */
297 /* already done by termios_init */
298 /* G.termios.c_oflag |= ONLCR; map NL in output to CR-NL */
299 }
300 G.termios.c_cc[VKILL] = DEF_KILL; 374 G.termios.c_cc[VKILL] = DEF_KILL;
375 /* Other control chars:
376 * VEOL2
377 * VERASE, VWERASE - (word) erase. we may set VERASE in get_logname
378 * VREPRINT - reprint current input buffer
379 * VLNEXT, VDISCARD, VSTATUS
380 * VSUSP, VDSUSP - send (delayed) SIGTSTP
381 * VSTART, VSTOP - chars used for IXON/IXOFF
382 */
301 383
302#ifdef CRTSCTS 384 set_termios();
303 /* Optionally enable hardware flow control */
304 if (option_mask32 & F_RTSCTS)
305 G.termios.c_cflag |= CRTSCTS;
306#endif
307
308 /* Finally, make the new settings effective */
309 if (tcsetattr_stdin_TCSANOW(&G.termios) < 0)
310 bb_perror_msg_and_die("tcsetattr");
311} 385}
312 386
313/* extract baud rate from modem status message */ 387/* extract baud rate from modem status message */
@@ -330,12 +404,8 @@ static void auto_baud(void)
330 * modem status messages is enabled. 404 * modem status messages is enabled.
331 */ 405 */
332 406
333 /* 407 G.termios.c_cc[VMIN] = 0; /* don't block reads (min read is 0 chars) */
334 * Don't block if input queue is empty. 408 set_termios();
335 * Errors will be dealt with later on.
336 */
337 G.termios.c_cc[VMIN] = 0; /* don't block if queue empty */
338 tcsetattr_stdin_TCSANOW(&G.termios);
339 409
340 /* 410 /*
341 * Wait for a while, then read everything the modem has said so far and 411 * Wait for a while, then read everything the modem has said so far and
@@ -357,9 +427,9 @@ static void auto_baud(void)
357 } 427 }
358 } 428 }
359 429
360 /* Restore terminal settings. Errors will be dealt with later on */ 430 /* Restore terminal settings */
361 G.termios.c_cc[VMIN] = 1; /* restore to value set by termios_init */ 431 G.termios.c_cc[VMIN] = 1; /* restore to value set by termios_init */
362 tcsetattr_stdin_TCSANOW(&G.termios); 432 set_termios();
363} 433}
364 434
365/* get user name, establish parity, speed, erase, kill, eol; 435/* get user name, establish parity, speed, erase, kill, eol;
@@ -538,6 +608,7 @@ int getty_main(int argc UNUSED_PARAM, char **argv)
538 608
539 /* Set the optional timer */ 609 /* Set the optional timer */
540 alarm(G.timeout); /* if 0, alarm is not set */ 610 alarm(G.timeout); /* if 0, alarm is not set */
611//BUG: death by signal won't restore termios
541 612
542 /* Optionally wait for CR or LF before writing /etc/issue */ 613 /* Optionally wait for CR or LF before writing /etc/issue */
543 if (option_mask32 & F_WAITCRLF) { 614 if (option_mask32 & F_WAITCRLF) {
@@ -565,7 +636,7 @@ int getty_main(int argc UNUSED_PARAM, char **argv)
565 /* We are here only if G.numspeed > 1 */ 636 /* We are here only if G.numspeed > 1 */
566 baud_index = (baud_index + 1) % G.numspeed; 637 baud_index = (baud_index + 1) % G.numspeed;
567 cfsetspeed(&G.termios, G.speeds[baud_index]); 638 cfsetspeed(&G.termios, G.speeds[baud_index]);
568 tcsetattr_stdin_TCSANOW(&G.termios); 639 set_termios();
569 } 640 }
570 } 641 }
571 642
diff --git a/miscutils/Config.src b/miscutils/Config.src
index da52e14c6..b5866bbd7 100644
--- a/miscutils/Config.src
+++ b/miscutils/Config.src
@@ -583,6 +583,7 @@ config RFKILL
583config RUNLEVEL 583config RUNLEVEL
584 bool "runlevel" 584 bool "runlevel"
585 default y 585 default y
586 depends on FEATURE_UTMP
586 help 587 help
587 find the current and previous system runlevel. 588 find the current and previous system runlevel.
588 589
@@ -658,6 +659,7 @@ config VOLNAME
658config WALL 659config WALL
659 bool "wall" 660 bool "wall"
660 default y 661 default y
662 depends on FEATURE_UTMP
661 help 663 help
662 Write a message to all users that are logged in. 664 Write a message to all users that are logged in.
663 665
diff --git a/miscutils/fbsplash.c b/miscutils/fbsplash.c
index 6b84563a3..150992343 100644
--- a/miscutils/fbsplash.c
+++ b/miscutils/fbsplash.c
@@ -83,11 +83,14 @@ static void fb_open(const char *strfb_device)
83 83
84 // map the device in memory 84 // map the device in memory
85 G.addr = mmap(NULL, 85 G.addr = mmap(NULL,
86 G.scr_var.xres * G.scr_var.yres 86 G.scr_var.xres * G.scr_var.yres_virtual
87 * BYTES_PER_PIXEL /*(G.scr_var.bits_per_pixel / 8)*/, 87 * BYTES_PER_PIXEL /*(G.scr_var.bits_per_pixel / 8)*/,
88 PROT_WRITE, MAP_SHARED, fbfd, 0); 88 PROT_WRITE, MAP_SHARED, fbfd, 0);
89 if (G.addr == MAP_FAILED) 89 if (G.addr == MAP_FAILED)
90 bb_perror_msg_and_die("mmap"); 90 bb_perror_msg_and_die("mmap");
91
92 // point to the start of the visible screen
93 G.addr += G.scr_var.yoffset * G.scr_fix.line_length + G.scr_var.xoffset * BYTES_PER_PIXEL;
91 close(fbfd); 94 close(fbfd);
92} 95}
93 96
diff --git a/miscutils/timeout.c b/miscutils/timeout.c
index 48b8d8fc0..841669416 100644
--- a/miscutils/timeout.c
+++ b/miscutils/timeout.c
@@ -83,7 +83,7 @@ int timeout_main(int argc UNUSED_PARAM, char **argv)
83 bb_daemonize_or_rexec(0, argv); 83 bb_daemonize_or_rexec(0, argv);
84 /* Here we are grandchild. Sleep, then kill grandparent */ 84 /* Here we are grandchild. Sleep, then kill grandparent */
85 grandchild: 85 grandchild:
86 /* Just sleep(NUGE_NUM); kill(parent) may kill wrong process! */ 86 /* Just sleep(HUGE_NUM); kill(parent) may kill wrong process! */
87 while (1) { 87 while (1) {
88 sleep(1); 88 sleep(1);
89 if (--timeout <= 0) 89 if (--timeout <= 0)
diff --git a/miscutils/ubi_attach_detach.c b/miscutils/ubi_attach_detach.c
index aa8a5171a..fef9250ee 100644
--- a/miscutils/ubi_attach_detach.c
+++ b/miscutils/ubi_attach_detach.c
@@ -3,34 +3,69 @@
3 * Licensed under GPLv2, see file LICENSE in this source tree. 3 * Licensed under GPLv2, see file LICENSE in this source tree.
4 */ 4 */
5 5
6//applet:IF_UBIATTACH(APPLET_ODDNAME(ubiattach, ubi_attach_detach, BB_DIR_USR_SBIN, BB_SUID_DROP, ubiattach))
7//applet:IF_UBIDETACH(APPLET_ODDNAME(ubidetach, ubi_attach_detach, BB_DIR_USR_SBIN, BB_SUID_DROP, ubidetach))
8
9//kbuild:lib-$(CONFIG_UBIATTACH) += ubi_attach_detach.o
10//kbuild:lib-$(CONFIG_UBIDETACH) += ubi_attach_detach.o
11
12//config:config UBIATTACH 6//config:config UBIATTACH
13//config: bool "ubiattach" 7//config: bool "ubiattach"
14//config: default n 8//config: default y
15//config: depends on PLATFORM_LINUX 9//config: depends on PLATFORM_LINUX
16//config: help 10//config: help
17//config: Attach MTD device to an UBI device. 11//config: Attach MTD device to an UBI device.
18//config: 12//config:
19//config:config UBIDETACH 13//config:config UBIDETACH
20//config: bool "ubidetach" 14//config: bool "ubidetach"
21//config: default n 15//config: default y
22//config: depends on PLATFORM_LINUX 16//config: depends on PLATFORM_LINUX
23//config: help 17//config: help
24//config: Detach MTD device from an UBI device. 18//config: Detach MTD device from an UBI device.
19//config:
20//config:config UBIMKVOL
21//config: bool "ubimkvol"
22//config: default y
23//config: depends on PLATFORM_LINUX
24//config: help
25//config: Create a UBI volume.
26//config:
27//config:config UBIRMVOL
28//config: bool "ubirmvol"
29//config: default y
30//config: depends on PLATFORM_LINUX
31//config: help
32//config: Delete a UBI volume.
33//config:
34//config:config UBIRSVOL
35//config: bool "ubirsvol"
36//config: default y
37//config: depends on PLATFORM_LINUX
38//config: help
39//config: Resize a UBI volume.
40
41//applet:IF_UBIATTACH(APPLET_ODDNAME(ubiattach, ubi_tools, BB_DIR_USR_SBIN, BB_SUID_DROP, ubiattach))
42//applet:IF_UBIDETACH(APPLET_ODDNAME(ubidetach, ubi_tools, BB_DIR_USR_SBIN, BB_SUID_DROP, ubidetach))
43//applet:IF_UBIMKVOL(APPLET_ODDNAME(ubimkvol, ubi_tools, BB_DIR_USR_SBIN, BB_SUID_DROP, ubimkvol))
44//applet:IF_UBIRMVOL(APPLET_ODDNAME(ubirmvol, ubi_tools, BB_DIR_USR_SBIN, BB_SUID_DROP, ubirmvol))
45//applet:IF_UBIRSVOL(APPLET_ODDNAME(ubirsvol, ubi_tools, BB_DIR_USR_SBIN, BB_SUID_DROP, ubirsvol))
46
47//kbuild:lib-$(CONFIG_UBIATTACH) += ubi_attach_detach.o
48//kbuild:lib-$(CONFIG_UBIDETACH) += ubi_attach_detach.o
49//kbuild:lib-$(CONFIG_UBIMKVOL) += ubi_attach_detach.o
50//kbuild:lib-$(CONFIG_UBIRMVOL) += ubi_attach_detach.o
51//kbuild:lib-$(CONFIG_UBIRSVOL) += ubi_attach_detach.o
25 52
26#include "libbb.h" 53#include "libbb.h"
27#include <mtd/ubi-user.h> 54#include <mtd/ubi-user.h>
28 55
29#define OPTION_M (1 << 0) 56#define OPTION_M (1 << 0)
30#define OPTION_D (1 << 1) 57#define OPTION_D (1 << 1)
31 58#define OPTION_n (1 << 2)
32#define do_attach (ENABLE_UBIATTACH && \ 59#define OPTION_N (1 << 3)
33 (!ENABLE_UBIDETACH || (applet_name[3] == 'a'))) 60#define OPTION_s (1 << 4)
61#define OPTION_a (1 << 5)
62#define OPTION_t (1 << 6)
63
64#define do_attach (ENABLE_UBIATTACH && applet_name[3] == 'a')
65#define do_detach (ENABLE_UBIDETACH && applet_name[3] == 'd')
66#define do_mkvol (ENABLE_UBIMKVOL && applet_name[3] == 'm')
67#define do_rmvol (ENABLE_UBIRMVOL && applet_name[4] == 'm')
68#define do_rsvol (ENABLE_UBIRSVOL && applet_name[4] == 's')
34 69
35//usage:#define ubiattach_trivial_usage 70//usage:#define ubiattach_trivial_usage
36//usage: "-m MTD_NUM [-d UBI_NUM] UBI_CTRL_DEV" 71//usage: "-m MTD_NUM [-d UBI_NUM] UBI_CTRL_DEV"
@@ -46,20 +81,54 @@
46//usage: "Detach MTD device from UBI\n" 81//usage: "Detach MTD device from UBI\n"
47//usage: "\nOptions:" 82//usage: "\nOptions:"
48//usage: "\n -d UBI_NUM UBI device number" 83//usage: "\n -d UBI_NUM UBI device number"
84//usage:
85//usage:#define ubimkvol_trivial_usage
86//usage: "UBI_DEVICE -N NAME -s SIZE"
87//usage:#define ubimkvol_full_usage "\n\n"
88//usage: "Create UBI Volume\n"
89//usage: "\nOptions:"
90//usage: "\n -a ALIGNMENT Volume alignment (default 1)"
91//usage: "\n -n VOLID Volume ID, if not specified, it"
92//usage: "\n will be assigned automatically"
93//usage: "\n -N NAME Volume name"
94//usage: "\n -s SIZE Size in bytes"
95//usage: "\n -t TYPE Volume type (static|dynamic)"
96//usage:
97//usage:#define ubirmvol_trivial_usage
98//usage: "UBI_DEVICE -n VOLID"
99//usage:#define ubirmvol_full_usage "\n\n"
100//usage: "Remove UBI Volume\n"
101//usage: "\nOptions:"
102//usage: "\n -n VOLID Volume ID"
103//usage:
104//usage:#define ubirsvol_trivial_usage
105//usage: "UBI_DEVICE -N NAME -s SIZE"
106//usage:#define ubirsvol_full_usage "\n\n"
107//usage: "Resize UBI Volume\n"
108//usage: "\nOptions:"
109//usage: "\n -N NAME Volume name"
110//usage: "\n -s SIZE Size in bytes"
49 111
50int ubi_attach_detach_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 112int ubi_tools_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
51int ubi_attach_detach_main(int argc UNUSED_PARAM, char **argv) 113int ubi_tools_main(int argc UNUSED_PARAM, char **argv)
52{ 114{
53 unsigned opts; 115 unsigned opts;
54 char *ubi_ctrl; 116 char *ubi_ctrl;
55 //struct stat st; 117 //struct stat st;
56 struct ubi_attach_req req;
57 int fd; 118 int fd;
58 int mtd_num; 119 int mtd_num;
59 int dev_num = UBI_DEV_NUM_AUTO; 120 int dev_num = UBI_DEV_NUM_AUTO;
60 121 int vol_id = UBI_VOL_NUM_AUTO;
61 opt_complementary = "=1:m+:d+"; 122 char *vol_name = NULL;
62 opts = getopt32(argv, "m:d:", &mtd_num, &dev_num); 123 int size_bytes;
124 int alignment = 1;
125 char *type = NULL;
126
127 opt_complementary = "=1:m+:d+:n+:s+:a+";
128 opts = getopt32(argv, "m:d:n:N:s:a:t:",
129 &mtd_num, &dev_num, &vol_id,
130 &vol_name, &size_bytes, &alignment, &type
131 );
63 ubi_ctrl = argv[optind]; 132 ubi_ctrl = argv[optind];
64 133
65 fd = xopen(ubi_ctrl, O_RDWR); 134 fd = xopen(ubi_ctrl, O_RDWR);
@@ -68,6 +137,7 @@ int ubi_attach_detach_main(int argc UNUSED_PARAM, char **argv)
68 // bb_error_msg_and_die("%s: not a char device", ubi_ctrl); 137 // bb_error_msg_and_die("%s: not a char device", ubi_ctrl);
69 138
70 if (do_attach) { 139 if (do_attach) {
140 struct ubi_attach_req req;
71 if (!(opts & OPTION_M)) 141 if (!(opts & OPTION_M))
72 bb_error_msg_and_die("%s device not specified", "MTD"); 142 bb_error_msg_and_die("%s device not specified", "MTD");
73 143
@@ -76,11 +146,59 @@ int ubi_attach_detach_main(int argc UNUSED_PARAM, char **argv)
76 req.ubi_num = dev_num; 146 req.ubi_num = dev_num;
77 147
78 xioctl(fd, UBI_IOCATT, &req); 148 xioctl(fd, UBI_IOCATT, &req);
79 } else { /* detach */ 149 } else
150 if (do_detach) {
80 if (!(opts & OPTION_D)) 151 if (!(opts & OPTION_D))
81 bb_error_msg_and_die("%s device not specified", "UBI"); 152 bb_error_msg_and_die("%s device not specified", "UBI");
82 153
83 xioctl(fd, UBI_IOCDET, &dev_num); 154 xioctl(fd, UBI_IOCDET, &dev_num);
155 } else
156 if (do_mkvol) {
157 struct ubi_mkvol_req req;
158 int vol_name_len;
159 if (!(opts & OPTION_s))
160 bb_error_msg_and_die("%s size not specified", "UBI");
161 if (!(opts & OPTION_N))
162 bb_error_msg_and_die("%s name not specified", "UBI");
163 vol_name_len = strlen(vol_name);
164 if (vol_name_len > UBI_MAX_VOLUME_NAME)
165 bb_error_msg_and_die("%s volume name too long", "UBI");
166
167 memset(&req, 0, sizeof(req));
168 req.vol_id = vol_id;
169 if (opts & OPTION_t) {
170 if (type[0] == 's')
171 req.vol_type = UBI_STATIC_VOLUME;
172 else
173 req.vol_type = UBI_DYNAMIC_VOLUME;
174 } else {
175 req.vol_type = UBI_DYNAMIC_VOLUME;
176 }
177 req.alignment = alignment;
178 req.bytes = size_bytes;
179 strncpy(req.name, vol_name, UBI_MAX_VOLUME_NAME);
180 req.name_len = vol_name_len;
181
182 xioctl(fd, UBI_IOCMKVOL, &req);
183 } else
184 if (do_rmvol) {
185 if (!(opts & OPTION_n))
186 bb_error_msg_and_die("%s volume id not specified", "UBI");
187
188 xioctl(fd, UBI_IOCRMVOL, &vol_id);
189 } else
190 if (do_rsvol) {
191 struct ubi_rsvol_req req;
192 if (!(opts & OPTION_s))
193 bb_error_msg_and_die("%s size not specified", "UBI");
194 if (!(opts & OPTION_n))
195 bb_error_msg_and_die("%s volume id not specified", "UBI");
196
197 memset(&req, 0, sizeof(req));
198 req.bytes = size_bytes;
199 req.vol_id = vol_id;
200
201 xioctl(fd, UBI_IOCRSVOL, &req);
84 } 202 }
85 203
86 if (ENABLE_FEATURE_CLEAN_UP) 204 if (ENABLE_FEATURE_CLEAN_UP)
diff --git a/modutils/insmod.c b/modutils/insmod.c
index e5b46f402..94e4e2863 100644
--- a/modutils/insmod.c
+++ b/modutils/insmod.c
@@ -59,7 +59,7 @@ int insmod_main(int argc UNUSED_PARAM, char **argv)
59 if (!filename) 59 if (!filename)
60 bb_show_usage(); 60 bb_show_usage();
61 61
62 rc = bb_init_module(filename, parse_cmdline_module_options(argv)); 62 rc = bb_init_module(filename, parse_cmdline_module_options(argv, /*quote_spaces:*/ 0));
63 if (rc) 63 if (rc)
64 bb_error_msg("can't insert '%s': %s", filename, moderror(rc)); 64 bb_error_msg("can't insert '%s': %s", filename, moderror(rc));
65 65
diff --git a/modutils/modinfo.c b/modutils/modinfo.c
index 731fc0553..db134bd00 100644
--- a/modutils/modinfo.c
+++ b/modutils/modinfo.c
@@ -23,9 +23,9 @@
23 23
24 24
25enum { 25enum {
26 OPT_TAGS = (1 << 6) - 1, 26 OPT_TAGS = (1 << 8) - 1,
27 OPT_F = (1 << 6), /* field name */ 27 OPT_F = (1 << 8), /* field name */
28 OPT_0 = (1 << 7), /* \0 as separator */ 28 OPT_0 = (1 << 9), /* \0 as separator */
29}; 29};
30 30
31struct modinfo_env { 31struct modinfo_env {
@@ -44,7 +44,7 @@ static int display(const char *data, const char *pattern, int flag)
44} 44}
45 45
46static void modinfo(const char *path, const char *version, 46static void modinfo(const char *path, const char *version,
47 struct modinfo_env *env) 47 const struct modinfo_env *env)
48{ 48{
49 static const char *const shortcuts[] = { 49 static const char *const shortcuts[] = {
50 "filename", 50 "filename",
@@ -53,6 +53,8 @@ static void modinfo(const char *path, const char *version,
53 "license", 53 "license",
54 "vermagic", 54 "vermagic",
55 "parm", 55 "parm",
56 "firmware",
57 "depends",
56 }; 58 };
57 size_t len; 59 size_t len;
58 int j, length; 60 int j, length;
@@ -80,11 +82,13 @@ static void modinfo(const char *path, const char *version,
80 if (field) 82 if (field)
81 tags |= OPT_F; 83 tags |= OPT_F;
82 for (j = 1; (1<<j) & (OPT_TAGS + OPT_F); j++) { 84 for (j = 1; (1<<j) & (OPT_TAGS + OPT_F); j++) {
83 const char *pattern = field; 85 const char *pattern;
84 if ((1<<j) & OPT_TAGS) 86
85 pattern = shortcuts[j];
86 if (!((1<<j) & tags)) 87 if (!((1<<j) & tags))
87 continue; 88 continue;
89 pattern = field;
90 if ((1<<j) & OPT_TAGS)
91 pattern = shortcuts[j];
88 length = strlen(pattern); 92 length = strlen(pattern);
89 ptr = the_module; 93 ptr = the_module;
90 while (1) { 94 while (1) {
diff --git a/modutils/modprobe.c b/modutils/modprobe.c
index e3bacac56..7ae459826 100644
--- a/modutils/modprobe.c
+++ b/modutils/modprobe.c
@@ -170,7 +170,7 @@ struct globals {
170#define G (*(struct globals*)&bb_common_bufsiz1) 170#define G (*(struct globals*)&bb_common_bufsiz1)
171#define INIT_G() do { } while (0) 171#define INIT_G() do { } while (0)
172struct BUG_G_too_big { 172struct BUG_G_too_big {
173 char BUG_G_too_big[sizeof(G) <= COMMON_BUFSIZE ? 1 : -1]; 173 char BUG_G_too_big[sizeof(G) <= COMMON_BUFSIZE ? 1 : -1];
174}; 174};
175 175
176 176
@@ -530,10 +530,14 @@ int modprobe_main(int argc UNUSED_PARAM, char **argv)
530 xchdir(G.uts.release); 530 xchdir(G.uts.release);
531 531
532 if (opt & OPT_LIST_ONLY) { 532 if (opt & OPT_LIST_ONLY) {
533 int i;
533 char name[MODULE_NAME_LEN]; 534 char name[MODULE_NAME_LEN];
534 char *colon, *tokens[2]; 535 char *colon, *tokens[2];
535 parser_t *p = config_open2(CONFIG_DEFAULT_DEPMOD_FILE, xfopen_for_read); 536 parser_t *p = config_open2(CONFIG_DEFAULT_DEPMOD_FILE, xfopen_for_read);
536 537
538 for (i = 0; argv[i]; i++)
539 replace(argv[i], '-', '_');
540
537 while (config_read(p, tokens, 2, 1, "# \t", PARSE_NORMAL)) { 541 while (config_read(p, tokens, 2, 1, "# \t", PARSE_NORMAL)) {
538 colon = last_char_is(tokens[0], ':'); 542 colon = last_char_is(tokens[0], ':');
539 if (!colon) 543 if (!colon)
@@ -543,7 +547,6 @@ int modprobe_main(int argc UNUSED_PARAM, char **argv)
543 if (!argv[0]) 547 if (!argv[0])
544 puts(tokens[0]); 548 puts(tokens[0]);
545 else { 549 else {
546 int i;
547 for (i = 0; argv[i]; i++) { 550 for (i = 0; argv[i]; i++) {
548 if (fnmatch(argv[i], name, 0) == 0) { 551 if (fnmatch(argv[i], name, 0) == 0) {
549 puts(tokens[0]); 552 puts(tokens[0]);
@@ -589,7 +592,7 @@ int modprobe_main(int argc UNUSED_PARAM, char **argv)
589 /* First argument is module name, rest are parameters */ 592 /* First argument is module name, rest are parameters */
590 DBG("probing just module %s", *argv); 593 DBG("probing just module %s", *argv);
591 add_probe(argv[0]); 594 add_probe(argv[0]);
592 G.cmdline_mopts = parse_cmdline_module_options(argv); 595 G.cmdline_mopts = parse_cmdline_module_options(argv, /*quote_spaces:*/ 1);
593 } 596 }
594 597
595 /* Happens if all requested modules are already loaded */ 598 /* Happens if all requested modules are already loaded */
diff --git a/modutils/modutils-24.c b/modutils/modutils-24.c
index 2b34954c0..bbc54e316 100644
--- a/modutils/modutils-24.c
+++ b/modutils/modutils-24.c
@@ -2444,14 +2444,12 @@ new_process_module_arguments(struct obj_file *f, const char *options)
2444 bb_error_msg_and_die("symbol for parameter %s not found", param); 2444 bb_error_msg_and_die("symbol for parameter %s not found", param);
2445 2445
2446 /* Number of parameters */ 2446 /* Number of parameters */
2447 min = max = 1;
2447 if (isdigit(*pinfo)) { 2448 if (isdigit(*pinfo)) {
2448 min = strtoul(pinfo, &pinfo, 10); 2449 min = max = strtoul(pinfo, &pinfo, 10);
2449 if (*pinfo == '-') 2450 if (*pinfo == '-')
2450 max = strtoul(pinfo + 1, &pinfo, 10); 2451 max = strtoul(pinfo + 1, &pinfo, 10);
2451 else 2452 }
2452 max = min;
2453 } else
2454 min = max = 1;
2455 2453
2456 contents = f->sections[sym->secidx]->contents; 2454 contents = f->sections[sym->secidx]->contents;
2457 loc = contents + sym->value; 2455 loc = contents + sym->value;
@@ -2473,7 +2471,8 @@ new_process_module_arguments(struct obj_file *f, const char *options)
2473 /* Parse parameter values */ 2471 /* Parse parameter values */
2474 n = 0; 2472 n = 0;
2475 p = val; 2473 p = val;
2476 while (*p != 0) { 2474 while (*p) {
2475 char sv_ch;
2477 char *endp; 2476 char *endp;
2478 2477
2479 if (++n > max) 2478 if (++n > max)
@@ -2482,21 +2481,25 @@ new_process_module_arguments(struct obj_file *f, const char *options)
2482 switch (*pinfo) { 2481 switch (*pinfo) {
2483 case 's': 2482 case 's':
2484 len = strcspn(p, ","); 2483 len = strcspn(p, ",");
2485 p[len] = 0; 2484 sv_ch = p[len];
2485 p[len] = '\0';
2486 obj_string_patch(f, sym->secidx, 2486 obj_string_patch(f, sym->secidx,
2487 loc - contents, p); 2487 loc - contents, p);
2488 loc += tgt_sizeof_char_p; 2488 loc += tgt_sizeof_char_p;
2489 p += len; 2489 p += len;
2490 *p = sv_ch;
2490 break; 2491 break;
2491 case 'c': 2492 case 'c':
2492 len = strcspn(p, ","); 2493 len = strcspn(p, ",");
2493 p[len] = 0; 2494 sv_ch = p[len];
2495 p[len] = '\0';
2494 if (len >= charssize) 2496 if (len >= charssize)
2495 bb_error_msg_and_die("string too long for %s (max %ld)", param, 2497 bb_error_msg_and_die("string too long for %s (max %ld)", param,
2496 charssize - 1); 2498 charssize - 1);
2497 strcpy((char *) loc, p); 2499 strcpy((char *) loc, p);
2498 loc += charssize; 2500 loc += charssize;
2499 p += len; 2501 p += len;
2502 *p = sv_ch;
2500 break; 2503 break;
2501 case 'b': 2504 case 'b':
2502 *loc++ = strtoul(p, &endp, 0); 2505 *loc++ = strtoul(p, &endp, 0);
diff --git a/modutils/modutils.c b/modutils/modutils.c
index 565d0d22f..6187ca72f 100644
--- a/modutils/modutils.c
+++ b/modutils/modutils.c
@@ -62,7 +62,7 @@ char* FAST_FUNC filename2modname(const char *filename, char *modname)
62 return modname; 62 return modname;
63} 63}
64 64
65char* FAST_FUNC parse_cmdline_module_options(char **argv) 65char* FAST_FUNC parse_cmdline_module_options(char **argv, int quote_spaces)
66{ 66{
67 char *options; 67 char *options;
68 int optlen; 68 int optlen;
@@ -70,14 +70,31 @@ char* FAST_FUNC parse_cmdline_module_options(char **argv)
70 options = xzalloc(1); 70 options = xzalloc(1);
71 optlen = 0; 71 optlen = 0;
72 while (*++argv) { 72 while (*++argv) {
73 options = xrealloc(options, optlen + 2 + strlen(*argv) + 2); 73 const char *fmt;
74 /* Older versions were enclosing space-containing *argv in "", 74 const char *var;
75 * but both modprobe and insmod from module-init-tools 3.11.1 75 const char *val;
76 * don't do this anymore. (As to extra trailing space, 76
77 * insmod adds it but modprobe does not. We do in both cases) 77 var = *argv;
78 */ 78 options = xrealloc(options, optlen + 2 + strlen(var) + 2);
79 optlen += sprintf(options + optlen, "%s ", *argv); 79 fmt = "%.*s%s ";
80 val = strchrnul(var, '=');
81 if (quote_spaces) {
82 /*
83 * modprobe (module-init-tools version 3.11.1) compat:
84 * quote only value:
85 * var="val with spaces", not "var=val with spaces"
86 * (note: var *name* is not checked for spaces!)
87 */
88 if (*val) { /* has var=val format. skip '=' */
89 val++;
90 if (strchr(val, ' '))
91 fmt = "%.*s\"%s\" ";
92 }
93 }
94 optlen += sprintf(options + optlen, fmt, (int)(val - var), var, val);
80 } 95 }
96 /* Remove trailing space. Disabled */
97 /* if (optlen != 0) options[optlen-1] = '\0'; */
81 return options; 98 return options;
82} 99}
83 100
diff --git a/modutils/modutils.h b/modutils/modutils.h
index 863bc26d3..5f059c716 100644
--- a/modutils/modutils.h
+++ b/modutils/modutils.h
@@ -21,7 +21,7 @@ void replace(char *s, char what, char with) FAST_FUNC;
21char *replace_underscores(char *s) FAST_FUNC; 21char *replace_underscores(char *s) FAST_FUNC;
22int string_to_llist(char *string, llist_t **llist, const char *delim) FAST_FUNC; 22int string_to_llist(char *string, llist_t **llist, const char *delim) FAST_FUNC;
23char *filename2modname(const char *filename, char *modname) FAST_FUNC; 23char *filename2modname(const char *filename, char *modname) FAST_FUNC;
24char *parse_cmdline_module_options(char **argv) FAST_FUNC; 24char *parse_cmdline_module_options(char **argv, int quote_spaces) FAST_FUNC;
25 25
26/* insmod for 2.4 and modprobe's options (insmod 2.6 has no options at all): */ 26/* insmod for 2.4 and modprobe's options (insmod 2.6 has no options at all): */
27#define INSMOD_OPTS \ 27#define INSMOD_OPTS \
diff --git a/networking/httpd_ssi.c b/networking/httpd_ssi.c
index 87f43fcfa..cfe64eb46 100644
--- a/networking/httpd_ssi.c
+++ b/networking/httpd_ssi.c
@@ -52,9 +52,9 @@ httpd_ssi.c -o httpd_ssi
52 52
53static char* skip_whitespace(char *s) 53static char* skip_whitespace(char *s)
54{ 54{
55 while (*s == ' ' || *s == '\t') ++s; 55 while (*s == ' ' || *s == '\t') ++s;
56 56
57 return s; 57 return s;
58} 58}
59 59
60static char line[64 * 1024]; 60static char line[64 * 1024];
diff --git a/networking/libiproute/ipaddress.c b/networking/libiproute/ipaddress.c
index 397a8ee34..b3748e8c5 100644
--- a/networking/libiproute/ipaddress.c
+++ b/networking/libiproute/ipaddress.c
@@ -162,6 +162,8 @@ static NOINLINE int print_linkinfo(const struct nlmsghdr *n)
162 printf("master %s ", ll_idx_n2a(*(int*)RTA_DATA(tb[IFLA_MASTER]), b1)); 162 printf("master %s ", ll_idx_n2a(*(int*)RTA_DATA(tb[IFLA_MASTER]), b1));
163 } 163 }
164#endif 164#endif
165/* IFLA_OPERSTATE was added to kernel with the same commit as IFF_DORMANT */
166#ifdef IFF_DORMANT
165 if (tb[IFLA_OPERSTATE]) { 167 if (tb[IFLA_OPERSTATE]) {
166 static const char operstate_labels[] ALIGN1 = 168 static const char operstate_labels[] ALIGN1 =
167 "UNKNOWN\0""NOTPRESENT\0""DOWN\0""LOWERLAYERDOWN\0" 169 "UNKNOWN\0""NOTPRESENT\0""DOWN\0""LOWERLAYERDOWN\0"
@@ -169,6 +171,7 @@ static NOINLINE int print_linkinfo(const struct nlmsghdr *n)
169 printf("state %s ", nth_string(operstate_labels, 171 printf("state %s ", nth_string(operstate_labels,
170 *(uint8_t *)RTA_DATA(tb[IFLA_OPERSTATE]))); 172 *(uint8_t *)RTA_DATA(tb[IFLA_OPERSTATE])));
171 } 173 }
174#endif
172 if (G_filter.showqueue) 175 if (G_filter.showqueue)
173 print_queuelen((char*)RTA_DATA(tb[IFLA_IFNAME])); 176 print_queuelen((char*)RTA_DATA(tb[IFLA_IFNAME]));
174 177
diff --git a/networking/libiproute/iproute.c b/networking/libiproute/iproute.c
index f6071b463..f8a67d9ee 100644
--- a/networking/libiproute/iproute.c
+++ b/networking/libiproute/iproute.c
@@ -31,8 +31,8 @@ struct filter_t {
31 //int type; - read-only 31 //int type; - read-only
32 //int typemask; - unused 32 //int typemask; - unused
33 //int tos, tosmask; - unused 33 //int tos, tosmask; - unused
34 int iif, iifmask; 34 int iif;
35 int oif, oifmask; 35 int oif;
36 //int realm, realmmask; - unused 36 //int realm, realmmask; - unused
37 //inet_prefix rprefsrc; - read-only 37 //inet_prefix rprefsrc; - read-only
38 inet_prefix rvia; 38 inet_prefix rvia;
@@ -82,7 +82,7 @@ static int FAST_FUNC print_route(const struct sockaddr_nl *who UNUSED_PARAM,
82{ 82{
83 struct rtmsg *r = NLMSG_DATA(n); 83 struct rtmsg *r = NLMSG_DATA(n);
84 int len = n->nlmsg_len; 84 int len = n->nlmsg_len;
85 struct rtattr * tb[RTA_MAX+1]; 85 struct rtattr *tb[RTA_MAX+1];
86 char abuf[256]; 86 char abuf[256];
87 inet_prefix dst; 87 inet_prefix dst;
88 inet_prefix src; 88 inet_prefix src;
@@ -159,8 +159,21 @@ static int FAST_FUNC print_route(const struct sockaddr_nl *who UNUSED_PARAM,
159 } 159 }
160 160
161 memset(tb, 0, sizeof(tb)); 161 memset(tb, 0, sizeof(tb));
162 memset(&src, 0, sizeof(src));
163 memset(&dst, 0, sizeof(dst));
162 parse_rtattr(tb, RTA_MAX, RTM_RTA(r), len); 164 parse_rtattr(tb, RTA_MAX, RTM_RTA(r), len);
163 165
166 if (tb[RTA_SRC]) {
167 src.bitlen = r->rtm_src_len;
168 src.bytelen = (r->rtm_family == AF_INET6 ? 16 : 4);
169 memcpy(src.data, RTA_DATA(tb[RTA_SRC]), src.bytelen);
170 }
171 if (tb[RTA_DST]) {
172 dst.bitlen = r->rtm_dst_len;
173 dst.bytelen = (r->rtm_family == AF_INET6 ? 16 : 4);
174 memcpy(dst.data, RTA_DATA(tb[RTA_DST]), dst.bytelen);
175 }
176
164 if (G_filter.rdst.family 177 if (G_filter.rdst.family
165 && inet_addr_match(&dst, &G_filter.rdst, G_filter.rdst.bitlen) 178 && inet_addr_match(&dst, &G_filter.rdst, G_filter.rdst.bitlen)
166 ) { 179 ) {
@@ -182,23 +195,32 @@ static int FAST_FUNC print_route(const struct sockaddr_nl *who UNUSED_PARAM,
182 ) { 195 ) {
183 return 0; 196 return 0;
184 } 197 }
185 if (G_filter.flushb 198 if (G_filter.oif != 0) {
186 && r->rtm_family == AF_INET6 199 if (!tb[RTA_OIF])
187 && r->rtm_dst_len == 0 200 return 0;
188 && r->rtm_type == RTN_UNREACHABLE 201 if (G_filter.oif != *(int*)RTA_DATA(tb[RTA_OIF]))
189 && tb[RTA_PRIORITY] 202 return 0;
190 && *(int*)RTA_DATA(tb[RTA_PRIORITY]) == -1
191 ) {
192 return 0;
193 } 203 }
194 204
195 if (G_filter.flushb) { 205 if (G_filter.flushb) {
196 struct nlmsghdr *fn; 206 struct nlmsghdr *fn;
207
208 /* We are creating route flush commands */
209
210 if (r->rtm_family == AF_INET6
211 && r->rtm_dst_len == 0
212 && r->rtm_type == RTN_UNREACHABLE
213 && tb[RTA_PRIORITY]
214 && *(int*)RTA_DATA(tb[RTA_PRIORITY]) == -1
215 ) {
216 return 0;
217 }
218
197 if (NLMSG_ALIGN(G_filter.flushp) + n->nlmsg_len > G_filter.flushe) { 219 if (NLMSG_ALIGN(G_filter.flushp) + n->nlmsg_len > G_filter.flushe) {
198 if (flush_update()) 220 if (flush_update())
199 bb_error_msg_and_die("flush"); 221 bb_error_msg_and_die("flush");
200 } 222 }
201 fn = (struct nlmsghdr*)(G_filter.flushb + NLMSG_ALIGN(G_filter.flushp)); 223 fn = (void*)(G_filter.flushb + NLMSG_ALIGN(G_filter.flushp));
202 memcpy(fn, n, n->nlmsg_len); 224 memcpy(fn, n, n->nlmsg_len);
203 fn->nlmsg_type = RTM_DELROUTE; 225 fn->nlmsg_type = RTM_DELROUTE;
204 fn->nlmsg_flags = NLM_F_REQUEST; 226 fn->nlmsg_flags = NLM_F_REQUEST;
@@ -208,6 +230,8 @@ static int FAST_FUNC print_route(const struct sockaddr_nl *who UNUSED_PARAM,
208 return 0; 230 return 0;
209 } 231 }
210 232
233 /* We are printing routes */
234
211 if (n->nlmsg_type == RTM_DELROUTE) { 235 if (n->nlmsg_type == RTM_DELROUTE) {
212 printf("Deleted "); 236 printf("Deleted ");
213 } 237 }
@@ -257,10 +281,12 @@ static int FAST_FUNC print_route(const struct sockaddr_nl *who UNUSED_PARAM,
257 RTA_DATA(tb[RTA_GATEWAY]), 281 RTA_DATA(tb[RTA_GATEWAY]),
258 abuf, sizeof(abuf))); 282 abuf, sizeof(abuf)));
259 } 283 }
260 if (tb[RTA_OIF] && G_filter.oifmask != -1) { 284 if (tb[RTA_OIF]) {
261 printf("dev %s ", ll_index_to_name(*(int*)RTA_DATA(tb[RTA_OIF]))); 285 printf("dev %s ", ll_index_to_name(*(int*)RTA_DATA(tb[RTA_OIF])));
262 } 286 }
263 287
288 /* Todo: parse & show "proto kernel", "scope link" here */
289
264 if (tb[RTA_PREFSRC] && /*G_filter.rprefsrc.bitlen - always 0*/ 0 != host_len) { 290 if (tb[RTA_PREFSRC] && /*G_filter.rprefsrc.bitlen - always 0*/ 0 != host_len) {
265 /* Do not use format_host(). It is our local addr 291 /* Do not use format_host(). It is our local addr
266 and symbolic name will not be useful. 292 and symbolic name will not be useful.
@@ -292,7 +318,7 @@ static int FAST_FUNC print_route(const struct sockaddr_nl *who UNUSED_PARAM,
292 printf(" error %d", ci->rta_error); 318 printf(" error %d", ci->rta_error);
293 } 319 }
294 } 320 }
295 if (tb[RTA_IIF] && G_filter.iifmask != -1) { 321 if (tb[RTA_IIF] && G_filter.iif == 0) {
296 printf(" iif %s", ll_index_to_name(*(int*)RTA_DATA(tb[RTA_IIF]))); 322 printf(" iif %s", ll_index_to_name(*(int*)RTA_DATA(tb[RTA_IIF])));
297 } 323 }
298 bb_putchar('\n'); 324 bb_putchar('\n');
@@ -413,7 +439,8 @@ IF_FEATURE_IP_RULE(ARG_table,)
413 NEXT_ARG(); 439 NEXT_ARG();
414 } 440 }
415 if ((**argv < '0' || **argv > '9') 441 if ((**argv < '0' || **argv > '9')
416 && rtnl_rtntype_a2n(&type, *argv) == 0) { 442 && rtnl_rtntype_a2n(&type, *argv) == 0
443 ) {
417 NEXT_ARG(); 444 NEXT_ARG();
418 req.r.rtm_type = type; 445 req.r.rtm_type = type;
419 ok |= type_ok; 446 ok |= type_ok;
@@ -662,12 +689,10 @@ static int iproute_list_or_flush(char **argv, int flush)
662 if (id) { 689 if (id) {
663 idx = xll_name_to_index(id); 690 idx = xll_name_to_index(id);
664 G_filter.iif = idx; 691 G_filter.iif = idx;
665 G_filter.iifmask = -1;
666 } 692 }
667 if (od) { 693 if (od) {
668 idx = xll_name_to_index(od); 694 idx = xll_name_to_index(od);
669 G_filter.oif = idx; 695 G_filter.oif = idx;
670 G_filter.oifmask = -1;
671 } 696 }
672 } 697 }
673 698
diff --git a/networking/libiproute/libnetlink.c b/networking/libiproute/libnetlink.c
index 7291ee2f1..c7533a4a7 100644
--- a/networking/libiproute/libnetlink.c
+++ b/networking/libiproute/libnetlink.c
@@ -55,6 +55,7 @@ int FAST_FUNC xrtnl_wilddump_request(struct rtnl_handle *rth, int family, int ty
55 return rtnl_send(rth, (void*)&req, sizeof(req)); 55 return rtnl_send(rth, (void*)&req, sizeof(req));
56} 56}
57 57
58//TODO: pass rth->fd instead of full rth?
58int FAST_FUNC rtnl_send(struct rtnl_handle *rth, char *buf, int len) 59int FAST_FUNC rtnl_send(struct rtnl_handle *rth, char *buf, int len)
59{ 60{
60 struct sockaddr_nl nladdr; 61 struct sockaddr_nl nladdr;
@@ -392,7 +393,7 @@ void FAST_FUNC parse_rtattr(struct rtattr *tb[], int max, struct rtattr *rta, in
392 if (rta->rta_type <= max) { 393 if (rta->rta_type <= max) {
393 tb[rta->rta_type] = rta; 394 tb[rta->rta_type] = rta;
394 } 395 }
395 rta = RTA_NEXT(rta,len); 396 rta = RTA_NEXT(rta, len);
396 } 397 }
397 if (len) { 398 if (len) {
398 bb_error_msg("deficit %d, rta_len=%d!", len, rta->rta_len); 399 bb_error_msg("deficit %d, rta_len=%d!", len, rta->rta_len);
diff --git a/networking/libiproute/utils.c b/networking/libiproute/utils.c
index d32db8de5..d0fe30605 100644
--- a/networking/libiproute/utils.c
+++ b/networking/libiproute/utils.c
@@ -83,20 +83,43 @@ int get_addr_1(inet_prefix *addr, char *name, int family)
83 return 0; 83 return 0;
84 } 84 }
85 85
86 addr->family = AF_INET;
87 if (family != AF_UNSPEC && family != AF_INET) 86 if (family != AF_UNSPEC && family != AF_INET)
88 return -1; 87 return -1;
88
89 /* Try to parse it as IPv4 */
90 addr->family = AF_INET;
91#if 0 /* Doesn't handle e.g. "10.10", for example, "ip r l root 10.10/16" */
89 if (inet_pton(AF_INET, name, addr->data) <= 0) 92 if (inet_pton(AF_INET, name, addr->data) <= 0)
90 return -1; 93 return -1;
94#else
95 {
96 unsigned i = 0;
97 unsigned n = 0;
98 const char *cp = name - 1;
99 while (*++cp) {
100 if ((unsigned char)(*cp - '0') <= 9) {
101 n = 10 * n + (unsigned char)(*cp - '0');
102 if (n >= 256)
103 return -1;
104 ((uint8_t*)addr->data)[i] = n;
105 continue;
106 }
107 if (*cp == '.' && ++i <= 3) {
108 n = 0;
109 continue;
110 }
111 return -1;
112 }
113 }
114#endif
91 addr->bytelen = 4; 115 addr->bytelen = 4;
92 addr->bitlen = -1; 116 addr->bitlen = -1;
117
93 return 0; 118 return 0;
94} 119}
95 120
96static int get_prefix_1(inet_prefix *dst, char *arg, int family) 121static void get_prefix_1(inet_prefix *dst, char *arg, int family)
97{ 122{
98 int err;
99 unsigned plen;
100 char *slash; 123 char *slash;
101 124
102 memset(dst, 0, sizeof(*dst)); 125 memset(dst, 0, sizeof(*dst));
@@ -108,48 +131,50 @@ static int get_prefix_1(inet_prefix *dst, char *arg, int family)
108 dst->family = family; 131 dst->family = family;
109 /*dst->bytelen = 0; - done by memset */ 132 /*dst->bytelen = 0; - done by memset */
110 /*dst->bitlen = 0;*/ 133 /*dst->bitlen = 0;*/
111 return 0; 134 return;
112 } 135 }
113 136
114 slash = strchr(arg, '/'); 137 slash = strchr(arg, '/');
115 if (slash) 138 if (slash)
116 *slash = '\0'; 139 *slash = '\0';
117 err = get_addr_1(dst, arg, family); 140
118 if (err == 0) { 141 if (get_addr_1(dst, arg, family) == 0) {
119 dst->bitlen = (dst->family == AF_INET6) ? 128 : 32; 142 dst->bitlen = (dst->family == AF_INET6) ? 128 : 32;
120 if (slash) { 143 if (slash) {
144 unsigned plen;
121 inet_prefix netmask_pfx; 145 inet_prefix netmask_pfx;
122 146
123 netmask_pfx.family = AF_UNSPEC; 147 netmask_pfx.family = AF_UNSPEC;
124 plen = bb_strtou(slash + 1, NULL, 0); 148 plen = bb_strtou(slash + 1, NULL, 0);
125 if ((errno || plen > dst->bitlen) 149 if ((errno || plen > dst->bitlen)
126 && (get_addr_1(&netmask_pfx, slash + 1, family))) 150 && get_addr_1(&netmask_pfx, slash + 1, family) != 0
127 err = -1; 151 ) {
128 else if (netmask_pfx.family == AF_INET) { 152 goto bad;
153 }
154 if (netmask_pfx.family == AF_INET) {
129 /* fill in prefix length of dotted quad */ 155 /* fill in prefix length of dotted quad */
130 uint32_t mask = ntohl(netmask_pfx.data[0]); 156 uint32_t mask = ntohl(netmask_pfx.data[0]);
131 uint32_t host = ~mask; 157 uint32_t host = ~mask;
132 158
133 /* a valid netmask must be 2^n - 1 */ 159 /* a valid netmask must be 2^n - 1 */
134 if (!(host & (host + 1))) { 160 if (host & (host + 1))
135 for (plen = 0; mask; mask <<= 1) 161 goto bad;
136 ++plen; 162
137 if (plen <= dst->bitlen) { 163 for (plen = 0; mask; mask <<= 1)
138 dst->bitlen = plen; 164 ++plen;
139 /* dst->flags |= PREFIXLEN_SPECIFIED; */ 165 if (plen > dst->bitlen)
140 } else 166 goto bad;
141 err = -1; 167 /* dst->flags |= PREFIXLEN_SPECIFIED; */
142 } else
143 err = -1;
144 } else {
145 /* plain prefix */
146 dst->bitlen = plen;
147 } 168 }
169 dst->bitlen = plen;
148 } 170 }
149 } 171 }
172
150 if (slash) 173 if (slash)
151 *slash = '/'; 174 *slash = '/';
152 return err; 175 return;
176 bad:
177 bb_error_msg_and_die("an %s %s is expected rather than \"%s\"", "inet", "prefix", arg);
153} 178}
154 179
155int get_addr(inet_prefix *dst, char *arg, int family) 180int get_addr(inet_prefix *dst, char *arg, int family)
@@ -163,15 +188,12 @@ int get_addr(inet_prefix *dst, char *arg, int family)
163 return 0; 188 return 0;
164} 189}
165 190
166int get_prefix(inet_prefix *dst, char *arg, int family) 191void get_prefix(inet_prefix *dst, char *arg, int family)
167{ 192{
168 if (family == AF_PACKET) { 193 if (family == AF_PACKET) {
169 bb_error_msg_and_die("\"%s\" may be inet %s, but it is not allowed in this context", arg, "prefix"); 194 bb_error_msg_and_die("\"%s\" may be inet %s, but it is not allowed in this context", arg, "prefix");
170 } 195 }
171 if (get_prefix_1(dst, arg, family)) { 196 get_prefix_1(dst, arg, family);
172 bb_error_msg_and_die("an %s %s is expected rather than \"%s\"", "inet", "prefix", arg);
173 }
174 return 0;
175} 197}
176 198
177uint32_t get_addr32(char *name) 199uint32_t get_addr32(char *name)
@@ -204,10 +226,10 @@ void duparg2(const char *key, const char *arg)
204 bb_error_msg_and_die("either \"%s\" is duplicate, or \"%s\" is garbage", key, arg); 226 bb_error_msg_and_die("either \"%s\" is duplicate, or \"%s\" is garbage", key, arg);
205} 227}
206 228
207int inet_addr_match(inet_prefix *a, inet_prefix *b, int bits) 229int inet_addr_match(const inet_prefix *a, const inet_prefix *b, int bits)
208{ 230{
209 uint32_t *a1 = a->data; 231 const uint32_t *a1 = a->data;
210 uint32_t *a2 = b->data; 232 const uint32_t *a2 = b->data;
211 int words = bits >> 5; 233 int words = bits >> 5;
212 234
213 bits &= 0x1f; 235 bits &= 0x1f;
diff --git a/networking/libiproute/utils.h b/networking/libiproute/utils.h
index 93c9d25d6..5fb4a862c 100644
--- a/networking/libiproute/utils.h
+++ b/networking/libiproute/utils.h
@@ -58,9 +58,9 @@ struct ipx_addr {
58 58
59extern uint32_t get_addr32(char *name); 59extern uint32_t get_addr32(char *name);
60extern int get_addr_1(inet_prefix *dst, char *arg, int family); 60extern int get_addr_1(inet_prefix *dst, char *arg, int family);
61/*extern int get_prefix_1(inet_prefix *dst, char *arg, int family);*/ 61/*extern void get_prefix_1(inet_prefix *dst, char *arg, int family);*/
62extern int get_addr(inet_prefix *dst, char *arg, int family); 62extern int get_addr(inet_prefix *dst, char *arg, int family);
63extern int get_prefix(inet_prefix *dst, char *arg, int family); 63extern void get_prefix(inet_prefix *dst, char *arg, int family);
64 64
65extern unsigned get_unsigned(char *arg, const char *errmsg); 65extern unsigned get_unsigned(char *arg, const char *errmsg);
66extern uint32_t get_u32(char *arg, const char *errmsg); 66extern uint32_t get_u32(char *arg, const char *errmsg);
@@ -77,7 +77,7 @@ extern const char *format_host(int af, int len, void *addr, char *buf, int bufle
77void invarg(const char *, const char *) NORETURN; 77void invarg(const char *, const char *) NORETURN;
78void duparg(const char *, const char *) NORETURN; 78void duparg(const char *, const char *) NORETURN;
79void duparg2(const char *, const char *) NORETURN; 79void duparg2(const char *, const char *) NORETURN;
80int inet_addr_match(inet_prefix *a, inet_prefix *b, int bits); 80int inet_addr_match(const inet_prefix *a, const inet_prefix *b, int bits);
81 81
82const char *dnet_ntop(int af, const void *addr, char *str, size_t len); 82const char *dnet_ntop(int af, const void *addr, char *str, size_t len);
83int dnet_pton(int af, const char *src, void *addr); 83int dnet_pton(int af, const char *src, void *addr);
diff --git a/networking/ntpd.c b/networking/ntpd.c
index 8fe529edb..3ed05ba29 100644
--- a/networking/ntpd.c
+++ b/networking/ntpd.c
@@ -882,7 +882,7 @@ fit(peer_t *p, double rd)
882// /* Do we have a loop? */ 882// /* Do we have a loop? */
883// if (p->refid == p->dstaddr || p->refid == s.refid) 883// if (p->refid == p->dstaddr || p->refid == s.refid)
884// return 0; 884// return 0;
885 return 1; 885 return 1;
886} 886}
887static peer_t* 887static peer_t*
888select_and_cluster(void) 888select_and_cluster(void)
diff --git a/networking/ping.c b/networking/ping.c
index 11ce24eb5..c2ff42e2c 100644
--- a/networking/ping.c
+++ b/networking/ping.c
@@ -419,16 +419,18 @@ static void print_stats_and_exit(int junk UNUSED_PARAM)
419 exit(nreceived == 0 || (deadline && nreceived < pingcount)); 419 exit(nreceived == 0 || (deadline && nreceived < pingcount));
420} 420}
421 421
422static void sendping_tail(void (*sp)(int), const void *pkt, int size_pkt) 422static void sendping_tail(void (*sp)(int), int size_pkt)
423{ 423{
424 int sz; 424 int sz;
425 425
426 CLR((uint16_t)ntransmitted % MAX_DUP_CHK); 426 CLR((uint16_t)ntransmitted % MAX_DUP_CHK);
427 ntransmitted++; 427 ntransmitted++;
428 428
429 size_pkt += datalen;
430
429 /* sizeof(pingaddr) can be larger than real sa size, but I think 431 /* sizeof(pingaddr) can be larger than real sa size, but I think
430 * it doesn't matter */ 432 * it doesn't matter */
431 sz = xsendto(pingsock, pkt, size_pkt, &pingaddr.sa, sizeof(pingaddr)); 433 sz = xsendto(pingsock, G.snd_packet, size_pkt, &pingaddr.sa, sizeof(pingaddr));
432 if (sz != size_pkt) 434 if (sz != size_pkt)
433 bb_error_msg_and_die(bb_msg_write_error); 435 bb_error_msg_and_die(bb_msg_write_error);
434 436
@@ -479,12 +481,12 @@ static void sendping4(int junk UNUSED_PARAM)
479 481
480 pkt->icmp_cksum = in_cksum((unsigned short *) pkt, datalen + ICMP_MINLEN); 482 pkt->icmp_cksum = in_cksum((unsigned short *) pkt, datalen + ICMP_MINLEN);
481 483
482 sendping_tail(sendping4, pkt, datalen + ICMP_MINLEN); 484 sendping_tail(sendping4, ICMP_MINLEN);
483} 485}
484#if ENABLE_PING6 486#if ENABLE_PING6
485static void sendping6(int junk UNUSED_PARAM) 487static void sendping6(int junk UNUSED_PARAM)
486{ 488{
487 struct icmp6_hdr *pkt = alloca(datalen + sizeof(struct icmp6_hdr) + 4); 489 struct icmp6_hdr *pkt = G.snd_packet;
488 490
489 //memset(pkt, 0, datalen + sizeof(struct icmp6_hdr) + 4); 491 //memset(pkt, 0, datalen + sizeof(struct icmp6_hdr) + 4);
490 pkt->icmp6_type = ICMP6_ECHO_REQUEST; 492 pkt->icmp6_type = ICMP6_ECHO_REQUEST;
@@ -498,7 +500,7 @@ static void sendping6(int junk UNUSED_PARAM)
498 500
499 //TODO? pkt->icmp_cksum = in_cksum(...); 501 //TODO? pkt->icmp_cksum = in_cksum(...);
500 502
501 sendping_tail(sendping6, pkt, datalen + sizeof(struct icmp6_hdr)); 503 sendping_tail(sendping6, sizeof(struct icmp6_hdr));
502} 504}
503#endif 505#endif
504 506
diff --git a/networking/tc.c b/networking/tc.c
index 2e2473a70..9b3245546 100644
--- a/networking/tc.c
+++ b/networking/tc.c
@@ -43,17 +43,15 @@ struct globals {
43 __u32 filter_proto; 43 __u32 filter_proto;
44} FIX_ALIASING; 44} FIX_ALIASING;
45#define G (*(struct globals*)&bb_common_bufsiz1) 45#define G (*(struct globals*)&bb_common_bufsiz1)
46struct BUG_G_too_big {
47 char BUG_G_too_big[sizeof(G) <= COMMON_BUFSIZE ? 1 : -1];
48};
46#define filter_ifindex (G.filter_ifindex) 49#define filter_ifindex (G.filter_ifindex)
47#define filter_qdisc (G.filter_qdisc) 50#define filter_qdisc (G.filter_qdisc)
48#define filter_parent (G.filter_parent) 51#define filter_parent (G.filter_parent)
49#define filter_prio (G.filter_prio) 52#define filter_prio (G.filter_prio)
50#define filter_proto (G.filter_proto) 53#define filter_proto (G.filter_proto)
51 54#define INIT_G() do { } while (0)
52void BUG_tc_globals_too_big(void);
53#define INIT_G() do { \
54 if (sizeof(G) > COMMON_BUFSIZE) \
55 BUG_tc_globals_too_big(); \
56} while (0)
57 55
58/* Allocates a buffer containing the name of a class id. 56/* Allocates a buffer containing the name of a class id.
59 * The caller must free the returned memory. */ 57 * The caller must free the returned memory. */
diff --git a/networking/tftp.c b/networking/tftp.c
index fcd933f6a..35cf0dbd9 100644
--- a/networking/tftp.c
+++ b/networking/tftp.c
@@ -107,19 +107,19 @@ struct BUG_G_too_big {
107#if ENABLE_FEATURE_TFTP_PROGRESS_BAR 107#if ENABLE_FEATURE_TFTP_PROGRESS_BAR
108static void tftp_progress_update(void) 108static void tftp_progress_update(void)
109{ 109{
110 bb_progress_update(&G.pmt, G.file, 0, G.pos, G.size); 110 bb_progress_update(&G.pmt, 0, G.pos, G.size);
111} 111}
112static void tftp_progress_init(void) 112static void tftp_progress_init(void)
113{ 113{
114 bb_progress_init(&G.pmt); 114 bb_progress_init(&G.pmt, G.file);
115 tftp_progress_update(); 115 tftp_progress_update();
116} 116}
117static void tftp_progress_done(void) 117static void tftp_progress_done(void)
118{ 118{
119 if (G.pmt.inited) { 119 if (is_bb_progress_inited(&G.pmt)) {
120 tftp_progress_update(); 120 tftp_progress_update();
121 bb_putchar_stderr('\n'); 121 bb_putchar_stderr('\n');
122 G.pmt.inited = 0; 122 bb_progress_free(&G.pmt);
123 } 123 }
124} 124}
125#else 125#else
@@ -445,7 +445,7 @@ static int tftp_protocol(
445#if ENABLE_FEATURE_TFTP_PROGRESS_BAR 445#if ENABLE_FEATURE_TFTP_PROGRESS_BAR
446 if (ENABLE_TFTP && remote_file) /* tftp */ 446 if (ENABLE_TFTP && remote_file) /* tftp */
447 G.pos = (block_nr - 1) * (uoff_t)blksize; 447 G.pos = (block_nr - 1) * (uoff_t)blksize;
448 if (G.pmt.inited) 448 if (is_bb_progress_inited(&G.pmt))
449 tftp_progress_update(); 449 tftp_progress_update();
450#endif 450#endif
451 /* Was it final ACK? then exit */ 451 /* Was it final ACK? then exit */
diff --git a/networking/udhcp/Config.src b/networking/udhcp/Config.src
index dcd493f13..750a53a32 100644
--- a/networking/udhcp/Config.src
+++ b/networking/udhcp/Config.src
@@ -39,7 +39,21 @@ config FEATURE_UDHCPD_WRITE_LEASES_EARLY
39 If selected, udhcpd will write a new file with leases every 39 If selected, udhcpd will write a new file with leases every
40 time a new lease has been accepted, thus eliminating the need 40 time a new lease has been accepted, thus eliminating the need
41 to send SIGUSR1 for the initial writing or updating. Any timed 41 to send SIGUSR1 for the initial writing or updating. Any timed
42 rewriting remains undisturbed 42 rewriting remains undisturbed.
43
44config FEATURE_UDHCPD_BASE_IP_ON_MAC
45 bool "Select IP address based on client MAC"
46 default n
47 depends on UDHCPD
48 help
49 If selected, udhcpd will base its selection of IP address to offer
50 on the client's hardware address. Otherwise udhcpd uses the next
51 consecutive free address.
52
53 This reduces the frequency of IP address changes for clients
54 which let their lease expire, and makes consecutive DHCPOFFERS
55 for the same client to (almost always) contain the same
56 IP address.
43 57
44config DHCPD_LEASES_FILE 58config DHCPD_LEASES_FILE
45 string "Absolute path to lease file" 59 string "Absolute path to lease file"
@@ -72,7 +86,7 @@ config FEATURE_UDHCPC_ARPING
72 86
73config FEATURE_UDHCP_PORT 87config FEATURE_UDHCP_PORT
74 bool "Enable '-P port' option for udhcpd and udhcpc" 88 bool "Enable '-P port' option for udhcpd and udhcpc"
75 default y 89 default n
76 depends on UDHCPD || UDHCPC 90 depends on UDHCPD || UDHCPC
77 help 91 help
78 At the cost of ~300 bytes, enables -P port option. 92 At the cost of ~300 bytes, enables -P port option.
diff --git a/networking/udhcp/common.c b/networking/udhcp/common.c
index 311f79e7e..0a60261ab 100644
--- a/networking/udhcp/common.c
+++ b/networking/udhcp/common.c
@@ -375,7 +375,7 @@ static NOINLINE void attach_option(
375 new->data = xmalloc(length + OPT_DATA); 375 new->data = xmalloc(length + OPT_DATA);
376 new->data[OPT_CODE] = optflag->code; 376 new->data[OPT_CODE] = optflag->code;
377 new->data[OPT_LEN] = length; 377 new->data[OPT_LEN] = length;
378 memcpy(new->data + OPT_DATA, buffer, length); 378 memcpy(new->data + OPT_DATA, (allocated ? allocated : buffer), length);
379 379
380 curr = opt_list; 380 curr = opt_list;
381 while (*curr && (*curr)->data[OPT_CODE] < optflag->code) 381 while (*curr && (*curr)->data[OPT_CODE] < optflag->code)
diff --git a/networking/udhcp/dhcpc.c b/networking/udhcp/dhcpc.c
index 7e5ab61fd..d97a404fa 100644
--- a/networking/udhcp/dhcpc.c
+++ b/networking/udhcp/dhcpc.c
@@ -714,22 +714,25 @@ static int udhcp_raw_socket(int ifindex)
714 * 714 *
715 * TODO: make conditional? 715 * TODO: make conditional?
716 */ 716 */
717#define SERVER_AND_CLIENT_PORTS ((67 << 16) + 68)
718 static const struct sock_filter filter_instr[] = { 717 static const struct sock_filter filter_instr[] = {
719 /* check for udp */ 718 /* load 9th byte (protocol) */
720 BPF_STMT(BPF_LD|BPF_B|BPF_ABS, 9), 719 BPF_STMT(BPF_LD|BPF_B|BPF_ABS, 9),
721 BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, IPPROTO_UDP, 2, 0), /* L5, L1, is UDP? */ 720 /* jump to L1 if it is IPPROTO_UDP, else to L4 */
722 /* ugly check for arp on ethernet-like and IPv4 */ 721 BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, IPPROTO_UDP, 0, 6),
723 BPF_STMT(BPF_LD|BPF_W|BPF_ABS, 2), /* L1: */ 722 /* L1: load halfword from offset 6 (flags and frag offset) */
724 BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, 0x08000604, 3, 4), /* L3, L4 */ 723 BPF_STMT(BPF_LD|BPF_H|BPF_ABS, 6),
725 /* skip IP header */ 724 /* jump to L4 if any bits in frag offset field are set, else to L2 */
726 BPF_STMT(BPF_LDX|BPF_B|BPF_MSH, 0), /* L5: */ 725 BPF_JUMP(BPF_JMP|BPF_JSET|BPF_K, 0x1fff, 4, 0),
727 /* check udp source and destination ports */ 726 /* L2: skip IP header (load index reg with header len) */
728 BPF_STMT(BPF_LD|BPF_W|BPF_IND, 0), 727 BPF_STMT(BPF_LDX|BPF_B|BPF_MSH, 0),
729 BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, SERVER_AND_CLIENT_PORTS, 0, 1), /* L3, L4 */ 728 /* load udp destination port from halfword[header_len + 2] */
730 /* returns */ 729 BPF_STMT(BPF_LD|BPF_H|BPF_IND, 2),
731 BPF_STMT(BPF_RET|BPF_K, 0x0fffffff ), /* L3: pass */ 730 /* jump to L3 if udp dport is CLIENT_PORT, else to L4 */
732 BPF_STMT(BPF_RET|BPF_K, 0), /* L4: reject */ 731 BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, 68, 0, 1),
732 /* L3: accept packet */
733 BPF_STMT(BPF_RET|BPF_K, 0xffffffff),
734 /* L4: discard packet */
735 BPF_STMT(BPF_RET|BPF_K, 0),
733 }; 736 };
734 static const struct sock_fprog filter_prog = { 737 static const struct sock_fprog filter_prog = {
735 .len = sizeof(filter_instr) / sizeof(filter_instr[0]), 738 .len = sizeof(filter_instr) / sizeof(filter_instr[0]),
@@ -742,18 +745,19 @@ static int udhcp_raw_socket(int ifindex)
742 fd = xsocket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_IP)); 745 fd = xsocket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_IP));
743 log1("Got raw socket fd %d", fd); //log2? 746 log1("Got raw socket fd %d", fd); //log2?
744 747
745 if (SERVER_PORT == 67 && CLIENT_PORT == 68) { 748 sock.sll_family = AF_PACKET;
746 /* Use only if standard ports are in use */ 749 sock.sll_protocol = htons(ETH_P_IP);
750 sock.sll_ifindex = ifindex;
751 xbind(fd, (struct sockaddr *) &sock, sizeof(sock));
752
753 if (CLIENT_PORT == 68) {
754 /* Use only if standard port is in use */
747 /* Ignoring error (kernel may lack support for this) */ 755 /* Ignoring error (kernel may lack support for this) */
748 if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &filter_prog, 756 if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &filter_prog,
749 sizeof(filter_prog)) >= 0) 757 sizeof(filter_prog)) >= 0)
750 log1("Attached filter to raw socket fd %d", fd); // log? 758 log1("Attached filter to raw socket fd %d", fd); // log?
751 } 759 }
752 760
753 sock.sll_family = AF_PACKET;
754 sock.sll_protocol = htons(ETH_P_IP);
755 sock.sll_ifindex = ifindex;
756 xbind(fd, (struct sockaddr *) &sock, sizeof(sock));
757 log1("Created raw socket"); 761 log1("Created raw socket");
758 762
759 return fd; 763 return fd;
diff --git a/networking/udhcp/leases.c b/networking/udhcp/leases.c
index 7aeb37bae..c5b60b108 100644
--- a/networking/udhcp/leases.c
+++ b/networking/udhcp/leases.c
@@ -137,21 +137,42 @@ uint32_t FAST_FUNC find_free_or_expired_nip(const uint8_t *safe_mac)
137 uint32_t addr; 137 uint32_t addr;
138 struct dyn_lease *oldest_lease = NULL; 138 struct dyn_lease *oldest_lease = NULL;
139 139
140 addr = server_config.start_ip; /* addr is in host order here */ 140#if ENABLE_FEATURE_UDHCPD_BASE_IP_ON_MAC
141 for (; addr <= server_config.end_ip; addr++) { 141 uint32_t stop;
142 unsigned i, hash;
143
144 /* hash hwaddr: use the SDBM hashing algorithm. Seems to give good
145 * dispersal even with similarly-valued "strings".
146 */
147 hash = 0;
148 for (i = 0; i < 6; i++)
149 hash += safe_mac[i] + (hash << 6) + (hash << 16) - hash;
150
151 /* pick a seed based on hwaddr then iterate until we find a free address. */
152 addr = server_config.start_ip
153 + (hash % (1 + server_config.end_ip - server_config.start_ip));
154 stop = addr;
155#else
156 addr = server_config.start_ip;
157#define stop (server_config.end_ip + 1)
158#endif
159 do {
142 uint32_t nip; 160 uint32_t nip;
143 struct dyn_lease *lease; 161 struct dyn_lease *lease;
144 162
145 /* ie, 192.168.55.0 */ 163 /* ie, 192.168.55.0 */
146 if ((addr & 0xff) == 0) 164 if ((addr & 0xff) == 0)
147 continue; 165 goto next_addr;
148 /* ie, 192.168.55.255 */ 166 /* ie, 192.168.55.255 */
149 if ((addr & 0xff) == 0xff) 167 if ((addr & 0xff) == 0xff)
150 continue; 168 goto next_addr;
151 nip = htonl(addr); 169 nip = htonl(addr);
170 /* skip our own address */
171 if (nip == server_config.server_nip)
172 goto next_addr;
152 /* is this a static lease addr? */ 173 /* is this a static lease addr? */
153 if (is_nip_reserved(server_config.static_leases, nip)) 174 if (is_nip_reserved(server_config.static_leases, nip))
154 continue; 175 goto next_addr;
155 176
156 lease = find_lease_by_nip(nip); 177 lease = find_lease_by_nip(nip);
157 if (!lease) { 178 if (!lease) {
@@ -162,7 +183,14 @@ uint32_t FAST_FUNC find_free_or_expired_nip(const uint8_t *safe_mac)
162 if (!oldest_lease || lease->expires < oldest_lease->expires) 183 if (!oldest_lease || lease->expires < oldest_lease->expires)
163 oldest_lease = lease; 184 oldest_lease = lease;
164 } 185 }
165 } 186
187 next_addr:
188 addr++;
189#if ENABLE_FEATURE_UDHCPD_BASE_IP_ON_MAC
190 if (addr > server_config.end_ip)
191 addr = server_config.start_ip;
192#endif
193 } while (addr != stop);
166 194
167 if (oldest_lease 195 if (oldest_lease
168 && is_expired_lease(oldest_lease) 196 && is_expired_lease(oldest_lease)
diff --git a/networking/wget.c b/networking/wget.c
index 16594c9bf..bbc161be8 100644
--- a/networking/wget.c
+++ b/networking/wget.c
@@ -10,9 +10,12 @@
10 */ 10 */
11#include "libbb.h" 11#include "libbb.h"
12 12
13//#define log_io(...) bb_error_msg(__VA_ARGS__)
14#define log_io(...) ((void)0)
15
16
13struct host_info { 17struct host_info {
14 // May be used if we ever will want to free() all xstrdup()s... 18 char *allocated;
15 /* char *allocated; */
16 const char *path; 19 const char *path;
17 const char *user; 20 const char *user;
18 char *host; 21 char *host;
@@ -30,17 +33,31 @@ struct globals {
30 const char *curfile; /* Name of current file being transferred */ 33 const char *curfile; /* Name of current file being transferred */
31 bb_progress_t pmt; 34 bb_progress_t pmt;
32#endif 35#endif
36 char *dir_prefix;
37#if ENABLE_FEATURE_WGET_LONG_OPTIONS
38 char *post_data;
39 char *extra_headers;
40#endif
41 char *fname_out; /* where to direct output (-O) */
42 const char *proxy_flag; /* Use proxies if env vars are set */
43 const char *user_agent; /* "User-Agent" header field */
33#if ENABLE_FEATURE_WGET_TIMEOUT 44#if ENABLE_FEATURE_WGET_TIMEOUT
34 unsigned timeout_seconds; 45 unsigned timeout_seconds;
35#endif 46#endif
47 int output_fd;
48 int o_flags;
36 smallint chunked; /* chunked transfer encoding */ 49 smallint chunked; /* chunked transfer encoding */
37 smallint got_clen; /* got content-length: from server */ 50 smallint got_clen; /* got content-length: from server */
51 /* Local downloads do benefit from big buffer.
52 * With 512 byte buffer, it was measured to be
53 * an order of magnitude slower than with big one.
54 */
55 uint64_t just_to_align_next_member;
56 char wget_buf[CONFIG_FEATURE_COPYBUF_KB*1024];
38} FIX_ALIASING; 57} FIX_ALIASING;
39#define G (*(struct globals*)&bb_common_bufsiz1) 58#define G (*ptr_to_globals)
40struct BUG_G_too_big {
41 char BUG_G_too_big[sizeof(G) <= COMMON_BUFSIZE ? 1 : -1];
42};
43#define INIT_G() do { \ 59#define INIT_G() do { \
60 SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
44 IF_FEATURE_WGET_TIMEOUT(G.timeout_seconds = 900;) \ 61 IF_FEATURE_WGET_TIMEOUT(G.timeout_seconds = 900;) \
45} while (0) 62} while (0)
46 63
@@ -73,12 +90,16 @@ static void progress_meter(int flag)
73 return; 90 return;
74 91
75 if (flag == PROGRESS_START) 92 if (flag == PROGRESS_START)
76 bb_progress_init(&G.pmt); 93 bb_progress_init(&G.pmt, G.curfile);
77 94
78 bb_progress_update(&G.pmt, G.curfile, G.beg_range, G.transferred, 95 bb_progress_update(&G.pmt,
79 G.chunked ? 0 : G.beg_range + G.transferred + G.content_len); 96 G.beg_range,
97 G.transferred,
98 (G.chunked || !G.got_clen) ? 0 : G.beg_range + G.transferred + G.content_len
99 );
80 100
81 if (flag == PROGRESS_END) { 101 if (flag == PROGRESS_END) {
102 bb_progress_free(&G.pmt);
82 bb_putchar_stderr('\n'); 103 bb_putchar_stderr('\n');
83 G.transferred = 0; 104 G.transferred = 0;
84 } 105 }
@@ -124,48 +145,15 @@ static void strip_ipv6_scope_id(char *host)
124 overlapping_strcpy(scope, cp); 145 overlapping_strcpy(scope, cp);
125} 146}
126 147
127/* Read NMEMB bytes into PTR from STREAM. Returns the number of bytes read,
128 * and a short count if an eof or non-interrupt error is encountered. */
129static size_t safe_fread(void *ptr, size_t nmemb, FILE *stream)
130{
131 size_t ret;
132 char *p = (char*)ptr;
133
134 do {
135 clearerr(stream);
136 errno = 0;
137 ret = fread(p, 1, nmemb, stream);
138 p += ret;
139 nmemb -= ret;
140 } while (nmemb && ferror(stream) && errno == EINTR);
141
142 return p - (char*)ptr;
143}
144
145/* Read a line or SIZE-1 bytes into S, whichever is less, from STREAM.
146 * Returns S, or NULL if an eof or non-interrupt error is encountered. */
147static char *safe_fgets(char *s, int size, FILE *stream)
148{
149 char *ret;
150
151 do {
152 clearerr(stream);
153 errno = 0;
154 ret = fgets(s, size, stream);
155 } while (ret == NULL && ferror(stream) && errno == EINTR);
156
157 return ret;
158}
159
160#if ENABLE_FEATURE_WGET_AUTHENTICATION 148#if ENABLE_FEATURE_WGET_AUTHENTICATION
161/* Base64-encode character string. buf is assumed to be char buf[512]. */ 149/* Base64-encode character string. */
162static char *base64enc_512(char buf[512], const char *str) 150static char *base64enc(const char *str)
163{ 151{
164 unsigned len = strlen(str); 152 unsigned len = strlen(str);
165 if (len > 512/4*3 - 10) /* paranoia */ 153 if (len > sizeof(G.wget_buf)/4*3 - 10) /* paranoia */
166 len = 512/4*3 - 10; 154 len = sizeof(G.wget_buf)/4*3 - 10;
167 bb_uuencode(buf, str, len, bb_uuenc_tbl_base64); 155 bb_uuencode(G.wget_buf, str, len, bb_uuenc_tbl_base64);
168 return buf; 156 return G.wget_buf;
169} 157}
170#endif 158#endif
171 159
@@ -186,43 +174,58 @@ static FILE *open_socket(len_and_sockaddr *lsa)
186 /* hopefully it understands what ESPIPE means... */ 174 /* hopefully it understands what ESPIPE means... */
187 fp = fdopen(xconnect_stream(lsa), "r+"); 175 fp = fdopen(xconnect_stream(lsa), "r+");
188 if (fp == NULL) 176 if (fp == NULL)
189 bb_perror_msg_and_die("fdopen"); 177 bb_perror_msg_and_die(bb_msg_memory_exhausted);
190 178
191 return fp; 179 return fp;
192} 180}
193 181
194static int ftpcmd(const char *s1, const char *s2, FILE *fp, char *buf) 182/* Returns '\n' if it was seen, else '\0'. Trims at first '\r' or '\n' */
183static char fgets_and_trim(FILE *fp)
184{
185 char c;
186 char *buf_ptr;
187
188 if (fgets(G.wget_buf, sizeof(G.wget_buf) - 1, fp) == NULL)
189 bb_perror_msg_and_die("error getting response");
190
191 buf_ptr = strchrnul(G.wget_buf, '\n');
192 c = *buf_ptr;
193 *buf_ptr = '\0';
194 buf_ptr = strchrnul(G.wget_buf, '\r');
195 *buf_ptr = '\0';
196
197 log_io("< %s", G.wget_buf);
198
199 return c;
200}
201
202static int ftpcmd(const char *s1, const char *s2, FILE *fp)
195{ 203{
196 int result; 204 int result;
197 if (s1) { 205 if (s1) {
198 if (!s2) s2 = ""; 206 if (!s2)
207 s2 = "";
199 fprintf(fp, "%s%s\r\n", s1, s2); 208 fprintf(fp, "%s%s\r\n", s1, s2);
200 fflush(fp); 209 fflush(fp);
210 log_io("> %s%s", s1, s2);
201 } 211 }
202 212
203 do { 213 do {
204 char *buf_ptr; 214 fgets_and_trim(fp);
205 215 } while (!isdigit(G.wget_buf[0]) || G.wget_buf[3] != ' ');
206 if (fgets(buf, 510, fp) == NULL) {
207 bb_perror_msg_and_die("error getting response");
208 }
209 buf_ptr = strstr(buf, "\r\n");
210 if (buf_ptr) {
211 *buf_ptr = '\0';
212 }
213 } while (!isdigit(buf[0]) || buf[3] != ' ');
214 216
215 buf[3] = '\0'; 217 G.wget_buf[3] = '\0';
216 result = xatoi_positive(buf); 218 result = xatoi_positive(G.wget_buf);
217 buf[3] = ' '; 219 G.wget_buf[3] = ' ';
218 return result; 220 return result;
219} 221}
220 222
221static void parse_url(char *src_url, struct host_info *h) 223static void parse_url(const char *src_url, struct host_info *h)
222{ 224{
223 char *url, *p, *sp; 225 char *url, *p, *sp;
224 226
225 /* h->allocated = */ url = xstrdup(src_url); 227 free(h->allocated);
228 h->allocated = url = xstrdup(src_url);
226 229
227 if (strncmp(url, "http://", 7) == 0) { 230 if (strncmp(url, "http://", 7) == 0) {
228 h->port = bb_lookup_port("http", "tcp", 80); 231 h->port = bb_lookup_port("http", "tcp", 80);
@@ -278,7 +281,7 @@ static void parse_url(char *src_url, struct host_info *h)
278 sp = h->host; 281 sp = h->host;
279} 282}
280 283
281static char *gethdr(char *buf, size_t bufsiz, FILE *fp /*, int *istrunc*/) 284static char *gethdr(FILE *fp)
282{ 285{
283 char *s, *hdrval; 286 char *s, *hdrval;
284 int c; 287 int c;
@@ -286,43 +289,32 @@ static char *gethdr(char *buf, size_t bufsiz, FILE *fp /*, int *istrunc*/)
286 /* *istrunc = 0; */ 289 /* *istrunc = 0; */
287 290
288 /* retrieve header line */ 291 /* retrieve header line */
289 if (fgets(buf, bufsiz, fp) == NULL) 292 c = fgets_and_trim(fp);
290 return NULL;
291 293
292 /* see if we are at the end of the headers */ 294 /* end of the headers? */
293 for (s = buf; *s == '\r'; ++s) 295 if (G.wget_buf[0] == '\0')
294 continue;
295 if (*s == '\n')
296 return NULL; 296 return NULL;
297 297
298 /* convert the header name to lower case */ 298 /* convert the header name to lower case */
299 for (s = buf; isalnum(*s) || *s == '-' || *s == '.'; ++s) { 299 for (s = G.wget_buf; isalnum(*s) || *s == '-' || *s == '.'; ++s) {
300 /* tolower for "A-Z", no-op for "0-9a-z-." */ 300 /* tolower for "A-Z", no-op for "0-9a-z-." */
301 *s = (*s | 0x20); 301 *s |= 0x20;
302 } 302 }
303 303
304 /* verify we are at the end of the header name */ 304 /* verify we are at the end of the header name */
305 if (*s != ':') 305 if (*s != ':')
306 bb_error_msg_and_die("bad header line: %s", sanitize_string(buf)); 306 bb_error_msg_and_die("bad header line: %s", sanitize_string(G.wget_buf));
307 307
308 /* locate the start of the header value */ 308 /* locate the start of the header value */
309 *s++ = '\0'; 309 *s++ = '\0';
310 hdrval = skip_whitespace(s); 310 hdrval = skip_whitespace(s);
311 311
312 /* locate the end of header */ 312 if (c != '\n') {
313 while (*s && *s != '\r' && *s != '\n') 313 /* Rats! The buffer isn't big enough to hold the entire header value */
314 ++s; 314 while (c = getc(fp), c != EOF && c != '\n')
315 315 continue;
316 /* end of header found */
317 if (*s) {
318 *s = '\0';
319 return hdrval;
320 } 316 }
321 317
322 /* Rats! The buffer isn't big enough to hold the entire header value */
323 while (c = getc(fp), c != EOF && c != '\n')
324 continue;
325 /* *istrunc = 1; */
326 return hdrval; 318 return hdrval;
327} 319}
328 320
@@ -366,7 +358,6 @@ static char *URL_escape(const char *str)
366 358
367static FILE* prepare_ftp_session(FILE **dfpp, struct host_info *target, len_and_sockaddr *lsa) 359static FILE* prepare_ftp_session(FILE **dfpp, struct host_info *target, len_and_sockaddr *lsa)
368{ 360{
369 char buf[512];
370 FILE *sfp; 361 FILE *sfp;
371 char *str; 362 char *str;
372 int port; 363 int port;
@@ -375,8 +366,8 @@ static FILE* prepare_ftp_session(FILE **dfpp, struct host_info *target, len_and_
375 target->user = xstrdup("anonymous:busybox@"); 366 target->user = xstrdup("anonymous:busybox@");
376 367
377 sfp = open_socket(lsa); 368 sfp = open_socket(lsa);
378 if (ftpcmd(NULL, NULL, sfp, buf) != 220) 369 if (ftpcmd(NULL, NULL, sfp) != 220)
379 bb_error_msg_and_die("%s", sanitize_string(buf+4)); 370 bb_error_msg_and_die("%s", sanitize_string(G.wget_buf + 4));
380 371
381 /* 372 /*
382 * Splitting username:password pair, 373 * Splitting username:password pair,
@@ -385,24 +376,24 @@ static FILE* prepare_ftp_session(FILE **dfpp, struct host_info *target, len_and_
385 str = strchr(target->user, ':'); 376 str = strchr(target->user, ':');
386 if (str) 377 if (str)
387 *str++ = '\0'; 378 *str++ = '\0';
388 switch (ftpcmd("USER ", target->user, sfp, buf)) { 379 switch (ftpcmd("USER ", target->user, sfp)) {
389 case 230: 380 case 230:
390 break; 381 break;
391 case 331: 382 case 331:
392 if (ftpcmd("PASS ", str, sfp, buf) == 230) 383 if (ftpcmd("PASS ", str, sfp) == 230)
393 break; 384 break;
394 /* fall through (failed login) */ 385 /* fall through (failed login) */
395 default: 386 default:
396 bb_error_msg_and_die("ftp login: %s", sanitize_string(buf+4)); 387 bb_error_msg_and_die("ftp login: %s", sanitize_string(G.wget_buf + 4));
397 } 388 }
398 389
399 ftpcmd("TYPE I", NULL, sfp, buf); 390 ftpcmd("TYPE I", NULL, sfp);
400 391
401 /* 392 /*
402 * Querying file size 393 * Querying file size
403 */ 394 */
404 if (ftpcmd("SIZE ", target->path, sfp, buf) == 213) { 395 if (ftpcmd("SIZE ", target->path, sfp) == 213) {
405 G.content_len = BB_STRTOOFF(buf+4, NULL, 10); 396 G.content_len = BB_STRTOOFF(G.wget_buf + 4, NULL, 10);
406 if (G.content_len < 0 || errno) { 397 if (G.content_len < 0 || errno) {
407 bb_error_msg_and_die("SIZE value is garbage"); 398 bb_error_msg_and_die("SIZE value is garbage");
408 } 399 }
@@ -412,20 +403,20 @@ static FILE* prepare_ftp_session(FILE **dfpp, struct host_info *target, len_and_
412 /* 403 /*
413 * Entering passive mode 404 * Entering passive mode
414 */ 405 */
415 if (ftpcmd("PASV", NULL, sfp, buf) != 227) { 406 if (ftpcmd("PASV", NULL, sfp) != 227) {
416 pasv_error: 407 pasv_error:
417 bb_error_msg_and_die("bad response to %s: %s", "PASV", sanitize_string(buf)); 408 bb_error_msg_and_die("bad response to %s: %s", "PASV", sanitize_string(G.wget_buf));
418 } 409 }
419 // Response is "227 garbageN1,N2,N3,N4,P1,P2[)garbage] 410 // Response is "227 garbageN1,N2,N3,N4,P1,P2[)garbage]
420 // Server's IP is N1.N2.N3.N4 (we ignore it) 411 // Server's IP is N1.N2.N3.N4 (we ignore it)
421 // Server's port for data connection is P1*256+P2 412 // Server's port for data connection is P1*256+P2
422 str = strrchr(buf, ')'); 413 str = strrchr(G.wget_buf, ')');
423 if (str) str[0] = '\0'; 414 if (str) str[0] = '\0';
424 str = strrchr(buf, ','); 415 str = strrchr(G.wget_buf, ',');
425 if (!str) goto pasv_error; 416 if (!str) goto pasv_error;
426 port = xatou_range(str+1, 0, 255); 417 port = xatou_range(str+1, 0, 255);
427 *str = '\0'; 418 *str = '\0';
428 str = strrchr(buf, ','); 419 str = strrchr(G.wget_buf, ',');
429 if (!str) goto pasv_error; 420 if (!str) goto pasv_error;
430 port += xatou_range(str+1, 0, 255) * 256; 421 port += xatou_range(str+1, 0, 255) * 256;
431 set_nport(lsa, htons(port)); 422 set_nport(lsa, htons(port));
@@ -433,20 +424,19 @@ static FILE* prepare_ftp_session(FILE **dfpp, struct host_info *target, len_and_
433 *dfpp = open_socket(lsa); 424 *dfpp = open_socket(lsa);
434 425
435 if (G.beg_range) { 426 if (G.beg_range) {
436 sprintf(buf, "REST %"OFF_FMT"u", G.beg_range); 427 sprintf(G.wget_buf, "REST %"OFF_FMT"u", G.beg_range);
437 if (ftpcmd(buf, NULL, sfp, buf) == 350) 428 if (ftpcmd(G.wget_buf, NULL, sfp) == 350)
438 G.content_len -= G.beg_range; 429 G.content_len -= G.beg_range;
439 } 430 }
440 431
441 if (ftpcmd("RETR ", target->path, sfp, buf) > 150) 432 if (ftpcmd("RETR ", target->path, sfp) > 150)
442 bb_error_msg_and_die("bad response to %s: %s", "RETR", sanitize_string(buf)); 433 bb_error_msg_and_die("bad response to %s: %s", "RETR", sanitize_string(G.wget_buf));
443 434
444 return sfp; 435 return sfp;
445} 436}
446 437
447static void NOINLINE retrieve_file_data(FILE *dfp, int output_fd) 438static void NOINLINE retrieve_file_data(FILE *dfp)
448{ 439{
449 char buf[512];
450#if ENABLE_FEATURE_WGET_STATUSBAR || ENABLE_FEATURE_WGET_TIMEOUT 440#if ENABLE_FEATURE_WGET_STATUSBAR || ENABLE_FEATURE_WGET_TIMEOUT
451# if ENABLE_FEATURE_WGET_TIMEOUT 441# if ENABLE_FEATURE_WGET_TIMEOUT
452 unsigned second_cnt; 442 unsigned second_cnt;
@@ -455,7 +445,6 @@ static void NOINLINE retrieve_file_data(FILE *dfp, int output_fd)
455 445
456 polldata.fd = fileno(dfp); 446 polldata.fd = fileno(dfp);
457 polldata.events = POLLIN | POLLPRI; 447 polldata.events = POLLIN | POLLPRI;
458 ndelay_on(polldata.fd);
459#endif 448#endif
460 progress_meter(PROGRESS_START); 449 progress_meter(PROGRESS_START);
461 450
@@ -464,18 +453,30 @@ static void NOINLINE retrieve_file_data(FILE *dfp, int output_fd)
464 453
465 /* Loops only if chunked */ 454 /* Loops only if chunked */
466 while (1) { 455 while (1) {
456
457#if ENABLE_FEATURE_WGET_STATUSBAR || ENABLE_FEATURE_WGET_TIMEOUT
458 /* Must use nonblocking I/O, otherwise fread will loop
459 * and *block* until it reads full buffer,
460 * which messes up progress bar and/or timeout logic.
461 * Because of nonblocking I/O, we need to dance
462 * very carefully around EAGAIN. See explanation at
463 * clearerr() call.
464 */
465 ndelay_on(polldata.fd);
466#endif
467 while (1) { 467 while (1) {
468 int n; 468 int n;
469 unsigned rdsz; 469 unsigned rdsz;
470 470
471 rdsz = sizeof(buf); 471 rdsz = sizeof(G.wget_buf);
472 if (G.got_clen) { 472 if (G.got_clen) {
473 if (G.content_len < (off_t)sizeof(buf)) { 473 if (G.content_len < (off_t)sizeof(G.wget_buf)) {
474 if ((int)G.content_len <= 0) 474 if ((int)G.content_len <= 0)
475 break; 475 break;
476 rdsz = (unsigned)G.content_len; 476 rdsz = (unsigned)G.content_len;
477 } 477 }
478 } 478 }
479
479#if ENABLE_FEATURE_WGET_STATUSBAR || ENABLE_FEATURE_WGET_TIMEOUT 480#if ENABLE_FEATURE_WGET_STATUSBAR || ENABLE_FEATURE_WGET_TIMEOUT
480# if ENABLE_FEATURE_WGET_TIMEOUT 481# if ENABLE_FEATURE_WGET_TIMEOUT
481 second_cnt = G.timeout_seconds; 482 second_cnt = G.timeout_seconds;
@@ -486,150 +487,107 @@ static void NOINLINE retrieve_file_data(FILE *dfp, int output_fd)
486# if ENABLE_FEATURE_WGET_TIMEOUT 487# if ENABLE_FEATURE_WGET_TIMEOUT
487 if (second_cnt != 0 && --second_cnt == 0) { 488 if (second_cnt != 0 && --second_cnt == 0) {
488 progress_meter(PROGRESS_END); 489 progress_meter(PROGRESS_END);
489 bb_perror_msg_and_die("download timed out"); 490 bb_error_msg_and_die("download timed out");
490 } 491 }
491# endif 492# endif
492 /* Needed for "stalled" indicator */ 493 /* Needed for "stalled" indicator */
493 progress_meter(PROGRESS_BUMP); 494 progress_meter(PROGRESS_BUMP);
494 } 495 }
496
497 /* fread internally uses read loop, which in our case
498 * is usually exited when we get EAGAIN.
499 * In this case, libc sets error marker on the stream.
500 * Need to clear it before next fread to avoid possible
501 * rare false positive ferror below. Rare because usually
502 * fread gets more than zero bytes, and we don't fall
503 * into if (n <= 0) ...
504 */
505 clearerr(dfp);
506 errno = 0;
495#endif 507#endif
496 n = safe_fread(buf, rdsz, dfp); 508 n = fread(G.wget_buf, 1, rdsz, dfp);
509 /* man fread:
510 * If error occurs, or EOF is reached, the return value
511 * is a short item count (or zero).
512 * fread does not distinguish between EOF and error.
513 */
497 if (n <= 0) { 514 if (n <= 0) {
498 if (ferror(dfp)) { 515#if ENABLE_FEATURE_WGET_STATUSBAR || ENABLE_FEATURE_WGET_TIMEOUT
499 /* perror will not work: ferror doesn't set errno */ 516 if (errno == EAGAIN) /* poll lied, there is no data? */
500 bb_error_msg_and_die(bb_msg_read_error); 517 continue; /* yes */
501 } 518#endif
502 break; 519 if (ferror(dfp))
520 bb_perror_msg_and_die(bb_msg_read_error);
521 break; /* EOF, not error */
503 } 522 }
504 xwrite(output_fd, buf, n); 523
524 xwrite(G.output_fd, G.wget_buf, n);
525
505#if ENABLE_FEATURE_WGET_STATUSBAR 526#if ENABLE_FEATURE_WGET_STATUSBAR
506 G.transferred += n; 527 G.transferred += n;
507 progress_meter(PROGRESS_BUMP); 528 progress_meter(PROGRESS_BUMP);
508#endif 529#endif
509 if (G.got_clen) 530 if (G.got_clen) {
510 G.content_len -= n; 531 G.content_len -= n;
532 if (G.content_len == 0)
533 break;
534 }
511 } 535 }
512 536#if ENABLE_FEATURE_WGET_STATUSBAR || ENABLE_FEATURE_WGET_TIMEOUT
537 clearerr(dfp);
538 ndelay_off(polldata.fd); /* else fgets can get very unhappy */
539#endif
513 if (!G.chunked) 540 if (!G.chunked)
514 break; 541 break;
515 542
516 safe_fgets(buf, sizeof(buf), dfp); /* This is a newline */ 543 fgets_and_trim(dfp); /* Eat empty line */
517 get_clen: 544 get_clen:
518 safe_fgets(buf, sizeof(buf), dfp); 545 fgets_and_trim(dfp);
519 G.content_len = STRTOOFF(buf, NULL, 16); 546 G.content_len = STRTOOFF(G.wget_buf, NULL, 16);
520 /* FIXME: error check? */ 547 /* FIXME: error check? */
521 if (G.content_len == 0) 548 if (G.content_len == 0)
522 break; /* all done! */ 549 break; /* all done! */
523 G.got_clen = 1; 550 G.got_clen = 1;
524 } 551 }
525 552
553 /* Draw full bar and free its resources */
554 G.chunked = 0; /* makes it show 100% even for chunked download */
555 G.got_clen = 1; /* makes it show 100% even for download of (formerly) unknown size */
526 progress_meter(PROGRESS_END); 556 progress_meter(PROGRESS_END);
527} 557}
528 558
529int wget_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 559static void download_one_url(const char *url)
530int wget_main(int argc UNUSED_PARAM, char **argv)
531{ 560{
532 char buf[512]; 561 bool use_proxy; /* Use proxies if env vars are set */
533 struct host_info server, target;
534 len_and_sockaddr *lsa;
535 unsigned opt;
536 int redir_limit; 562 int redir_limit;
537 char *proxy = NULL; 563 len_and_sockaddr *lsa;
538 char *dir_prefix = NULL;
539#if ENABLE_FEATURE_WGET_LONG_OPTIONS
540 char *post_data;
541 char *extra_headers = NULL;
542 llist_t *headers_llist = NULL;
543#endif
544 FILE *sfp; /* socket to web/ftp server */ 564 FILE *sfp; /* socket to web/ftp server */
545 FILE *dfp; /* socket to ftp server (data) */ 565 FILE *dfp; /* socket to ftp server (data) */
546 char *fname_out; /* where to direct output (-O) */ 566 char *proxy = NULL;
547 int output_fd = -1; 567 char *fname_out_alloc;
548 bool use_proxy; /* Use proxies if env vars are set */ 568 struct host_info server;
549 const char *proxy_flag = "on"; /* Use proxies if env vars are set */ 569 struct host_info target;
550 const char *user_agent = "Wget";/* "User-Agent" header field */
551
552 static const char keywords[] ALIGN1 =
553 "content-length\0""transfer-encoding\0""chunked\0""location\0";
554 enum {
555 KEY_content_length = 1, KEY_transfer_encoding, KEY_chunked, KEY_location
556 };
557#if ENABLE_FEATURE_WGET_LONG_OPTIONS
558 static const char wget_longopts[] ALIGN1 =
559 /* name, has_arg, val */
560 "continue\0" No_argument "c"
561 "spider\0" No_argument "s"
562 "quiet\0" No_argument "q"
563 "output-document\0" Required_argument "O"
564 "directory-prefix\0" Required_argument "P"
565 "proxy\0" Required_argument "Y"
566 "user-agent\0" Required_argument "U"
567#if ENABLE_FEATURE_WGET_TIMEOUT
568 "timeout\0" Required_argument "T"
569#endif
570 /* Ignored: */
571 // "tries\0" Required_argument "t"
572 /* Ignored (we always use PASV): */
573 "passive-ftp\0" No_argument "\xff"
574 "header\0" Required_argument "\xfe"
575 "post-data\0" Required_argument "\xfd"
576 /* Ignored (we don't do ssl) */
577 "no-check-certificate\0" No_argument "\xfc"
578 ;
579#endif
580
581 INIT_G();
582 IF_WIN32_NET(init_winsock();)
583
584#if ENABLE_FEATURE_WGET_LONG_OPTIONS
585 applet_long_options = wget_longopts;
586#endif
587 /* server.allocated = target.allocated = NULL; */
588 opt_complementary = "-1" IF_FEATURE_WGET_TIMEOUT(":T+") IF_FEATURE_WGET_LONG_OPTIONS(":\xfe::");
589 opt = getopt32(argv, "csqO:P:Y:U:T:" /*ignored:*/ "t:",
590 &fname_out, &dir_prefix,
591 &proxy_flag, &user_agent,
592 IF_FEATURE_WGET_TIMEOUT(&G.timeout_seconds) IF_NOT_FEATURE_WGET_TIMEOUT(NULL),
593 NULL /* -t RETRIES */
594 IF_FEATURE_WGET_LONG_OPTIONS(, &headers_llist)
595 IF_FEATURE_WGET_LONG_OPTIONS(, &post_data)
596 );
597#if ENABLE_FEATURE_WGET_LONG_OPTIONS
598 if (headers_llist) {
599 int size = 1;
600 char *cp;
601 llist_t *ll = headers_llist;
602 while (ll) {
603 size += strlen(ll->data) + 2;
604 ll = ll->link;
605 }
606 extra_headers = cp = xmalloc(size);
607 while (headers_llist) {
608 cp += sprintf(cp, "%s\r\n", (char*)llist_pop(&headers_llist));
609 }
610 }
611#endif
612
613 /* TODO: compat issue: should handle "wget URL1 URL2..." */
614 570
571 server.allocated = NULL;
572 target.allocated = NULL;
573 server.user = NULL;
615 target.user = NULL; 574 target.user = NULL;
616 parse_url(argv[optind], &target); 575
576 parse_url(url, &target);
617 577
618 /* Use the proxy if necessary */ 578 /* Use the proxy if necessary */
619 use_proxy = (strcmp(proxy_flag, "off") != 0); 579 use_proxy = (strcmp(G.proxy_flag, "off") != 0);
620 if (use_proxy) { 580 if (use_proxy) {
621 proxy = getenv(target.is_ftp ? "ftp_proxy" : "http_proxy"); 581 proxy = getenv(target.is_ftp ? "ftp_proxy" : "http_proxy");
622 if (proxy && proxy[0]) { 582 use_proxy = (proxy && proxy[0]);
623 server.user = NULL; 583 if (use_proxy)
624 parse_url(proxy, &server); 584 parse_url(proxy, &server);
625 } else {
626 use_proxy = 0;
627 }
628 } 585 }
629 if (!use_proxy) { 586 if (!use_proxy) {
630 server.port = target.port; 587 server.port = target.port;
631 if (ENABLE_FEATURE_IPV6) { 588 if (ENABLE_FEATURE_IPV6) {
632 server.host = xstrdup(target.host); 589 //free(server.allocated); - can't be non-NULL
590 server.host = server.allocated = xstrdup(target.host);
633 } else { 591 } else {
634 server.host = target.host; 592 server.host = target.host;
635 } 593 }
@@ -638,50 +596,44 @@ int wget_main(int argc UNUSED_PARAM, char **argv)
638 if (ENABLE_FEATURE_IPV6) 596 if (ENABLE_FEATURE_IPV6)
639 strip_ipv6_scope_id(target.host); 597 strip_ipv6_scope_id(target.host);
640 598
641 /* Guess an output filename, if there was no -O FILE */ 599 /* If there was no -O FILE, guess output filename */
642 if (!(opt & WGET_OPT_OUTNAME)) { 600 fname_out_alloc = NULL;
643 fname_out = bb_get_last_path_component_nostrip(target.path); 601 if (!(option_mask32 & WGET_OPT_OUTNAME)) {
602 G.fname_out = bb_get_last_path_component_nostrip(target.path);
644 /* handle "wget http://kernel.org//" */ 603 /* handle "wget http://kernel.org//" */
645 if (fname_out[0] == '/' || !fname_out[0]) 604 if (G.fname_out[0] == '/' || !G.fname_out[0])
646 fname_out = (char*)"index.html"; 605 G.fname_out = (char*)"index.html";
647 /* -P DIR is considered only if there was no -O FILE */ 606 /* -P DIR is considered only if there was no -O FILE */
648 if (dir_prefix) 607 if (G.dir_prefix)
649 fname_out = concat_path_file(dir_prefix, fname_out); 608 G.fname_out = fname_out_alloc = concat_path_file(G.dir_prefix, G.fname_out);
650 } else {
651 if (LONE_DASH(fname_out)) {
652 /* -O - */
653 output_fd = 1;
654 opt &= ~WGET_OPT_CONTINUE;
655 }
656 } 609 }
657#if ENABLE_FEATURE_WGET_STATUSBAR 610#if ENABLE_FEATURE_WGET_STATUSBAR
658 G.curfile = bb_get_last_path_component_nostrip(fname_out); 611 G.curfile = bb_get_last_path_component_nostrip(G.fname_out);
659#endif 612#endif
660 613
661 /* Impossible?
662 if ((opt & WGET_OPT_CONTINUE) && !fname_out)
663 bb_error_msg_and_die("can't specify continue (-c) without a filename (-O)");
664 */
665
666 /* Determine where to start transfer */ 614 /* Determine where to start transfer */
667 if (opt & WGET_OPT_CONTINUE) { 615 G.beg_range = 0;
668 output_fd = open(fname_out, O_WRONLY); 616 if (option_mask32 & WGET_OPT_CONTINUE) {
669 if (output_fd >= 0) { 617 G.output_fd = open(G.fname_out, O_WRONLY);
670 G.beg_range = xlseek(output_fd, 0, SEEK_END); 618 if (G.output_fd >= 0) {
619 G.beg_range = xlseek(G.output_fd, 0, SEEK_END);
671 } 620 }
672 /* File doesn't exist. We do not create file here yet. 621 /* File doesn't exist. We do not create file here yet.
673 * We are not sure it exists on remove side */ 622 * We are not sure it exists on remote side */
674 } 623 }
675 624
676 redir_limit = 5; 625 redir_limit = 5;
677 resolve_lsa: 626 resolve_lsa:
678 lsa = xhost2sockaddr(server.host, server.port); 627 lsa = xhost2sockaddr(server.host, server.port);
679 if (!(opt & WGET_OPT_QUIET)) { 628 if (!(option_mask32 & WGET_OPT_QUIET)) {
680 char *s = xmalloc_sockaddr2dotted(&lsa->u.sa); 629 char *s = xmalloc_sockaddr2dotted(&lsa->u.sa);
681 fprintf(stderr, "Connecting to %s (%s)\n", server.host, s); 630 fprintf(stderr, "Connecting to %s (%s)\n", server.host, s);
682 free(s); 631 free(s);
683 } 632 }
684 establish_session: 633 establish_session:
634 /*G.content_len = 0; - redundant, got_clen = 0 is enough */
635 G.got_clen = 0;
636 G.chunked = 0;
685 if (use_proxy || !target.is_ftp) { 637 if (use_proxy || !target.is_ftp) {
686 /* 638 /*
687 * HTTP session 639 * HTTP session
@@ -689,6 +641,7 @@ int wget_main(int argc UNUSED_PARAM, char **argv)
689 char *str; 641 char *str;
690 int status; 642 int status;
691 643
644
692 /* Open socket to http server */ 645 /* Open socket to http server */
693 sfp = open_socket(lsa); 646 sfp = open_socket(lsa);
694 647
@@ -698,44 +651,52 @@ int wget_main(int argc UNUSED_PARAM, char **argv)
698 target.is_ftp ? "f" : "ht", target.host, 651 target.is_ftp ? "f" : "ht", target.host,
699 target.path); 652 target.path);
700 } else { 653 } else {
701 if (opt & WGET_OPT_POST_DATA) 654 if (option_mask32 & WGET_OPT_POST_DATA)
702 fprintf(sfp, "POST /%s HTTP/1.1\r\n", target.path); 655 fprintf(sfp, "POST /%s HTTP/1.1\r\n", target.path);
703 else 656 else
704 fprintf(sfp, "GET /%s HTTP/1.1\r\n", target.path); 657 fprintf(sfp, "GET /%s HTTP/1.1\r\n", target.path);
705 } 658 }
706 659
707 fprintf(sfp, "Host: %s\r\nUser-Agent: %s\r\n", 660 fprintf(sfp, "Host: %s\r\nUser-Agent: %s\r\n",
708 target.host, user_agent); 661 target.host, G.user_agent);
662
663 /* Ask server to close the connection as soon as we are done
664 * (IOW: we do not intend to send more requests)
665 */
666 fprintf(sfp, "Connection: close\r\n");
709 667
710#if ENABLE_FEATURE_WGET_AUTHENTICATION 668#if ENABLE_FEATURE_WGET_AUTHENTICATION
711 if (target.user) { 669 if (target.user) {
712 fprintf(sfp, "Proxy-Authorization: Basic %s\r\n"+6, 670 fprintf(sfp, "Proxy-Authorization: Basic %s\r\n"+6,
713 base64enc_512(buf, target.user)); 671 base64enc(target.user));
714 } 672 }
715 if (use_proxy && server.user) { 673 if (use_proxy && server.user) {
716 fprintf(sfp, "Proxy-Authorization: Basic %s\r\n", 674 fprintf(sfp, "Proxy-Authorization: Basic %s\r\n",
717 base64enc_512(buf, server.user)); 675 base64enc(server.user));
718 } 676 }
719#endif 677#endif
720 678
721 if (G.beg_range) 679 if (G.beg_range)
722 fprintf(sfp, "Range: bytes=%"OFF_FMT"u-\r\n", G.beg_range); 680 fprintf(sfp, "Range: bytes=%"OFF_FMT"u-\r\n", G.beg_range);
681
723#if ENABLE_FEATURE_WGET_LONG_OPTIONS 682#if ENABLE_FEATURE_WGET_LONG_OPTIONS
724 if (extra_headers) 683 if (G.extra_headers)
725 fputs(extra_headers, sfp); 684 fputs(G.extra_headers, sfp);
726 685
727 if (opt & WGET_OPT_POST_DATA) { 686 if (option_mask32 & WGET_OPT_POST_DATA) {
728 char *estr = URL_escape(post_data); 687 char *estr = URL_escape(G.post_data);
729 fprintf(sfp, "Content-Type: application/x-www-form-urlencoded\r\n"); 688 fprintf(sfp,
730 fprintf(sfp, "Content-Length: %u\r\n" "\r\n" "%s", 689 "Content-Type: application/x-www-form-urlencoded\r\n"
731 (int) strlen(estr), estr); 690 "Content-Length: %u\r\n"
732 /*fprintf(sfp, "Connection: Keep-Alive\r\n\r\n");*/ 691 "\r\n"
733 /*fprintf(sfp, "%s\r\n", estr);*/ 692 "%s",
693 (int) strlen(estr), estr
694 );
734 free(estr); 695 free(estr);
735 } else 696 } else
736#endif 697#endif
737 { /* If "Connection:" is needed, document why */ 698 {
738 fprintf(sfp, /* "Connection: close\r\n" */ "\r\n"); 699 fprintf(sfp, "\r\n");
739 } 700 }
740 701
741 fflush(sfp); 702 fflush(sfp);
@@ -744,10 +705,9 @@ int wget_main(int argc UNUSED_PARAM, char **argv)
744 * Retrieve HTTP response line and check for "200" status code. 705 * Retrieve HTTP response line and check for "200" status code.
745 */ 706 */
746 read_response: 707 read_response:
747 if (fgets(buf, sizeof(buf), sfp) == NULL) 708 fgets_and_trim(sfp);
748 bb_error_msg_and_die("no response from server");
749 709
750 str = buf; 710 str = G.wget_buf;
751 str = skip_non_whitespace(str); 711 str = skip_non_whitespace(str);
752 str = skip_whitespace(str); 712 str = skip_whitespace(str);
753 // FIXME: no error check 713 // FIXME: no error check
@@ -756,7 +716,7 @@ int wget_main(int argc UNUSED_PARAM, char **argv)
756 switch (status) { 716 switch (status) {
757 case 0: 717 case 0:
758 case 100: 718 case 100:
759 while (gethdr(buf, sizeof(buf), sfp /*, &n*/) != NULL) 719 while (gethdr(sfp) != NULL)
760 /* eat all remaining headers */; 720 /* eat all remaining headers */;
761 goto read_response; 721 goto read_response;
762 case 200: 722 case 200:
@@ -796,22 +756,29 @@ However, in real world it was observed that some web servers
796 break; 756 break;
797 /* fall through */ 757 /* fall through */
798 default: 758 default:
799 bb_error_msg_and_die("server returned error: %s", sanitize_string(buf)); 759 bb_error_msg_and_die("server returned error: %s", sanitize_string(G.wget_buf));
800 } 760 }
801 761
802 /* 762 /*
803 * Retrieve HTTP headers. 763 * Retrieve HTTP headers.
804 */ 764 */
805 while ((str = gethdr(buf, sizeof(buf), sfp /*, &n*/)) != NULL) { 765 while ((str = gethdr(sfp)) != NULL) {
806 /* gethdr converted "FOO:" string to lowercase */ 766 static const char keywords[] ALIGN1 =
767 "content-length\0""transfer-encoding\0""location\0";
768 enum {
769 KEY_content_length = 1, KEY_transfer_encoding, KEY_location
770 };
807 smalluint key; 771 smalluint key;
772
773 /* gethdr converted "FOO:" string to lowercase */
774
808 /* strip trailing whitespace */ 775 /* strip trailing whitespace */
809 char *s = strchrnul(str, '\0') - 1; 776 char *s = strchrnul(str, '\0') - 1;
810 while (s >= str && (*s == ' ' || *s == '\t')) { 777 while (s >= str && (*s == ' ' || *s == '\t')) {
811 *s = '\0'; 778 *s = '\0';
812 s--; 779 s--;
813 } 780 }
814 key = index_in_strings(keywords, buf) + 1; 781 key = index_in_strings(keywords, G.wget_buf) + 1;
815 if (key == KEY_content_length) { 782 if (key == KEY_content_length) {
816 G.content_len = BB_STRTOOFF(str, NULL, 10); 783 G.content_len = BB_STRTOOFF(str, NULL, 10);
817 if (G.content_len < 0 || errno) { 784 if (G.content_len < 0 || errno) {
@@ -821,23 +788,23 @@ However, in real world it was observed that some web servers
821 continue; 788 continue;
822 } 789 }
823 if (key == KEY_transfer_encoding) { 790 if (key == KEY_transfer_encoding) {
824 if (index_in_strings(keywords, str_tolower(str)) + 1 != KEY_chunked) 791 if (strcmp(str_tolower(str), "chunked") != 0)
825 bb_error_msg_and_die("transfer encoding '%s' is not supported", sanitize_string(str)); 792 bb_error_msg_and_die("transfer encoding '%s' is not supported", sanitize_string(str));
826 G.chunked = G.got_clen = 1; 793 G.chunked = 1;
827 } 794 }
828 if (key == KEY_location && status >= 300) { 795 if (key == KEY_location && status >= 300) {
829 if (--redir_limit == 0) 796 if (--redir_limit == 0)
830 bb_error_msg_and_die("too many redirections"); 797 bb_error_msg_and_die("too many redirections");
831 fclose(sfp); 798 fclose(sfp);
832 G.got_clen = 0; 799 if (str[0] == '/') {
833 G.chunked = 0; 800 free(target.allocated);
834 if (str[0] == '/') 801 target.path = target.allocated = xstrdup(str+1);
835 /* free(target.allocated); */
836 target.path = /* target.allocated = */ xstrdup(str+1);
837 /* lsa stays the same: it's on the same server */ 802 /* lsa stays the same: it's on the same server */
838 else { 803 } else {
839 parse_url(str, &target); 804 parse_url(str, &target);
840 if (!use_proxy) { 805 if (!use_proxy) {
806 free(server.allocated);
807 server.allocated = NULL;
841 server.host = target.host; 808 server.host = target.host;
842 /* strip_ipv6_scope_id(target.host); - no! */ 809 /* strip_ipv6_scope_id(target.host); - no! */
843 /* we assume remote never gives us IPv6 addr with scope id */ 810 /* we assume remote never gives us IPv6 addr with scope id */
@@ -862,30 +829,117 @@ However, in real world it was observed that some web servers
862 sfp = prepare_ftp_session(&dfp, &target, lsa); 829 sfp = prepare_ftp_session(&dfp, &target, lsa);
863 } 830 }
864 831
865 if (opt & WGET_OPT_SPIDER) { 832 free(lsa);
866 if (ENABLE_FEATURE_CLEAN_UP)
867 fclose(sfp);
868 return EXIT_SUCCESS;
869 }
870 833
871 if (output_fd < 0) { 834 if (!(option_mask32 & WGET_OPT_SPIDER)) {
872 int o_flags = O_WRONLY | O_CREAT | O_TRUNC | O_EXCL; 835 if (G.output_fd < 0)
873 /* compat with wget: -O FILE can overwrite */ 836 G.output_fd = xopen(G.fname_out, G.o_flags);
874 if (opt & WGET_OPT_OUTNAME) 837 retrieve_file_data(dfp);
875 o_flags = O_WRONLY | O_CREAT | O_TRUNC; 838 if (!(option_mask32 & WGET_OPT_OUTNAME)) {
876 output_fd = xopen(fname_out, o_flags); 839 xclose(G.output_fd);
840 G.output_fd = -1;
841 }
877 } 842 }
878 843
879 retrieve_file_data(dfp, output_fd);
880 xclose(output_fd);
881
882 if (dfp != sfp) { 844 if (dfp != sfp) {
883 /* It's ftp. Close it properly */ 845 /* It's ftp. Close data connection properly */
884 fclose(dfp); 846 fclose(dfp);
885 if (ftpcmd(NULL, NULL, sfp, buf) != 226) 847 if (ftpcmd(NULL, NULL, sfp) != 226)
886 bb_error_msg_and_die("ftp error: %s", sanitize_string(buf+4)); 848 bb_error_msg_and_die("ftp error: %s", sanitize_string(G.wget_buf + 4));
887 /* ftpcmd("QUIT", NULL, sfp, buf); - why bother? */ 849 /* ftpcmd("QUIT", NULL, sfp); - why bother? */
850 }
851 fclose(sfp);
852
853 free(server.allocated);
854 free(target.allocated);
855 free(fname_out_alloc);
856}
857
858int wget_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
859int wget_main(int argc UNUSED_PARAM, char **argv)
860{
861#if ENABLE_FEATURE_WGET_LONG_OPTIONS
862 static const char wget_longopts[] ALIGN1 =
863 /* name, has_arg, val */
864 "continue\0" No_argument "c"
865//FIXME: -s isn't --spider, it's --save-headers!
866 "spider\0" No_argument "s"
867 "quiet\0" No_argument "q"
868 "output-document\0" Required_argument "O"
869 "directory-prefix\0" Required_argument "P"
870 "proxy\0" Required_argument "Y"
871 "user-agent\0" Required_argument "U"
872#if ENABLE_FEATURE_WGET_TIMEOUT
873 "timeout\0" Required_argument "T"
874#endif
875 /* Ignored: */
876 // "tries\0" Required_argument "t"
877 /* Ignored (we always use PASV): */
878 "passive-ftp\0" No_argument "\xff"
879 "header\0" Required_argument "\xfe"
880 "post-data\0" Required_argument "\xfd"
881 /* Ignored (we don't do ssl) */
882 "no-check-certificate\0" No_argument "\xfc"
883 ;
884#endif
885
886#if ENABLE_FEATURE_WGET_LONG_OPTIONS
887 llist_t *headers_llist = NULL;
888#endif
889
890 INIT_G();
891 IF_WIN32_NET(init_winsock();)
892
893 IF_FEATURE_WGET_TIMEOUT(G.timeout_seconds = 900;)
894 G.proxy_flag = "on"; /* use proxies if env vars are set */
895 G.user_agent = "Wget"; /* "User-Agent" header field */
896
897#if ENABLE_FEATURE_WGET_LONG_OPTIONS
898 applet_long_options = wget_longopts;
899#endif
900 opt_complementary = "-1" IF_FEATURE_WGET_TIMEOUT(":T+") IF_FEATURE_WGET_LONG_OPTIONS(":\xfe::");
901 getopt32(argv, "csqO:P:Y:U:T:" /*ignored:*/ "t:",
902 &G.fname_out, &G.dir_prefix,
903 &G.proxy_flag, &G.user_agent,
904 IF_FEATURE_WGET_TIMEOUT(&G.timeout_seconds) IF_NOT_FEATURE_WGET_TIMEOUT(NULL),
905 NULL /* -t RETRIES */
906 IF_FEATURE_WGET_LONG_OPTIONS(, &headers_llist)
907 IF_FEATURE_WGET_LONG_OPTIONS(, &G.post_data)
908 );
909 argv += optind;
910
911#if ENABLE_FEATURE_WGET_LONG_OPTIONS
912 if (headers_llist) {
913 int size = 1;
914 char *cp;
915 llist_t *ll = headers_llist;
916 while (ll) {
917 size += strlen(ll->data) + 2;
918 ll = ll->link;
919 }
920 G.extra_headers = cp = xmalloc(size);
921 while (headers_llist) {
922 cp += sprintf(cp, "%s\r\n", (char*)llist_pop(&headers_llist));
923 }
888 } 924 }
925#endif
926
927 G.output_fd = -1;
928 G.o_flags = O_WRONLY | O_CREAT | O_TRUNC | O_EXCL;
929 if (G.fname_out) { /* -O FILE ? */
930 if (LONE_DASH(G.fname_out)) { /* -O - ? */
931 G.output_fd = 1;
932 option_mask32 &= ~WGET_OPT_CONTINUE;
933 }
934 /* compat with wget: -O FILE can overwrite */
935 G.o_flags = O_WRONLY | O_CREAT | O_TRUNC;
936 }
937
938 while (*argv)
939 download_one_url(*argv++);
940
941 if (G.output_fd >= 0)
942 xclose(G.output_fd);
889 943
890 return EXIT_SUCCESS; 944 return EXIT_SUCCESS;
891} 945}
diff --git a/procps/kill.c b/procps/kill.c
index 3da39f030..e6f27af50 100644
--- a/procps/kill.c
+++ b/procps/kill.c
@@ -208,9 +208,27 @@ int kill_main(int argc, char **argv)
208 208
209 /* Looks like they want to do a kill. Do that */ 209 /* Looks like they want to do a kill. Do that */
210 while (arg) { 210 while (arg) {
211 /* Support shell 'space' trick */ 211#if ENABLE_ASH || ENABLE_HUSH
212 if (arg[0] == ' ') 212 /*
213 arg++; 213 * We need to support shell's "hack formats" of
214 * " -PRGP_ID" (yes, with a leading space)
215 * and " PID1 PID2 PID3" (with degenerate case "")
216 */
217 while (*arg != '\0') {
218 char *end;
219 if (*arg == ' ')
220 arg++;
221 pid = bb_strtoi(arg, &end, 10);
222 if (errno && (errno != EINVAL || *end != ' ')) {
223 bb_error_msg("invalid number '%s'", arg);
224 errors++;
225 } else if (kill(pid, signo) != 0) {
226 bb_perror_msg("can't kill pid %d", (int)pid);
227 errors++;
228 }
229 arg = end; /* can only point to ' ' or '\0' now */
230 }
231#else
214 pid = bb_strtoi(arg, NULL, 10); 232 pid = bb_strtoi(arg, NULL, 10);
215 if (errno) { 233 if (errno) {
216 bb_error_msg("invalid number '%s'", arg); 234 bb_error_msg("invalid number '%s'", arg);
@@ -219,6 +237,7 @@ int kill_main(int argc, char **argv)
219 bb_perror_msg("can't kill pid %d", (int)pid); 237 bb_perror_msg("can't kill pid %d", (int)pid);
220 errors++; 238 errors++;
221 } 239 }
240#endif
222 arg = *++argv; 241 arg = *++argv;
223 } 242 }
224 return errors; 243 return errors;
diff --git a/procps/ps.c b/procps/ps.c
index 3f7437b38..c54618248 100644
--- a/procps/ps.c
+++ b/procps/ps.c
@@ -18,17 +18,36 @@ enum { MAX_WIDTH = 2*1024 };
18 18
19#include <sys/times.h> /* for times() */ 19#include <sys/times.h> /* for times() */
20#ifndef AT_CLKTCK 20#ifndef AT_CLKTCK
21#define AT_CLKTCK 17 21# define AT_CLKTCK 17
22#endif 22#endif
23 23
24 24/* TODO:
25 * http://pubs.opengroup.org/onlinepubs/9699919799/utilities/ps.html
26 * specifies (for XSI-conformant systems) following default columns
27 * (l and f mark columns shown with -l and -f respectively):
28 * F l Flags (octal and additive) associated with the process (??)
29 * S l The state of the process
30 * UID f,l The user ID; the login name is printed with -f
31 * PID The process ID
32 * PPID f,l The parent process
33 * C f,l Processor utilization
34 * PRI l The priority of the process; higher numbers mean lower priority
35 * NI l Nice value
36 * ADDR l The address of the process
37 * SZ l The size in blocks of the core image of the process
38 * WCHAN l The event for which the process is waiting or sleeping
39 * STIME f Starting time of the process
40 * TTY The controlling terminal for the process
41 * TIME The cumulative execution time for the process
42 * CMD The command name; the full command line is shown with -f
43 */
25#if ENABLE_SELINUX 44#if ENABLE_SELINUX
26#define SELINUX_O_PREFIX "label," 45# define SELINUX_O_PREFIX "label,"
27#define DEFAULT_O_STR (SELINUX_O_PREFIX "pid,user" IF_FEATURE_PS_TIME(",time") ",args") 46# define DEFAULT_O_STR (SELINUX_O_PREFIX "pid,user" IF_FEATURE_PS_TIME(",time") ",args")
28#elif ENABLE_PLATFORM_MINGW32 47#elif ENABLE_PLATFORM_MINGW32
29#define DEFAULT_O_STR ("pid,comm") 48# define DEFAULT_O_STR ("pid,comm")
30#else 49#else
31#define DEFAULT_O_STR ("pid,user" IF_FEATURE_PS_TIME(",time") ",args") 50# define DEFAULT_O_STR ("pid,user" IF_FEATURE_PS_TIME(",time") ",args")
32#endif 51#endif
33 52
34typedef struct { 53typedef struct {
@@ -70,7 +89,8 @@ static ptrdiff_t find_elf_note(ptrdiff_t findme)
70{ 89{
71 ptrdiff_t *ep = (ptrdiff_t *) environ; 90 ptrdiff_t *ep = (ptrdiff_t *) environ;
72 91
73 while (*ep++); 92 while (*ep++)
93 continue;
74 while (*ep) { 94 while (*ep) {
75 if (ep[0] == findme) { 95 if (ep[0] == findme) {
76 return ep[1]; 96 return ep[1];
@@ -186,6 +206,11 @@ static void func_comm(char *buf, int size, const procps_status_t *ps)
186 safe_strncpy(buf, ps->comm, size+1); 206 safe_strncpy(buf, ps->comm, size+1);
187} 207}
188 208
209static void func_stat(char *buf, int size, const procps_status_t *ps)
210{
211 safe_strncpy(buf, ps->state, size+1);
212}
213
189static void func_args(char *buf, int size, const procps_status_t *ps) 214static void func_args(char *buf, int size, const procps_status_t *ps)
190{ 215{
191 read_cmdline(buf, size+1, ps->pid, ps->comm); 216 read_cmdline(buf, size+1, ps->pid, ps->comm);
@@ -302,7 +327,7 @@ static void func_pcpu(char *buf, int size, const procps_status_t *ps)
302*/ 327*/
303 328
304static const ps_out_t out_spec[] = { 329static const ps_out_t out_spec[] = {
305// Mandated by POSIX: 330/* Mandated by http://pubs.opengroup.org/onlinepubs/9699919799/utilities/ps.html: */
306#if !ENABLE_PLATFORM_MINGW32 331#if !ENABLE_PLATFORM_MINGW32
307 { 8 , "user" ,"USER" ,func_user ,PSSCAN_UIDGID }, 332 { 8 , "user" ,"USER" ,func_user ,PSSCAN_UIDGID },
308 { 8 , "group" ,"GROUP" ,func_group ,PSSCAN_UIDGID }, 333 { 8 , "group" ,"GROUP" ,func_group ,PSSCAN_UIDGID },
@@ -331,7 +356,8 @@ static const ps_out_t out_spec[] = {
331#if !ENABLE_PLATFORM_MINGW32 356#if !ENABLE_PLATFORM_MINGW32
332 { 6 , "tty" ,"TT" ,func_tty ,PSSCAN_TTY }, 357 { 6 , "tty" ,"TT" ,func_tty ,PSSCAN_TTY },
333 { 4 , "vsz" ,"VSZ" ,func_vsz ,PSSCAN_VSZ }, 358 { 4 , "vsz" ,"VSZ" ,func_vsz ,PSSCAN_VSZ },
334// Not mandated by POSIX, but useful: 359/* Not mandated, but useful: */
360 { 4 , "stat" ,"STAT" ,func_stat ,PSSCAN_STAT },
335 { 4 , "rss" ,"RSS" ,func_rss ,PSSCAN_RSS }, 361 { 4 , "rss" ,"RSS" ,func_rss ,PSSCAN_RSS },
336#endif 362#endif
337#if ENABLE_SELINUX 363#if ENABLE_SELINUX
diff --git a/procps/pstree.c b/procps/pstree.c
index ddf5dba59..4cd8cb458 100644
--- a/procps/pstree.c
+++ b/procps/pstree.c
@@ -76,7 +76,7 @@ struct globals {
76}; 76};
77#define G (*ptr_to_globals) 77#define G (*ptr_to_globals)
78#define INIT_G() do { \ 78#define INIT_G() do { \
79 SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \ 79 SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
80} while (0) 80} while (0)
81 81
82 82
diff --git a/runit/runsv.c b/runit/runsv.c
index ebb031837..e76572daa 100644
--- a/runit/runsv.c
+++ b/runit/runsv.c
@@ -139,16 +139,6 @@ static void s_term(int sig_no UNUSED_PARAM)
139 write(selfpipe.wr, "", 1); /* XXX */ 139 write(selfpipe.wr, "", 1); /* XXX */
140} 140}
141 141
142/* libbb candidate */
143static char *bb_stpcpy(char *p, const char *to_add)
144{
145 while ((*p = *to_add) != '\0') {
146 p++;
147 to_add++;
148 }
149 return p;
150}
151
152static int open_trunc_or_warn(const char *name) 142static int open_trunc_or_warn(const char *name)
153{ 143{
154 /* Why O_NDELAY? */ 144 /* Why O_NDELAY? */
@@ -192,26 +182,26 @@ static void update_status(struct svdir *s)
192 char *p = stat_buf; 182 char *p = stat_buf;
193 switch (s->state) { 183 switch (s->state) {
194 case S_DOWN: 184 case S_DOWN:
195 p = bb_stpcpy(p, "down"); 185 p = stpcpy(p, "down");
196 break; 186 break;
197 case S_RUN: 187 case S_RUN:
198 p = bb_stpcpy(p, "run"); 188 p = stpcpy(p, "run");
199 break; 189 break;
200 case S_FINISH: 190 case S_FINISH:
201 p = bb_stpcpy(p, "finish"); 191 p = stpcpy(p, "finish");
202 break; 192 break;
203 } 193 }
204 if (s->ctrl & C_PAUSE) 194 if (s->ctrl & C_PAUSE)
205 p = bb_stpcpy(p, ", paused"); 195 p = stpcpy(p, ", paused");
206 if (s->ctrl & C_TERM) 196 if (s->ctrl & C_TERM)
207 p = bb_stpcpy(p, ", got TERM"); 197 p = stpcpy(p, ", got TERM");
208 if (s->state != S_DOWN) 198 if (s->state != S_DOWN)
209 switch (s->sd_want) { 199 switch (s->sd_want) {
210 case W_DOWN: 200 case W_DOWN:
211 p = bb_stpcpy(p, ", want down"); 201 p = stpcpy(p, ", want down");
212 break; 202 break;
213 case W_EXIT: 203 case W_EXIT:
214 p = bb_stpcpy(p, ", want exit"); 204 p = stpcpy(p, ", want exit");
215 break; 205 break;
216 } 206 }
217 *p++ = '\n'; 207 *p++ = '\n';
diff --git a/scripts/defconfig.tig b/scripts/defconfig.tig
index ed3058e54..52fddfbf6 100644
--- a/scripts/defconfig.tig
+++ b/scripts/defconfig.tig
@@ -1,7 +1,7 @@
1# 1#
2# Automatically generated make config: don't edit 2# Automatically generated make config: don't edit
3# Busybox version: 1.19.0.git 3# Busybox version: 1.19.0.git
4# Wed Mar 21 20:40:54 2012 4# Thu Mar 22 12:00:44 2012
5# 5#
6CONFIG_HAVE_DOT_CONFIG=y 6CONFIG_HAVE_DOT_CONFIG=y
7# CONFIG_PLATFORM_POSIX is not set 7# CONFIG_PLATFORM_POSIX is not set
@@ -111,6 +111,7 @@ CONFIG_FEATURE_TAB_COMPLETION=y
111CONFIG_FEATURE_NON_POSIX_CP=y 111CONFIG_FEATURE_NON_POSIX_CP=y
112# CONFIG_FEATURE_VERBOSE_CP_MESSAGE is not set 112# CONFIG_FEATURE_VERBOSE_CP_MESSAGE is not set
113CONFIG_FEATURE_COPYBUF_KB=4 113CONFIG_FEATURE_COPYBUF_KB=4
114CONFIG_FEATURE_SKIP_ROOTFS=y
114# CONFIG_MONOTONIC_SYSCALL is not set 115# CONFIG_MONOTONIC_SYSCALL is not set
115CONFIG_IOCTL_HEX2STR_ERROR=y 116CONFIG_IOCTL_HEX2STR_ERROR=y
116# CONFIG_FEATURE_HWIB is not set 117# CONFIG_FEATURE_HWIB is not set
@@ -221,7 +222,6 @@ CONFIG_FEATURE_FANCY_HEAD=y
221# CONFIG_ID is not set 222# CONFIG_ID is not set
222# CONFIG_INSTALL is not set 223# CONFIG_INSTALL is not set
223# CONFIG_FEATURE_INSTALL_LONG_OPTIONS is not set 224# CONFIG_FEATURE_INSTALL_LONG_OPTIONS is not set
224CONFIG_LENGTH=y
225# CONFIG_LN is not set 225# CONFIG_LN is not set
226# CONFIG_LOGNAME is not set 226# CONFIG_LOGNAME is not set
227CONFIG_LS=y 227CONFIG_LS=y
@@ -608,6 +608,9 @@ CONFIG_HD=y
608# CONFIG_NANDDUMP is not set 608# CONFIG_NANDDUMP is not set
609# CONFIG_UBIATTACH is not set 609# CONFIG_UBIATTACH is not set
610# CONFIG_UBIDETACH is not set 610# CONFIG_UBIDETACH is not set
611# CONFIG_UBIMKVOL is not set
612# CONFIG_UBIRMVOL is not set
613# CONFIG_UBIRSVOL is not set
611# CONFIG_ADJTIMEX is not set 614# CONFIG_ADJTIMEX is not set
612CONFIG_BBCONFIG=y 615CONFIG_BBCONFIG=y
613CONFIG_FEATURE_COMPRESS_BBCONFIG=y 616CONFIG_FEATURE_COMPRESS_BBCONFIG=y
@@ -806,6 +809,7 @@ CONFIG_IFUPDOWN_IFSTATE_PATH=""
806# CONFIG_DHCPRELAY is not set 809# CONFIG_DHCPRELAY is not set
807# CONFIG_DUMPLEASES is not set 810# CONFIG_DUMPLEASES is not set
808# CONFIG_FEATURE_UDHCPD_WRITE_LEASES_EARLY is not set 811# CONFIG_FEATURE_UDHCPD_WRITE_LEASES_EARLY is not set
812# CONFIG_FEATURE_UDHCPD_BASE_IP_ON_MAC is not set
809CONFIG_DHCPD_LEASES_FILE="" 813CONFIG_DHCPD_LEASES_FILE=""
810# CONFIG_UDHCPC is not set 814# CONFIG_UDHCPC is not set
811# CONFIG_FEATURE_UDHCPC_ARPING is not set 815# CONFIG_FEATURE_UDHCPC_ARPING is not set
@@ -915,6 +919,7 @@ CONFIG_SV_DEFAULT_SERVICE_DIR=""
915# 919#
916CONFIG_ASH=y 920CONFIG_ASH=y
917CONFIG_ASH_BASH_COMPAT=y 921CONFIG_ASH_BASH_COMPAT=y
922# CONFIG_ASH_IDLE_TIMEOUT is not set
918# CONFIG_ASH_JOB_CONTROL is not set 923# CONFIG_ASH_JOB_CONTROL is not set
919CONFIG_ASH_ALIAS=y 924CONFIG_ASH_ALIAS=y
920CONFIG_ASH_GETOPTS=y 925CONFIG_ASH_GETOPTS=y
diff --git a/scripts/kconfig/mconf.c b/scripts/kconfig/mconf.c
index 0c548bfc0..d292b46cc 100644
--- a/scripts/kconfig/mconf.c
+++ b/scripts/kconfig/mconf.c
@@ -256,7 +256,7 @@ search_help[] = N_(
256 " USB$ => find all CONFIG_ symbols ending with USB\n" 256 " USB$ => find all CONFIG_ symbols ending with USB\n"
257 "\n"); 257 "\n");
258 258
259static char buf[4096], *bufptr = buf; 259static char buf[4096*10], *bufptr = buf;
260static char input_buf[4096]; 260static char input_buf[4096];
261static const char filename[] = ".config"; 261static const char filename[] = ".config";
262static char *args[1024], **argptr = args; 262static char *args[1024], **argptr = args;
diff --git a/scripts/mkconfigs b/scripts/mkconfigs
index 47ac53330..7488d294e 100755
--- a/scripts/mkconfigs
+++ b/scripts/mkconfigs
@@ -42,7 +42,7 @@ echo "\
42 */ 42 */
43static const char bbconfig_config[] ALIGN1 =" 43static const char bbconfig_config[] ALIGN1 ="
44 44
45grep '^#\? \?CONFIG_' "$config" \ 45grep -e '^# CONFIG_' -e '^CONFIG_' "$config" \
46| sed -e 's/\"/\\\"/g' -e 's/^/"/' -e 's/$/\\n"/' 46| sed -e 's/\"/\\\"/g' -e 's/^/"/' -e 's/$/\\n"/'
47 47
48echo ";" 48echo ";"
diff --git a/shell/Config.src b/shell/Config.src
index c9c2439e7..e96c21620 100644
--- a/shell/Config.src
+++ b/shell/Config.src
@@ -123,9 +123,9 @@ config FEATURE_SH_NOFORK
123 default n 123 default n
124 depends on (HUSH || ASH) && FEATURE_PREFER_APPLETS 124 depends on (HUSH || ASH) && FEATURE_PREFER_APPLETS
125 help 125 help
126 This option causes busybox shells [currently only ash] 126 This option causes busybox shells to not execute typical
127 to not execute typical fork/exec/wait sequence, but call <applet>_main 127 fork/exec/wait sequence, but call <applet>_main directly,
128 directly, if possible. (Sometimes it is not possible: for example, 128 if possible. (Sometimes it is not possible: for example,
129 this is not possible in pipes). 129 this is not possible in pipes).
130 130
131 This will be done only for some applets (those which are marked 131 This will be done only for some applets (those which are marked
@@ -133,6 +133,7 @@ config FEATURE_SH_NOFORK
133 133
134 This may significantly speed up some shell scripts. 134 This may significantly speed up some shell scripts.
135 135
136 This feature is relatively new. Use with care. 136 This feature is relatively new. Use with care. Report bugs
137 to project mailing list.
137 138
138endmenu 139endmenu
diff --git a/shell/ash.c b/shell/ash.c
index 28f988698..fd9141661 100644
--- a/shell/ash.c
+++ b/shell/ash.c
@@ -111,6 +111,13 @@
111//config: help 111//config: help
112//config: Enable bash-compatible extensions. 112//config: Enable bash-compatible extensions.
113//config: 113//config:
114//config:config ASH_IDLE_TIMEOUT
115//config: bool "Idle timeout variable"
116//config: default n
117//config: depends on ASH
118//config: help
119//config: Enables bash-like auto-logout after $TMOUT seconds of idle time.
120//config:
114//config:config ASH_JOB_CONTROL 121//config:config ASH_JOB_CONTROL
115//config: bool "Job control" 122//config: bool "Job control"
116//config: default y 123//config: default y
@@ -119,7 +126,7 @@
119//config: Enable job control in the ash shell. 126//config: Enable job control in the ash shell.
120//config: 127//config:
121//config:config ASH_ALIAS 128//config:config ASH_ALIAS
122//config: bool "alias support" 129//config: bool "Alias support"
123//config: default y 130//config: default y
124//config: depends on ASH 131//config: depends on ASH
125//config: help 132//config: help
@@ -130,28 +137,28 @@
130//config: default y 137//config: default y
131//config: depends on ASH 138//config: depends on ASH
132//config: help 139//config: help
133//config: Enable getopts builtin in the ash shell. 140//config: Enable support for getopts builtin in ash.
134//config: 141//config:
135//config:config ASH_BUILTIN_ECHO 142//config:config ASH_BUILTIN_ECHO
136//config: bool "Builtin version of 'echo'" 143//config: bool "Builtin version of 'echo'"
137//config: default y 144//config: default y
138//config: depends on ASH 145//config: depends on ASH
139//config: help 146//config: help
140//config: Enable support for echo, builtin to ash. 147//config: Enable support for echo builtin in ash.
141//config: 148//config:
142//config:config ASH_BUILTIN_PRINTF 149//config:config ASH_BUILTIN_PRINTF
143//config: bool "Builtin version of 'printf'" 150//config: bool "Builtin version of 'printf'"
144//config: default y 151//config: default y
145//config: depends on ASH 152//config: depends on ASH
146//config: help 153//config: help
147//config: Enable support for printf, builtin to ash. 154//config: Enable support for printf builtin in ash.
148//config: 155//config:
149//config:config ASH_BUILTIN_TEST 156//config:config ASH_BUILTIN_TEST
150//config: bool "Builtin version of 'test'" 157//config: bool "Builtin version of 'test'"
151//config: default y 158//config: default y
152//config: depends on ASH 159//config: depends on ASH
153//config: help 160//config: help
154//config: Enable support for test, builtin to ash. 161//config: Enable support for test builtin in ash.
155//config: 162//config:
156//config:config ASH_CMDCMD 163//config:config ASH_CMDCMD
157//config: bool "'command' command to override shell builtins" 164//config: bool "'command' command to override shell builtins"
@@ -167,7 +174,7 @@
167//config: default n 174//config: default n
168//config: depends on ASH 175//config: depends on ASH
169//config: help 176//config: help
170//config: Enable "check for new mail" in the ash shell. 177//config: Enable "check for new mail" function in the ash shell.
171//config: 178//config:
172//config:config ASH_OPTIMIZE_FOR_SIZE 179//config:config ASH_OPTIMIZE_FOR_SIZE
173//config: bool "Optimize for size instead of speed" 180//config: bool "Optimize for size instead of speed"
@@ -449,6 +456,9 @@ static const char *var_end(const char *var)
449 456
450 457
451/* ============ Interrupts / exceptions */ 458/* ============ Interrupts / exceptions */
459
460static void exitshell(void) NORETURN;
461
452/* 462/*
453 * These macros allow the user to suspend the handling of interrupt signals 463 * These macros allow the user to suspend the handling of interrupt signals
454 * over a period of time. This is similar to SIGHOLD or to sigblock, but 464 * over a period of time. This is similar to SIGHOLD or to sigblock, but
@@ -1929,7 +1939,9 @@ change_lc_ctype(const char *value)
1929#endif 1939#endif
1930#if ENABLE_ASH_MAIL 1940#if ENABLE_ASH_MAIL
1931static void chkmail(void); 1941static void chkmail(void);
1932static void changemail(const char *) FAST_FUNC; 1942static void changemail(const char *var_value) FAST_FUNC;
1943#else
1944# define chkmail() ((void)0)
1933#endif 1945#endif
1934static void changepath(const char *) FAST_FUNC; 1946static void changepath(const char *) FAST_FUNC;
1935#if ENABLE_ASH_RANDOM_SUPPORT 1947#if ENABLE_ASH_RANDOM_SUPPORT
@@ -3937,18 +3949,51 @@ setjobctl(int on)
3937static int FAST_FUNC 3949static int FAST_FUNC
3938killcmd(int argc, char **argv) 3950killcmd(int argc, char **argv)
3939{ 3951{
3940 int i = 1;
3941 if (argv[1] && strcmp(argv[1], "-l") != 0) { 3952 if (argv[1] && strcmp(argv[1], "-l") != 0) {
3953 int i = 1;
3942 do { 3954 do {
3943 if (argv[i][0] == '%') { 3955 if (argv[i][0] == '%') {
3944 struct job *jp = getjob(argv[i], 0); 3956 /*
3945 unsigned pid = jp->ps[0].ps_pid; 3957 * "kill %N" - job kill
3946 /* Enough space for ' -NNN<nul>' */ 3958 * Converting to pgrp / pid kill
3947 argv[i] = alloca(sizeof(int)*3 + 3); 3959 */
3948 /* kill_main has matching code to expect 3960 struct job *jp;
3949 * leading space. Needed to not confuse 3961 char *dst;
3950 * negative pids with "kill -SIGNAL_NO" syntax */ 3962 int j, n;
3951 sprintf(argv[i], " -%u", pid); 3963
3964 jp = getjob(argv[i], 0);
3965 /*
3966 * In jobs started under job control, we signal
3967 * entire process group by kill -PGRP_ID.
3968 * This happens, f.e., in interactive shell.
3969 *
3970 * Otherwise, we signal each child via
3971 * kill PID1 PID2 PID3.
3972 * Testcases:
3973 * sh -c 'sleep 1|sleep 1 & kill %1'
3974 * sh -c 'true|sleep 2 & sleep 1; kill %1'
3975 * sh -c 'true|sleep 1 & sleep 2; kill %1'
3976 */
3977 n = jp->nprocs; /* can't be 0 (I hope) */
3978 if (jp->jobctl)
3979 n = 1;
3980 dst = alloca(n * sizeof(int)*4);
3981 argv[i] = dst;
3982 for (j = 0; j < n; j++) {
3983 struct procstat *ps = &jp->ps[j];
3984 /* Skip non-running and not-stopped members
3985 * (i.e. dead members) of the job
3986 */
3987 if (ps->ps_status != -1 && !WIFSTOPPED(ps->ps_status))
3988 continue;
3989 /*
3990 * kill_main has matching code to expect
3991 * leading space. Needed to not confuse
3992 * negative pids with "kill -SIGNAL_NO" syntax
3993 */
3994 dst += sprintf(dst, jp->jobctl ? " -%u" : " %u", (int)ps->ps_pid);
3995 }
3996 *dst = '\0';
3952 } 3997 }
3953 } while (argv[++i]); 3998 } while (argv[++i]);
3954 } 3999 }
@@ -4046,6 +4091,7 @@ sprint_status(char *s, int status, int sigonly)
4046#endif 4091#endif
4047 } 4092 }
4048 st &= 0x7f; 4093 st &= 0x7f;
4094//TODO: use bbox's get_signame? strsignal adds ~600 bytes to text+rodata
4049 col = fmtstr(s, 32, strsignal(st)); 4095 col = fmtstr(s, 32, strsignal(st));
4050 if (WCOREDUMP(status)) { 4096 if (WCOREDUMP(status)) {
4051 col += fmtstr(s + col, 16, " (core dumped)"); 4097 col += fmtstr(s + col, 16, " (core dumped)");
@@ -4462,8 +4508,9 @@ waitcmd(int argc UNUSED_PARAM, char **argv)
4462 break; 4508 break;
4463 job = job->prev_job; 4509 job = job->prev_job;
4464 } 4510 }
4465 } else 4511 } else {
4466 job = getjob(*argv, 0); 4512 job = getjob(*argv, 0);
4513 }
4467 /* loop until process terminated or stopped */ 4514 /* loop until process terminated or stopped */
4468 while (job->state == JOBRUNNING) 4515 while (job->state == JOBRUNNING)
4469 blocking_wait_with_raise_on_sig(); 4516 blocking_wait_with_raise_on_sig();
@@ -4959,7 +5006,7 @@ forkchild(struct job *jp, union node *n, int mode)
4959#if JOBS 5006#if JOBS
4960 /* do job control only in root shell */ 5007 /* do job control only in root shell */
4961 doing_jobctl = 0; 5008 doing_jobctl = 0;
4962 if (mode != FORK_NOJOB && jp->jobctl && !oldlvl) { 5009 if (mode != FORK_NOJOB && jp->jobctl && oldlvl == 0) {
4963 pid_t pgrp; 5010 pid_t pgrp;
4964 5011
4965 if (jp->nprocs == 0) 5012 if (jp->nprocs == 0)
@@ -4985,7 +5032,7 @@ forkchild(struct job *jp, union node *n, int mode)
4985 ash_msg_and_raise_error("can't open '%s'", bb_dev_null); 5032 ash_msg_and_raise_error("can't open '%s'", bb_dev_null);
4986 } 5033 }
4987 } 5034 }
4988 if (!oldlvl) { 5035 if (oldlvl == 0) {
4989 if (iflag) { /* why if iflag only? */ 5036 if (iflag) { /* why if iflag only? */
4990 setsignal(SIGINT); 5037 setsignal(SIGINT);
4991 setsignal(SIGTERM); 5038 setsignal(SIGTERM);
@@ -10065,10 +10112,21 @@ preadfd(void)
10065 if (!iflag || g_parsefile->pf_fd != STDIN_FILENO) 10112 if (!iflag || g_parsefile->pf_fd != STDIN_FILENO)
10066 nr = nonblock_safe_read(g_parsefile->pf_fd, buf, IBUFSIZ - 1); 10113 nr = nonblock_safe_read(g_parsefile->pf_fd, buf, IBUFSIZ - 1);
10067 else { 10114 else {
10068#if ENABLE_FEATURE_TAB_COMPLETION 10115 int timeout = -1;
10116# if ENABLE_ASH_IDLE_TIMEOUT
10117 if (iflag) {
10118 const char *tmout_var = lookupvar("TMOUT");
10119 if (tmout_var) {
10120 timeout = atoi(tmout_var) * 1000;
10121 if (timeout <= 0)
10122 timeout = -1;
10123 }
10124 }
10125# endif
10126# if ENABLE_FEATURE_TAB_COMPLETION
10069 line_input_state->path_lookup = pathval(); 10127 line_input_state->path_lookup = pathval();
10070#endif 10128# endif
10071 nr = read_line_input(cmdedit_prompt, buf, IBUFSIZ, line_input_state); 10129 nr = read_line_input(line_input_state, cmdedit_prompt, buf, IBUFSIZ, timeout);
10072 if (nr == 0) { 10130 if (nr == 0) {
10073 /* Ctrl+C pressed */ 10131 /* Ctrl+C pressed */
10074 if (trap[SIGINT]) { 10132 if (trap[SIGINT]) {
@@ -10079,17 +10137,24 @@ preadfd(void)
10079 } 10137 }
10080 goto retry; 10138 goto retry;
10081 } 10139 }
10082 if (nr < 0 && errno == 0) { 10140 if (nr < 0) {
10083 /* Ctrl+D pressed */ 10141 if (errno == 0) {
10084 nr = 0; 10142 /* Ctrl+D pressed */
10143 nr = 0;
10144 }
10145# if ENABLE_ASH_IDLE_TIMEOUT
10146 else if (errno == EAGAIN && timeout > 0) {
10147 printf("\007timed out waiting for input: auto-logout\n");
10148 exitshell();
10149 }
10150# endif
10085 } 10151 }
10086 } 10152 }
10087#else 10153#else
10088 nr = nonblock_safe_read(g_parsefile->pf_fd, buf, IBUFSIZ - 1); 10154 nr = nonblock_safe_read(g_parsefile->pf_fd, buf, IBUFSIZ - 1);
10089#endif 10155#endif
10090 10156
10091#if 0 10157#if 0 /* disabled: nonblock_safe_read() handles this problem */
10092/* nonblock_safe_read() handles this problem */
10093 if (nr < 0) { 10158 if (nr < 0) {
10094 if (parsefile->fd == 0 && errno == EWOULDBLOCK) { 10159 if (parsefile->fd == 0 && errno == EWOULDBLOCK) {
10095 int flags = fcntl(0, F_GETFL); 10160 int flags = fcntl(0, F_GETFL);
@@ -12574,9 +12639,7 @@ cmdloop(int top)
12574 inter = 0; 12639 inter = 0;
12575 if (iflag && top) { 12640 if (iflag && top) {
12576 inter++; 12641 inter++;
12577#if ENABLE_ASH_MAIL
12578 chkmail(); 12642 chkmail();
12579#endif
12580 } 12643 }
12581 n = parsecmd(inter); 12644 n = parsecmd(inter);
12582#if DEBUG 12645#if DEBUG
@@ -13346,7 +13409,6 @@ ulimitcmd(int argc UNUSED_PARAM, char **argv)
13346/* 13409/*
13347 * Called to exit the shell. 13410 * Called to exit the shell.
13348 */ 13411 */
13349static void exitshell(void) NORETURN;
13350static void 13412static void
13351exitshell(void) 13413exitshell(void)
13352{ 13414{
diff --git a/shell/ash_test/ash-misc/echo_write_error.right b/shell/ash_test/ash-misc/echo_write_error.right
new file mode 100644
index 000000000..3e91a13d3
--- /dev/null
+++ b/shell/ash_test/ash-misc/echo_write_error.right
@@ -0,0 +1,2 @@
1ash: write error: Broken pipe
2Ok: 1
diff --git a/shell/ash_test/ash-misc/echo_write_error.tests b/shell/ash_test/ash-misc/echo_write_error.tests
new file mode 100644
index 000000000..0a40c9ff7
--- /dev/null
+++ b/shell/ash_test/ash-misc/echo_write_error.tests
@@ -0,0 +1,7 @@
1trap "" PIPE
2
3{
4sleep 1
5echo Cant write this - get EPIPE
6echo Ok: $? >&2
7} | { true; }
diff --git a/shell/ash_test/ash-redir/redir.right b/shell/ash_test/ash-redir/redir.right
index 2a02d41ce..c1a6e729a 100644
--- a/shell/ash_test/ash-redir/redir.right
+++ b/shell/ash_test/ash-redir/redir.right
@@ -1 +1,2 @@
1ash: write error: Bad file descriptor
1TEST 2TEST
diff --git a/shell/ash_test/ash-signals/sigint1.right b/shell/ash_test/ash-signals/sigint1.right
new file mode 100644
index 000000000..a9094b056
--- /dev/null
+++ b/shell/ash_test/ash-signals/sigint1.right
@@ -0,0 +1 @@
Sending SIGINT to main shell PID
diff --git a/shell/ash_test/ash-signals/sigint1.tests b/shell/ash_test/ash-signals/sigint1.tests
new file mode 100755
index 000000000..3d483d32a
--- /dev/null
+++ b/shell/ash_test/ash-signals/sigint1.tests
@@ -0,0 +1,41 @@
1# What should happen if non-interactive shell gets SIGINT?
2
3(sleep 1; echo Sending SIGINT to main shell PID; exec kill -INT $$) &
4
5# We create a child which exits with 0 even on SIGINT
6# (The complex command is necessary only if SIGINT is generated by ^C,
7# in this testcase even bare "sleep 2" would do because
8# in the testcase we don't send SIGINT *to the child*...)
9$THIS_SH -c 'trap "exit 0" SIGINT; sleep 2'
10
11# In one second, we (main shell) get SIGINT here.
12# The question is whether we should, or should not, exit.
13
14# bash will not stop here. It will execute next command(s).
15
16# The rationale for this is described here:
17# http://www.cons.org/cracauer/sigint.html
18#
19# Basically, bash will not exit on SIGINT immediately if it waits
20# for a child. It will wait for the child to exit.
21# If child exits NOT by dying on SIGINT, then bash will not exit.
22#
23# The idea is that the following script:
24# | emacs file.txt
25# | more cmds
26# User may use ^C to interrupt editor's ops like search. But then
27# emacs exits normally. User expects that script doesn't stop.
28#
29# This is a nice idea, but detecting "did process really exit
30# with SIGINT?" is racy. Consider:
31# | bash -c 'while true; do /bin/true; done'
32# When ^C is pressed while bash waits for /bin/true to exit,
33# it may happen that /bin/true exits with exitcode 0 before
34# ^C is delivered to it as SIGINT. bash will see SIGINT, then
35# it will see that child exited with 0, and bash will NOT EXIT.
36
37# Therefore we do not implement bash behavior.
38# I'd say that emacs need to put itself into a separate pgrp
39# to isolate shell from getting stray SIGINTs from ^C.
40
41echo Next command after SIGINT was executed
diff --git a/shell/hush.c b/shell/hush.c
index 1709fd9d1..4d9e5f8c7 100644
--- a/shell/hush.c
+++ b/shell/hush.c
@@ -1418,6 +1418,7 @@ static void sigexit(int sig)
1418static void hush_exit(int exitcode) NORETURN; 1418static void hush_exit(int exitcode) NORETURN;
1419static void hush_exit(int exitcode) 1419static void hush_exit(int exitcode)
1420{ 1420{
1421 fflush_all();
1421 if (G.exiting <= 0 && G.traps && G.traps[0] && G.traps[0][0]) { 1422 if (G.exiting <= 0 && G.traps && G.traps[0] && G.traps[0][0]) {
1422 /* Prevent recursion: 1423 /* Prevent recursion:
1423 * trap "echo Hi; exit" EXIT; exit 1424 * trap "echo Hi; exit" EXIT; exit
@@ -1901,7 +1902,7 @@ static void get_user_input(struct in_str *i)
1901 G.flag_SIGINT = 0; 1902 G.flag_SIGINT = 0;
1902 /* buglet: SIGINT will not make new prompt to appear _at once_, 1903 /* buglet: SIGINT will not make new prompt to appear _at once_,
1903 * only after <Enter>. (^C will work) */ 1904 * only after <Enter>. (^C will work) */
1904 r = read_line_input(prompt_str, G.user_input_buf, CONFIG_FEATURE_EDITING_MAX_LEN-1, G.line_input_state); 1905 r = read_line_input(G.line_input_state, prompt_str, G.user_input_buf, CONFIG_FEATURE_EDITING_MAX_LEN-1, /*timeout*/ -1);
1905 /* catch *SIGINT* etc (^C is handled by read_line_input) */ 1906 /* catch *SIGINT* etc (^C is handled by read_line_input) */
1906 check_and_run_traps(0); 1907 check_and_run_traps(0);
1907 } while (r == 0 || G.flag_SIGINT); /* repeat if ^C or SIGINT */ 1908 } while (r == 0 || G.flag_SIGINT); /* repeat if ^C or SIGINT */
@@ -6105,10 +6106,13 @@ static void exec_builtin(char ***to_free,
6105 char **argv) 6106 char **argv)
6106{ 6107{
6107#if BB_MMU 6108#if BB_MMU
6108 int rcode = x->b_function(argv); 6109 int rcode;
6110 fflush_all();
6111 rcode = x->b_function(argv);
6109 fflush_all(); 6112 fflush_all();
6110 _exit(rcode); 6113 _exit(rcode);
6111#else 6114#else
6115 fflush_all();
6112 /* On NOMMU, we must never block! 6116 /* On NOMMU, we must never block!
6113 * Example: { sleep 99 | read line; } & echo Ok 6117 * Example: { sleep 99 | read line; } & echo Ok
6114 */ 6118 */
@@ -6500,13 +6504,15 @@ static int checkjobs(struct pipe *fg_pipe)
6500 fg_pipe->alive_cmds--; 6504 fg_pipe->alive_cmds--;
6501 ex = WEXITSTATUS(status); 6505 ex = WEXITSTATUS(status);
6502 /* bash prints killer signal's name for *last* 6506 /* bash prints killer signal's name for *last*
6503 * process in pipe (prints just newline for SIGINT). 6507 * process in pipe (prints just newline for SIGINT/SIGPIPE).
6504 * Mimic this. Example: "sleep 5" + (^\ or kill -QUIT) 6508 * Mimic this. Example: "sleep 5" + (^\ or kill -QUIT)
6505 */ 6509 */
6506 if (WIFSIGNALED(status)) { 6510 if (WIFSIGNALED(status)) {
6507 int sig = WTERMSIG(status); 6511 int sig = WTERMSIG(status);
6508 if (i == fg_pipe->num_cmds-1) 6512 if (i == fg_pipe->num_cmds-1)
6509 printf("%s\n", sig == SIGINT ? "" : get_signame(sig)); 6513 /* TODO: use strsignal() instead for bash compat? but that's bloat... */
6514 printf("%s\n", sig == SIGINT || sig == SIGPIPE ? "" : get_signame(sig));
6515 /* TODO: if (WCOREDUMP(status)) + " (core dumped)"; */
6510 /* TODO: MIPS has 128 sigs (1..128), what if sig==128 here? 6516 /* TODO: MIPS has 128 sigs (1..128), what if sig==128 here?
6511 * Maybe we need to use sig | 128? */ 6517 * Maybe we need to use sig | 128? */
6512 ex = sig + 128; 6518 ex = sig + 128;
@@ -6615,7 +6621,7 @@ static int checkjobs_and_fg_shell(struct pipe *fg_pipe)
6615 * cmd ; ... { list } ; ... 6621 * cmd ; ... { list } ; ...
6616 * cmd && ... { list } && ... 6622 * cmd && ... { list } && ...
6617 * cmd || ... { list } || ... 6623 * cmd || ... { list } || ...
6618 * If it is, then we can run cmd as a builtin, NOFORK [do we do this?], 6624 * If it is, then we can run cmd as a builtin, NOFORK,
6619 * or (if SH_STANDALONE) an applet, and we can run the { list } 6625 * or (if SH_STANDALONE) an applet, and we can run the { list }
6620 * with run_list. If it isn't one of these, we fork and exec cmd. 6626 * with run_list. If it isn't one of these, we fork and exec cmd.
6621 * 6627 *
@@ -6797,13 +6803,12 @@ static NOINLINE int run_pipe(struct pipe *pi)
6797 } 6803 }
6798 6804
6799 /* Expand the rest into (possibly) many strings each */ 6805 /* Expand the rest into (possibly) many strings each */
6800 if (0) {}
6801#if ENABLE_HUSH_BASH_COMPAT 6806#if ENABLE_HUSH_BASH_COMPAT
6802 else if (command->cmd_type == CMD_SINGLEWORD_NOGLOB) { 6807 if (command->cmd_type == CMD_SINGLEWORD_NOGLOB) {
6803 argv_expanded = expand_strvec_to_strvec_singleword_noglob(argv + command->assignment_cnt); 6808 argv_expanded = expand_strvec_to_strvec_singleword_noglob(argv + command->assignment_cnt);
6804 } 6809 } else
6805#endif 6810#endif
6806 else { 6811 {
6807 argv_expanded = expand_strvec_to_strvec(argv + command->assignment_cnt); 6812 argv_expanded = expand_strvec_to_strvec(argv + command->assignment_cnt);
6808 } 6813 }
6809 6814
@@ -6833,6 +6838,7 @@ static NOINLINE int run_pipe(struct pipe *pi)
6833 if (!funcp) { 6838 if (!funcp) {
6834 debug_printf_exec(": builtin '%s' '%s'...\n", 6839 debug_printf_exec(": builtin '%s' '%s'...\n",
6835 x->b_cmd, argv_expanded[1]); 6840 x->b_cmd, argv_expanded[1]);
6841 fflush_all();
6836 rcode = x->b_function(argv_expanded) & 0xff; 6842 rcode = x->b_function(argv_expanded) & 0xff;
6837 fflush_all(); 6843 fflush_all();
6838 } 6844 }
@@ -6865,7 +6871,7 @@ static NOINLINE int run_pipe(struct pipe *pi)
6865 return rcode; 6871 return rcode;
6866 } 6872 }
6867 6873
6868 if (ENABLE_FEATURE_SH_STANDALONE) { 6874 if (ENABLE_FEATURE_SH_NOFORK) {
6869 int n = find_applet_by_name(argv_expanded[0]); 6875 int n = find_applet_by_name(argv_expanded[0]);
6870 if (n >= 0 && APPLET_IS_NOFORK(n)) { 6876 if (n >= 0 && APPLET_IS_NOFORK(n)) {
6871 rcode = redirect_and_varexp_helper(&new_env, &old_vars, command, squirrel, argv_expanded); 6877 rcode = redirect_and_varexp_helper(&new_env, &old_vars, command, squirrel, argv_expanded);
@@ -7642,6 +7648,7 @@ int hush_main(int argc, char **argv)
7642 G.global_argc -= builtin_argc; /* skip [BARGV...] "" */ 7648 G.global_argc -= builtin_argc; /* skip [BARGV...] "" */
7643 G.global_argv += builtin_argc; 7649 G.global_argv += builtin_argc;
7644 G.global_argv[-1] = NULL; /* replace "" */ 7650 G.global_argv[-1] = NULL; /* replace "" */
7651 fflush_all();
7645 G.last_exitcode = x->b_function(argv + optind - 1); 7652 G.last_exitcode = x->b_function(argv + optind - 1);
7646 } 7653 }
7647 goto final_return; 7654 goto final_return;
diff --git a/shell/hush_test/hush-misc/echo_write_error.right b/shell/hush_test/hush-misc/echo_write_error.right
new file mode 100644
index 000000000..ddcad4363
--- /dev/null
+++ b/shell/hush_test/hush-misc/echo_write_error.right
@@ -0,0 +1,2 @@
1hush: write error: Broken pipe
2Ok: 1
diff --git a/shell/hush_test/hush-misc/echo_write_error.tests b/shell/hush_test/hush-misc/echo_write_error.tests
new file mode 100755
index 000000000..0a40c9ff7
--- /dev/null
+++ b/shell/hush_test/hush-misc/echo_write_error.tests
@@ -0,0 +1,7 @@
1trap "" PIPE
2
3{
4sleep 1
5echo Cant write this - get EPIPE
6echo Ok: $? >&2
7} | { true; }
diff --git a/shell/hush_test/hush-misc/sigint1.right b/shell/hush_test/hush-misc/sigint1.right
new file mode 100644
index 000000000..a9094b056
--- /dev/null
+++ b/shell/hush_test/hush-misc/sigint1.right
@@ -0,0 +1 @@
Sending SIGINT to main shell PID
diff --git a/shell/hush_test/hush-misc/sigint1.tests b/shell/hush_test/hush-misc/sigint1.tests
new file mode 100755
index 000000000..3d483d32a
--- /dev/null
+++ b/shell/hush_test/hush-misc/sigint1.tests
@@ -0,0 +1,41 @@
1# What should happen if non-interactive shell gets SIGINT?
2
3(sleep 1; echo Sending SIGINT to main shell PID; exec kill -INT $$) &
4
5# We create a child which exits with 0 even on SIGINT
6# (The complex command is necessary only if SIGINT is generated by ^C,
7# in this testcase even bare "sleep 2" would do because
8# in the testcase we don't send SIGINT *to the child*...)
9$THIS_SH -c 'trap "exit 0" SIGINT; sleep 2'
10
11# In one second, we (main shell) get SIGINT here.
12# The question is whether we should, or should not, exit.
13
14# bash will not stop here. It will execute next command(s).
15
16# The rationale for this is described here:
17# http://www.cons.org/cracauer/sigint.html
18#
19# Basically, bash will not exit on SIGINT immediately if it waits
20# for a child. It will wait for the child to exit.
21# If child exits NOT by dying on SIGINT, then bash will not exit.
22#
23# The idea is that the following script:
24# | emacs file.txt
25# | more cmds
26# User may use ^C to interrupt editor's ops like search. But then
27# emacs exits normally. User expects that script doesn't stop.
28#
29# This is a nice idea, but detecting "did process really exit
30# with SIGINT?" is racy. Consider:
31# | bash -c 'while true; do /bin/true; done'
32# When ^C is pressed while bash waits for /bin/true to exit,
33# it may happen that /bin/true exits with exitcode 0 before
34# ^C is delivered to it as SIGINT. bash will see SIGINT, then
35# it will see that child exited with 0, and bash will NOT EXIT.
36
37# Therefore we do not implement bash behavior.
38# I'd say that emacs need to put itself into a separate pgrp
39# to isolate shell from getting stray SIGINTs from ^C.
40
41echo Next command after SIGINT was executed
diff --git a/shell/shell_common.c b/shell/shell_common.c
index 2d5c0db0f..c7b5b6122 100644
--- a/shell/shell_common.c
+++ b/shell/shell_common.c
@@ -375,9 +375,9 @@ shell_builtin_ulimit(char **argv)
375#endif 375#endif
376 /* optarg = NULL; opterr = 0; optopt = 0; - do we need this?? */ 376 /* optarg = NULL; opterr = 0; optopt = 0; - do we need this?? */
377 377
378 argc = 1; 378 argc = 1;
379 while (argv[argc]) 379 while (argv[argc])
380 argc++; 380 argc++;
381 381
382 opts = 0; 382 opts = 0;
383 while (1) { 383 while (1) {
diff --git a/testsuite/echo/echo-prints-dash b/testsuite/echo/echo-prints-dash
new file mode 100644
index 000000000..ddcdbadf7
--- /dev/null
+++ b/testsuite/echo/echo-prints-dash
@@ -0,0 +1 @@
test "`busybox echo - | od -t x1 | head -n 1`" = "0000000 2d 0a"
diff --git a/testsuite/echo/echo-prints-non-opts b/testsuite/echo/echo-prints-non-opts
new file mode 100644
index 000000000..c7d1e201a
--- /dev/null
+++ b/testsuite/echo/echo-prints-non-opts
@@ -0,0 +1 @@
test "`busybox echo -neEZ | od -t x1 | head -n 1`" = "0000000 2d 6e 65 45 5a 0a"
diff --git a/testsuite/echo/echo-prints-slash_00041 b/testsuite/echo/echo-prints-slash_00041
new file mode 100644
index 000000000..9cffabde0
--- /dev/null
+++ b/testsuite/echo/echo-prints-slash_00041
@@ -0,0 +1,3 @@
1# FEATURE: CONFIG_FEATURE_FANCY_ECHO
2
3test "`busybox echo -ne '\00041z' | od -t x1 | head -n 1`" = "0000000 04 31 7a"
diff --git a/testsuite/echo/echo-prints-slash_0041 b/testsuite/echo/echo-prints-slash_0041
new file mode 100644
index 000000000..b07429dfd
--- /dev/null
+++ b/testsuite/echo/echo-prints-slash_0041
@@ -0,0 +1,3 @@
1# FEATURE: CONFIG_FEATURE_FANCY_ECHO
2
3test "`busybox echo -ne '\0041z' | od -t x1 | head -n 1`" = "0000000 21 7a"
diff --git a/testsuite/echo/echo-prints-slash_041 b/testsuite/echo/echo-prints-slash_041
new file mode 100644
index 000000000..1d70cec92
--- /dev/null
+++ b/testsuite/echo/echo-prints-slash_041
@@ -0,0 +1,3 @@
1# FEATURE: CONFIG_FEATURE_FANCY_ECHO
2
3test "`busybox echo -ne '\041z' | od -t x1 | head -n 1`" = "0000000 21 7a"
diff --git a/testsuite/echo/echo-prints-slash_41 b/testsuite/echo/echo-prints-slash_41
new file mode 100644
index 000000000..6d8999b4e
--- /dev/null
+++ b/testsuite/echo/echo-prints-slash_41
@@ -0,0 +1,3 @@
1# FEATURE: CONFIG_FEATURE_FANCY_ECHO
2
3test "`busybox echo -ne '\41z' | od -t x1 | head -n 1`" = "0000000 21 7a"
diff --git a/testsuite/tar.tests b/testsuite/tar.tests
index f2f4e9348..472064f7f 100755
--- a/testsuite/tar.tests
+++ b/testsuite/tar.tests
@@ -154,6 +154,20 @@ dr-xr-x--- input_dir
154SKIP= 154SKIP=
155} 155}
156 156
157# Had a bug where on extract autodetect first "switched off" -z
158# and then failed to recognize .tgz extension
159testing "tar extract tgz" "\
160dd count=1 bs=1M if=/dev/zero of=F0 2>/dev/null
161tar -czf F0.tgz F0
162rm F0
163tar -xzvf F0.tgz && echo Ok
164rm F0 || echo BAD
165" "\
166F0
167Ok
168" \
169"" ""
170
157 171
158cd .. && rm -rf tar.tempdir || exit 1 172cd .. && rm -rf tar.tempdir || exit 1
159 173
diff --git a/util-linux/blockdev.c b/util-linux/blockdev.c
index c2fcaee88..4aed02982 100644
--- a/util-linux/blockdev.c
+++ b/util-linux/blockdev.c
@@ -26,7 +26,8 @@
26//usage: "\n --getss Get sector size" 26//usage: "\n --getss Get sector size"
27//usage: "\n --getbsz Get block size" 27//usage: "\n --getbsz Get block size"
28//usage: "\n --setbsz BYTES Set block size" 28//usage: "\n --setbsz BYTES Set block size"
29//usage: "\n --getsize Get device size in 512-byte sectors" 29//usage: "\n --getsz Get device size in 512-byte sectors"
30/*//usage: "\n --getsize Get device size in sectors (deprecated)"*/
30//usage: "\n --getsize64 Get device size in bytes" 31//usage: "\n --getsize64 Get device size in bytes"
31//usage: "\n --flushbufs Flush buffers" 32//usage: "\n --flushbufs Flush buffers"
32//usage: "\n --rereadpt Reread partition table" 33//usage: "\n --rereadpt Reread partition table"
@@ -45,6 +46,7 @@ enum {
45 46
46 FL_USRARG = 4, /* argument is provided by user */ 47 FL_USRARG = 4, /* argument is provided by user */
47 FL_NORESULT = 8, 48 FL_NORESULT = 8,
49 FL_SCALE512 = 16,
48}; 50};
49 51
50struct bdc { 52struct bdc {
@@ -86,6 +88,11 @@ static const struct bdc bdcommands[] = {
86 .flags = ARG_INT + FL_NORESULT + FL_USRARG, 88 .flags = ARG_INT + FL_NORESULT + FL_USRARG,
87 .argval = 0, 89 .argval = 0,
88 },{ 90 },{
91 .ioc = BLKGETSIZE64,
92 .name = "getsz",
93 .flags = ARG_U64 + FL_SCALE512,
94 .argval = -1,
95 },{
89 .ioc = BLKGETSIZE, 96 .ioc = BLKGETSIZE,
90 .name = "getsize", 97 .name = "getsize",
91 .flags = ARG_ULONG, 98 .flags = ARG_ULONG,
@@ -123,7 +130,7 @@ static const struct bdc *find_cmd(const char *s)
123} 130}
124 131
125int blockdev_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 132int blockdev_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
126int blockdev_main(int argc, char **argv) 133int blockdev_main(int argc UNUSED_PARAM, char **argv)
127{ 134{
128 const struct bdc *bdcmd; 135 const struct bdc *bdcmd;
129 int fd; 136 int fd;
@@ -134,18 +141,20 @@ int blockdev_main(int argc, char **argv)
134 uint64_t u64; 141 uint64_t u64;
135 } ioctl_val_on_stack; 142 } ioctl_val_on_stack;
136 143
137 if ((unsigned)(argc - 3) > 1) /* must have 2 or 3 args */ 144 argv++;
145 if (!argv[0] || !argv[1]) /* must have at least 2 args */
138 bb_show_usage(); 146 bb_show_usage();
139 147
140 bdcmd = find_cmd(*++argv); 148 bdcmd = find_cmd(*argv);
141 149
142 u64 = (int)bdcmd->argval; 150 u64 = (int)bdcmd->argval;
143 if (bdcmd->flags & FL_USRARG) 151 if (bdcmd->flags & FL_USRARG)
144 u64 = xatoi_positive(*++argv); 152 u64 = xatoi_positive(*++argv);
145 153
146 if (!*++argv || argv[1]) 154 argv++;
155 if (!argv[0] || argv[1])
147 bb_show_usage(); 156 bb_show_usage();
148 fd = xopen(*argv, O_RDONLY); 157 fd = xopen(argv[0], O_RDONLY);
149 158
150 ioctl_val_on_stack.u64 = u64; 159 ioctl_val_on_stack.u64 = u64;
151#if BB_BIG_ENDIAN 160#if BB_BIG_ENDIAN
@@ -173,6 +182,9 @@ int blockdev_main(int argc, char **argv)
173 /* Fetch it into register(s) */ 182 /* Fetch it into register(s) */
174 u64 = ioctl_val_on_stack.u64; 183 u64 = ioctl_val_on_stack.u64;
175 184
185 if (bdcmd->flags & FL_SCALE512)
186 u64 >>= 9;
187
176 /* Zero- or one-extend the value if needed, then print */ 188 /* Zero- or one-extend the value if needed, then print */
177 switch (bdcmd->flags & (ARG_MASK+FL_NORESULT)) { 189 switch (bdcmd->flags & (ARG_MASK+FL_NORESULT)) {
178 case ARG_INT: 190 case ARG_INT:
diff --git a/util-linux/fdisk.c b/util-linux/fdisk.c
index 02785ab85..0b93c22cc 100644
--- a/util-linux/fdisk.c
+++ b/util-linux/fdisk.c
@@ -548,7 +548,7 @@ read_line(const char *prompt)
548{ 548{
549 int sz; 549 int sz;
550 550
551 sz = read_line_input(prompt, line_buffer, sizeof(line_buffer), NULL); 551 sz = read_line_input(NULL, prompt, line_buffer, sizeof(line_buffer), /*timeout*/ -1);
552 if (sz <= 0) 552 if (sz <= 0)
553 exit(EXIT_SUCCESS); /* Ctrl-D or Ctrl-C */ 553 exit(EXIT_SUCCESS); /* Ctrl-D or Ctrl-C */
554 554
diff --git a/util-linux/fdisk_osf.c b/util-linux/fdisk_osf.c
index 79be0cd0f..65e6bd7c4 100644
--- a/util-linux/fdisk_osf.c
+++ b/util-linux/fdisk_osf.c
@@ -43,19 +43,15 @@
43 43
44#define BSD_LINUX_BOOTDIR "/usr/ucb/mdec" 44#define BSD_LINUX_BOOTDIR "/usr/ucb/mdec"
45 45
46#if defined(i386) || defined(__sparc__) || defined(__arm__) \ 46#if defined(__alpha__) \
47 || defined(__m68k__) || defined(__mips__) || defined(__s390__) \ 47 || defined(__powerpc__) \
48 || defined(__s390__) || defined(__s390x__) \ 48 || defined(__ia64__) \
49 || defined(__sh__) || defined(__x86_64__) || defined(__avr32__) \
50 || defined(__nds32__)
51# define BSD_LABELSECTOR 1
52# define BSD_LABELOFFSET 0
53#elif defined(__alpha__) || defined(__powerpc__) || defined(__ia64__) \
54 || defined(__hppa__) 49 || defined(__hppa__)
55# define BSD_LABELSECTOR 0 50# define BSD_LABELSECTOR 0
56# define BSD_LABELOFFSET 64 51# define BSD_LABELOFFSET 64
57#else 52#else
58# error unknown architecture 53# define BSD_LABELSECTOR 1
54# define BSD_LABELOFFSET 0
59#endif 55#endif
60 56
61#define BSD_BBSIZE 8192 /* size of boot area, with label */ 57#define BSD_BBSIZE 8192 /* size of boot area, with label */
diff --git a/util-linux/flock.c b/util-linux/flock.c
index be3d552fa..77fe1f809 100644
--- a/util-linux/flock.c
+++ b/util-linux/flock.c
@@ -19,7 +19,7 @@ int flock_main(int argc UNUSED_PARAM, char **argv)
19 }; 19 };
20 20
21#if ENABLE_LONG_OPTS 21#if ENABLE_LONG_OPTS
22 static const char getopt_longopts[] ALIGN1 = 22 static const char getopt_longopts[] ALIGN1 =
23 "shared\0" No_argument "s" 23 "shared\0" No_argument "s"
24 "exclusive\0" No_argument "x" 24 "exclusive\0" No_argument "x"
25 "unlock\0" No_argument "u" 25 "unlock\0" No_argument "u"
diff --git a/util-linux/mount.c b/util-linux/mount.c
index 0f213bb30..0baa74c7c 100644
--- a/util-linux/mount.c
+++ b/util-linux/mount.c
@@ -241,14 +241,14 @@ static const char mount_option_str[] =
241 "union\0" 241 "union\0"
242 "bind\0" 242 "bind\0"
243 "move\0" 243 "move\0"
244 "shared\0" 244 "make-shared\0"
245 "slave\0" 245 "make-slave\0"
246 "private\0" 246 "make-private\0"
247 "unbindable\0" 247 "make-unbindable\0"
248 "rshared\0" 248 "make-rshared\0"
249 "rslave\0" 249 "make-rslave\0"
250 "rprivate\0" 250 "make-rprivate\0"
251 "runbindable\0" 251 "make-runbindable\0"
252 ) 252 )
253 253
254 // Always understood. 254 // Always understood.
@@ -1129,6 +1129,9 @@ static NOINLINE int nfsmount(struct mntent *mp, long vfsflags, char *filteropts)
1129 continue; 1129 continue;
1130 case 20: // "addr" - ignore 1130 case 20: // "addr" - ignore
1131 continue; 1131 continue;
1132 case -1: // unknown
1133 if (vfsflags & MS_REMOUNT)
1134 continue;
1132 } 1135 }
1133 1136
1134 val = xatoi_positive(opteq); 1137 val = xatoi_positive(opteq);
diff --git a/util-linux/volume_id/linux_swap.c b/util-linux/volume_id/linux_swap.c
index 62d588522..1ee534ab0 100644
--- a/util-linux/volume_id/linux_swap.c
+++ b/util-linux/volume_id/linux_swap.c
@@ -55,7 +55,6 @@ int FAST_FUNC volume_id_probe_linux_swap(struct volume_id *id /*,uint64_t off*/)
55 if (memcmp(buf, "SWAPSPACE2", 10) == 0 55 if (memcmp(buf, "SWAPSPACE2", 10) == 0
56 || memcmp(buf, "S1SUSPEND", 9) == 0 56 || memcmp(buf, "S1SUSPEND", 9) == 0
57 || memcmp(buf, "S2SUSPEND", 9) == 0 57 || memcmp(buf, "S2SUSPEND", 9) == 0
58 || memcmp(buf, "LINHIB0001", 10) == 0
59 || memcmp(buf, "ULSUSPEND", 9) == 0 58 || memcmp(buf, "ULSUSPEND", 9) == 0
60 ) { 59 ) {
61 sw = volume_id_get_buffer(id, off, sizeof(struct swap_header_v1_2)); 60 sw = volume_id_get_buffer(id, off, sizeof(struct swap_header_v1_2));
diff --git a/win32/sys/sysmacros.h b/win32/sys/sysmacros.h
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/win32/sys/sysmacros.h