diff options
author | Ron Yorston <rmy@pobox.com> | 2014-06-30 21:13:06 +0100 |
---|---|---|
committer | Ron Yorston <rmy@pobox.com> | 2014-06-30 21:13:06 +0100 |
commit | 099e8b6438345baae560a629d548af07a8c3125c (patch) | |
tree | 71b5600b22b0019af675e4a991394ce32c8207c5 | |
parent | e19594cc6e49e78fa50a654f15cf9a04e77d054a (diff) | |
parent | 184b2669175e562d58894e22f6320cebf3316c25 (diff) | |
download | busybox-w32-099e8b6438345baae560a629d548af07a8c3125c.tar.gz busybox-w32-099e8b6438345baae560a629d548af07a8c3125c.tar.bz2 busybox-w32-099e8b6438345baae560a629d548af07a8c3125c.zip |
Merge branch 'busybox' into merge
43 files changed, 908 insertions, 139 deletions
@@ -700,6 +700,14 @@ config DEBUG_PESSIMIZE | |||
700 | in a much bigger executable that more closely matches the source | 700 | in a much bigger executable that more closely matches the source |
701 | code. | 701 | code. |
702 | 702 | ||
703 | config UNIT_TEST | ||
704 | bool "Build unit tests" | ||
705 | default n | ||
706 | help | ||
707 | Say Y here if you want to build unit tests (both the framework and | ||
708 | test cases) as a Busybox applet. This results in bigger code, so you | ||
709 | probably don't want this option in production builds. | ||
710 | |||
703 | config WERROR | 711 | config WERROR |
704 | bool "Abort compilation on any warning" | 712 | bool "Abort compilation on any warning" |
705 | default n | 713 | default n |
diff --git a/Makefile.custom b/Makefile.custom index 8c95ef2d4..f8a12831d 100644 --- a/Makefile.custom +++ b/Makefile.custom | |||
@@ -55,7 +55,11 @@ endif | |||
55 | # (cp -pPR is POSIX-compliant (cp -dpR or cp -a would not be)) | 55 | # (cp -pPR is POSIX-compliant (cp -dpR or cp -a would not be)) |
56 | .PHONY: check | 56 | .PHONY: check |
57 | .PHONY: test | 57 | .PHONY: test |
58 | ifeq ($(CONFIG_UNIT_TEST),y) | ||
59 | UNIT_CMD = ./busybox unit | ||
60 | endif | ||
58 | check test: busybox busybox.links | 61 | check test: busybox busybox.links |
62 | $(UNIT_CMD) | ||
59 | test -d $(objtree)/testsuite || cp -pPR $(srctree)/testsuite $(objtree) | 63 | test -d $(objtree)/testsuite || cp -pPR $(srctree)/testsuite $(objtree) |
60 | bindir=$(objtree) srcdir=$(srctree)/testsuite \ | 64 | bindir=$(objtree) srcdir=$(srctree)/testsuite \ |
61 | $(SHELL) -c "cd $(objtree)/testsuite && $(srctree)/testsuite/runtest $(if $(KBUILD_VERBOSE:0=),-v)" | 65 | $(SHELL) -c "cd $(objtree)/testsuite && $(srctree)/testsuite/runtest $(if $(KBUILD_VERBOSE:0=),-v)" |
diff --git a/archival/cpio.c b/archival/cpio.c index 1cce7c8b4..454648d68 100644 --- a/archival/cpio.c +++ b/archival/cpio.c | |||
@@ -65,6 +65,7 @@ | |||
65 | //usage: IF_FEATURE_CPIO_P( | 65 | //usage: IF_FEATURE_CPIO_P( |
66 | //usage: "\n -p DIR Copy files to DIR" | 66 | //usage: "\n -p DIR Copy files to DIR" |
67 | //usage: ) | 67 | //usage: ) |
68 | //usage: "\nOptions:" | ||
68 | //usage: "\n -d Make leading directories" | 69 | //usage: "\n -d Make leading directories" |
69 | //usage: "\n -m Preserve mtime" | 70 | //usage: "\n -m Preserve mtime" |
70 | //usage: "\n -v Verbose" | 71 | //usage: "\n -v Verbose" |
diff --git a/archival/libarchive/liblzo.h b/archival/libarchive/liblzo.h index 843997cb9..4596620fe 100644 --- a/archival/libarchive/liblzo.h +++ b/archival/libarchive/liblzo.h | |||
@@ -76,11 +76,13 @@ | |||
76 | # define TEST_IP (ip < ip_end) | 76 | # define TEST_IP (ip < ip_end) |
77 | # define NEED_IP(x) \ | 77 | # define NEED_IP(x) \ |
78 | if ((unsigned)(ip_end - ip) < (unsigned)(x)) goto input_overrun | 78 | if ((unsigned)(ip_end - ip) < (unsigned)(x)) goto input_overrun |
79 | # define TEST_IV(x) if ((x) > (unsigned)0 - (511)) goto input_overrun | ||
79 | 80 | ||
80 | # undef TEST_OP /* don't need both of the tests here */ | 81 | # undef TEST_OP /* don't need both of the tests here */ |
81 | # define TEST_OP 1 | 82 | # define TEST_OP 1 |
82 | # define NEED_OP(x) \ | 83 | # define NEED_OP(x) \ |
83 | if ((unsigned)(op_end - op) < (unsigned)(x)) goto output_overrun | 84 | if ((unsigned)(op_end - op) < (unsigned)(x)) goto output_overrun |
85 | # define TEST_OV(x) if ((x) > (unsigned)0 - (511)) goto output_overrun | ||
84 | 86 | ||
85 | #define HAVE_ANY_OP 1 | 87 | #define HAVE_ANY_OP 1 |
86 | 88 | ||
diff --git a/archival/libarchive/lzo1x_d.c b/archival/libarchive/lzo1x_d.c index 9bc1270da..40b167e68 100644 --- a/archival/libarchive/lzo1x_d.c +++ b/archival/libarchive/lzo1x_d.c | |||
@@ -92,6 +92,7 @@ int lzo1x_decompress_safe(const uint8_t* in, unsigned in_len, | |||
92 | ip++; | 92 | ip++; |
93 | NEED_IP(1); | 93 | NEED_IP(1); |
94 | } | 94 | } |
95 | TEST_IV(t); | ||
95 | t += 15 + *ip++; | 96 | t += 15 + *ip++; |
96 | } | 97 | } |
97 | /* copy literals */ | 98 | /* copy literals */ |
@@ -224,6 +225,7 @@ int lzo1x_decompress_safe(const uint8_t* in, unsigned in_len, | |||
224 | ip++; | 225 | ip++; |
225 | NEED_IP(1); | 226 | NEED_IP(1); |
226 | } | 227 | } |
228 | TEST_IV(t); | ||
227 | t += 31 + *ip++; | 229 | t += 31 + *ip++; |
228 | } | 230 | } |
229 | #if defined(COPY_DICT) | 231 | #if defined(COPY_DICT) |
@@ -265,6 +267,7 @@ int lzo1x_decompress_safe(const uint8_t* in, unsigned in_len, | |||
265 | ip++; | 267 | ip++; |
266 | NEED_IP(1); | 268 | NEED_IP(1); |
267 | } | 269 | } |
270 | TEST_IV(t); | ||
268 | t += 7 + *ip++; | 271 | t += 7 + *ip++; |
269 | } | 272 | } |
270 | #if defined(COPY_DICT) | 273 | #if defined(COPY_DICT) |
diff --git a/coreutils/Config.src b/coreutils/Config.src index 82b6bf0d9..2914fc36a 100644 --- a/coreutils/Config.src +++ b/coreutils/Config.src | |||
@@ -738,6 +738,16 @@ config YES | |||
738 | yes is used to repeatedly output a specific string, or | 738 | yes is used to repeatedly output a specific string, or |
739 | the default string `y'. | 739 | the default string `y'. |
740 | 740 | ||
741 | comment "Common options" | ||
742 | |||
743 | config FEATURE_VERBOSE | ||
744 | bool "Support verbose options (usually -v) for various applets" | ||
745 | default y | ||
746 | help | ||
747 | Enable cp -v, rm -v and similar messages. | ||
748 | Also enables long option (--verbose) if it exists. | ||
749 | Without this option, -v is accepted but ignored. | ||
750 | |||
741 | comment "Common options for cp and mv" | 751 | comment "Common options for cp and mv" |
742 | depends on CP || MV | 752 | depends on CP || MV |
743 | 753 | ||
diff --git a/coreutils/cp.c b/coreutils/cp.c index de2e512be..247ed0fda 100644 --- a/coreutils/cp.c +++ b/coreutils/cp.c | |||
@@ -79,7 +79,6 @@ int cp_main(int argc, char **argv) | |||
79 | "parents\0" No_argument "\xff" | 79 | "parents\0" No_argument "\xff" |
80 | ; | 80 | ; |
81 | #endif | 81 | #endif |
82 | // -v (--verbose) is ignored | ||
83 | flags = getopt32(argv, FILEUTILS_CP_OPTSTR "arPv"); | 82 | flags = getopt32(argv, FILEUTILS_CP_OPTSTR "arPv"); |
84 | /* Options of cp from GNU coreutils 6.10: | 83 | /* Options of cp from GNU coreutils 6.10: |
85 | * -a, --archive | 84 | * -a, --archive |
diff --git a/coreutils/install.c b/coreutils/install.c index 445497f9a..6c88ae11c 100644 --- a/coreutils/install.c +++ b/coreutils/install.c | |||
@@ -28,6 +28,9 @@ | |||
28 | 28 | ||
29 | #if ENABLE_FEATURE_INSTALL_LONG_OPTIONS | 29 | #if ENABLE_FEATURE_INSTALL_LONG_OPTIONS |
30 | static const char install_longopts[] ALIGN1 = | 30 | static const char install_longopts[] ALIGN1 = |
31 | IF_FEATURE_VERBOSE( | ||
32 | "verbose\0" No_argument "v" | ||
33 | ) | ||
31 | "directory\0" No_argument "d" | 34 | "directory\0" No_argument "d" |
32 | "preserve-timestamps\0" No_argument "p" | 35 | "preserve-timestamps\0" No_argument "p" |
33 | "strip\0" No_argument "s" | 36 | "strip\0" No_argument "s" |
@@ -89,6 +92,7 @@ int install_main(int argc, char **argv) | |||
89 | const char *gid_str; | 92 | const char *gid_str; |
90 | const char *uid_str; | 93 | const char *uid_str; |
91 | const char *mode_str; | 94 | const char *mode_str; |
95 | int mkdir_flags = FILEUTILS_RECUR; | ||
92 | int copy_flags = FILEUTILS_DEREFERENCE | FILEUTILS_FORCE; | 96 | int copy_flags = FILEUTILS_DEREFERENCE | FILEUTILS_FORCE; |
93 | int opts; | 97 | int opts; |
94 | int min_args = 1; | 98 | int min_args = 1; |
@@ -120,7 +124,6 @@ int install_main(int argc, char **argv) | |||
120 | #endif | 124 | #endif |
121 | opt_complementary = "s--d:d--s" IF_FEATURE_INSTALL_LONG_OPTIONS(IF_SELINUX(":Z--\xff:\xff--Z")); | 125 | opt_complementary = "s--d:d--s" IF_FEATURE_INSTALL_LONG_OPTIONS(IF_SELINUX(":Z--\xff:\xff--Z")); |
122 | /* -c exists for backwards compatibility, it's needed */ | 126 | /* -c exists for backwards compatibility, it's needed */ |
123 | /* -v is ignored ("print name of each created directory") */ | ||
124 | /* -b is ignored ("make a backup of each existing destination file") */ | 127 | /* -b is ignored ("make a backup of each existing destination file") */ |
125 | opts = getopt32(argv, "cvb" "Ddpsg:m:o:" IF_SELINUX("Z:"), | 128 | opts = getopt32(argv, "cvb" "Ddpsg:m:o:" IF_SELINUX("Z:"), |
126 | &gid_str, &mode_str, &uid_str IF_SELINUX(, &scontext)); | 129 | &gid_str, &mode_str, &uid_str IF_SELINUX(, &scontext)); |
@@ -141,6 +144,11 @@ int install_main(int argc, char **argv) | |||
141 | } | 144 | } |
142 | #endif | 145 | #endif |
143 | 146 | ||
147 | if ((opts & OPT_v) && FILEUTILS_VERBOSE) { | ||
148 | mkdir_flags |= FILEUTILS_VERBOSE; | ||
149 | copy_flags |= FILEUTILS_VERBOSE; | ||
150 | } | ||
151 | |||
144 | /* preserve access and modification time, this is GNU behaviour, | 152 | /* preserve access and modification time, this is GNU behaviour, |
145 | * BSD only preserves modification time */ | 153 | * BSD only preserves modification time */ |
146 | if (opts & OPT_PRESERVE_TIME) { | 154 | if (opts & OPT_PRESERVE_TIME) { |
@@ -171,14 +179,14 @@ int install_main(int argc, char **argv) | |||
171 | /* GNU coreutils 6.9 does not set uid:gid | 179 | /* GNU coreutils 6.9 does not set uid:gid |
172 | * on intermediate created directories | 180 | * on intermediate created directories |
173 | * (only on last one) */ | 181 | * (only on last one) */ |
174 | if (bb_make_directory(dest, 0755, FILEUTILS_RECUR)) { | 182 | if (bb_make_directory(dest, 0755, mkdir_flags)) { |
175 | ret = EXIT_FAILURE; | 183 | ret = EXIT_FAILURE; |
176 | goto next; | 184 | goto next; |
177 | } | 185 | } |
178 | } else { | 186 | } else { |
179 | if (opts & OPT_MKDIR_LEADING) { | 187 | if (opts & OPT_MKDIR_LEADING) { |
180 | char *ddir = xstrdup(dest); | 188 | char *ddir = xstrdup(dest); |
181 | bb_make_directory(dirname(ddir), 0755, FILEUTILS_RECUR); | 189 | bb_make_directory(dirname(ddir), 0755, mkdir_flags); |
182 | /* errors are not checked. copy_file | 190 | /* errors are not checked. copy_file |
183 | * will fail if dir is not created. */ | 191 | * will fail if dir is not created. */ |
184 | free(ddir); | 192 | free(ddir); |
diff --git a/coreutils/mkdir.c b/coreutils/mkdir.c index 4a8e43e43..864edfb0a 100644 --- a/coreutils/mkdir.c +++ b/coreutils/mkdir.c | |||
@@ -48,7 +48,9 @@ static const char mkdir_longopts[] ALIGN1 = | |||
48 | #if ENABLE_SELINUX | 48 | #if ENABLE_SELINUX |
49 | "context\0" Required_argument "Z" | 49 | "context\0" Required_argument "Z" |
50 | #endif | 50 | #endif |
51 | #if ENABLE_FEATURE_VERBOSE | ||
51 | "verbose\0" No_argument "v" | 52 | "verbose\0" No_argument "v" |
53 | #endif | ||
52 | ; | 54 | ; |
53 | #endif | 55 | #endif |
54 | 56 | ||
@@ -67,7 +69,7 @@ int mkdir_main(int argc UNUSED_PARAM, char **argv) | |||
67 | #if ENABLE_FEATURE_MKDIR_LONG_OPTIONS | 69 | #if ENABLE_FEATURE_MKDIR_LONG_OPTIONS |
68 | applet_long_options = mkdir_longopts; | 70 | applet_long_options = mkdir_longopts; |
69 | #endif | 71 | #endif |
70 | opt = getopt32(argv, "m:p" IF_SELINUX("Z:") "v", &smode IF_SELINUX(,&scontext)); | 72 | opt = getopt32(argv, "m:pv" IF_SELINUX("Z:"), &smode IF_SELINUX(,&scontext)); |
71 | if (opt & 1) { | 73 | if (opt & 1) { |
72 | mode_t mmode = 0777; | 74 | mode_t mmode = 0777; |
73 | if (!bb_parse_mode(smode, &mmode)) { | 75 | if (!bb_parse_mode(smode, &mmode)) { |
@@ -77,8 +79,10 @@ int mkdir_main(int argc UNUSED_PARAM, char **argv) | |||
77 | } | 79 | } |
78 | if (opt & 2) | 80 | if (opt & 2) |
79 | flags |= FILEUTILS_RECUR; | 81 | flags |= FILEUTILS_RECUR; |
82 | if ((opt & 4) && FILEUTILS_VERBOSE) | ||
83 | flags |= FILEUTILS_VERBOSE; | ||
80 | #if ENABLE_SELINUX | 84 | #if ENABLE_SELINUX |
81 | if (opt & 4) { | 85 | if (opt & 8) { |
82 | selinux_or_die(); | 86 | selinux_or_die(); |
83 | setfscreatecon_or_die(scontext); | 87 | setfscreatecon_or_die(scontext); |
84 | } | 88 | } |
diff --git a/coreutils/mv.c b/coreutils/mv.c index f127dfabd..50571755b 100644 --- a/coreutils/mv.c +++ b/coreutils/mv.c | |||
@@ -33,13 +33,17 @@ static const char mv_longopts[] ALIGN1 = | |||
33 | "interactive\0" No_argument "i" | 33 | "interactive\0" No_argument "i" |
34 | "force\0" No_argument "f" | 34 | "force\0" No_argument "f" |
35 | "no-clobber\0" No_argument "n" | 35 | "no-clobber\0" No_argument "n" |
36 | IF_FEATURE_VERBOSE( | ||
36 | "verbose\0" No_argument "v" | 37 | "verbose\0" No_argument "v" |
38 | ) | ||
37 | ; | 39 | ; |
38 | #endif | 40 | #endif |
39 | 41 | ||
40 | #define OPT_FORCE (1 << 0) | 42 | #define OPT_FORCE (1 << 0) |
41 | #define OPT_INTERACTIVE (1 << 1) | 43 | #define OPT_INTERACTIVE (1 << 1) |
42 | #define OPT_NOCLOBBER (1 << 2) | 44 | #define OPT_NOCLOBBER (1 << 2) |
45 | #define OPT_VERBOSE ((1 << 3) * ENABLE_FEATURE_VERBOSE) | ||
46 | |||
43 | 47 | ||
44 | int mv_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | 48 | int mv_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
45 | int mv_main(int argc, char **argv) | 49 | int mv_main(int argc, char **argv) |
@@ -58,7 +62,6 @@ int mv_main(int argc, char **argv) | |||
58 | /* Need at least two arguments. | 62 | /* Need at least two arguments. |
59 | * If more than one of -f, -i, -n is specified , only the final one | 63 | * If more than one of -f, -i, -n is specified , only the final one |
60 | * takes effect (it unsets previous options). | 64 | * takes effect (it unsets previous options). |
61 | * -v is accepted but ignored. | ||
62 | */ | 65 | */ |
63 | opt_complementary = "-2:f-in:i-fn:n-fi"; | 66 | opt_complementary = "-2:f-in:i-fn:n-fi"; |
64 | flags = getopt32(argv, "finv"); | 67 | flags = getopt32(argv, "finv"); |
@@ -148,6 +151,9 @@ int mv_main(int argc, char **argv) | |||
148 | status = 1; | 151 | status = 1; |
149 | } | 152 | } |
150 | RET_0: | 153 | RET_0: |
154 | if (flags & OPT_VERBOSE) { | ||
155 | printf("'%s' -> '%s'\n", *argv, dest); | ||
156 | } | ||
151 | if (dest != last) { | 157 | if (dest != last) { |
152 | free((void *) dest); | 158 | free((void *) dest); |
153 | } | 159 | } |
diff --git a/coreutils/rm.c b/coreutils/rm.c index 042fba162..d0ad81dfc 100644 --- a/coreutils/rm.c +++ b/coreutils/rm.c | |||
@@ -38,7 +38,6 @@ int rm_main(int argc UNUSED_PARAM, char **argv) | |||
38 | unsigned opt; | 38 | unsigned opt; |
39 | 39 | ||
40 | opt_complementary = "f-i:i-f"; | 40 | opt_complementary = "f-i:i-f"; |
41 | /* -v (verbose) is ignored */ | ||
42 | opt = getopt32(argv, "fiRrv"); | 41 | opt = getopt32(argv, "fiRrv"); |
43 | argv += optind; | 42 | argv += optind; |
44 | if (opt & 1) | 43 | if (opt & 1) |
@@ -47,6 +46,8 @@ int rm_main(int argc UNUSED_PARAM, char **argv) | |||
47 | flags |= FILEUTILS_INTERACTIVE; | 46 | flags |= FILEUTILS_INTERACTIVE; |
48 | if (opt & (8|4)) | 47 | if (opt & (8|4)) |
49 | flags |= FILEUTILS_RECUR; | 48 | flags |= FILEUTILS_RECUR; |
49 | if ((opt & 16) && FILEUTILS_VERBOSE) | ||
50 | flags |= FILEUTILS_VERBOSE; | ||
50 | 51 | ||
51 | if (*argv != NULL) { | 52 | if (*argv != NULL) { |
52 | do { | 53 | do { |
diff --git a/coreutils/rmdir.c b/coreutils/rmdir.c index cc2dea010..0792a1c8e 100644 --- a/coreutils/rmdir.c +++ b/coreutils/rmdir.c | |||
@@ -31,7 +31,7 @@ | |||
31 | 31 | ||
32 | 32 | ||
33 | #define PARENTS (1 << 0) | 33 | #define PARENTS (1 << 0) |
34 | //efine VERBOSE (1 << 1) //accepted but ignored | 34 | #define VERBOSE ((1 << 1) * ENABLE_FEATURE_VERBOSE) |
35 | #define IGNORE_NON_EMPTY (1 << 2) | 35 | #define IGNORE_NON_EMPTY (1 << 2) |
36 | 36 | ||
37 | int rmdir_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | 37 | int rmdir_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
@@ -44,10 +44,12 @@ int rmdir_main(int argc UNUSED_PARAM, char **argv) | |||
44 | #if ENABLE_FEATURE_RMDIR_LONG_OPTIONS | 44 | #if ENABLE_FEATURE_RMDIR_LONG_OPTIONS |
45 | static const char rmdir_longopts[] ALIGN1 = | 45 | static const char rmdir_longopts[] ALIGN1 = |
46 | "parents\0" No_argument "p" | 46 | "parents\0" No_argument "p" |
47 | "verbose\0" No_argument "v" | ||
48 | /* Debian etch: many packages fail to be purged or installed | 47 | /* Debian etch: many packages fail to be purged or installed |
49 | * because they desperately want this option: */ | 48 | * because they desperately want this option: */ |
50 | "ignore-fail-on-non-empty\0" No_argument "\xff" | 49 | "ignore-fail-on-non-empty\0" No_argument "\xff" |
50 | IF_FEATURE_VERBOSE( | ||
51 | "verbose\0" No_argument "v" | ||
52 | ) | ||
51 | ; | 53 | ; |
52 | applet_long_options = rmdir_longopts; | 54 | applet_long_options = rmdir_longopts; |
53 | #endif | 55 | #endif |
@@ -62,6 +64,10 @@ int rmdir_main(int argc UNUSED_PARAM, char **argv) | |||
62 | path = *argv; | 64 | path = *argv; |
63 | 65 | ||
64 | while (1) { | 66 | while (1) { |
67 | if (flags & VERBOSE) { | ||
68 | printf("rmdir: removing directory, '%s'\n", path); | ||
69 | } | ||
70 | |||
65 | if (rmdir(path) < 0) { | 71 | if (rmdir(path) < 0) { |
66 | #if ENABLE_FEATURE_RMDIR_LONG_OPTIONS | 72 | #if ENABLE_FEATURE_RMDIR_LONG_OPTIONS |
67 | if ((flags & IGNORE_NON_EMPTY) && errno == ENOTEMPTY) | 73 | if ((flags & IGNORE_NON_EMPTY) && errno == ENOTEMPTY) |
diff --git a/coreutils/stat.c b/coreutils/stat.c index dc9d81c35..769fac078 100644 --- a/coreutils/stat.c +++ b/coreutils/stat.c | |||
@@ -655,7 +655,7 @@ static bool do_stat(const char *filename, const char *format) | |||
655 | ); | 655 | ); |
656 | # if ENABLE_SELINUX | 656 | # if ENABLE_SELINUX |
657 | if (option_mask32 & OPT_SELINUX) | 657 | if (option_mask32 & OPT_SELINUX) |
658 | printf(" %lc\n", *scontext); | 658 | printf(" %s\n", scontext); |
659 | else | 659 | else |
660 | bb_putchar('\n'); | 660 | bb_putchar('\n'); |
661 | # endif | 661 | # endif |
@@ -700,7 +700,8 @@ static bool do_stat(const char *filename, const char *format) | |||
700 | (unsigned long) statbuf.st_gid, | 700 | (unsigned long) statbuf.st_gid, |
701 | (gw_ent != NULL) ? gw_ent->gr_name : "UNKNOWN"); | 701 | (gw_ent != NULL) ? gw_ent->gr_name : "UNKNOWN"); |
702 | # if ENABLE_SELINUX | 702 | # if ENABLE_SELINUX |
703 | printf(" S_Context: %lc\n", *scontext); | 703 | if (option_mask32 & OPT_SELINUX) |
704 | printf(" S_Context: %s\n", scontext); | ||
704 | # endif | 705 | # endif |
705 | printf("Access: %s\n", human_time(statbuf.st_atime)); | 706 | printf("Access: %s\n", human_time(statbuf.st_atime)); |
706 | printf("Modify: %s\n", human_time(statbuf.st_mtime)); | 707 | printf("Modify: %s\n", human_time(statbuf.st_mtime)); |
diff --git a/coreutils/unlink.c b/coreutils/unlink.c new file mode 100644 index 000000000..3b7d0fb2b --- /dev/null +++ b/coreutils/unlink.c | |||
@@ -0,0 +1,34 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* unlink for busybox | ||
3 | * | ||
4 | * Copyright (C) 2014 Isaac Dunham <ibid.ag@gmail.com> | ||
5 | * | ||
6 | * Licensed under GPLv2, see LICENSE in this source tree | ||
7 | */ | ||
8 | |||
9 | //config:config UNLINK | ||
10 | //config: bool "unlink" | ||
11 | //config: default y | ||
12 | //config: help | ||
13 | //config: unlink deletes a file by calling unlink() | ||
14 | |||
15 | //kbuild:lib-$(CONFIG_UNLINK) += unlink.o | ||
16 | |||
17 | //applet:IF_UNLINK(APPLET(unlink, BB_DIR_USR_BIN, BB_SUID_DROP)) | ||
18 | |||
19 | //usage:#define unlink_trivial_usage | ||
20 | //usage: "FILE" | ||
21 | //usage:#define unlink_full_usage "\n\n" | ||
22 | //usage: "Delete FILE by calling unlink()" | ||
23 | |||
24 | #include "libbb.h" | ||
25 | |||
26 | int unlink_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | ||
27 | int unlink_main(int argc UNUSED_PARAM, char **argv) | ||
28 | { | ||
29 | opt_complementary = "=1"; /* must have exactly 1 param */ | ||
30 | getopt32(argv, ""); | ||
31 | argv += optind; | ||
32 | xunlink(argv[0]); | ||
33 | return 0; | ||
34 | } | ||
diff --git a/docs/unit-tests.txt b/docs/unit-tests.txt new file mode 100644 index 000000000..0fb522086 --- /dev/null +++ b/docs/unit-tests.txt | |||
@@ -0,0 +1,50 @@ | |||
1 | Busybox unit test framework | ||
2 | =========================== | ||
3 | |||
4 | This document describes what you need to do to write test cases using the | ||
5 | Busybox unit test framework. | ||
6 | |||
7 | |||
8 | Building unit tests | ||
9 | ------------------- | ||
10 | |||
11 | The framework and all tests are built as a regular Busybox applet if option | ||
12 | CONFIG_UNIT_TEST (found in General Configuration -> Debugging Options) is set. | ||
13 | |||
14 | |||
15 | Writing test cases | ||
16 | ------------------ | ||
17 | |||
18 | Unit testing interface can be found in include/bbunit.h. | ||
19 | |||
20 | Tests can be placed in any .c file in Busybox tree - preferably right next to | ||
21 | the functions they test. Test cases should be enclosed within an #if, and | ||
22 | should start with BBUNIT_DEFINE_TEST macro and end with BBUNIT_ENDTEST within | ||
23 | the test curly brackets. If an assertion fails the test ends immediately, ie. | ||
24 | the following assertions will not be reached. Any code placed after | ||
25 | BBUNIT_ENDTEST is executed regardless of the test result. Here's an example: | ||
26 | |||
27 | #if ENABLE_UNIT_TEST | ||
28 | |||
29 | BBUNIT_DEFINE_TEST(test_name) | ||
30 | { | ||
31 | int *i; | ||
32 | |||
33 | i = malloc(sizeof(int)); | ||
34 | BBUNIT_ASSERT_NOTNULL(i); | ||
35 | *i = 2; | ||
36 | BBUNIT_ASSERT_EQ((*i)*(*i), 4); | ||
37 | |||
38 | BBUNIT_ENDTEST; | ||
39 | |||
40 | free(i); | ||
41 | } | ||
42 | |||
43 | #endif /* ENABLE_UNIT_TEST */ | ||
44 | |||
45 | |||
46 | Running the unit test suite | ||
47 | --------------------------- | ||
48 | |||
49 | To run the tests you can either directly run 'busybox unit' or use 'make test' | ||
50 | to run both the unit tests (if compiled) and regular test suite. | ||
diff --git a/editors/awk.c b/editors/awk.c index 3d2183e5e..5b8e484a7 100644 --- a/editors/awk.c +++ b/editors/awk.c | |||
@@ -1544,12 +1544,14 @@ static void chain_group(void) | |||
1544 | debug_printf_parse("%s: OC_BREAK\n", __func__); | 1544 | debug_printf_parse("%s: OC_BREAK\n", __func__); |
1545 | n = chain_node(OC_EXEC); | 1545 | n = chain_node(OC_EXEC); |
1546 | n->a.n = break_ptr; | 1546 | n->a.n = break_ptr; |
1547 | chain_expr(t_info); | ||
1547 | break; | 1548 | break; |
1548 | 1549 | ||
1549 | case OC_CONTINUE: | 1550 | case OC_CONTINUE: |
1550 | debug_printf_parse("%s: OC_CONTINUE\n", __func__); | 1551 | debug_printf_parse("%s: OC_CONTINUE\n", __func__); |
1551 | n = chain_node(OC_EXEC); | 1552 | n = chain_node(OC_EXEC); |
1552 | n->a.n = continue_ptr; | 1553 | n->a.n = continue_ptr; |
1554 | chain_expr(t_info); | ||
1553 | break; | 1555 | break; |
1554 | 1556 | ||
1555 | /* delete, next, nextfile, return, exit */ | 1557 | /* delete, next, nextfile, return, exit */ |
diff --git a/findutils/find.c b/findutils/find.c index 6d34f4d68..56a7ed3ab 100644 --- a/findutils/find.c +++ b/findutils/find.c | |||
@@ -137,6 +137,16 @@ | |||
137 | //config: Support the 'find -exec' option for executing commands based upon | 137 | //config: Support the 'find -exec' option for executing commands based upon |
138 | //config: the files matched. | 138 | //config: the files matched. |
139 | //config: | 139 | //config: |
140 | //config:config FEATURE_FIND_EXEC_PLUS | ||
141 | //config: bool "Enable -exec ... {} +" | ||
142 | //config: default y | ||
143 | //config: depends on FEATURE_FIND_EXEC | ||
144 | //config: help | ||
145 | //config: Support the 'find -exec ... {} +' option for executing commands | ||
146 | //config: for all matched files at once. | ||
147 | //config: Without this option, -exec + is a synonym for -exec ; | ||
148 | //config: (IOW: it works correctly, but without expected speedup) | ||
149 | //config: | ||
140 | //config:config FEATURE_FIND_USER | 150 | //config:config FEATURE_FIND_USER |
141 | //config: bool "Enable -user: username/uid matching" | 151 | //config: bool "Enable -user: username/uid matching" |
142 | //config: default y | 152 | //config: default y |
@@ -319,6 +329,9 @@ | |||
319 | //usage: "\n -exec CMD ARG ; Run CMD with all instances of {} replaced by" | 329 | //usage: "\n -exec CMD ARG ; Run CMD with all instances of {} replaced by" |
320 | //usage: "\n file name. Fails if CMD exits with nonzero" | 330 | //usage: "\n file name. Fails if CMD exits with nonzero" |
321 | //usage: ) | 331 | //usage: ) |
332 | //usage: IF_FEATURE_FIND_EXEC_PLUS( | ||
333 | //usage: "\n -exec CMD ARG + Run CMD with {} replaced by list of file names" | ||
334 | //usage: ) | ||
322 | //usage: IF_FEATURE_FIND_DELETE( | 335 | //usage: IF_FEATURE_FIND_DELETE( |
323 | //usage: "\n -delete Delete current file/directory. Turns on -depth option" | 336 | //usage: "\n -delete Delete current file/directory. Turns on -depth option" |
324 | //usage: ) | 337 | //usage: ) |
@@ -337,8 +350,12 @@ | |||
337 | # define FNM_CASEFOLD 0 | 350 | # define FNM_CASEFOLD 0 |
338 | #endif | 351 | #endif |
339 | 352 | ||
340 | #define dbg(...) ((void)0) | 353 | #if 1 |
341 | /* #define dbg(...) bb_error_msg(__VA_ARGS__) */ | 354 | # define dbg(...) ((void)0) |
355 | #else | ||
356 | # define dbg(...) bb_error_msg(__VA_ARGS__) | ||
357 | #endif | ||
358 | |||
342 | 359 | ||
343 | /* This is a NOEXEC applet. Be very careful! */ | 360 | /* This is a NOEXEC applet. Be very careful! */ |
344 | 361 | ||
@@ -375,7 +392,20 @@ IF_FEATURE_FIND_CONTEXT(ACTS(context, security_context_t context;)) | |||
375 | IF_FEATURE_FIND_PAREN( ACTS(paren, action ***subexpr;)) | 392 | IF_FEATURE_FIND_PAREN( ACTS(paren, action ***subexpr;)) |
376 | IF_FEATURE_FIND_PRUNE( ACTS(prune)) | 393 | IF_FEATURE_FIND_PRUNE( ACTS(prune)) |
377 | IF_FEATURE_FIND_DELETE( ACTS(delete)) | 394 | IF_FEATURE_FIND_DELETE( ACTS(delete)) |
378 | IF_FEATURE_FIND_EXEC( ACTS(exec, char **exec_argv; unsigned *subst_count; int exec_argc;)) | 395 | IF_FEATURE_FIND_EXEC( ACTS(exec, |
396 | char **exec_argv; /* -exec ARGS */ | ||
397 | unsigned *subst_count; | ||
398 | int exec_argc; /* count of ARGS */ | ||
399 | IF_FEATURE_FIND_EXEC_PLUS( | ||
400 | /* | ||
401 | * filelist is NULL if "exec ;" | ||
402 | * non-NULL if "exec +" | ||
403 | */ | ||
404 | char **filelist; | ||
405 | int filelist_idx; | ||
406 | int file_len; | ||
407 | ) | ||
408 | )) | ||
379 | IF_FEATURE_FIND_GROUP( ACTS(group, gid_t gid;)) | 409 | IF_FEATURE_FIND_GROUP( ACTS(group, gid_t gid;)) |
380 | IF_FEATURE_FIND_LINKS( ACTS(links, char links_char; int links_count;)) | 410 | IF_FEATURE_FIND_LINKS( ACTS(links, char links_char; int links_count;)) |
381 | 411 | ||
@@ -389,6 +419,7 @@ struct globals { | |||
389 | smallint need_print; | 419 | smallint need_print; |
390 | smallint xdev_on; | 420 | smallint xdev_on; |
391 | recurse_flags_t recurse_flags; | 421 | recurse_flags_t recurse_flags; |
422 | IF_FEATURE_FIND_EXEC_PLUS(unsigned max_argv_len;) | ||
392 | } FIX_ALIASING; | 423 | } FIX_ALIASING; |
393 | #define G (*(struct globals*)&bb_common_bufsiz1) | 424 | #define G (*(struct globals*)&bb_common_bufsiz1) |
394 | #define INIT_G() do { \ | 425 | #define INIT_G() do { \ |
@@ -398,6 +429,7 @@ struct globals { | |||
398 | /* we have to zero it out because of NOEXEC */ \ | 429 | /* we have to zero it out because of NOEXEC */ \ |
399 | memset(&G, 0, sizeof(G)); \ | 430 | memset(&G, 0, sizeof(G)); \ |
400 | IF_FEATURE_FIND_MAXDEPTH(G.minmaxdepth[1] = INT_MAX;) \ | 431 | IF_FEATURE_FIND_MAXDEPTH(G.minmaxdepth[1] = INT_MAX;) \ |
432 | IF_FEATURE_FIND_EXEC_PLUS(G.max_argv_len = bb_arg_max() - 2048;) \ | ||
401 | G.need_print = 1; \ | 433 | G.need_print = 1; \ |
402 | G.recurse_flags = ACTION_RECURSE; \ | 434 | G.recurse_flags = ACTION_RECURSE; \ |
403 | } while (0) | 435 | } while (0) |
@@ -452,7 +484,6 @@ static int exec_actions(action ***appp, const char *fileName, const struct stat | |||
452 | return rc ^ TRUE; /* restore TRUE bit */ | 484 | return rc ^ TRUE; /* restore TRUE bit */ |
453 | } | 485 | } |
454 | 486 | ||
455 | |||
456 | #if !FNM_CASEFOLD | 487 | #if !FNM_CASEFOLD |
457 | static char *strcpy_upcase(char *dst, const char *src) | 488 | static char *strcpy_upcase(char *dst, const char *src) |
458 | { | 489 | { |
@@ -576,17 +607,57 @@ ACTF(inum) | |||
576 | } | 607 | } |
577 | #endif | 608 | #endif |
578 | #if ENABLE_FEATURE_FIND_EXEC | 609 | #if ENABLE_FEATURE_FIND_EXEC |
579 | ACTF(exec) | 610 | static int do_exec(action_exec *ap, const char *fileName) |
580 | { | 611 | { |
581 | int i, rc; | 612 | int i, rc; |
582 | #if ENABLE_USE_PORTABLE_CODE | 613 | # if ENABLE_FEATURE_FIND_EXEC_PLUS |
583 | char **argv = alloca(sizeof(char*) * (ap->exec_argc + 1)); | 614 | int size = ap->exec_argc + ap->filelist_idx + 1; |
584 | #else /* gcc 4.3.1 generates smaller code: */ | 615 | # else |
585 | char *argv[ap->exec_argc + 1]; | 616 | int size = ap->exec_argc + 1; |
586 | #endif | 617 | # endif |
587 | for (i = 0; i < ap->exec_argc; i++) | 618 | # if ENABLE_USE_PORTABLE_CODE |
588 | argv[i] = xmalloc_substitute_string(ap->exec_argv[i], ap->subst_count[i], "{}", fileName); | 619 | char **argv = alloca(sizeof(char*) * size); |
589 | argv[i] = NULL; /* terminate the list */ | 620 | # else /* gcc 4.3.1 generates smaller code: */ |
621 | char *argv[size]; | ||
622 | # endif | ||
623 | char **pp = argv; | ||
624 | |||
625 | for (i = 0; i < ap->exec_argc; i++) { | ||
626 | const char *arg = ap->exec_argv[i]; | ||
627 | |||
628 | # if ENABLE_FEATURE_FIND_EXEC_PLUS | ||
629 | if (ap->filelist) { | ||
630 | /* Handling "-exec +" | ||
631 | * Only one exec_argv[i] has substitution in it. | ||
632 | * Expand that one exec_argv[i] into file list. | ||
633 | */ | ||
634 | if (ap->subst_count[i] == 0) { | ||
635 | *pp++ = xstrdup(arg); | ||
636 | } else { | ||
637 | int j = 0; | ||
638 | while (ap->filelist[j]) { | ||
639 | /* 2nd arg here should be ap->subst_count[i], but it is always 1: */ | ||
640 | *pp++ = xmalloc_substitute_string(arg, 1, "{}", ap->filelist[j]); | ||
641 | free(ap->filelist[j]); | ||
642 | j++; | ||
643 | } | ||
644 | } | ||
645 | } else | ||
646 | # endif | ||
647 | { | ||
648 | /* Handling "-exec ;" */ | ||
649 | *pp++ = xmalloc_substitute_string(arg, ap->subst_count[i], "{}", fileName); | ||
650 | } | ||
651 | } | ||
652 | *pp = NULL; /* terminate the list */ | ||
653 | |||
654 | # if ENABLE_FEATURE_FIND_EXEC_PLUS | ||
655 | if (ap->filelist) { | ||
656 | ap->filelist[0] = NULL; | ||
657 | ap->filelist_idx = 0; | ||
658 | ap->file_len = 0; | ||
659 | } | ||
660 | # endif | ||
590 | 661 | ||
591 | rc = spawn_and_wait(argv); | 662 | rc = spawn_and_wait(argv); |
592 | if (rc < 0) | 663 | if (rc < 0) |
@@ -597,6 +668,48 @@ ACTF(exec) | |||
597 | free(argv[i++]); | 668 | free(argv[i++]); |
598 | return rc == 0; /* return 1 if exitcode 0 */ | 669 | return rc == 0; /* return 1 if exitcode 0 */ |
599 | } | 670 | } |
671 | ACTF(exec) | ||
672 | { | ||
673 | # if ENABLE_FEATURE_FIND_EXEC_PLUS | ||
674 | if (ap->filelist) { | ||
675 | int rc; | ||
676 | |||
677 | ap->filelist = xrealloc_vector(ap->filelist, 8, ap->filelist_idx); | ||
678 | ap->filelist[ap->filelist_idx++] = xstrdup(fileName); | ||
679 | ap->file_len += strlen(fileName) + sizeof(char*) + 1; | ||
680 | /* If we have lots of files already, exec the command */ | ||
681 | rc = 1; | ||
682 | if (ap->file_len >= G.max_argv_len) | ||
683 | rc = do_exec(ap, NULL); | ||
684 | return rc; | ||
685 | } | ||
686 | # endif | ||
687 | return do_exec(ap, fileName); | ||
688 | } | ||
689 | # if ENABLE_FEATURE_FIND_EXEC_PLUS | ||
690 | static int flush_exec_plus(void) | ||
691 | { | ||
692 | action *ap; | ||
693 | action **app; | ||
694 | action ***appp = G.actions; | ||
695 | while ((app = *appp++) != NULL) { | ||
696 | while ((ap = *app++) != NULL) { | ||
697 | if (ap->f == (action_fp)func_exec) { | ||
698 | action_exec *ae = (void*)ap; | ||
699 | if (ae->filelist_idx != 0) { | ||
700 | int rc = do_exec(ae, NULL); | ||
701 | # if ENABLE_FEATURE_FIND_NOT | ||
702 | if (ap->invert) rc = !rc; | ||
703 | # endif | ||
704 | if (rc == 0) | ||
705 | return 1; | ||
706 | } | ||
707 | } | ||
708 | } | ||
709 | } | ||
710 | return 0; | ||
711 | } | ||
712 | # endif | ||
600 | #endif | 713 | #endif |
601 | #if ENABLE_FEATURE_FIND_USER | 714 | #if ENABLE_FEATURE_FIND_USER |
602 | ACTF(user) | 715 | ACTF(user) |
@@ -1037,6 +1150,7 @@ static action*** parse_params(char **argv) | |||
1037 | else if (parm == PARM_exec) { | 1150 | else if (parm == PARM_exec) { |
1038 | int i; | 1151 | int i; |
1039 | action_exec *ap; | 1152 | action_exec *ap; |
1153 | IF_FEATURE_FIND_EXEC_PLUS(int all_subst = 0;) | ||
1040 | dbg("%d", __LINE__); | 1154 | dbg("%d", __LINE__); |
1041 | G.need_print = 0; | 1155 | G.need_print = 0; |
1042 | ap = ALLOC_ACTION(exec); | 1156 | ap = ALLOC_ACTION(exec); |
@@ -1049,10 +1163,13 @@ static action*** parse_params(char **argv) | |||
1049 | // executes "echo Foo >FILENAME<", | 1163 | // executes "echo Foo >FILENAME<", |
1050 | // find -exec echo Foo ">{}<" "+" | 1164 | // find -exec echo Foo ">{}<" "+" |
1051 | // executes "echo Foo FILENAME1 FILENAME2 FILENAME3...". | 1165 | // executes "echo Foo FILENAME1 FILENAME2 FILENAME3...". |
1052 | // TODO (so far we treat "+" just like ";") | ||
1053 | if ((argv[0][0] == ';' || argv[0][0] == '+') | 1166 | if ((argv[0][0] == ';' || argv[0][0] == '+') |
1054 | && argv[0][1] == '\0' | 1167 | && argv[0][1] == '\0' |
1055 | ) { | 1168 | ) { |
1169 | # if ENABLE_FEATURE_FIND_EXEC_PLUS | ||
1170 | if (argv[0][0] == '+') | ||
1171 | ap->filelist = xzalloc(sizeof(ap->filelist[0])); | ||
1172 | # endif | ||
1056 | break; | 1173 | break; |
1057 | } | 1174 | } |
1058 | argv++; | 1175 | argv++; |
@@ -1062,8 +1179,17 @@ static action*** parse_params(char **argv) | |||
1062 | bb_error_msg_and_die(bb_msg_requires_arg, arg); | 1179 | bb_error_msg_and_die(bb_msg_requires_arg, arg); |
1063 | ap->subst_count = xmalloc(ap->exec_argc * sizeof(int)); | 1180 | ap->subst_count = xmalloc(ap->exec_argc * sizeof(int)); |
1064 | i = ap->exec_argc; | 1181 | i = ap->exec_argc; |
1065 | while (i--) | 1182 | while (i--) { |
1066 | ap->subst_count[i] = count_strstr(ap->exec_argv[i], "{}"); | 1183 | ap->subst_count[i] = count_strstr(ap->exec_argv[i], "{}"); |
1184 | IF_FEATURE_FIND_EXEC_PLUS(all_subst += ap->subst_count[i];) | ||
1185 | } | ||
1186 | # if ENABLE_FEATURE_FIND_EXEC_PLUS | ||
1187 | /* | ||
1188 | * coreutils expects {} to appear only once in "-exec +" | ||
1189 | */ | ||
1190 | if (all_subst != 1 && ap->filelist) | ||
1191 | bb_error_msg_and_die("only one '{}' allowed for -exec +"); | ||
1192 | # endif | ||
1067 | } | 1193 | } |
1068 | #endif | 1194 | #endif |
1069 | #if ENABLE_FEATURE_FIND_PAREN | 1195 | #if ENABLE_FEATURE_FIND_PAREN |
@@ -1335,8 +1461,11 @@ int find_main(int argc UNUSED_PARAM, char **argv) | |||
1335 | 0) /* depth */ | 1461 | 0) /* depth */ |
1336 | ) { | 1462 | ) { |
1337 | status = EXIT_FAILURE; | 1463 | status = EXIT_FAILURE; |
1464 | goto out; | ||
1338 | } | 1465 | } |
1339 | } | 1466 | } |
1340 | 1467 | ||
1468 | IF_FEATURE_FIND_EXEC_PLUS(status = flush_exec_plus();) | ||
1469 | out: | ||
1341 | return status; | 1470 | return status; |
1342 | } | 1471 | } |
diff --git a/findutils/xargs.c b/findutils/xargs.c index 0ba5b566d..76c4747fe 100644 --- a/findutils/xargs.c +++ b/findutils/xargs.c | |||
@@ -523,12 +523,7 @@ int xargs_main(int argc, char **argv) | |||
523 | argc++; | 523 | argc++; |
524 | } | 524 | } |
525 | 525 | ||
526 | /* -s NUM default. fileutils-4.4.2 uses 128k, but I heasitate | 526 | /* |
527 | * to use such a big value - first need to change code to use | ||
528 | * growable buffer instead of fixed one. | ||
529 | */ | ||
530 | n_max_chars = 32 * 1024; | ||
531 | /* Make smaller if system does not allow our default value. | ||
532 | * The Open Group Base Specifications Issue 6: | 527 | * The Open Group Base Specifications Issue 6: |
533 | * "The xargs utility shall limit the command line length such that | 528 | * "The xargs utility shall limit the command line length such that |
534 | * when the command line is invoked, the combined argument | 529 | * when the command line is invoked, the combined argument |
@@ -536,16 +531,15 @@ int xargs_main(int argc, char **argv) | |||
536 | * in the System Interfaces volume of IEEE Std 1003.1-2001) | 531 | * in the System Interfaces volume of IEEE Std 1003.1-2001) |
537 | * shall not exceed {ARG_MAX}-2048 bytes". | 532 | * shall not exceed {ARG_MAX}-2048 bytes". |
538 | */ | 533 | */ |
539 | { | 534 | n_max_chars = bb_arg_max(); |
540 | long arg_max = 0; | 535 | if (n_max_chars > 32 * 1024) |
541 | #if defined _SC_ARG_MAX | 536 | n_max_chars = 32 * 1024; |
542 | arg_max = sysconf(_SC_ARG_MAX) - 2048; | 537 | /* |
543 | #elif defined ARG_MAX | 538 | * POSIX suggests substracting 2048 bytes from sysconf(_SC_ARG_MAX) |
544 | arg_max = ARG_MAX - 2048; | 539 | * so that the process may safely modify its environment. |
545 | #endif | 540 | */ |
546 | if (arg_max > 0 && n_max_chars > arg_max) | 541 | n_max_chars -= 2048; |
547 | n_max_chars = arg_max; | 542 | |
548 | } | ||
549 | if (opt & OPT_UPTO_SIZE) { | 543 | if (opt & OPT_UPTO_SIZE) { |
550 | n_max_chars = xatou_range(max_chars, 1, INT_MAX); | 544 | n_max_chars = xatou_range(max_chars, 1, INT_MAX); |
551 | } | 545 | } |
diff --git a/include/libbb.h b/include/libbb.h index c80cd80d7..f5ecc025f 100644 --- a/include/libbb.h +++ b/include/libbb.h | |||
@@ -347,6 +347,8 @@ enum { /* DO NOT CHANGE THESE VALUES! cp.c, mv.c, install.c depend on them. */ | |||
347 | FILEUTILS_SET_SECURITY_CONTEXT = 1 << 10, | 347 | FILEUTILS_SET_SECURITY_CONTEXT = 1 << 10, |
348 | #endif | 348 | #endif |
349 | FILEUTILS_IGNORE_CHMOD_ERR = 1 << 11, | 349 | FILEUTILS_IGNORE_CHMOD_ERR = 1 << 11, |
350 | /* -v */ | ||
351 | FILEUTILS_VERBOSE = (1 << 12) * ENABLE_FEATURE_VERBOSE, | ||
350 | }; | 352 | }; |
351 | #define FILEUTILS_CP_OPTSTR "pdRfilsLH" IF_SELINUX("c") | 353 | #define FILEUTILS_CP_OPTSTR "pdRfilsLH" IF_SELINUX("c") |
352 | extern int remove_file(const char *path, int flags) FAST_FUNC; | 354 | extern int remove_file(const char *path, int flags) FAST_FUNC; |
@@ -742,6 +744,15 @@ extern void *xmalloc_open_read_close(const char *filename, size_t *maxsz_p) FAST | |||
742 | /* Never returns NULL */ | 744 | /* Never returns NULL */ |
743 | extern void *xmalloc_xopen_read_close(const char *filename, size_t *maxsz_p) FAST_FUNC RETURNS_MALLOC; | 745 | extern void *xmalloc_xopen_read_close(const char *filename, size_t *maxsz_p) FAST_FUNC RETURNS_MALLOC; |
744 | 746 | ||
747 | #if defined ARG_MAX | ||
748 | # define bb_arg_max() ((unsigned)ARG_MAX) | ||
749 | #elif defined _SC_ARG_MAX | ||
750 | unsigned bb_arg_max(void) FAST_FUNC; | ||
751 | #else | ||
752 | # define bb_arg_max() ((unsigned)(32 * 1024)) | ||
753 | #endif | ||
754 | unsigned bb_clk_tck(void) FAST_FUNC; | ||
755 | |||
745 | #define SEAMLESS_COMPRESSION (0 \ | 756 | #define SEAMLESS_COMPRESSION (0 \ |
746 | || ENABLE_FEATURE_SEAMLESS_XZ \ | 757 | || ENABLE_FEATURE_SEAMLESS_XZ \ |
747 | || ENABLE_FEATURE_SEAMLESS_LZMA \ | 758 | || ENABLE_FEATURE_SEAMLESS_LZMA \ |
@@ -1957,6 +1968,141 @@ static ALWAYS_INLINE unsigned char bb_ascii_tolower(unsigned char a) | |||
1957 | #define isprint_asciionly(a) ((unsigned)((a) - 0x20) <= 0x7e - 0x20) | 1968 | #define isprint_asciionly(a) ((unsigned)((a) - 0x20) <= 0x7e - 0x20) |
1958 | 1969 | ||
1959 | 1970 | ||
1971 | /* Simple unit-testing framework */ | ||
1972 | |||
1973 | typedef void (*bbunit_testfunc)(void); | ||
1974 | |||
1975 | struct bbunit_listelem { | ||
1976 | struct bbunit_listelem* next; | ||
1977 | const char* name; | ||
1978 | bbunit_testfunc testfunc; | ||
1979 | }; | ||
1980 | |||
1981 | void bbunit_registertest(struct bbunit_listelem* test); | ||
1982 | void bbunit_settestfailed(void); | ||
1983 | |||
1984 | #define BBUNIT_DEFINE_TEST(NAME) \ | ||
1985 | static void bbunit_##NAME##_test(void); \ | ||
1986 | static struct bbunit_listelem bbunit_##NAME##_elem = { \ | ||
1987 | .name = #NAME, \ | ||
1988 | .testfunc = bbunit_##NAME##_test, \ | ||
1989 | }; \ | ||
1990 | static void INIT_FUNC bbunit_##NAME##_register(void) \ | ||
1991 | { \ | ||
1992 | bbunit_registertest(&bbunit_##NAME##_elem); \ | ||
1993 | } \ | ||
1994 | static void bbunit_##NAME##_test(void) | ||
1995 | |||
1996 | /* | ||
1997 | * Both 'goto bbunit_end' and 'break' are here only to get rid | ||
1998 | * of compiler warnings. | ||
1999 | */ | ||
2000 | #define BBUNIT_ENDTEST \ | ||
2001 | do { \ | ||
2002 | goto bbunit_end; \ | ||
2003 | bbunit_end: \ | ||
2004 | break; \ | ||
2005 | } while (0) | ||
2006 | |||
2007 | #define BBUNIT_PRINTASSERTFAIL \ | ||
2008 | do { \ | ||
2009 | bb_error_msg( \ | ||
2010 | "[ERROR] Assertion failed in file %s, line %d", \ | ||
2011 | __FILE__, __LINE__); \ | ||
2012 | } while (0) | ||
2013 | |||
2014 | #define BBUNIT_ASSERTION_FAILED \ | ||
2015 | do { \ | ||
2016 | bbunit_settestfailed(); \ | ||
2017 | goto bbunit_end; \ | ||
2018 | } while (0) | ||
2019 | |||
2020 | /* | ||
2021 | * Assertions. | ||
2022 | * For now we only offer assertions which cause tests to fail | ||
2023 | * immediately. In the future 'expects' might be added too - | ||
2024 | * similar to those offered by the gtest framework. | ||
2025 | */ | ||
2026 | #define BBUNIT_ASSERT_EQ(EXPECTED, ACTUAL) \ | ||
2027 | do { \ | ||
2028 | if ((EXPECTED) != (ACTUAL)) { \ | ||
2029 | BBUNIT_PRINTASSERTFAIL; \ | ||
2030 | bb_error_msg("[ERROR] '%s' isn't equal to '%s'", \ | ||
2031 | #EXPECTED, #ACTUAL); \ | ||
2032 | BBUNIT_ASSERTION_FAILED; \ | ||
2033 | } \ | ||
2034 | } while (0) | ||
2035 | |||
2036 | #define BBUNIT_ASSERT_NOTEQ(EXPECTED, ACTUAL) \ | ||
2037 | do { \ | ||
2038 | if ((EXPECTED) == (ACTUAL)) { \ | ||
2039 | BBUNIT_PRINTASSERTFAIL; \ | ||
2040 | bb_error_msg("[ERROR] '%s' is equal to '%s'", \ | ||
2041 | #EXPECTED, #ACTUAL); \ | ||
2042 | BBUNIT_ASSERTION_FAILED; \ | ||
2043 | } \ | ||
2044 | } while (0) | ||
2045 | |||
2046 | #define BBUNIT_ASSERT_NOTNULL(PTR) \ | ||
2047 | do { \ | ||
2048 | if ((PTR) == NULL) { \ | ||
2049 | BBUNIT_PRINTASSERTFAIL; \ | ||
2050 | bb_error_msg("[ERROR] '%s' is NULL!", #PTR); \ | ||
2051 | BBUNIT_ASSERTION_FAILED; \ | ||
2052 | } \ | ||
2053 | } while (0) | ||
2054 | |||
2055 | #define BBUNIT_ASSERT_NULL(PTR) \ | ||
2056 | do { \ | ||
2057 | if ((PTR) != NULL) { \ | ||
2058 | BBUNIT_PRINTASSERTFAIL; \ | ||
2059 | bb_error_msg("[ERROR] '%s' is not NULL!", #PTR); \ | ||
2060 | BBUNIT_ASSERTION_FAILED; \ | ||
2061 | } \ | ||
2062 | } while (0) | ||
2063 | |||
2064 | #define BBUNIT_ASSERT_FALSE(STATEMENT) \ | ||
2065 | do { \ | ||
2066 | if ((STATEMENT)) { \ | ||
2067 | BBUNIT_PRINTASSERTFAIL; \ | ||
2068 | bb_error_msg("[ERROR] Statement '%s' evaluated to true!", \ | ||
2069 | #STATEMENT); \ | ||
2070 | BBUNIT_ASSERTION_FAILED; \ | ||
2071 | } \ | ||
2072 | } while (0) | ||
2073 | |||
2074 | #define BBUNIT_ASSERT_TRUE(STATEMENT) \ | ||
2075 | do { \ | ||
2076 | if (!(STATEMENT)) { \ | ||
2077 | BBUNIT_PRINTASSERTFAIL; \ | ||
2078 | bb_error_msg("[ERROR] Statement '%s' evaluated to false!", \ | ||
2079 | #STATEMENT); \ | ||
2080 | BBUNIT_ASSERTION_FAILED; \ | ||
2081 | } \ | ||
2082 | } while (0) | ||
2083 | |||
2084 | #define BBUNIT_ASSERT_STREQ(STR1, STR2) \ | ||
2085 | do { \ | ||
2086 | if (strcmp(STR1, STR2) != 0) { \ | ||
2087 | BBUNIT_PRINTASSERTFAIL; \ | ||
2088 | bb_error_msg("[ERROR] Strings '%s' and '%s' " \ | ||
2089 | "are not the same", STR1, STR2); \ | ||
2090 | BBUNIT_ASSERTION_FAILED; \ | ||
2091 | } \ | ||
2092 | } while (0) | ||
2093 | |||
2094 | #define BBUNIT_ASSERT_STRNOTEQ(STR1, STR2) \ | ||
2095 | do { \ | ||
2096 | if (strcmp(STR1, STR2) == 0) { \ | ||
2097 | BBUNIT_PRINTASSERTFAIL; \ | ||
2098 | bb_error_msg("[ERROR] Strings '%s' and '%s' " \ | ||
2099 | "are the same, but were " \ | ||
2100 | "expected to differ", STR1, STR2); \ | ||
2101 | BBUNIT_ASSERTION_FAILED; \ | ||
2102 | } \ | ||
2103 | } while (0) | ||
2104 | |||
2105 | |||
1960 | POP_SAVED_FUNCTION_VISIBILITY | 2106 | POP_SAVED_FUNCTION_VISIBILITY |
1961 | 2107 | ||
1962 | #endif | 2108 | #endif |
diff --git a/include/platform.h b/include/platform.h index 884c6e95f..d9b82b2ed 100644 --- a/include/platform.h +++ b/include/platform.h | |||
@@ -85,6 +85,9 @@ | |||
85 | # define UNUSED_PARAM_RESULT | 85 | # define UNUSED_PARAM_RESULT |
86 | #endif | 86 | #endif |
87 | 87 | ||
88 | /* used by unit test machinery to run registration functions before calling main() */ | ||
89 | #define INIT_FUNC __attribute__ ((constructor)) | ||
90 | |||
88 | /* -fwhole-program makes all symbols local. The attribute externally_visible | 91 | /* -fwhole-program makes all symbols local. The attribute externally_visible |
89 | * forces a symbol global. */ | 92 | * forces a symbol global. */ |
90 | #if __GNUC_PREREQ(4,1) | 93 | #if __GNUC_PREREQ(4,1) |
diff --git a/libbb/Kbuild.src b/libbb/Kbuild.src index 107c80689..d7ab1b129 100644 --- a/libbb/Kbuild.src +++ b/libbb/Kbuild.src | |||
@@ -84,6 +84,7 @@ lib-y += skip_whitespace.o | |||
84 | lib-y += speed_table.o | 84 | lib-y += speed_table.o |
85 | lib-y += str_tolower.o | 85 | lib-y += str_tolower.o |
86 | lib-y += strrstr.o | 86 | lib-y += strrstr.o |
87 | lib-y += sysconf.o | ||
87 | lib-y += time.o | 88 | lib-y += time.o |
88 | lib-y += trim.o | 89 | lib-y += trim.o |
89 | lib-y += u_signal_names.o | 90 | lib-y += u_signal_names.o |
diff --git a/libbb/bbunit.c b/libbb/bbunit.c new file mode 100644 index 000000000..256014441 --- /dev/null +++ b/libbb/bbunit.c | |||
@@ -0,0 +1,90 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * bbunit: Simple unit-testing framework for Busybox. | ||
4 | * | ||
5 | * Copyright (C) 2014 by Bartosz Golaszewski <bartekgola@gmail.com> | ||
6 | * | ||
7 | * Licensed under GPLv2 or later, see file LICENSE in this source tree. | ||
8 | */ | ||
9 | |||
10 | //kbuild:lib-$(CONFIG_UNIT_TEST) += bbunit.o | ||
11 | //applet:IF_UNIT_TEST(APPLET(unit, BB_DIR_USR_BIN, BB_SUID_DROP)) | ||
12 | |||
13 | //usage:#define unit_trivial_usage | ||
14 | //usage: "" | ||
15 | //usage:#define unit_full_usage "\n\n" | ||
16 | //usage: "Run the unit-test suite" | ||
17 | |||
18 | #include "libbb.h" | ||
19 | |||
20 | #define WANT_TIMING 0 | ||
21 | |||
22 | static llist_t *tests = NULL; | ||
23 | static unsigned tests_registered = 0; | ||
24 | static int test_retval; | ||
25 | |||
26 | void bbunit_registertest(struct bbunit_listelem *test) | ||
27 | { | ||
28 | llist_add_to_end(&tests, test); | ||
29 | tests_registered++; | ||
30 | } | ||
31 | |||
32 | void bbunit_settestfailed(void) | ||
33 | { | ||
34 | test_retval = -1; | ||
35 | } | ||
36 | |||
37 | #if WANT_TIMING | ||
38 | static void timeval_diff(struct timeval* res, | ||
39 | const struct timeval* x, | ||
40 | const struct timeval* y) | ||
41 | { | ||
42 | long udiff = x->tv_usec - y->tv_usec; | ||
43 | |||
44 | res->tv_sec = x->tv_sec - y->tv_sec - (udiff < 0); | ||
45 | res->tv_usec = (udiff >= 0 ? udiff : udiff + 1000000); | ||
46 | } | ||
47 | #endif | ||
48 | |||
49 | int unit_main(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) MAIN_EXTERNALLY_VISIBLE; | ||
50 | int unit_main(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) | ||
51 | { | ||
52 | unsigned tests_run = 0; | ||
53 | unsigned tests_failed = 0; | ||
54 | #if WANT_TIMING | ||
55 | struct timeval begin; | ||
56 | struct timeval end; | ||
57 | struct timeval time_spent; | ||
58 | gettimeofday(&begin, NULL); | ||
59 | #endif | ||
60 | |||
61 | bb_error_msg("Running %d test(s)...", tests_registered); | ||
62 | for (;;) { | ||
63 | struct bbunit_listelem* el = llist_pop(&tests); | ||
64 | if (!el) | ||
65 | break; | ||
66 | bb_error_msg("Case: [%s]", el->name); | ||
67 | test_retval = 0; | ||
68 | el->testfunc(); | ||
69 | if (test_retval < 0) { | ||
70 | bb_error_msg("[ERROR] [%s]: TEST FAILED", el->name); | ||
71 | tests_failed++; | ||
72 | } | ||
73 | tests_run++; | ||
74 | el = el->next; | ||
75 | } | ||
76 | |||
77 | #if WANT_TIMING | ||
78 | gettimeofday(&end, NULL); | ||
79 | timeval_diff(&time_spent, &end, &begin); | ||
80 | bb_error_msg("Elapsed time %u.%06u seconds" | ||
81 | (int)time_spent.tv_sec, | ||
82 | (int)time_spent.tv_usec); | ||
83 | #endif | ||
84 | if (tests_failed > 0) { | ||
85 | bb_error_msg("[ERROR] %u test(s) FAILED", tests_failed); | ||
86 | return EXIT_FAILURE; | ||
87 | } | ||
88 | bb_error_msg("All tests passed"); | ||
89 | return EXIT_SUCCESS; | ||
90 | } | ||
diff --git a/libbb/copy_file.c b/libbb/copy_file.c index be65c4b47..935a9a4c4 100644 --- a/libbb/copy_file.c +++ b/libbb/copy_file.c | |||
@@ -398,5 +398,9 @@ int FAST_FUNC copy_file(const char *source, const char *dest, int flags) | |||
398 | bb_perror_msg("can't preserve %s of '%s'", "permissions", dest); | 398 | bb_perror_msg("can't preserve %s of '%s'", "permissions", dest); |
399 | } | 399 | } |
400 | 400 | ||
401 | if (flags & FILEUTILS_VERBOSE) { | ||
402 | printf("'%s' -> '%s'\n", source, dest); | ||
403 | } | ||
404 | |||
401 | return retval; | 405 | return retval; |
402 | } | 406 | } |
diff --git a/libbb/make_directory.c b/libbb/make_directory.c index 1f874f1f2..3980376ec 100644 --- a/libbb/make_directory.c +++ b/libbb/make_directory.c | |||
@@ -124,6 +124,10 @@ int FAST_FUNC bb_make_directory(char *path, long mode, int flags) | |||
124 | if (!c) { | 124 | if (!c) { |
125 | goto ret0; | 125 | goto ret0; |
126 | } | 126 | } |
127 | } else { | ||
128 | if (flags & FILEUTILS_VERBOSE) { | ||
129 | printf("created directory: '%s'\n", path); | ||
130 | } | ||
127 | } | 131 | } |
128 | 132 | ||
129 | if (!c) { | 133 | if (!c) { |
diff --git a/libbb/obscure.c b/libbb/obscure.c index 24c4ac917..ad17d1ff1 100644 --- a/libbb/obscure.c +++ b/libbb/obscure.c | |||
@@ -182,3 +182,41 @@ int FAST_FUNC obscure(const char *old, const char *newval, const struct passwd * | |||
182 | } | 182 | } |
183 | return 0; | 183 | return 0; |
184 | } | 184 | } |
185 | |||
186 | #if ENABLE_UNIT_TEST | ||
187 | |||
188 | /* Test obscure_msg() instead of obscure() in order not to print anything. */ | ||
189 | |||
190 | static const struct passwd pw = { | ||
191 | .pw_name = (char *)"johndoe", | ||
192 | .pw_gecos = (char *)"John Doe", | ||
193 | }; | ||
194 | |||
195 | BBUNIT_DEFINE_TEST(obscure_weak_pass) | ||
196 | { | ||
197 | /* Empty password */ | ||
198 | BBUNIT_ASSERT_NOTNULL(obscure_msg("Ad4#21?'S|", "", &pw)); | ||
199 | /* Pure numbers */ | ||
200 | BBUNIT_ASSERT_NOTNULL(obscure_msg("Ad4#21?'S|", "23577315", &pw)); | ||
201 | /* Similar to pw_name */ | ||
202 | BBUNIT_ASSERT_NOTNULL(obscure_msg("Ad4#21?'S|", "johndoe123%", &pw)); | ||
203 | /* Similar to pw_gecos, reversed */ | ||
204 | BBUNIT_ASSERT_NOTNULL(obscure_msg("Ad4#21?'S|", "eoD nhoJ^44@", &pw)); | ||
205 | /* Similar to the old password */ | ||
206 | BBUNIT_ASSERT_NOTNULL(obscure_msg("Ad4#21?'S|", "d4#21?'S", &pw)); | ||
207 | /* adjacent letters */ | ||
208 | BBUNIT_ASSERT_NOTNULL(obscure_msg("Ad4#21?'S|", "qwerty123", &pw)); | ||
209 | /* Many similar chars */ | ||
210 | BBUNIT_ASSERT_NOTNULL(obscure_msg("Ad4#21?'S|", "^33Daaaaaa1", &pw)); | ||
211 | |||
212 | BBUNIT_ENDTEST; | ||
213 | } | ||
214 | |||
215 | BBUNIT_DEFINE_TEST(obscure_strong_pass) | ||
216 | { | ||
217 | BBUNIT_ASSERT_NULL(obscure_msg("Rt4##2&:'|", "}(^#rrSX3S*22", &pw)); | ||
218 | |||
219 | BBUNIT_ENDTEST; | ||
220 | } | ||
221 | |||
222 | #endif /* ENABLE_UNIT_TEST */ | ||
diff --git a/libbb/remove_file.c b/libbb/remove_file.c index 5b75f7f30..eaca293d9 100644 --- a/libbb/remove_file.c +++ b/libbb/remove_file.c | |||
@@ -78,6 +78,10 @@ int FAST_FUNC remove_file(const char *path, int flags) | |||
78 | return -1; | 78 | return -1; |
79 | } | 79 | } |
80 | 80 | ||
81 | if (flags & FILEUTILS_VERBOSE) { | ||
82 | printf("removed directory: '%s'\n", path); | ||
83 | } | ||
84 | |||
81 | return status; | 85 | return status; |
82 | } | 86 | } |
83 | 87 | ||
@@ -98,5 +102,9 @@ int FAST_FUNC remove_file(const char *path, int flags) | |||
98 | return -1; | 102 | return -1; |
99 | } | 103 | } |
100 | 104 | ||
105 | if (flags & FILEUTILS_VERBOSE) { | ||
106 | printf("removed '%s'\n", path); | ||
107 | } | ||
108 | |||
101 | return 0; | 109 | return 0; |
102 | } | 110 | } |
diff --git a/libbb/strrstr.c b/libbb/strrstr.c index d8823fc51..93d970a1b 100644 --- a/libbb/strrstr.c +++ b/libbb/strrstr.c | |||
@@ -7,13 +7,7 @@ | |||
7 | * Licensed under GPLv2 or later, see file LICENSE in this source tree. | 7 | * Licensed under GPLv2 or later, see file LICENSE in this source tree. |
8 | */ | 8 | */ |
9 | 9 | ||
10 | #ifdef __DO_STRRSTR_TEST | ||
11 | #include <stdlib.h> | ||
12 | #include <string.h> | ||
13 | #include <stdio.h> | ||
14 | #else | ||
15 | #include "libbb.h" | 10 | #include "libbb.h" |
16 | #endif | ||
17 | 11 | ||
18 | /* | 12 | /* |
19 | * The strrstr() function finds the last occurrence of the substring needle | 13 | * The strrstr() function finds the last occurrence of the substring needle |
@@ -34,8 +28,9 @@ char* FAST_FUNC strrstr(const char *haystack, const char *needle) | |||
34 | } | 28 | } |
35 | } | 29 | } |
36 | 30 | ||
37 | #ifdef __DO_STRRSTR_TEST | 31 | #if ENABLE_UNIT_TEST |
38 | int main(int argc, char **argv) | 32 | |
33 | BBUNIT_DEFINE_TEST(strrstr) | ||
39 | { | 34 | { |
40 | static const struct { | 35 | static const struct { |
41 | const char *h, *n; | 36 | const char *h, *n; |
@@ -59,13 +54,13 @@ int main(int argc, char **argv) | |||
59 | i = 0; | 54 | i = 0; |
60 | while (i < sizeof(test_array) / sizeof(test_array[0])) { | 55 | while (i < sizeof(test_array) / sizeof(test_array[0])) { |
61 | const char *r = strrstr(test_array[i].h, test_array[i].n); | 56 | const char *r = strrstr(test_array[i].h, test_array[i].n); |
62 | printf("'%s' vs. '%s': '%s' - ", test_array[i].h, test_array[i].n, r); | ||
63 | if (r == NULL) | 57 | if (r == NULL) |
64 | r = test_array[i].h - 1; | 58 | r = test_array[i].h - 1; |
65 | printf("%s\n", r == test_array[i].h + test_array[i].pos ? "PASSED" : "FAILED"); | 59 | BBUNIT_ASSERT_EQ(r, test_array[i].h + test_array[i].pos); |
66 | i++; | 60 | i++; |
67 | } | 61 | } |
68 | 62 | ||
69 | return 0; | 63 | BBUNIT_ENDTEST; |
70 | } | 64 | } |
71 | #endif | 65 | |
66 | #endif /* ENABLE_UNIT_TEST */ | ||
diff --git a/libbb/sysconf.c b/libbb/sysconf.c new file mode 100644 index 000000000..031901980 --- /dev/null +++ b/libbb/sysconf.c | |||
@@ -0,0 +1,22 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * Various system configuration helpers. | ||
4 | * | ||
5 | * Copyright (C) 2014 Bartosz Golaszewski <bartekgola@gmail.com> | ||
6 | * | ||
7 | * Licensed under GPLv2 or later, see file LICENSE in this source tree. | ||
8 | */ | ||
9 | #include "libbb.h" | ||
10 | |||
11 | #if !defined(ARG_MAX) && defined(_SC_ARG_MAX) | ||
12 | unsigned FAST_FUNC bb_arg_max(void) | ||
13 | { | ||
14 | return sysconf(_SC_ARG_MAX); | ||
15 | } | ||
16 | #endif | ||
17 | |||
18 | /* Return the number of clock ticks per second. */ | ||
19 | unsigned FAST_FUNC bb_clk_tck(void) | ||
20 | { | ||
21 | return sysconf(_SC_CLK_TCK); | ||
22 | } | ||
diff --git a/loginutils/getty.c b/loginutils/getty.c index 0f060ae6c..4b1b73bef 100644 --- a/loginutils/getty.c +++ b/loginutils/getty.c | |||
@@ -556,6 +556,16 @@ int getty_main(int argc UNUSED_PARAM, char **argv) | |||
556 | // pid, getppid(), | 556 | // pid, getppid(), |
557 | // getsid(0), getpgid(0)); | 557 | // getsid(0), getpgid(0)); |
558 | bb_perror_msg_and_die("setsid"); | 558 | bb_perror_msg_and_die("setsid"); |
559 | /* | ||
560 | * When we can end up here? | ||
561 | * Example: setsid() fails when run alone in interactive shell: | ||
562 | * # getty 115200 /dev/tty2 | ||
563 | * because shell's child (getty) is put in a new process group. | ||
564 | * But doesn't fail if shell is not interactive | ||
565 | * (and therefore doesn't create process groups for pipes), | ||
566 | * or if getty is not the first process in the process group: | ||
567 | * # true | getty 115200 /dev/tty2 | ||
568 | */ | ||
559 | } | 569 | } |
560 | /* Looks like we are already a session leader. | 570 | /* Looks like we are already a session leader. |
561 | * In this case (setsid failed) we may still have ctty, | 571 | * In this case (setsid failed) we may still have ctty, |
diff --git a/miscutils/nandwrite.c b/miscutils/nandwrite.c index e3f9b565d..29ff351a3 100644 --- a/miscutils/nandwrite.c +++ b/miscutils/nandwrite.c | |||
@@ -36,14 +36,16 @@ | |||
36 | //usage: "\n -s ADDR Start address" | 36 | //usage: "\n -s ADDR Start address" |
37 | 37 | ||
38 | //usage:#define nanddump_trivial_usage | 38 | //usage:#define nanddump_trivial_usage |
39 | //usage: "[-o] [-b] [-s ADDR] [-l LEN] [-f FILE] MTD_DEVICE" | 39 | //usage: "[-o] [--bb=padbad|skipbad] [-s ADDR] [-l LEN] [-f FILE] MTD_DEVICE" |
40 | //usage:#define nanddump_full_usage "\n\n" | 40 | //usage:#define nanddump_full_usage "\n\n" |
41 | //usage: "Dump MTD_DEVICE\n" | 41 | //usage: "Dump MTD_DEVICE\n" |
42 | //usage: "\n -o Dump oob data" | 42 | //usage: "\n -o Dump oob data" |
43 | //usage: "\n -b Omit bad block from the dump" | ||
44 | //usage: "\n -s ADDR Start address" | 43 | //usage: "\n -s ADDR Start address" |
45 | //usage: "\n -l LEN Length" | 44 | //usage: "\n -l LEN Length" |
46 | //usage: "\n -f FILE Dump to file ('-' for stdout)" | 45 | //usage: "\n -f FILE Dump to file ('-' for stdout)" |
46 | //usage: "\n --bb=METHOD:" | ||
47 | //usage: "\n skipbad: skip bad blocks" | ||
48 | //usage: "\n padbad: substitute bad blocks by 0xff (default)" | ||
47 | 49 | ||
48 | #include "libbb.h" | 50 | #include "libbb.h" |
49 | #include <mtd/mtd-user.h> | 51 | #include <mtd/mtd-user.h> |
@@ -54,9 +56,12 @@ | |||
54 | #define OPT_p (1 << 0) /* nandwrite only */ | 56 | #define OPT_p (1 << 0) /* nandwrite only */ |
55 | #define OPT_o (1 << 0) /* nanddump only */ | 57 | #define OPT_o (1 << 0) /* nanddump only */ |
56 | #define OPT_s (1 << 1) | 58 | #define OPT_s (1 << 1) |
57 | #define OPT_b (1 << 2) | 59 | #define OPT_f (1 << 2) |
58 | #define OPT_f (1 << 3) | 60 | #define OPT_l (1 << 3) |
59 | #define OPT_l (1 << 4) | 61 | #define OPT_bb (1 << 4) /* must be the last one in the list */ |
62 | |||
63 | #define BB_PADBAD (1 << 0) | ||
64 | #define BB_SKIPBAD (1 << 1) | ||
60 | 65 | ||
61 | /* helper for writing out 0xff for bad blocks pad */ | 66 | /* helper for writing out 0xff for bad blocks pad */ |
62 | static void dump_bad(struct mtd_info_user *meminfo, unsigned len, int oob) | 67 | static void dump_bad(struct mtd_info_user *meminfo, unsigned len, int oob) |
@@ -64,8 +69,8 @@ static void dump_bad(struct mtd_info_user *meminfo, unsigned len, int oob) | |||
64 | unsigned char buf[meminfo->writesize]; | 69 | unsigned char buf[meminfo->writesize]; |
65 | unsigned count; | 70 | unsigned count; |
66 | 71 | ||
67 | /* round len to the next page */ | 72 | /* round len to the next page only if len is not already on a page */ |
68 | len = (len | ~(meminfo->writesize - 1)) + 1; | 73 | len = ((len - 1) | (meminfo->writesize - 1)) + 1; |
69 | 74 | ||
70 | memset(buf, 0xff, sizeof(buf)); | 75 | memset(buf, 0xff, sizeof(buf)); |
71 | for (count = 0; count < len; count += meminfo->writesize) { | 76 | for (count = 0; count < len; count += meminfo->writesize) { |
@@ -102,6 +107,7 @@ int nandwrite_main(int argc UNUSED_PARAM, char **argv) | |||
102 | /* Buffer for OOB data */ | 107 | /* Buffer for OOB data */ |
103 | unsigned char *oobbuf; | 108 | unsigned char *oobbuf; |
104 | unsigned opts; | 109 | unsigned opts; |
110 | unsigned bb_method = BB_SKIPBAD; | ||
105 | int fd; | 111 | int fd; |
106 | ssize_t cnt; | 112 | ssize_t cnt; |
107 | unsigned mtdoffset, meminfo_writesize, blockstart, limit; | 113 | unsigned mtdoffset, meminfo_writesize, blockstart, limit; |
@@ -109,11 +115,14 @@ int nandwrite_main(int argc UNUSED_PARAM, char **argv) | |||
109 | struct mtd_info_user meminfo; | 115 | struct mtd_info_user meminfo; |
110 | struct mtd_oob_buf oob; | 116 | struct mtd_oob_buf oob; |
111 | unsigned char *filebuf; | 117 | unsigned char *filebuf; |
112 | const char *opt_s = "0", *opt_f = "-", *opt_l; | 118 | const char *opt_s = "0", *opt_f = "-", *opt_l, *opt_bb; |
119 | static const char nanddump_longopts[] ALIGN1 = | ||
120 | "bb\0" Required_argument "\xff"; /* no short equivalent */ | ||
113 | 121 | ||
114 | if (IS_NANDDUMP) { | 122 | if (IS_NANDDUMP) { |
115 | opt_complementary = "=1"; | 123 | opt_complementary = "=1"; |
116 | opts = getopt32(argv, "os:bf:l:", &opt_s, &opt_f, &opt_l); | 124 | applet_long_options = nanddump_longopts; |
125 | opts = getopt32(argv, "os:f:l:", &opt_s, &opt_f, &opt_l, &opt_bb); | ||
117 | } else { /* nandwrite */ | 126 | } else { /* nandwrite */ |
118 | opt_complementary = "-1:?2"; | 127 | opt_complementary = "-1:?2"; |
119 | opts = getopt32(argv, "ps:", &opt_s); | 128 | opts = getopt32(argv, "ps:", &opt_s); |
@@ -138,6 +147,14 @@ int nandwrite_main(int argc UNUSED_PARAM, char **argv) | |||
138 | if (length < meminfo.size - mtdoffset) | 147 | if (length < meminfo.size - mtdoffset) |
139 | end_addr = mtdoffset + length; | 148 | end_addr = mtdoffset + length; |
140 | } | 149 | } |
150 | if (IS_NANDDUMP && (opts & OPT_bb)) { | ||
151 | if (strcmp("skipbad", opt_bb) == 0) | ||
152 | bb_method = BB_SKIPBAD; | ||
153 | else if (strcmp("padbad", opt_bb) == 0) | ||
154 | bb_method = BB_PADBAD; | ||
155 | else | ||
156 | bb_show_usage(); | ||
157 | } | ||
141 | 158 | ||
142 | /* Pull it into a CPU register (hopefully) - smaller code that way */ | 159 | /* Pull it into a CPU register (hopefully) - smaller code that way */ |
143 | meminfo_writesize = meminfo.writesize; | 160 | meminfo_writesize = meminfo.writesize; |
@@ -162,9 +179,15 @@ int nandwrite_main(int argc UNUSED_PARAM, char **argv) | |||
162 | tmp = next_good_eraseblock(fd, &meminfo, blockstart); | 179 | tmp = next_good_eraseblock(fd, &meminfo, blockstart); |
163 | if (tmp != blockstart) { | 180 | if (tmp != blockstart) { |
164 | /* bad block(s), advance mtdoffset */ | 181 | /* bad block(s), advance mtdoffset */ |
165 | if (IS_NANDDUMP && !(opts & OPT_b)) { | 182 | if (IS_NANDDUMP) { |
166 | int bad_len = MIN(tmp, end_addr) - mtdoffset; | 183 | if (bb_method == BB_PADBAD) { |
167 | dump_bad(&meminfo, bad_len, opts & OPT_o); | 184 | int bad_len = MIN(tmp, end_addr) - mtdoffset; |
185 | dump_bad(&meminfo, bad_len, opts & OPT_o); | ||
186 | } | ||
187 | /* with option skipbad, increase the total length */ | ||
188 | if (bb_method == BB_SKIPBAD) { | ||
189 | end_addr += (tmp - blockstart); | ||
190 | } | ||
168 | } | 191 | } |
169 | mtdoffset = tmp; | 192 | mtdoffset = tmp; |
170 | } | 193 | } |
@@ -182,9 +205,19 @@ int nandwrite_main(int argc UNUSED_PARAM, char **argv) | |||
182 | mtdoffset = next_good_eraseblock(fd, &meminfo, blockstart); | 205 | mtdoffset = next_good_eraseblock(fd, &meminfo, blockstart); |
183 | if (IS_NANDWRITE) | 206 | if (IS_NANDWRITE) |
184 | printf("Writing at 0x%08x\n", mtdoffset); | 207 | printf("Writing at 0x%08x\n", mtdoffset); |
185 | else if (mtdoffset > blockstart && !(opts & OPT_b)) { | 208 | else if (mtdoffset > blockstart) { |
186 | int bad_len = MIN(mtdoffset, limit) - blockstart; | 209 | if (bb_method == BB_PADBAD) { |
187 | dump_bad(&meminfo, bad_len, opts & OPT_o); | 210 | /* dump FF padded bad block */ |
211 | int bad_len = MIN(mtdoffset, limit) - blockstart; | ||
212 | dump_bad(&meminfo, bad_len, opts & OPT_o); | ||
213 | } else if (bb_method == BB_SKIPBAD) { | ||
214 | /* for skipbad, increase the length */ | ||
215 | if ((end_addr + mtdoffset - blockstart) > end_addr) | ||
216 | end_addr += (mtdoffset - blockstart); | ||
217 | else | ||
218 | end_addr = ~0; | ||
219 | limit = MIN(meminfo.size, end_addr); | ||
220 | } | ||
188 | } | 221 | } |
189 | if (mtdoffset >= limit) | 222 | if (mtdoffset >= limit) |
190 | break; | 223 | break; |
diff --git a/modutils/modprobe-small.c b/modutils/modprobe-small.c index 91e0c1380..b7990bff1 100644 --- a/modutils/modprobe-small.c +++ b/modutils/modprobe-small.c | |||
@@ -297,7 +297,7 @@ static int pathname_matches_modname(const char *pathname, const char *modname) | |||
297 | char name[MODULE_NAME_LEN]; | 297 | char name[MODULE_NAME_LEN]; |
298 | const char *fname = bb_get_last_path_component_nostrip(pathname); | 298 | const char *fname = bb_get_last_path_component_nostrip(pathname); |
299 | const char *suffix = strrstr(fname, ".ko"); | 299 | const char *suffix = strrstr(fname, ".ko"); |
300 | safe_strncpy(name, fname, suffix - fname); | 300 | safe_strncpy(name, fname, suffix - fname + 1); |
301 | replace(name, '-', '_'); | 301 | replace(name, '-', '_'); |
302 | r = (strcmp(name, modname) == 0); | 302 | r = (strcmp(name, modname) == 0); |
303 | return r; | 303 | return r; |
diff --git a/networking/ftpd.c b/networking/ftpd.c index 33db964fa..2d2a3a44c 100644 --- a/networking/ftpd.c +++ b/networking/ftpd.c | |||
@@ -623,13 +623,7 @@ popen_ls(const char *opt) | |||
623 | 623 | ||
624 | argv[0] = "ftpd"; | 624 | argv[0] = "ftpd"; |
625 | argv[1] = opt; /* "-l" or "-1" */ | 625 | argv[1] = opt; /* "-l" or "-1" */ |
626 | #if BB_MMU | ||
627 | argv[2] = "--"; | 626 | argv[2] = "--"; |
628 | #else | ||
629 | /* NOMMU ftpd ls helper chdirs to argv[2], | ||
630 | * preventing peer from seeing real root. */ | ||
631 | argv[2] = xrealloc_getcwd_or_warn(NULL); | ||
632 | #endif | ||
633 | argv[3] = G.ftp_arg; | 627 | argv[3] = G.ftp_arg; |
634 | argv[4] = NULL; | 628 | argv[4] = NULL; |
635 | 629 | ||
@@ -650,17 +644,10 @@ popen_ls(const char *opt) | |||
650 | /*fflush_all(); - so far we dont use stdio on output */ | 644 | /*fflush_all(); - so far we dont use stdio on output */ |
651 | pid = BB_MMU ? xfork() : xvfork(); | 645 | pid = BB_MMU ? xfork() : xvfork(); |
652 | if (pid == 0) { | 646 | if (pid == 0) { |
653 | /* child */ | ||
654 | #if !BB_MMU | 647 | #if !BB_MMU |
655 | /* On NOMMU, we want to execute a child - copy of ourself. | 648 | int cur_fd; |
656 | * In chroot we usually can't do it. Thus we chdir | ||
657 | * out of the chroot back to original root, | ||
658 | * and (see later below) execute bb_busybox_exec_path | ||
659 | * relative to current directory */ | ||
660 | if (fchdir(G.root_fd) != 0) | ||
661 | _exit(127); | ||
662 | /*close(G.root_fd); - close_on_exec_on() took care of this */ | ||
663 | #endif | 649 | #endif |
650 | /* child */ | ||
664 | /* NB: close _first_, then move fd! */ | 651 | /* NB: close _first_, then move fd! */ |
665 | close(outfd.rd); | 652 | close(outfd.rd); |
666 | xmove_fd(outfd.wr, STDOUT_FILENO); | 653 | xmove_fd(outfd.wr, STDOUT_FILENO); |
@@ -674,19 +661,26 @@ popen_ls(const char *opt) | |||
674 | /* memset(&G, 0, sizeof(G)); - ls_main does it */ | 661 | /* memset(&G, 0, sizeof(G)); - ls_main does it */ |
675 | exit(ls_main(ARRAY_SIZE(argv) - 1, (char**) argv)); | 662 | exit(ls_main(ARRAY_SIZE(argv) - 1, (char**) argv)); |
676 | #else | 663 | #else |
677 | /* + 1: we must use relative path here if in chroot. | 664 | cur_fd = xopen(".", O_RDONLY | O_DIRECTORY); |
678 | * For example, execv("/proc/self/exe") will fail, since | 665 | /* On NOMMU, we want to execute a child - copy of ourself |
679 | * it looks for "/proc/self/exe" _relative to chroot!_ */ | 666 | * in order to unblock parent after vfork. |
680 | execv(bb_busybox_exec_path + 1, (char**) argv); | 667 | * In chroot we usually can't re-exec. Thus we escape |
668 | * out of the chroot back to original root. | ||
669 | */ | ||
670 | if (G.root_fd >= 0) { | ||
671 | if (fchdir(G.root_fd) != 0 || chroot(".") != 0) | ||
672 | _exit(127); | ||
673 | /*close(G.root_fd); - close_on_exec_on() took care of this */ | ||
674 | } | ||
675 | /* Child expects directory to list on fd #3 */ | ||
676 | xmove_fd(cur_fd, 3); | ||
677 | execv(bb_busybox_exec_path, (char**) argv); | ||
681 | _exit(127); | 678 | _exit(127); |
682 | #endif | 679 | #endif |
683 | } | 680 | } |
684 | 681 | ||
685 | /* parent */ | 682 | /* parent */ |
686 | close(outfd.wr); | 683 | close(outfd.wr); |
687 | #if !BB_MMU | ||
688 | free((char*)argv[2]); | ||
689 | #endif | ||
690 | return outfd.rd; | 684 | return outfd.rd; |
691 | } | 685 | } |
692 | 686 | ||
@@ -705,10 +699,9 @@ handle_dir_common(int opts) | |||
705 | if (!(opts & USE_CTRL_CONN) && !port_or_pasv_was_seen()) | 699 | if (!(opts & USE_CTRL_CONN) && !port_or_pasv_was_seen()) |
706 | return; /* port_or_pasv_was_seen emitted error response */ | 700 | return; /* port_or_pasv_was_seen emitted error response */ |
707 | 701 | ||
708 | /* -n prevents user/groupname display, | ||
709 | * which can be problematic in chroot */ | ||
710 | ls_fd = popen_ls((opts & LONG_LISTING) ? "-l" : "-1"); | 702 | ls_fd = popen_ls((opts & LONG_LISTING) ? "-l" : "-1"); |
711 | ls_fp = xfdopen_for_read(ls_fd); | 703 | ls_fp = xfdopen_for_read(ls_fd); |
704 | /* FIXME: filenames with embedded newlines are mishandled */ | ||
712 | 705 | ||
713 | if (opts & USE_CTRL_CONN) { | 706 | if (opts & USE_CTRL_CONN) { |
714 | /* STAT <filename> */ | 707 | /* STAT <filename> */ |
@@ -729,16 +722,20 @@ handle_dir_common(int opts) | |||
729 | int remote_fd = get_remote_transfer_fd(" Directory listing"); | 722 | int remote_fd = get_remote_transfer_fd(" Directory listing"); |
730 | if (remote_fd >= 0) { | 723 | if (remote_fd >= 0) { |
731 | while (1) { | 724 | while (1) { |
732 | line = xmalloc_fgetline(ls_fp); | 725 | unsigned len; |
726 | |||
727 | line = xmalloc_fgets(ls_fp); | ||
733 | if (!line) | 728 | if (!line) |
734 | break; | 729 | break; |
735 | /* I've seen clients complaining when they | 730 | /* I've seen clients complaining when they |
736 | * are fed with ls output with bare '\n'. | 731 | * are fed with ls output with bare '\n'. |
737 | * Pity... that would be much simpler. | 732 | * Replace trailing "\n\0" with "\r\n". |
738 | */ | 733 | */ |
739 | /* TODO: need to s/LF/NUL/g here */ | 734 | len = strlen(line); |
740 | xwrite_str(remote_fd, line); | 735 | if (len != 0) /* paranoia check */ |
741 | xwrite(remote_fd, "\r\n", 2); | 736 | line[len - 1] = '\r'; |
737 | line[len] = '\n'; | ||
738 | xwrite(remote_fd, line, len + 1); | ||
742 | free(line); | 739 | free(line); |
743 | } | 740 | } |
744 | } | 741 | } |
@@ -1085,6 +1082,8 @@ enum { | |||
1085 | const_PASV = mk_const4('P', 'A', 'S', 'V'), | 1082 | const_PASV = mk_const4('P', 'A', 'S', 'V'), |
1086 | const_PORT = mk_const4('P', 'O', 'R', 'T'), | 1083 | const_PORT = mk_const4('P', 'O', 'R', 'T'), |
1087 | const_PWD = mk_const3('P', 'W', 'D'), | 1084 | const_PWD = mk_const3('P', 'W', 'D'), |
1085 | /* Same as PWD. Reportedly used by windows ftp client */ | ||
1086 | const_XPWD = mk_const4('X', 'P', 'W', 'D'), | ||
1088 | const_QUIT = mk_const4('Q', 'U', 'I', 'T'), | 1087 | const_QUIT = mk_const4('Q', 'U', 'I', 'T'), |
1089 | const_REST = mk_const4('R', 'E', 'S', 'T'), | 1088 | const_REST = mk_const4('R', 'E', 'S', 'T'), |
1090 | const_RETR = mk_const4('R', 'E', 'T', 'R'), | 1089 | const_RETR = mk_const4('R', 'E', 'T', 'R'), |
@@ -1132,11 +1131,10 @@ int ftpd_main(int argc UNUSED_PARAM, char **argv) | |||
1132 | opts = getopt32(argv, "l1vS" IF_FEATURE_FTP_WRITE("w") "t:T:", &G.timeout, &abs_timeout, &G.verbose, &verbose_S); | 1131 | opts = getopt32(argv, "l1vS" IF_FEATURE_FTP_WRITE("w") "t:T:", &G.timeout, &abs_timeout, &G.verbose, &verbose_S); |
1133 | if (opts & (OPT_l|OPT_1)) { | 1132 | if (opts & (OPT_l|OPT_1)) { |
1134 | /* Our secret backdoor to ls */ | 1133 | /* Our secret backdoor to ls */ |
1135 | /* TODO: pass -n? It prevents user/group resolution, which may not work in chroot anyway */ | ||
1136 | /* TODO: pass -A? It shows dot files */ | 1134 | /* TODO: pass -A? It shows dot files */ |
1137 | /* TODO: pass --group-directories-first? would be nice, but ls doesn't do that yet */ | 1135 | /* TODO: pass --group-directories-first? would be nice, but ls doesn't do that yet */ |
1138 | xchdir(argv[2]); | 1136 | if (fchdir(3) != 0) |
1139 | argv[2] = (char*)"--"; | 1137 | _exit(127); |
1140 | /* memset(&G, 0, sizeof(G)); - ls_main does it */ | 1138 | /* memset(&G, 0, sizeof(G)); - ls_main does it */ |
1141 | return ls_main(argc, argv); | 1139 | return ls_main(argc, argv); |
1142 | } | 1140 | } |
@@ -1175,12 +1173,15 @@ int ftpd_main(int argc UNUSED_PARAM, char **argv) | |||
1175 | applet_name = xasprintf("%s[%u]", applet_name, (int)getpid()); | 1173 | applet_name = xasprintf("%s[%u]", applet_name, (int)getpid()); |
1176 | 1174 | ||
1177 | #if !BB_MMU | 1175 | #if !BB_MMU |
1178 | G.root_fd = xopen("/", O_RDONLY | O_DIRECTORY); | 1176 | G.root_fd = -1; |
1179 | close_on_exec_on(G.root_fd); | ||
1180 | #endif | 1177 | #endif |
1181 | 1178 | argv += optind; | |
1182 | if (argv[optind]) { | 1179 | if (argv[0]) { |
1183 | xchroot(argv[optind]); | 1180 | #if !BB_MMU |
1181 | G.root_fd = xopen("/", O_RDONLY | O_DIRECTORY); | ||
1182 | close_on_exec_on(G.root_fd); | ||
1183 | #endif | ||
1184 | xchroot(argv[0]); | ||
1184 | } | 1185 | } |
1185 | 1186 | ||
1186 | //umask(077); - admin can set umask before starting us | 1187 | //umask(077); - admin can set umask before starting us |
@@ -1292,7 +1293,7 @@ int ftpd_main(int argc UNUSED_PARAM, char **argv) | |||
1292 | WRITE_OK(FTP_ALLOOK); | 1293 | WRITE_OK(FTP_ALLOOK); |
1293 | else if (cmdval == const_SYST) | 1294 | else if (cmdval == const_SYST) |
1294 | cmdio_write_raw(STR(FTP_SYSTOK)" UNIX Type: L8\r\n"); | 1295 | cmdio_write_raw(STR(FTP_SYSTOK)" UNIX Type: L8\r\n"); |
1295 | else if (cmdval == const_PWD) | 1296 | else if (cmdval == const_PWD || cmdval == const_XPWD) |
1296 | handle_pwd(); | 1297 | handle_pwd(); |
1297 | else if (cmdval == const_CWD) | 1298 | else if (cmdval == const_CWD) |
1298 | handle_cwd(); | 1299 | handle_cwd(); |
diff --git a/networking/libiproute/iproute.c b/networking/libiproute/iproute.c index f8a67d9ee..ec4d8ba03 100644 --- a/networking/libiproute/iproute.c +++ b/networking/libiproute/iproute.c | |||
@@ -73,7 +73,7 @@ static unsigned get_hz(void) | |||
73 | fclose(fp); | 73 | fclose(fp); |
74 | } | 74 | } |
75 | if (!hz_internal) | 75 | if (!hz_internal) |
76 | hz_internal = sysconf(_SC_CLK_TCK); | 76 | hz_internal = bb_clk_tck(); |
77 | return hz_internal; | 77 | return hz_internal; |
78 | } | 78 | } |
79 | 79 | ||
diff --git a/networking/route.c b/networking/route.c index 4235ea72c..24cc2eb5a 100644 --- a/networking/route.c +++ b/networking/route.c | |||
@@ -283,7 +283,7 @@ static NOINLINE void INET_setroute(int action, char **args) | |||
283 | if (k == KW_IPVx_IRTT) { | 283 | if (k == KW_IPVx_IRTT) { |
284 | rt->rt_flags |= RTF_IRTT; | 284 | rt->rt_flags |= RTF_IRTT; |
285 | rt->rt_irtt = xatoul(args_m1); | 285 | rt->rt_irtt = xatoul(args_m1); |
286 | rt->rt_irtt *= (sysconf(_SC_CLK_TCK) / 100); /* FIXME */ | 286 | rt->rt_irtt *= (bb_clk_tck() / 100); /* FIXME */ |
287 | #if 0 /* FIXME: do we need to check anything of this? */ | 287 | #if 0 /* FIXME: do we need to check anything of this? */ |
288 | if (rt->rt_irtt < 1 || rt->rt_irtt > (120 * HZ)) { | 288 | if (rt->rt_irtt < 1 || rt->rt_irtt > (120 * HZ)) { |
289 | bb_error_msg_and_die("bad irtt"); | 289 | bb_error_msg_and_die("bad irtt"); |
diff --git a/networking/udhcp/Config.src b/networking/udhcp/Config.src index 6bfa398ea..c34c8d6f0 100644 --- a/networking/udhcp/Config.src +++ b/networking/udhcp/Config.src | |||
@@ -84,6 +84,17 @@ config FEATURE_UDHCPC_ARPING | |||
84 | will DHCPDECLINE the offer if the address is in use, | 84 | will DHCPDECLINE the offer if the address is in use, |
85 | and restart the discover process. | 85 | and restart the discover process. |
86 | 86 | ||
87 | config FEATURE_UDHCPC_SANITIZEOPT | ||
88 | bool "Do not pass malformed host and domain names" | ||
89 | default y | ||
90 | depends on UDHCPC | ||
91 | help | ||
92 | If selected, udhcpc will check some options (such as option 12 - | ||
93 | hostname) and if they don't look like valid hostnames | ||
94 | (for example, if they start with dash or contain spaces), | ||
95 | they will be replaced with string "bad" when exporting | ||
96 | to the environment. | ||
97 | |||
87 | config FEATURE_UDHCP_PORT | 98 | config FEATURE_UDHCP_PORT |
88 | bool "Enable '-P port' option for udhcpd and udhcpc" | 99 | bool "Enable '-P port' option for udhcpd and udhcpc" |
89 | default n | 100 | default n |
diff --git a/networking/udhcp/dhcpc.c b/networking/udhcp/dhcpc.c index 7dfc160e2..e468b7bbb 100644 --- a/networking/udhcp/dhcpc.c +++ b/networking/udhcp/dhcpc.c | |||
@@ -136,6 +136,7 @@ static int mton(uint32_t mask) | |||
136 | return i; | 136 | return i; |
137 | } | 137 | } |
138 | 138 | ||
139 | #if ENABLE_FEATURE_UDHCPC_SANITIZEOPT | ||
139 | /* Check if a given label represents a valid DNS label | 140 | /* Check if a given label represents a valid DNS label |
140 | * Return pointer to the first character after the label upon success, | 141 | * Return pointer to the first character after the label upon success, |
141 | * NULL otherwise. | 142 | * NULL otherwise. |
@@ -192,6 +193,9 @@ static int good_hostname(const char *name) | |||
192 | name++; | 193 | name++; |
193 | } | 194 | } |
194 | } | 195 | } |
196 | #else | ||
197 | # define good_hostname(name) 1 | ||
198 | #endif | ||
195 | 199 | ||
196 | /* Create "opt_name=opt_value" string */ | 200 | /* Create "opt_name=opt_value" string */ |
197 | static NOINLINE char *xmalloc_optname_optval(uint8_t *option, const struct dhcp_optflag *optflag, const char *opt_name) | 201 | static NOINLINE char *xmalloc_optname_optval(uint8_t *option, const struct dhcp_optflag *optflag, const char *opt_name) |
diff --git a/procps/iostat.c b/procps/iostat.c index 978d23430..8d272c87c 100644 --- a/procps/iostat.c +++ b/procps/iostat.c | |||
@@ -109,11 +109,6 @@ enum { | |||
109 | OPT_m = 1 << 5, | 109 | OPT_m = 1 << 5, |
110 | }; | 110 | }; |
111 | 111 | ||
112 | static ALWAYS_INLINE unsigned get_user_hz(void) | ||
113 | { | ||
114 | return sysconf(_SC_CLK_TCK); | ||
115 | } | ||
116 | |||
117 | static ALWAYS_INLINE int this_is_smp(void) | 112 | static ALWAYS_INLINE int this_is_smp(void) |
118 | { | 113 | { |
119 | return (G.total_cpus > 1); | 114 | return (G.total_cpus > 1); |
@@ -414,7 +409,7 @@ int iostat_main(int argc UNUSED_PARAM, char **argv) | |||
414 | memset(&stats_data, 0, sizeof(stats_data)); | 409 | memset(&stats_data, 0, sizeof(stats_data)); |
415 | 410 | ||
416 | /* Get number of clock ticks per sec */ | 411 | /* Get number of clock ticks per sec */ |
417 | G.clk_tck = get_user_hz(); | 412 | G.clk_tck = bb_clk_tck(); |
418 | 413 | ||
419 | /* Determine number of CPUs */ | 414 | /* Determine number of CPUs */ |
420 | G.total_cpus = get_cpu_count(); | 415 | G.total_cpus = get_cpu_count(); |
diff --git a/procps/mpstat.c b/procps/mpstat.c index aa5a5c73f..c628d6215 100644 --- a/procps/mpstat.c +++ b/procps/mpstat.c | |||
@@ -775,12 +775,6 @@ static void main_loop(void) | |||
775 | 775 | ||
776 | /* Initialization */ | 776 | /* Initialization */ |
777 | 777 | ||
778 | /* Get number of clock ticks per sec */ | ||
779 | static ALWAYS_INLINE unsigned get_hz(void) | ||
780 | { | ||
781 | return sysconf(_SC_CLK_TCK); | ||
782 | } | ||
783 | |||
784 | static void alloc_struct(int cpus) | 778 | static void alloc_struct(int cpus) |
785 | { | 779 | { |
786 | int i; | 780 | int i; |
@@ -873,7 +867,7 @@ int mpstat_main(int UNUSED_PARAM argc, char **argv) | |||
873 | G.cpu_nr = get_cpu_count(); | 867 | G.cpu_nr = get_cpu_count(); |
874 | 868 | ||
875 | /* Get number of clock ticks per sec */ | 869 | /* Get number of clock ticks per sec */ |
876 | G.hz = get_hz(); | 870 | G.hz = bb_clk_tck(); |
877 | 871 | ||
878 | /* Calculate number of interrupts per processor */ | 872 | /* Calculate number of interrupts per processor */ |
879 | G.irqcpu_nr = get_irqcpu_nr(PROCFS_INTERRUPTS, NR_IRQS) + NR_IRQCPU_PREALLOC; | 873 | G.irqcpu_nr = get_irqcpu_nr(PROCFS_INTERRUPTS, NR_IRQS) + NR_IRQCPU_PREALLOC; |
diff --git a/shell/ash.c b/shell/ash.c index c3e950893..550a98db6 100644 --- a/shell/ash.c +++ b/shell/ash.c | |||
@@ -13222,7 +13222,7 @@ timescmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) | |||
13222 | const unsigned char *p; | 13222 | const unsigned char *p; |
13223 | struct tms buf; | 13223 | struct tms buf; |
13224 | 13224 | ||
13225 | clk_tck = sysconf(_SC_CLK_TCK); | 13225 | clk_tck = bb_clk_tck(); |
13226 | times(&buf); | 13226 | times(&buf); |
13227 | 13227 | ||
13228 | p = timescmd_str; | 13228 | p = timescmd_str; |
diff --git a/sysklogd/syslogd.c b/sysklogd/syslogd.c index a6a4ff25c..f75851085 100644 --- a/sysklogd/syslogd.c +++ b/sysklogd/syslogd.c | |||
@@ -648,22 +648,24 @@ static void log_locally(time_t now, char *msg, logFile_t *log_file) | |||
648 | } | 648 | } |
649 | /* newFile == "f.0" now */ | 649 | /* newFile == "f.0" now */ |
650 | rename(log_file->path, newFile); | 650 | rename(log_file->path, newFile); |
651 | /* Incredibly, if F and F.0 are hardlinks, POSIX | 651 | } |
652 | * _demands_ that rename returns 0 but does not | 652 | |
653 | * remove F!!! | 653 | /* We may or may not have just renamed the file away; |
654 | * (hardlinked F/F.0 pair was observed after | 654 | * if we didn't rename because we aren't keeping any backlog, |
655 | * power failure during rename()). | 655 | * then it's time to clobber the file. If we did rename it..., |
656 | * Ensure old file is gone: | 656 | * incredibly, if F and F.0 are hardlinks, POSIX _demands_ |
657 | */ | 657 | * that rename returns 0 but does not remove F!!! |
658 | unlink(log_file->path); | 658 | * (hardlinked F/F.0 pair was observed after |
659 | * power failure during rename()). | ||
660 | * So ensure old file is gone in any case: | ||
661 | */ | ||
662 | unlink(log_file->path); | ||
659 | #ifdef SYSLOGD_WRLOCK | 663 | #ifdef SYSLOGD_WRLOCK |
660 | fl.l_type = F_UNLCK; | 664 | fl.l_type = F_UNLCK; |
661 | fcntl(log_file->fd, F_SETLKW, &fl); | 665 | fcntl(log_file->fd, F_SETLKW, &fl); |
662 | #endif | 666 | #endif |
663 | close(log_file->fd); | 667 | close(log_file->fd); |
664 | goto reopen; | 668 | goto reopen; |
665 | } | ||
666 | ftruncate(log_file->fd, 0); | ||
667 | } | 669 | } |
668 | log_file->size += | 670 | log_file->size += |
669 | #endif | 671 | #endif |
diff --git a/testsuite/awk.tests b/testsuite/awk.tests index 132afc6a9..9e6952ffd 100755 --- a/testsuite/awk.tests +++ b/testsuite/awk.tests | |||
@@ -295,6 +295,22 @@ testing "awk -e and ARGC" \ | |||
295 | "" | 295 | "" |
296 | SKIP= | 296 | SKIP= |
297 | 297 | ||
298 | # The examples are in fact not valid awk programs (break/continue | ||
299 | # can only be used inside loops). | ||
300 | # But we do accept them outside of loops. | ||
301 | # We had a bug with misparsing "break ; else" sequence. | ||
302 | # Test that *that* bug is fixed, using simplest possible scripts: | ||
303 | testing "awk break" \ | ||
304 | "awk -f - 2>&1; echo \$?" \ | ||
305 | "0\n" \ | ||
306 | "" \ | ||
307 | 'BEGIN { if (1) break; else a = 1 }' | ||
308 | testing "awk continue" \ | ||
309 | "awk -f - 2>&1; echo \$?" \ | ||
310 | "0\n" \ | ||
311 | "" \ | ||
312 | 'BEGIN { if (1) continue; else a = 1 }' | ||
313 | |||
298 | # testing "description" "command" "result" "infile" "stdin" | 314 | # testing "description" "command" "result" "infile" "stdin" |
299 | 315 | ||
300 | exit $FAILCOUNT | 316 | exit $FAILCOUNT |
diff --git a/testsuite/find.tests b/testsuite/find.tests index 345d1e82e..f041106c3 100755 --- a/testsuite/find.tests +++ b/testsuite/find.tests | |||
@@ -15,6 +15,32 @@ testing "find -type f" \ | |||
15 | "./testfile\n" \ | 15 | "./testfile\n" \ |
16 | "" "" | 16 | "" "" |
17 | 17 | ||
18 | optional FEATURE_FIND_EXEC | ||
19 | testing "find -exec exitcode 1" \ | ||
20 | "cd find.tempdir && find testfile -exec true {} \; 2>&1; echo \$?" \ | ||
21 | "0\n" \ | ||
22 | "" "" | ||
23 | SKIP= | ||
24 | optional FEATURE_FIND_EXEC_PLUS | ||
25 | testing "find -exec exitcode 2" \ | ||
26 | "cd find.tempdir && find testfile -exec true {} + 2>&1; echo \$?" \ | ||
27 | "0\n" \ | ||
28 | "" "" | ||
29 | SKIP= | ||
30 | # Surprisingly, "-exec false ;" results in exitcode 0! "-exec false +" is different!!! | ||
31 | optional FEATURE_FIND_EXEC | ||
32 | testing "find -exec exitcode 3" \ | ||
33 | "cd find.tempdir && find testfile -exec false {} \; 2>&1; echo \$?" \ | ||
34 | "0\n" \ | ||
35 | "" "" | ||
36 | SKIP= | ||
37 | optional FEATURE_FIND_EXEC_PLUS | ||
38 | testing "find -exec exitcode 4" \ | ||
39 | "cd find.tempdir && find testfile -exec false {} + 2>&1; echo \$?" \ | ||
40 | "1\n" \ | ||
41 | "" "" | ||
42 | SKIP= | ||
43 | |||
18 | # testing "description" "command" "result" "infile" "stdin" | 44 | # testing "description" "command" "result" "infile" "stdin" |
19 | 45 | ||
20 | rm -rf find.tempdir | 46 | rm -rf find.tempdir |
diff --git a/util-linux/fatattr.c b/util-linux/fatattr.c new file mode 100644 index 000000000..0f8d63268 --- /dev/null +++ b/util-linux/fatattr.c | |||
@@ -0,0 +1,104 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * Display or change file attributes on a fat file system | ||
4 | * | ||
5 | * Copyright 2005 H. Peter Anvin | ||
6 | * Busybox'ed (2014) by Pascal Bellard <pascal.bellard@ads-lu.com> | ||
7 | * | ||
8 | * This file can be redistributed under the terms of the GNU General | ||
9 | * Public License | ||
10 | */ | ||
11 | //config:config FATATTR | ||
12 | //config: bool "fatattr" | ||
13 | //config: default y | ||
14 | //config: select PLATFORM_LINUX | ||
15 | //config: help | ||
16 | //config: fatattr lists or changes the file attributes on a fat file system. | ||
17 | |||
18 | //applet:IF_FATATTR(APPLET(fatattr, BB_DIR_BIN, BB_SUID_DROP)) | ||
19 | //kbuild:lib-$(CONFIG_FATATTR) += fatattr.o | ||
20 | |||
21 | //usage:#define fatattr_trivial_usage | ||
22 | //usage: "[-+rhsvda] FILE..." | ||
23 | //usage:#define fatattr_full_usage "\n\n" | ||
24 | //usage: "Change file attributes on FAT filesystem\n" | ||
25 | //usage: "\n - Clear attributes" | ||
26 | //usage: "\n + Set attributes" | ||
27 | //usage: "\n r Read only" | ||
28 | //usage: "\n h Hidden" | ||
29 | //usage: "\n s System" | ||
30 | //usage: "\n v Volume label" | ||
31 | //usage: "\n d Directory" | ||
32 | //usage: "\n a Archive" | ||
33 | |||
34 | #include "libbb.h" | ||
35 | /* linux/msdos_fs.h says: */ | ||
36 | #ifndef FAT_IOCTL_GET_ATTRIBUTES | ||
37 | # define FAT_IOCTL_GET_ATTRIBUTES _IOR('r', 0x10, __u32) | ||
38 | # define FAT_IOCTL_SET_ATTRIBUTES _IOW('r', 0x11, __u32) | ||
39 | #endif | ||
40 | |||
41 | /* Currently supports only the FAT flags, not the NTFS ones. | ||
42 | * Extra space at the end is a hack to print space separator in file listing. | ||
43 | * Let's hope no one ever passes space as an option char :) | ||
44 | */ | ||
45 | static const char bit_to_char[] = "rhsvda67 "; | ||
46 | |||
47 | static inline unsigned long get_flag(char c) | ||
48 | { | ||
49 | const char *fp = strchr(bit_to_char, c); | ||
50 | if (!fp) | ||
51 | bb_error_msg_and_die("invalid character '%c'", c); | ||
52 | return 1 << (fp - bit_to_char); | ||
53 | } | ||
54 | |||
55 | static unsigned decode_arg(const char *arg) | ||
56 | { | ||
57 | unsigned fl = 0; | ||
58 | while (*++arg) | ||
59 | fl |= get_flag(*arg); | ||
60 | return fl; | ||
61 | } | ||
62 | |||
63 | int fatattr_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | ||
64 | int fatattr_main(int argc UNUSED_PARAM, char **argv) | ||
65 | { | ||
66 | unsigned set_mask = 0; | ||
67 | unsigned clear_mask = 0; | ||
68 | |||
69 | for (;;) { | ||
70 | unsigned fl; | ||
71 | char *arg = *++argv; | ||
72 | |||
73 | if (!arg) | ||
74 | bb_show_usage(); | ||
75 | if (arg[0] != '-' && arg[0] != '+') | ||
76 | break; | ||
77 | fl = decode_arg(arg); | ||
78 | if (arg[0] == '+') | ||
79 | set_mask |= fl; | ||
80 | else | ||
81 | clear_mask |= fl; | ||
82 | } | ||
83 | |||
84 | do { | ||
85 | int fd, i; | ||
86 | uint32_t attr; | ||
87 | |||
88 | fd = xopen(*argv, O_RDONLY); | ||
89 | xioctl(fd, FAT_IOCTL_GET_ATTRIBUTES, &attr); | ||
90 | attr = (attr | set_mask) & ~clear_mask; | ||
91 | if (set_mask | clear_mask) | ||
92 | xioctl(fd, FAT_IOCTL_SET_ATTRIBUTES, &attr); | ||
93 | else { | ||
94 | for (i = 0; bit_to_char[i]; i++) { | ||
95 | bb_putchar((attr & 1) ? bit_to_char[i] : ' '); | ||
96 | attr >>= 1; | ||
97 | } | ||
98 | puts(*argv); | ||
99 | } | ||
100 | close(fd); | ||
101 | } while (*++argv); | ||
102 | |||
103 | return EXIT_SUCCESS; | ||
104 | } | ||