diff options
author | Ron Yorston <rmy@pobox.com> | 2012-03-22 13:15:08 +0000 |
---|---|---|
committer | Ron Yorston <rmy@pobox.com> | 2012-03-22 13:15:08 +0000 |
commit | c0d4367d6b581eb5989c02815880cf0fa2851ae8 (patch) | |
tree | 868c266e627e2d7f65ba5a4d5f98a1c421453181 | |
parent | f6bad5ef766b0447158e3de2f55c35f1f6cecb58 (diff) | |
parent | da4441c44f6efccb6f7b7588404d9c6bfb7b6af8 (diff) | |
download | busybox-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
103 files changed, 1896 insertions, 1153 deletions
@@ -149,7 +149,6 @@ config FEATURE_INSTALLER | |||
149 | config INSTALL_NO_USR | 149 | config 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 | ||
496 | config NOMMU | 498 | config NOMMU |
@@ -724,7 +726,6 @@ config INSTALL_APPLET_SCRIPT_WRAPPERS | |||
724 | 726 | ||
725 | config INSTALL_APPLET_DONT | 727 | config 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 | |||
189 | CONFIG_ID=y | 189 | CONFIG_ID=y |
190 | CONFIG_INSTALL=y | 190 | CONFIG_INSTALL=y |
191 | CONFIG_FEATURE_INSTALL_LONG_OPTIONS=y | 191 | CONFIG_FEATURE_INSTALL_LONG_OPTIONS=y |
192 | CONFIG_LENGTH=y | ||
193 | CONFIG_LN=y | 192 | CONFIG_LN=y |
194 | CONFIG_LOGNAME=y | 193 | CONFIG_LOGNAME=y |
195 | CONFIG_LS=y | 194 | CONFIG_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 | |||
200 | CONFIG_ID=y | 200 | CONFIG_ID=y |
201 | CONFIG_INSTALL=y | 201 | CONFIG_INSTALL=y |
202 | CONFIG_FEATURE_INSTALL_LONG_OPTIONS=y | 202 | CONFIG_FEATURE_INSTALL_LONG_OPTIONS=y |
203 | CONFIG_LENGTH=y | ||
204 | CONFIG_LN=y | 203 | CONFIG_LN=y |
205 | CONFIG_LOGNAME=y | 204 | CONFIG_LOGNAME=y |
206 | CONFIG_LS=y | 205 | CONFIG_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} | |||
14 | APPLETS_H=${2:-include/applets.h} | 14 | APPLETS_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 }, |
33 | static struct usage_data { | 33 | static 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 @@ | |||
9 | char FAST_FUNC get_header_tar_gz(archive_handle_t *archive_handle) | 9 | char 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 | ||
298 | config 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 | ||
304 | config LN | 304 | config 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 | |||
39 | lib-$(CONFIG_HOSTID) += hostid.o | 39 | lib-$(CONFIG_HOSTID) += hostid.o |
40 | lib-$(CONFIG_ID) += id.o | 40 | lib-$(CONFIG_ID) += id.o |
41 | lib-$(CONFIG_INSTALL) += install.o | 41 | lib-$(CONFIG_INSTALL) += install.o |
42 | lib-$(CONFIG_LENGTH) += length.o | 42 | #lib-$(CONFIG_LENGTH) += length.o |
43 | lib-$(CONFIG_LN) += ln.o | 43 | lib-$(CONFIG_LN) += ln.o |
44 | lib-$(CONFIG_LOGNAME) += logname.o | 44 | lib-$(CONFIG_LOGNAME) += logname.o |
45 | lib-$(CONFIG_LS) += ls.o | 45 | lib-$(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 | |||
32 | int echo_main(int argc UNUSED_PARAM, char **argv) | 46 | int 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 | ||
46 | NOFORK applet should work correctly if another applet simply runs | 46 | NOFORK 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. |
48 | find, shells can do it). This poses much more serious limitations | 48 | xargs, find, shells do it (grep for "spawn_and_wait" and |
49 | on what applet can/cannot do: | 49 | "run_nofork_applet" to find more users). |
50 | |||
51 | This 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 | |||
77 | are probably not worth the effort. | 79 | are probably not worth the effort. |
78 | 80 | ||
79 | Any NOFORK applet is also a NOEXEC applet. | 81 | Any NOFORK applet is also a NOEXEC applet. |
82 | |||
83 | |||
84 | Relevant CONFIG options | ||
85 | |||
86 | FEATURE_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 | |||
92 | FEATURE_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 | |||
97 | FEATURE_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) | |||
155 | blk_t ext2fs_inode_data_blocks(ext2_filsys fs, | 155 | blk_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) |
126 | struct BUG_G_too_big { | 126 | struct 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;)) | |||
278 | struct globals { | 374 | struct 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 | ||
587 | static int FAST_FUNC fileAction(const char *fileName, | 688 | static 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) | |||
674 | static action*** parse_params(char **argv) | 773 | static 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 | |||
1140 | int find_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | 1162 | int find_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
1141 | int find_main(int argc UNUSED_PARAM, char **argv) | 1163 | int find_main(int argc UNUSED_PARAM, char **argv) |
1142 | { | 1164 | { |
1143 | static const char options[] ALIGN1 = | ||
1144 | "-follow\0" | ||
1145 | IF_FEATURE_FIND_XDEV( "-xdev\0" ) | ||
1146 | IF_FEATURE_FIND_MAXDEPTH("-mindepth\0""-maxdepth\0") | ||
1147 | ; | ||
1148 | enum { | ||
1149 | OPT_FOLLOW, | ||
1150 | IF_FEATURE_FIND_XDEV( OPT_XDEV ,) | ||
1151 | IF_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)) | |||
211 | IF_KILLALL5(APPLET_ODDNAME(killall5, kill, BB_DIR_USR_BIN, BB_SUID_DROP, killall5)) | 211 | IF_KILLALL5(APPLET_ODDNAME(killall5, kill, BB_DIR_USR_BIN, BB_SUID_DROP, killall5)) |
212 | IF_KLOGD(APPLET(klogd, BB_DIR_SBIN, BB_SUID_DROP)) | 212 | IF_KLOGD(APPLET(klogd, BB_DIR_SBIN, BB_SUID_DROP)) |
213 | IF_LAST(APPLET(last, BB_DIR_USR_BIN, BB_SUID_DROP)) | 213 | IF_LAST(APPLET(last, BB_DIR_USR_BIN, BB_SUID_DROP)) |
214 | IF_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)) |
215 | IF_LESS(APPLET(less, BB_DIR_USR_BIN, BB_SUID_DROP)) | 215 | IF_LESS(APPLET(less, BB_DIR_USR_BIN, BB_SUID_DROP)) |
216 | IF_SETARCH(APPLET_ODDNAME(linux32, setarch, BB_DIR_BIN, BB_SUID_DROP, linux32)) | 216 | IF_SETARCH(APPLET_ODDNAME(linux32, setarch, BB_DIR_BIN, BB_SUID_DROP, linux32)) |
217 | IF_SETARCH(APPLET_ODDNAME(linux64, setarch, BB_DIR_BIN, BB_SUID_DROP, linux64)) | 217 | IF_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 | |||
878 | int execable_file(const char *name) FAST_FUNC; | 881 | int execable_file(const char *name) FAST_FUNC; |
879 | char *find_execable(const char *filename, char **PATHp) FAST_FUNC; | 882 | char *find_execable(const char *filename, char **PATHp) FAST_FUNC; |
880 | int exists_execable(const char *filename) FAST_FUNC; | 883 | int 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 |
886 | int bb_execvp(const char *file, char *const argv[]) FAST_FUNC; | 889 | int 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 |
895 | int BB_EXECVP_or_die(char **argv) NORETURN FAST_FUNC; | 900 | int BB_EXECVP_or_die(char **argv) NORETURN FAST_FUNC; |
896 | 901 | ||
@@ -924,19 +929,8 @@ pid_t wait_any_nohang(int *wstat) FAST_FUNC; | |||
924 | int wait4pid(pid_t pid) FAST_FUNC; | 929 | int 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: */ |
926 | int spawn_and_wait(char **argv) FAST_FUNC; | 931 | int spawn_and_wait(char **argv) FAST_FUNC; |
927 | struct 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 | }; | ||
935 | void save_nofork_data(struct nofork_save_area *save) FAST_FUNC; | ||
936 | void 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 */ |
938 | int run_nofork_applet(int applet_no, char **argv) FAST_FUNC; | 933 | int run_nofork_applet(int applet_no, char **argv) FAST_FUNC; |
939 | int 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 | }; |
1418 | line_input_t *new_line_input_t(int flags) FAST_FUNC; | 1412 | line_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 | */ |
1426 | int read_line_input(const char* prompt, char* command, int maxsize, line_input_t *state) FAST_FUNC; | 1421 | int 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 |
1429 | int read_line_input(const char* prompt, char* command, int maxsize) FAST_FUNC; | 1424 | int 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, | |||
1608 | int print_flags(const masks_labels_t *ml, int flags) FAST_FUNC; | 1603 | int print_flags(const masks_labels_t *ml, int flags) FAST_FUNC; |
1609 | 1604 | ||
1610 | typedef struct bb_progress_t { | 1605 | typedef 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 | ||
1617 | void bb_progress_init(bb_progress_t *p) FAST_FUNC; | 1613 | #define is_bb_progress_inited(p) ((p)->curfile != NULL) |
1618 | void 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) | ||
1618 | void bb_progress_init(bb_progress_t *p, const char *curfile) FAST_FUNC; | ||
1619 | void 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 | ||
1622 | extern const char *applet_name; | 1625 | extern 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; | |||
444 | typedef void (*sighandler_t)(int); | 450 | typedef void (*sighandler_t)(int); |
445 | #endif | 451 | #endif |
446 | 452 | ||
453 | #ifndef HAVE_STPCPY | ||
454 | extern char *stpcpy(char *p, const char *to_add) FAST_FUNC; | ||
455 | #endif | ||
456 | |||
447 | #ifndef HAVE_STRCASESTR | 457 | #ifndef HAVE_STRCASESTR |
448 | extern char *strcasestr(const char *s, const char *pattern) FAST_FUNC; | 458 | extern 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 | ||
164 | config 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 | |||
164 | config MONOTONIC_SYSCALL | 183 | config 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 | */ | 105 | int FAST_FUNC BB_EXECVP(const char *file, char *const argv[]) |
106 | int 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 | ||
1820 | static int lineedit_read_key(char *read_key_buffer) | 1820 | static 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 | */ |
1928 | int FAST_FUNC read_line_input(const char *prompt, char *command, int maxsize, line_input_t *st) | 1927 | int 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 @@ | |||
22 | const char bb_banner[] ALIGN1 = BANNER; | 22 | const char bb_banner[] ALIGN1 = BANNER; |
23 | 23 | ||
24 | 24 | ||
25 | const char bb_msg_memory_exhausted[] ALIGN1 = "memory exhausted"; | 25 | const char bb_msg_memory_exhausted[] ALIGN1 = "out of memory"; |
26 | const char bb_msg_invalid_date[] ALIGN1 = "invalid date '%s'"; | 26 | const char bb_msg_invalid_date[] ALIGN1 = "invalid date '%s'"; |
27 | const char bb_msg_unknown[] ALIGN1 = "(unknown)"; | 27 | const char bb_msg_unknown[] ALIGN1 = "(unknown)"; |
28 | const char bb_msg_can_not_create_raw_socket[] ALIGN1 = "can't create raw socket"; | 28 | const 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 | ||
152 | char* 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 | ||
55 | void FAST_FUNC bb_progress_init(bb_progress_t *p) | 55 | void 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 | */ | ||
63 | void FAST_FUNC bb_progress_update(bb_progress_t *p, | 76 | void 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 |
74 | void FAST_FUNC save_nofork_data(struct nofork_save_area *save) | 75 | struct 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 | }; | ||
82 | static 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 | 90 | static void restore_nofork_data(struct nofork_save_area *save) | |
84 | void 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 | ||
93 | int FAST_FUNC run_nofork_applet_prime(struct nofork_save_area *old, int applet_no, char **argv) | 99 | int 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 | |||
171 | int 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 | ||
181 | int FAST_FUNC spawn_and_wait(char **argv) | 172 | int 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 | ||
186 | config LOGIN | 198 | config 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 | ||
227 | static 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 | */ |
234 | static void termios_init(int speed) | 240 | static 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 | ||
275 | static void termios_final(void) | 312 | static 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 | |||
583 | config RUNLEVEL | 583 | config 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 | |||
658 | config WALL | 659 | config 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 | ||
50 | int ubi_attach_detach_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | 112 | int ubi_tools_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
51 | int ubi_attach_detach_main(int argc UNUSED_PARAM, char **argv) | 113 | int 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 | ||
25 | enum { | 25 | enum { |
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 | ||
31 | struct modinfo_env { | 31 | struct modinfo_env { |
@@ -44,7 +44,7 @@ static int display(const char *data, const char *pattern, int flag) | |||
44 | } | 44 | } |
45 | 45 | ||
46 | static void modinfo(const char *path, const char *version, | 46 | static 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) |
172 | struct BUG_G_too_big { | 172 | struct 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 | ||
65 | char* FAST_FUNC parse_cmdline_module_options(char **argv) | 65 | char* 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; | |||
21 | char *replace_underscores(char *s) FAST_FUNC; | 21 | char *replace_underscores(char *s) FAST_FUNC; |
22 | int string_to_llist(char *string, llist_t **llist, const char *delim) FAST_FUNC; | 22 | int string_to_llist(char *string, llist_t **llist, const char *delim) FAST_FUNC; |
23 | char *filename2modname(const char *filename, char *modname) FAST_FUNC; | 23 | char *filename2modname(const char *filename, char *modname) FAST_FUNC; |
24 | char *parse_cmdline_module_options(char **argv) FAST_FUNC; | 24 | char *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 | ||
53 | static char* skip_whitespace(char *s) | 53 | static 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 | ||
60 | static char line[64 * 1024]; | 60 | static 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? | ||
58 | int FAST_FUNC rtnl_send(struct rtnl_handle *rth, char *buf, int len) | 59 | int 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 | ||
96 | static int get_prefix_1(inet_prefix *dst, char *arg, int family) | 121 | static 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 | ||
155 | int get_addr(inet_prefix *dst, char *arg, int family) | 180 | int 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 | ||
166 | int get_prefix(inet_prefix *dst, char *arg, int family) | 191 | void 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 | ||
177 | uint32_t get_addr32(char *name) | 199 | uint32_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 | ||
207 | int inet_addr_match(inet_prefix *a, inet_prefix *b, int bits) | 229 | int 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 | ||
59 | extern uint32_t get_addr32(char *name); | 59 | extern uint32_t get_addr32(char *name); |
60 | extern int get_addr_1(inet_prefix *dst, char *arg, int family); | 60 | extern 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);*/ |
62 | extern int get_addr(inet_prefix *dst, char *arg, int family); | 62 | extern int get_addr(inet_prefix *dst, char *arg, int family); |
63 | extern int get_prefix(inet_prefix *dst, char *arg, int family); | 63 | extern void get_prefix(inet_prefix *dst, char *arg, int family); |
64 | 64 | ||
65 | extern unsigned get_unsigned(char *arg, const char *errmsg); | 65 | extern unsigned get_unsigned(char *arg, const char *errmsg); |
66 | extern uint32_t get_u32(char *arg, const char *errmsg); | 66 | extern 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 | |||
77 | void invarg(const char *, const char *) NORETURN; | 77 | void invarg(const char *, const char *) NORETURN; |
78 | void duparg(const char *, const char *) NORETURN; | 78 | void duparg(const char *, const char *) NORETURN; |
79 | void duparg2(const char *, const char *) NORETURN; | 79 | void duparg2(const char *, const char *) NORETURN; |
80 | int inet_addr_match(inet_prefix *a, inet_prefix *b, int bits); | 80 | int inet_addr_match(const inet_prefix *a, const inet_prefix *b, int bits); |
81 | 81 | ||
82 | const char *dnet_ntop(int af, const void *addr, char *str, size_t len); | 82 | const char *dnet_ntop(int af, const void *addr, char *str, size_t len); |
83 | int dnet_pton(int af, const char *src, void *addr); | 83 | int 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 | } |
887 | static peer_t* | 887 | static peer_t* |
888 | select_and_cluster(void) | 888 | select_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 | ||
422 | static void sendping_tail(void (*sp)(int), const void *pkt, int size_pkt) | 422 | static 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 |
485 | static void sendping6(int junk UNUSED_PARAM) | 487 | static 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) |
46 | struct 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) | |
52 | void 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 |
108 | static void tftp_progress_update(void) | 108 | static 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 | } |
112 | static void tftp_progress_init(void) | 112 | static 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 | } |
117 | static void tftp_progress_done(void) | 117 | static 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 | |||
44 | config 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 | ||
44 | config DHCPD_LEASES_FILE | 58 | config 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 | ||
73 | config FEATURE_UDHCP_PORT | 87 | config 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 | |||
13 | struct host_info { | 17 | struct 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) |
40 | struct 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. */ | ||
129 | static 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. */ | ||
147 | static 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. */ |
162 | static char *base64enc_512(char buf[512], const char *str) | 150 | static 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 | ||
194 | static 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' */ |
183 | static 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 | |||
202 | static 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 | ||
221 | static void parse_url(char *src_url, struct host_info *h) | 223 | static 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 | ||
281 | static char *gethdr(char *buf, size_t bufsiz, FILE *fp /*, int *istrunc*/) | 284 | static 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 | ||
367 | static FILE* prepare_ftp_session(FILE **dfpp, struct host_info *target, len_and_sockaddr *lsa) | 359 | static 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 | ||
447 | static void NOINLINE retrieve_file_data(FILE *dfp, int output_fd) | 438 | static 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 | ||
529 | int wget_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | 559 | static void download_one_url(const char *url) |
530 | int 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 | |||
858 | int wget_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | ||
859 | int 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 | ||
34 | typedef struct { | 53 | typedef 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 | ||
209 | static void func_stat(char *buf, int size, const procps_status_t *ps) | ||
210 | { | ||
211 | safe_strncpy(buf, ps->state, size+1); | ||
212 | } | ||
213 | |||
189 | static void func_args(char *buf, int size, const procps_status_t *ps) | 214 | static 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 | ||
304 | static const ps_out_t out_spec[] = { | 329 | static 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 */ | ||
143 | static 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 | |||
152 | static int open_trunc_or_warn(const char *name) | 142 | static 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 | # |
6 | CONFIG_HAVE_DOT_CONFIG=y | 6 | CONFIG_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 | |||
111 | CONFIG_FEATURE_NON_POSIX_CP=y | 111 | CONFIG_FEATURE_NON_POSIX_CP=y |
112 | # CONFIG_FEATURE_VERBOSE_CP_MESSAGE is not set | 112 | # CONFIG_FEATURE_VERBOSE_CP_MESSAGE is not set |
113 | CONFIG_FEATURE_COPYBUF_KB=4 | 113 | CONFIG_FEATURE_COPYBUF_KB=4 |
114 | CONFIG_FEATURE_SKIP_ROOTFS=y | ||
114 | # CONFIG_MONOTONIC_SYSCALL is not set | 115 | # CONFIG_MONOTONIC_SYSCALL is not set |
115 | CONFIG_IOCTL_HEX2STR_ERROR=y | 116 | CONFIG_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 |
224 | CONFIG_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 |
227 | CONFIG_LS=y | 227 | CONFIG_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 |
612 | CONFIG_BBCONFIG=y | 615 | CONFIG_BBCONFIG=y |
613 | CONFIG_FEATURE_COMPRESS_BBCONFIG=y | 616 | CONFIG_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 | ||
809 | CONFIG_DHCPD_LEASES_FILE="" | 813 | CONFIG_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 | # |
916 | CONFIG_ASH=y | 920 | CONFIG_ASH=y |
917 | CONFIG_ASH_BASH_COMPAT=y | 921 | CONFIG_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 |
919 | CONFIG_ASH_ALIAS=y | 924 | CONFIG_ASH_ALIAS=y |
920 | CONFIG_ASH_GETOPTS=y | 925 | CONFIG_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 | ||
259 | static char buf[4096], *bufptr = buf; | 259 | static char buf[4096*10], *bufptr = buf; |
260 | static char input_buf[4096]; | 260 | static char input_buf[4096]; |
261 | static const char filename[] = ".config"; | 261 | static const char filename[] = ".config"; |
262 | static char *args[1024], **argptr = args; | 262 | static 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 | */ |
43 | static const char bbconfig_config[] ALIGN1 =" | 43 | static const char bbconfig_config[] ALIGN1 =" |
44 | 44 | ||
45 | grep '^#\? \?CONFIG_' "$config" \ | 45 | grep -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 | ||
48 | echo ";" | 48 | echo ";" |
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 | ||
138 | endmenu | 139 | endmenu |
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 | |||
460 | static 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 |
1931 | static void chkmail(void); | 1941 | static void chkmail(void); |
1932 | static void changemail(const char *) FAST_FUNC; | 1942 | static void changemail(const char *var_value) FAST_FUNC; |
1943 | #else | ||
1944 | # define chkmail() ((void)0) | ||
1933 | #endif | 1945 | #endif |
1934 | static void changepath(const char *) FAST_FUNC; | 1946 | static 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) | |||
3937 | static int FAST_FUNC | 3949 | static int FAST_FUNC |
3938 | killcmd(int argc, char **argv) | 3950 | killcmd(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 | */ |
13349 | static void exitshell(void) NORETURN; | ||
13350 | static void | 13412 | static void |
13351 | exitshell(void) | 13413 | exitshell(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 @@ | |||
1 | ash: write error: Broken pipe | ||
2 | Ok: 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 @@ | |||
1 | trap "" PIPE | ||
2 | |||
3 | { | ||
4 | sleep 1 | ||
5 | echo Cant write this - get EPIPE | ||
6 | echo 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 @@ | |||
1 | ash: write error: Bad file descriptor | ||
1 | TEST | 2 | TEST |
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 | |||
41 | echo 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) | |||
1418 | static void hush_exit(int exitcode) NORETURN; | 1418 | static void hush_exit(int exitcode) NORETURN; |
1419 | static void hush_exit(int exitcode) | 1419 | static 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 @@ | |||
1 | hush: write error: Broken pipe | ||
2 | Ok: 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 @@ | |||
1 | trap "" PIPE | ||
2 | |||
3 | { | ||
4 | sleep 1 | ||
5 | echo Cant write this - get EPIPE | ||
6 | echo 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 | |||
41 | echo 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 | |||
3 | test "`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 | |||
3 | test "`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 | |||
3 | test "`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 | |||
3 | test "`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 | |||
154 | SKIP= | 154 | SKIP= |
155 | } | 155 | } |
156 | 156 | ||
157 | # Had a bug where on extract autodetect first "switched off" -z | ||
158 | # and then failed to recognize .tgz extension | ||
159 | testing "tar extract tgz" "\ | ||
160 | dd count=1 bs=1M if=/dev/zero of=F0 2>/dev/null | ||
161 | tar -czf F0.tgz F0 | ||
162 | rm F0 | ||
163 | tar -xzvf F0.tgz && echo Ok | ||
164 | rm F0 || echo BAD | ||
165 | " "\ | ||
166 | F0 | ||
167 | Ok | ||
168 | " \ | ||
169 | "" "" | ||
170 | |||
157 | 171 | ||
158 | cd .. && rm -rf tar.tempdir || exit 1 | 172 | cd .. && 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 | ||
50 | struct bdc { | 52 | struct 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 | ||
125 | int blockdev_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | 132 | int blockdev_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
126 | int blockdev_main(int argc, char **argv) | 133 | int 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 | |||