diff options
author | Ron Yorston <rmy@pobox.com> | 2018-03-01 15:37:12 +0000 |
---|---|---|
committer | Ron Yorston <rmy@pobox.com> | 2018-03-01 15:37:12 +0000 |
commit | 5b726f8a78c33e117c2a968739b1b4a6964905f8 (patch) | |
tree | af063c6bf3e99b7480c2fad2dffc2a76c09cb5e0 | |
parent | 5f8dac68690e92f0be220f8f8d9f797a2aedc806 (diff) | |
parent | cc222747ae7e264cbe9b1c8a9c253860275db8a9 (diff) | |
download | busybox-w32-5b726f8a78c33e117c2a968739b1b4a6964905f8.tar.gz busybox-w32-5b726f8a78c33e117c2a968739b1b4a6964905f8.tar.bz2 busybox-w32-5b726f8a78c33e117c2a968739b1b4a6964905f8.zip |
Merge branch 'busybox' into merge
35 files changed, 604 insertions, 238 deletions
diff --git a/applets/install.sh b/applets/install.sh index ae99381d7..c75a78e9d 100755 --- a/applets/install.sh +++ b/applets/install.sh | |||
@@ -38,7 +38,7 @@ while [ ${#} -gt 0 ]; do | |||
38 | shift | 38 | shift |
39 | done | 39 | done |
40 | 40 | ||
41 | if [ -n "$DO_INSTALL_LIBS" ] && [ "$DO_INSTALL_LIBS" != "n" ]; then | 41 | if [ -n "$DO_INSTALL_LIBS" ] && [ x"$DO_INSTALL_LIBS" != x"n" ]; then |
42 | # get the target dir for the libs | 42 | # get the target dir for the libs |
43 | # assume it starts with lib | 43 | # assume it starts with lib |
44 | libdir=$($CC -print-file-name=libc.so | \ | 44 | libdir=$($CC -print-file-name=libc.so | \ |
@@ -58,7 +58,7 @@ if [ -n "$DO_INSTALL_LIBS" ] && [ "$DO_INSTALL_LIBS" != "n" ]; then | |||
58 | done | 58 | done |
59 | fi | 59 | fi |
60 | 60 | ||
61 | if [ "$cleanup" = "1" ] && [ -e "$prefix/bin/busybox" ]; then | 61 | if [ x"$cleanup" = x"1" ] && [ -e "$prefix/bin/busybox" ]; then |
62 | inode=`ls -i "$prefix/bin/busybox" | awk '{print $1}'` | 62 | inode=`ls -i "$prefix/bin/busybox" | awk '{print $1}'` |
63 | sub_shell_it=` | 63 | sub_shell_it=` |
64 | cd "$prefix" | 64 | cd "$prefix" |
@@ -81,13 +81,13 @@ install -m 755 busybox "$prefix/bin/busybox" || exit 1 | |||
81 | for i in $h; do | 81 | for i in $h; do |
82 | appdir=`dirname "$i"` | 82 | appdir=`dirname "$i"` |
83 | app=`basename "$i"` | 83 | app=`basename "$i"` |
84 | if [ "$noclobber" = "1" ] && [ -e "$prefix/$i" ]; then | 84 | if [ x"$noclobber" = x"1" ] && [ -e "$prefix/$i" ]; then |
85 | echo " $prefix/$i already exists" | 85 | echo " $prefix/$i already exists" |
86 | continue | 86 | continue |
87 | fi | 87 | fi |
88 | mkdir -p "$prefix/$appdir" || exit 1 | 88 | mkdir -p "$prefix/$appdir" || exit 1 |
89 | if [ "$scriptwrapper" = "y" ]; then | 89 | if [ x"$scriptwrapper" = x"y" ]; then |
90 | if [ "$swrapall" != "y" ] && [ "$i" = "/bin/sh" ]; then | 90 | if [ x"$swrapall" != x"y" ] && [ x"$i" = x"/bin/sh" ]; then |
91 | ln $linkopts busybox "$prefix/$i" || exit 1 | 91 | ln $linkopts busybox "$prefix/$i" || exit 1 |
92 | else | 92 | else |
93 | rm -f "$prefix/$i" | 93 | rm -f "$prefix/$i" |
@@ -95,17 +95,17 @@ for i in $h; do | |||
95 | chmod +x "$prefix/$i" | 95 | chmod +x "$prefix/$i" |
96 | fi | 96 | fi |
97 | echo " $prefix/$i" | 97 | echo " $prefix/$i" |
98 | elif [ "$binaries" = "y" ]; then | 98 | elif [ x"$binaries" = x"y" ]; then |
99 | # Copy the binary over rather | 99 | # Copy the binary over rather |
100 | if [ -e $sharedlib_dir/$app ]; then | 100 | if [ -e "$sharedlib_dir/$app" ]; then |
101 | echo " Copying $sharedlib_dir/$app to $prefix/$i" | 101 | echo " Copying $sharedlib_dir/$app to $prefix/$i" |
102 | cp -pPR $sharedlib_dir/$app $prefix/$i || exit 1 | 102 | cp -pPR "$sharedlib_dir/$app" "$prefix/$i" || exit 1 |
103 | else | 103 | else |
104 | echo "Error: Could not find $sharedlib_dir/$app" | 104 | echo "Error: Could not find $sharedlib_dir/$app" |
105 | exit 1 | 105 | exit 1 |
106 | fi | 106 | fi |
107 | else | 107 | else |
108 | if [ "$2" = "--hardlinks" ]; then | 108 | if [ x"$linkopts" = x"-f" ]; then |
109 | bb_path="$prefix/bin/busybox" | 109 | bb_path="$prefix/bin/busybox" |
110 | else | 110 | else |
111 | case "$appdir" in | 111 | case "$appdir" in |
diff --git a/archival/libarchive/data_extract_all.c b/archival/libarchive/data_extract_all.c index d3a6df5e8..8fa69ffaf 100644 --- a/archival/libarchive/data_extract_all.c +++ b/archival/libarchive/data_extract_all.c | |||
@@ -107,9 +107,7 @@ void FAST_FUNC data_extract_all(archive_handle_t *archive_handle) | |||
107 | } | 107 | } |
108 | } | 108 | } |
109 | else if (existing_sb.st_mtime >= file_header->mtime) { | 109 | else if (existing_sb.st_mtime >= file_header->mtime) { |
110 | if (!(archive_handle->ah_flags & ARCHIVE_EXTRACT_QUIET) | 110 | if (!S_ISDIR(file_header->mode)) { |
111 | && !S_ISDIR(file_header->mode) | ||
112 | ) { | ||
113 | bb_error_msg("%s not created: newer or " | 111 | bb_error_msg("%s not created: newer or " |
114 | "same age file exists", dst_name); | 112 | "same age file exists", dst_name); |
115 | } | 113 | } |
@@ -125,7 +123,7 @@ void FAST_FUNC data_extract_all(archive_handle_t *archive_handle) | |||
125 | /* Handle hard links separately */ | 123 | /* Handle hard links separately */ |
126 | if (hard_link) { | 124 | if (hard_link) { |
127 | res = link(hard_link, dst_name); | 125 | res = link(hard_link, dst_name); |
128 | if (res != 0 && !(archive_handle->ah_flags & ARCHIVE_EXTRACT_QUIET)) { | 126 | if (res != 0) { |
129 | /* shared message */ | 127 | /* shared message */ |
130 | bb_perror_msg("can't create %slink '%s' to '%s'", | 128 | bb_perror_msg("can't create %slink '%s' to '%s'", |
131 | "hard", dst_name, hard_link | 129 | "hard", dst_name, hard_link |
@@ -165,10 +163,9 @@ void FAST_FUNC data_extract_all(archive_handle_t *archive_handle) | |||
165 | } | 163 | } |
166 | case S_IFDIR: | 164 | case S_IFDIR: |
167 | res = mkdir(dst_name, file_header->mode); | 165 | res = mkdir(dst_name, file_header->mode); |
168 | if ((res == -1) | 166 | if ((res != 0) |
169 | && (errno != EISDIR) /* btw, Linux doesn't return this */ | 167 | && (errno != EISDIR) /* btw, Linux doesn't return this */ |
170 | && (errno != EEXIST) | 168 | && (errno != EEXIST) |
171 | && !(archive_handle->ah_flags & ARCHIVE_EXTRACT_QUIET) | ||
172 | ) { | 169 | ) { |
173 | bb_perror_msg("can't make dir %s", dst_name); | 170 | bb_perror_msg("can't make dir %s", dst_name); |
174 | } | 171 | } |
@@ -198,27 +195,16 @@ void FAST_FUNC data_extract_all(archive_handle_t *archive_handle) | |||
198 | * | 195 | * |
199 | * Untarring bug.tar would otherwise place evil.py in '/tmp'. | 196 | * Untarring bug.tar would otherwise place evil.py in '/tmp'. |
200 | */ | 197 | */ |
201 | if (!unsafe_symlink_target(file_header->link_target)) { | 198 | create_or_remember_symlink(&archive_handle->symlink_placeholders, |
202 | res = symlink(file_header->link_target, dst_name); | 199 | file_header->link_target, |
203 | if (res != 0 | 200 | dst_name); |
204 | && !(archive_handle->ah_flags & ARCHIVE_EXTRACT_QUIET) | ||
205 | ) { | ||
206 | /* shared message */ | ||
207 | bb_perror_msg("can't create %slink '%s' to '%s'", | ||
208 | "sym", | ||
209 | dst_name, file_header->link_target | ||
210 | ); | ||
211 | } | ||
212 | } | ||
213 | break; | 201 | break; |
214 | case S_IFSOCK: | 202 | case S_IFSOCK: |
215 | case S_IFBLK: | 203 | case S_IFBLK: |
216 | case S_IFCHR: | 204 | case S_IFCHR: |
217 | case S_IFIFO: | 205 | case S_IFIFO: |
218 | res = mknod(dst_name, file_header->mode, file_header->device); | 206 | res = mknod(dst_name, file_header->mode, file_header->device); |
219 | if ((res == -1) | 207 | if (res != 0) { |
220 | && !(archive_handle->ah_flags & ARCHIVE_EXTRACT_QUIET) | ||
221 | ) { | ||
222 | bb_perror_msg("can't create node %s", dst_name); | 208 | bb_perror_msg("can't create node %s", dst_name); |
223 | } | 209 | } |
224 | break; | 210 | break; |
diff --git a/archival/libarchive/unsafe_symlink_target.c b/archival/libarchive/unsafe_symlink_target.c index 441ba8b24..8dcafeaa1 100644 --- a/archival/libarchive/unsafe_symlink_target.c +++ b/archival/libarchive/unsafe_symlink_target.c | |||
@@ -5,44 +5,37 @@ | |||
5 | #include "libbb.h" | 5 | #include "libbb.h" |
6 | #include "bb_archive.h" | 6 | #include "bb_archive.h" |
7 | 7 | ||
8 | int FAST_FUNC unsafe_symlink_target(const char *target) | 8 | void FAST_FUNC create_or_remember_symlink(llist_t **symlink_placeholders, |
9 | const char *target, | ||
10 | const char *linkname) | ||
9 | { | 11 | { |
10 | const char *dot; | 12 | if (target[0] == '/' || strstr(target, "..")) { |
11 | 13 | llist_add_to(symlink_placeholders, | |
12 | if (target[0] == '/') { | 14 | xasprintf("%s%c%s", linkname, '\0', target) |
13 | const char *var; | 15 | ); |
14 | unsafe: | 16 | return; |
15 | var = getenv("EXTRACT_UNSAFE_SYMLINKS"); | 17 | } |
16 | if (var) { | 18 | if (symlink(target, linkname) != 0) { |
17 | if (LONE_CHAR(var, '1')) | 19 | /* shared message */ |
18 | return 0; /* pretend it's safe */ | 20 | bb_perror_msg_and_die("can't create %slink '%s' to '%s'", |
19 | return 1; /* "UNSAFE!" */ | 21 | "sym", linkname, target |
20 | } | ||
21 | bb_error_msg("skipping unsafe symlink to '%s' in archive," | ||
22 | " set %s=1 to extract", | ||
23 | target, | ||
24 | "EXTRACT_UNSAFE_SYMLINKS" | ||
25 | ); | 22 | ); |
26 | /* Prevent further messages */ | ||
27 | setenv("EXTRACT_UNSAFE_SYMLINKS", "0", 0); | ||
28 | return 1; /* "UNSAFE!" */ | ||
29 | } | 23 | } |
24 | } | ||
30 | 25 | ||
31 | dot = target; | 26 | void FAST_FUNC create_symlinks_from_list(llist_t *list) |
32 | for (;;) { | 27 | { |
33 | dot = strchr(dot, '.'); | 28 | while (list) { |
34 | if (!dot) | 29 | char *target; |
35 | return 0; /* safe target */ | ||
36 | 30 | ||
37 | /* Is it a path component starting with ".."? */ | 31 | target = list->data + strlen(list->data) + 1; |
38 | if ((dot[1] == '.') | 32 | if (symlink(target, list->data)) { |
39 | && (dot == target || dot[-1] == '/') | 33 | /* shared message */ |
40 | /* Is it exactly ".."? */ | 34 | bb_error_msg_and_die("can't create %slink '%s' to '%s'", |
41 | && (dot[2] == '/' || dot[2] == '\0') | 35 | "sym", |
42 | ) { | 36 | list->data, target |
43 | goto unsafe; | 37 | ); |
44 | } | 38 | } |
45 | /* NB: it can even be trailing ".", should only add 1 */ | 39 | list = list->link; |
46 | dot += 1; | ||
47 | } | 40 | } |
48 | } | 41 | } |
diff --git a/archival/tar.c b/archival/tar.c index fbe5e3be8..d0684b69e 100644 --- a/archival/tar.c +++ b/archival/tar.c | |||
@@ -1279,6 +1279,8 @@ int tar_main(int argc UNUSED_PARAM, char **argv) | |||
1279 | while (get_header_tar(tar_handle) == EXIT_SUCCESS) | 1279 | while (get_header_tar(tar_handle) == EXIT_SUCCESS) |
1280 | bb_got_signal = EXIT_SUCCESS; /* saw at least one header, good */ | 1280 | bb_got_signal = EXIT_SUCCESS; /* saw at least one header, good */ |
1281 | 1281 | ||
1282 | create_symlinks_from_list(tar_handle->symlink_placeholders); | ||
1283 | |||
1282 | /* Check that every file that should have been extracted was */ | 1284 | /* Check that every file that should have been extracted was */ |
1283 | while (tar_handle->accept) { | 1285 | while (tar_handle->accept) { |
1284 | if (!find_list_entry(tar_handle->reject, tar_handle->accept->data) | 1286 | if (!find_list_entry(tar_handle->reject, tar_handle->accept->data) |
diff --git a/archival/unzip.c b/archival/unzip.c index fb58b62c0..369c6c028 100644 --- a/archival/unzip.c +++ b/archival/unzip.c | |||
@@ -348,7 +348,9 @@ static void unzip_create_leading_dirs(const char *fn) | |||
348 | } | 348 | } |
349 | 349 | ||
350 | #if ENABLE_FEATURE_UNZIP_CDF | 350 | #if ENABLE_FEATURE_UNZIP_CDF |
351 | static void unzip_extract_symlink(zip_header_t *zip, const char *dst_fn) | 351 | static void unzip_extract_symlink(llist_t **symlink_placeholders, |
352 | zip_header_t *zip, | ||
353 | const char *dst_fn) | ||
352 | { | 354 | { |
353 | char *target; | 355 | char *target; |
354 | 356 | ||
@@ -373,15 +375,9 @@ static void unzip_extract_symlink(zip_header_t *zip, const char *dst_fn) | |||
373 | target[xstate.mem_output_size] = '\0'; | 375 | target[xstate.mem_output_size] = '\0'; |
374 | #endif | 376 | #endif |
375 | } | 377 | } |
376 | if (!unsafe_symlink_target(target)) { | 378 | create_or_remember_symlink(symlink_placeholders, |
377 | //TODO: libbb candidate | 379 | target, |
378 | if (symlink(target, dst_fn)) { | 380 | dst_fn); |
379 | /* shared message */ | ||
380 | bb_perror_msg_and_die("can't create %slink '%s' to '%s'", | ||
381 | "sym", dst_fn, target | ||
382 | ); | ||
383 | } | ||
384 | } | ||
385 | free(target); | 381 | free(target); |
386 | } | 382 | } |
387 | #endif | 383 | #endif |
@@ -493,6 +489,9 @@ int unzip_main(int argc, char **argv) | |||
493 | llist_t *zaccept = NULL; | 489 | llist_t *zaccept = NULL; |
494 | llist_t *zreject = NULL; | 490 | llist_t *zreject = NULL; |
495 | char *base_dir = NULL; | 491 | char *base_dir = NULL; |
492 | #if ENABLE_FEATURE_UNZIP_CDF | ||
493 | llist_t *symlink_placeholders = NULL; | ||
494 | #endif | ||
496 | int i; | 495 | int i; |
497 | char key_buf[80]; /* must match size used by my_fgets80 */ | 496 | char key_buf[80]; /* must match size used by my_fgets80 */ |
498 | 497 | ||
@@ -957,7 +956,7 @@ int unzip_main(int argc, char **argv) | |||
957 | #if ENABLE_FEATURE_UNZIP_CDF | 956 | #if ENABLE_FEATURE_UNZIP_CDF |
958 | if (S_ISLNK(file_mode)) { | 957 | if (S_ISLNK(file_mode)) { |
959 | if (dst_fd != STDOUT_FILENO) /* not -p? */ | 958 | if (dst_fd != STDOUT_FILENO) /* not -p? */ |
960 | unzip_extract_symlink(&zip, dst_fn); | 959 | unzip_extract_symlink(&symlink_placeholders, &zip, dst_fn); |
961 | } else | 960 | } else |
962 | #endif | 961 | #endif |
963 | { | 962 | { |
@@ -993,6 +992,10 @@ int unzip_main(int argc, char **argv) | |||
993 | total_entries++; | 992 | total_entries++; |
994 | } | 993 | } |
995 | 994 | ||
995 | #if ENABLE_FEATURE_UNZIP_CDF | ||
996 | create_symlinks_from_list(symlink_placeholders); | ||
997 | #endif | ||
998 | |||
996 | if ((opts & OPT_l) && quiet <= 1) { | 999 | if ((opts & OPT_l) && quiet <= 1) { |
997 | if (!verbose) { | 1000 | if (!verbose) { |
998 | // " Length Date Time Name\n" | 1001 | // " Length Date Time Name\n" |
diff --git a/coreutils/df.c b/coreutils/df.c index 4076b5fec..50dccac52 100644 --- a/coreutils/df.c +++ b/coreutils/df.c | |||
@@ -91,8 +91,6 @@ static unsigned long kscale(unsigned long b, unsigned long bs) | |||
91 | int df_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | 91 | int df_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
92 | int df_main(int argc UNUSED_PARAM, char **argv) | 92 | int df_main(int argc UNUSED_PARAM, char **argv) |
93 | { | 93 | { |
94 | unsigned long blocks_used; | ||
95 | unsigned blocks_percent_used; | ||
96 | unsigned long df_disp_hr = 1024; | 94 | unsigned long df_disp_hr = 1024; |
97 | int status = EXIT_SUCCESS; | 95 | int status = EXIT_SUCCESS; |
98 | unsigned opt; | 96 | unsigned opt; |
@@ -208,6 +206,11 @@ int df_main(int argc UNUSED_PARAM, char **argv) | |||
208 | } | 206 | } |
209 | 207 | ||
210 | device = mount_entry->mnt_fsname; | 208 | device = mount_entry->mnt_fsname; |
209 | |||
210 | /* GNU coreutils 6.10 skips certain mounts, try to be compatible */ | ||
211 | if (ENABLE_FEATURE_SKIP_ROOTFS && strcmp(device, "rootfs") == 0) | ||
212 | continue; | ||
213 | |||
211 | mount_point = mount_entry->mnt_dir; | 214 | mount_point = mount_entry->mnt_dir; |
212 | fs_type = mount_entry->mnt_type; | 215 | fs_type = mount_entry->mnt_type; |
213 | 216 | ||
@@ -222,26 +225,31 @@ int df_main(int argc UNUSED_PARAM, char **argv) | |||
222 | s.f_frsize = s.f_bsize; | 225 | s.f_frsize = s.f_bsize; |
223 | 226 | ||
224 | if ((s.f_blocks > 0) || !mount_table || (opt & OPT_ALL)) { | 227 | if ((s.f_blocks > 0) || !mount_table || (opt & OPT_ALL)) { |
228 | unsigned long long blocks_used; | ||
229 | unsigned long long blocks_total; | ||
230 | unsigned blocks_percent_used; | ||
231 | |||
225 | if (opt & OPT_INODE) { | 232 | if (opt & OPT_INODE) { |
226 | s.f_blocks = s.f_files; | 233 | s.f_blocks = s.f_files; |
227 | s.f_bavail = s.f_bfree = s.f_ffree; | 234 | s.f_bavail = s.f_bfree = s.f_ffree; |
228 | s.f_frsize = 1; | 235 | s.f_frsize = 1; |
229 | |||
230 | if (df_disp_hr) | 236 | if (df_disp_hr) |
231 | df_disp_hr = 1; | 237 | df_disp_hr = 1; |
232 | } | 238 | } |
233 | blocks_used = s.f_blocks - s.f_bfree; | 239 | blocks_used = s.f_blocks - s.f_bfree; |
234 | blocks_percent_used = 0; | 240 | blocks_total = blocks_used + s.f_bavail; |
235 | if (blocks_used + s.f_bavail) { | 241 | blocks_percent_used = blocks_total; /* 0% if blocks_total == 0, else... */ |
236 | blocks_percent_used = (blocks_used * 100ULL | 242 | if (blocks_total != 0) { |
237 | + (blocks_used + s.f_bavail)/2 | 243 | /* Downscale sizes for narrower division */ |
238 | ) / (blocks_used + s.f_bavail); | 244 | unsigned u; |
245 | while (blocks_total >= INT_MAX / 101) { | ||
246 | blocks_total >>= 1; | ||
247 | blocks_used >>= 1; | ||
248 | } | ||
249 | u = (unsigned)blocks_used * 100u + (unsigned)blocks_total / 2; | ||
250 | blocks_percent_used = u / (unsigned)blocks_total; | ||
239 | } | 251 | } |
240 | 252 | ||
241 | /* GNU coreutils 6.10 skips certain mounts, try to be compatible. */ | ||
242 | if (ENABLE_FEATURE_SKIP_ROOTFS && strcmp(device, "rootfs") == 0) | ||
243 | continue; | ||
244 | |||
245 | #ifdef WHY_WE_DO_IT_FOR_DEV_ROOT_ONLY | 253 | #ifdef WHY_WE_DO_IT_FOR_DEV_ROOT_ONLY |
246 | if (strcmp(device, "/dev/root") == 0) { | 254 | if (strcmp(device, "/dev/root") == 0) { |
247 | /* Adjusts device to be the real root device, | 255 | /* Adjusts device to be the real root device, |
diff --git a/coreutils/od_bloaty.c b/coreutils/od_bloaty.c index 645a05f57..75e14ef7d 100644 --- a/coreutils/od_bloaty.c +++ b/coreutils/od_bloaty.c | |||
@@ -218,7 +218,14 @@ struct globals { | |||
218 | 218 | ||
219 | bool not_first; | 219 | bool not_first; |
220 | bool prev_pair_equal; | 220 | bool prev_pair_equal; |
221 | |||
222 | char address_fmt[sizeof("%0n"OFF_FMT"xc")]; | ||
221 | } FIX_ALIASING; | 223 | } FIX_ALIASING; |
224 | /* Corresponds to 'x' above */ | ||
225 | #define address_base_char G.address_fmt[sizeof(G.address_fmt)-3] | ||
226 | /* Corresponds to 'n' above */ | ||
227 | #define address_pad_len_char G.address_fmt[2] | ||
228 | |||
222 | #if !ENABLE_LONG_OPTS | 229 | #if !ENABLE_LONG_OPTS |
223 | enum { G_pseudo_offset = 0 }; | 230 | enum { G_pseudo_offset = 0 }; |
224 | #endif | 231 | #endif |
@@ -227,6 +234,7 @@ enum { G_pseudo_offset = 0 }; | |||
227 | setup_common_bufsiz(); \ | 234 | setup_common_bufsiz(); \ |
228 | BUILD_BUG_ON(sizeof(G) > COMMON_BUFSIZE); \ | 235 | BUILD_BUG_ON(sizeof(G) > COMMON_BUFSIZE); \ |
229 | G.bytes_per_block = 32; \ | 236 | G.bytes_per_block = 32; \ |
237 | strcpy(G.address_fmt, "%0n"OFF_FMT"xc"); \ | ||
230 | } while (0) | 238 | } while (0) |
231 | 239 | ||
232 | 240 | ||
@@ -851,18 +859,12 @@ format_address_none(off_t address UNUSED_PARAM, char c UNUSED_PARAM) | |||
851 | { | 859 | { |
852 | } | 860 | } |
853 | 861 | ||
854 | static char address_fmt[] ALIGN1 = "%0n"OFF_FMT"xc"; | ||
855 | /* Corresponds to 'x' above */ | ||
856 | #define address_base_char address_fmt[sizeof(address_fmt)-3] | ||
857 | /* Corresponds to 'n' above */ | ||
858 | #define address_pad_len_char address_fmt[2] | ||
859 | |||
860 | static void | 862 | static void |
861 | format_address_std(off_t address, char c) | 863 | format_address_std(off_t address, char c) |
862 | { | 864 | { |
863 | /* Corresponds to 'c' */ | 865 | /* Corresponds to 'c' */ |
864 | address_fmt[sizeof(address_fmt)-2] = c; | 866 | G.address_fmt[sizeof(G.address_fmt)-2] = c; |
865 | printf(address_fmt, address); | 867 | printf(G.address_fmt, address); |
866 | } | 868 | } |
867 | 869 | ||
868 | #if ENABLE_LONG_OPTS | 870 | #if ENABLE_LONG_OPTS |
diff --git a/coreutils/sort.c b/coreutils/sort.c index ceea24491..b39297a26 100644 --- a/coreutils/sort.c +++ b/coreutils/sort.c | |||
@@ -62,9 +62,9 @@ | |||
62 | //usage: "\n -u Suppress duplicate lines" | 62 | //usage: "\n -u Suppress duplicate lines" |
63 | //usage: IF_FEATURE_SORT_BIG( | 63 | //usage: IF_FEATURE_SORT_BIG( |
64 | //usage: "\n -z Lines are terminated by NUL, not newline" | 64 | //usage: "\n -z Lines are terminated by NUL, not newline" |
65 | ////usage: "\n -m Ignored for GNU compatibility" | 65 | ///////: "\n -m Ignored for GNU compatibility" |
66 | ////usage: "\n -S BUFSZ Ignored for GNU compatibility" | 66 | ///////: "\n -S BUFSZ Ignored for GNU compatibility" |
67 | ////usage: "\n -T TMPDIR Ignored for GNU compatibility" | 67 | ///////: "\n -T TMPDIR Ignored for GNU compatibility" |
68 | //usage: ) | 68 | //usage: ) |
69 | //usage: | 69 | //usage: |
70 | //usage:#define sort_example_usage | 70 | //usage:#define sort_example_usage |
@@ -85,16 +85,7 @@ | |||
85 | 85 | ||
86 | #include "libbb.h" | 86 | #include "libbb.h" |
87 | 87 | ||
88 | /* This is a NOEXEC applet. Be very careful! */ | ||
89 | |||
90 | |||
91 | /* | ||
92 | sort [-m][-o output][-bdfinru][-t char][-k keydef]... [file...] | ||
93 | sort -c [-bdfinru][-t char][-k keydef][file] | ||
94 | */ | ||
95 | |||
96 | /* These are sort types */ | 88 | /* These are sort types */ |
97 | #define OPT_STR "ngMucszbrdfimS:T:o:k:*t:" | ||
98 | enum { | 89 | enum { |
99 | FLAG_n = 1, /* Numeric sort */ | 90 | FLAG_n = 1, /* Numeric sort */ |
100 | FLAG_g = 2, /* Sort using strtod() */ | 91 | FLAG_g = 2, /* Sort using strtod() */ |
@@ -117,8 +108,18 @@ enum { | |||
117 | FLAG_k = 0x10000, | 108 | FLAG_k = 0x10000, |
118 | FLAG_t = 0x20000, | 109 | FLAG_t = 0x20000, |
119 | FLAG_bb = 0x80000000, /* Ignore trailing blanks */ | 110 | FLAG_bb = 0x80000000, /* Ignore trailing blanks */ |
111 | FLAG_no_tie_break = 0x40000000, | ||
120 | }; | 112 | }; |
121 | 113 | ||
114 | static const char sort_opt_str[] ALIGN1 = "^" | ||
115 | "ngMucszbrdfimS:T:o:k:*t:" | ||
116 | "\0" "o--o:t--t"/*-t, -o: at most one of each*/; | ||
117 | /* | ||
118 | * OPT_STR must not be string literal, needs to have stable address: | ||
119 | * code uses "strchr(OPT_STR,c) - OPT_STR" idiom. | ||
120 | */ | ||
121 | #define OPT_STR (sort_opt_str + 1) | ||
122 | |||
122 | #if ENABLE_FEATURE_SORT_BIG | 123 | #if ENABLE_FEATURE_SORT_BIG |
123 | static char key_separator; | 124 | static char key_separator; |
124 | 125 | ||
@@ -128,6 +129,10 @@ static struct sort_key { | |||
128 | unsigned flags; | 129 | unsigned flags; |
129 | } *key_list; | 130 | } *key_list; |
130 | 131 | ||
132 | |||
133 | /* This is a NOEXEC applet. Be very careful! */ | ||
134 | |||
135 | |||
131 | static char *get_key(char *str, struct sort_key *key, int flags) | 136 | static char *get_key(char *str, struct sort_key *key, int flags) |
132 | { | 137 | { |
133 | int start = start; /* for compiler */ | 138 | int start = start; /* for compiler */ |
@@ -340,10 +345,35 @@ static int compare_keys(const void *xarg, const void *yarg) | |||
340 | #endif | 345 | #endif |
341 | } /* for */ | 346 | } /* for */ |
342 | 347 | ||
343 | /* Perform fallback sort if necessary */ | 348 | if (retval == 0) { |
344 | if (!retval && !(option_mask32 & FLAG_s)) { | 349 | /* So far lines are "the same" */ |
345 | flags = option_mask32; | 350 | |
346 | retval = strcmp(*(char **)xarg, *(char **)yarg); | 351 | if (option_mask32 & FLAG_s) { |
352 | /* "Stable sort": later line is "greater than", | ||
353 | * IOW: do not allow qsort() to swap equal lines. | ||
354 | */ | ||
355 | uint32_t *p32; | ||
356 | uint32_t x32, y32; | ||
357 | char *line; | ||
358 | unsigned len; | ||
359 | |||
360 | line = *(char**)xarg; | ||
361 | len = (strlen(line) + 4) & (~3u); | ||
362 | p32 = (void*)(line + len); | ||
363 | x32 = *p32; | ||
364 | line = *(char**)yarg; | ||
365 | len = (strlen(line) + 4) & (~3u); | ||
366 | p32 = (void*)(line + len); | ||
367 | y32 = *p32; | ||
368 | |||
369 | /* If x > y, 1, else -1 */ | ||
370 | retval = (x32 > y32) * 2 - 1; | ||
371 | } else | ||
372 | if (!(option_mask32 & FLAG_no_tie_break)) { | ||
373 | /* fallback sort */ | ||
374 | flags = option_mask32; | ||
375 | retval = strcmp(*(char **)xarg, *(char **)yarg); | ||
376 | } | ||
347 | } | 377 | } |
348 | 378 | ||
349 | if (flags & FLAG_r) | 379 | if (flags & FLAG_r) |
@@ -368,7 +398,7 @@ static unsigned str2u(char **str) | |||
368 | int sort_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | 398 | int sort_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
369 | int sort_main(int argc UNUSED_PARAM, char **argv) | 399 | int sort_main(int argc UNUSED_PARAM, char **argv) |
370 | { | 400 | { |
371 | char *line, **lines; | 401 | char **lines; |
372 | char *str_ignored, *str_o, *str_t; | 402 | char *str_ignored, *str_o, *str_t; |
373 | llist_t *lst_k = NULL; | 403 | llist_t *lst_k = NULL; |
374 | int i; | 404 | int i; |
@@ -378,9 +408,8 @@ int sort_main(int argc UNUSED_PARAM, char **argv) | |||
378 | xfunc_error_retval = 2; | 408 | xfunc_error_retval = 2; |
379 | 409 | ||
380 | /* Parse command line options */ | 410 | /* Parse command line options */ |
381 | opts = getopt32(argv, "^" | 411 | opts = getopt32(argv, |
382 | OPT_STR | 412 | sort_opt_str, |
383 | "\0" "o--o:t--t"/*-t, -o: at most one of each*/, | ||
384 | &str_ignored, &str_ignored, &str_o, &lst_k, &str_t | 413 | &str_ignored, &str_ignored, &str_o, &lst_k, &str_t |
385 | ); | 414 | ); |
386 | /* global b strips leading and trailing spaces */ | 415 | /* global b strips leading and trailing spaces */ |
@@ -457,7 +486,7 @@ int sort_main(int argc UNUSED_PARAM, char **argv) | |||
457 | * do not continue to next file: */ | 486 | * do not continue to next file: */ |
458 | FILE *fp = xfopen_stdin(*argv); | 487 | FILE *fp = xfopen_stdin(*argv); |
459 | for (;;) { | 488 | for (;;) { |
460 | line = GET_LINE(fp); | 489 | char *line = GET_LINE(fp); |
461 | if (!line) | 490 | if (!line) |
462 | break; | 491 | break; |
463 | lines = xrealloc_vector(lines, 6, linecount); | 492 | lines = xrealloc_vector(lines, 6, linecount); |
@@ -482,15 +511,35 @@ int sort_main(int argc UNUSED_PARAM, char **argv) | |||
482 | return EXIT_SUCCESS; | 511 | return EXIT_SUCCESS; |
483 | } | 512 | } |
484 | #endif | 513 | #endif |
514 | |||
515 | /* For stable sort, store original line position beyond terminating NUL */ | ||
516 | if (option_mask32 & FLAG_s) { | ||
517 | for (i = 0; i < linecount; i++) { | ||
518 | uint32_t *p32; | ||
519 | char *line; | ||
520 | unsigned len; | ||
521 | |||
522 | line = lines[i]; | ||
523 | len = (strlen(line) + 4) & (~3u); | ||
524 | lines[i] = line = xrealloc(line, len + 4); | ||
525 | p32 = (void*)(line + len); | ||
526 | *p32 = i; | ||
527 | } | ||
528 | /*option_mask32 |= FLAG_no_tie_break;*/ | ||
529 | /* ^^^redundant: if FLAG_s, compare_keys() does no tie break */ | ||
530 | } | ||
531 | |||
485 | /* Perform the actual sort */ | 532 | /* Perform the actual sort */ |
486 | qsort(lines, linecount, sizeof(lines[0]), compare_keys); | 533 | qsort(lines, linecount, sizeof(lines[0]), compare_keys); |
487 | 534 | ||
488 | /* Handle -u */ | 535 | /* Handle -u */ |
489 | if (option_mask32 & FLAG_u) { | 536 | if (option_mask32 & FLAG_u) { |
490 | int j = 0; | 537 | int j = 0; |
491 | /* coreutils 6.3 drop lines for which only key is the same */ | 538 | /* coreutils 6.3 drop lines for which only key is the same |
492 | /* -- disabling last-resort compare... */ | 539 | * -- disabling last-resort compare, or else compare_keys() |
493 | option_mask32 |= FLAG_s; | 540 | * will be the same only for completely identical lines. |
541 | */ | ||
542 | option_mask32 |= FLAG_no_tie_break; | ||
494 | for (i = 1; i < linecount; i++) { | 543 | for (i = 1; i < linecount; i++) { |
495 | if (compare_keys(&lines[j], &lines[i]) == 0) | 544 | if (compare_keys(&lines[j], &lines[i]) == 0) |
496 | free(lines[i]); | 545 | free(lines[i]); |
diff --git a/include/bb_archive.h b/include/bb_archive.h index c67a299d1..8028736b8 100644 --- a/include/bb_archive.h +++ b/include/bb_archive.h | |||
@@ -74,6 +74,9 @@ typedef struct archive_handle_t { | |||
74 | /* Currently processed file's header */ | 74 | /* Currently processed file's header */ |
75 | file_header_t *file_header; | 75 | file_header_t *file_header; |
76 | 76 | ||
77 | /* List of symlink placeholders */ | ||
78 | llist_t *symlink_placeholders; | ||
79 | |||
77 | /* Process the header component, e.g. tar -t */ | 80 | /* Process the header component, e.g. tar -t */ |
78 | void FAST_FUNC (*action_header)(const file_header_t *); | 81 | void FAST_FUNC (*action_header)(const file_header_t *); |
79 | 82 | ||
@@ -129,15 +132,14 @@ typedef struct archive_handle_t { | |||
129 | #define ARCHIVE_RESTORE_DATE (1 << 0) | 132 | #define ARCHIVE_RESTORE_DATE (1 << 0) |
130 | #define ARCHIVE_CREATE_LEADING_DIRS (1 << 1) | 133 | #define ARCHIVE_CREATE_LEADING_DIRS (1 << 1) |
131 | #define ARCHIVE_UNLINK_OLD (1 << 2) | 134 | #define ARCHIVE_UNLINK_OLD (1 << 2) |
132 | #define ARCHIVE_EXTRACT_QUIET (1 << 3) | 135 | #define ARCHIVE_EXTRACT_NEWER (1 << 3) |
133 | #define ARCHIVE_EXTRACT_NEWER (1 << 4) | 136 | #define ARCHIVE_DONT_RESTORE_OWNER (1 << 4) |
134 | #define ARCHIVE_DONT_RESTORE_OWNER (1 << 5) | 137 | #define ARCHIVE_DONT_RESTORE_PERM (1 << 5) |
135 | #define ARCHIVE_DONT_RESTORE_PERM (1 << 6) | 138 | #define ARCHIVE_NUMERIC_OWNER (1 << 6) |
136 | #define ARCHIVE_NUMERIC_OWNER (1 << 7) | 139 | #define ARCHIVE_O_TRUNC (1 << 7) |
137 | #define ARCHIVE_O_TRUNC (1 << 8) | 140 | #define ARCHIVE_REMEMBER_NAMES (1 << 8) |
138 | #define ARCHIVE_REMEMBER_NAMES (1 << 9) | ||
139 | #if ENABLE_RPM | 141 | #if ENABLE_RPM |
140 | #define ARCHIVE_REPLACE_VIA_RENAME (1 << 10) | 142 | #define ARCHIVE_REPLACE_VIA_RENAME (1 << 9) |
141 | #endif | 143 | #endif |
142 | 144 | ||
143 | 145 | ||
@@ -207,7 +209,10 @@ void seek_by_jump(int fd, off_t amount) FAST_FUNC; | |||
207 | void seek_by_read(int fd, off_t amount) FAST_FUNC; | 209 | void seek_by_read(int fd, off_t amount) FAST_FUNC; |
208 | 210 | ||
209 | const char *strip_unsafe_prefix(const char *str) FAST_FUNC; | 211 | const char *strip_unsafe_prefix(const char *str) FAST_FUNC; |
210 | int unsafe_symlink_target(const char *target) FAST_FUNC; | 212 | void create_or_remember_symlink(llist_t **symlink_placeholders, |
213 | const char *target, | ||
214 | const char *linkname) FAST_FUNC; | ||
215 | void create_symlinks_from_list(llist_t *list) FAST_FUNC; | ||
211 | 216 | ||
212 | void data_align(archive_handle_t *archive_handle, unsigned boundary) FAST_FUNC; | 217 | void data_align(archive_handle_t *archive_handle, unsigned boundary) FAST_FUNC; |
213 | const llist_t *find_list_entry(const llist_t *list, const char *filename) FAST_FUNC; | 218 | const llist_t *find_list_entry(const llist_t *list, const char *filename) FAST_FUNC; |
diff --git a/include/libbb.h b/include/libbb.h index 4a59ee7d8..f8e54de54 100644 --- a/include/libbb.h +++ b/include/libbb.h | |||
@@ -1536,6 +1536,7 @@ extern void run_shell(const char *shell, int loginshell, const char **args) NORE | |||
1536 | */ | 1536 | */ |
1537 | const char *get_shell_name(void) FAST_FUNC; | 1537 | const char *get_shell_name(void) FAST_FUNC; |
1538 | 1538 | ||
1539 | #if ENABLE_FEATURE_SETPRIV_CAPABILITIES || ENABLE_RUN_INIT | ||
1539 | unsigned cap_name_to_number(const char *cap) FAST_FUNC; | 1540 | unsigned cap_name_to_number(const char *cap) FAST_FUNC; |
1540 | void printf_cap(const char *pfx, unsigned cap_no) FAST_FUNC; | 1541 | void printf_cap(const char *pfx, unsigned cap_no) FAST_FUNC; |
1541 | void drop_capability(int cap_ordinal) FAST_FUNC; | 1542 | void drop_capability(int cap_ordinal) FAST_FUNC; |
@@ -1547,9 +1548,7 @@ struct caps { \ | |||
1547 | struct __user_cap_data_struct data[2]; \ | 1548 | struct __user_cap_data_struct data[2]; \ |
1548 | } | 1549 | } |
1549 | void getcaps(void *caps) FAST_FUNC; | 1550 | void getcaps(void *caps) FAST_FUNC; |
1550 | 1551 | #endif | |
1551 | unsigned cap_name_to_number(const char *name) FAST_FUNC; | ||
1552 | void printf_cap(const char *pfx, unsigned cap_no) FAST_FUNC; | ||
1553 | 1552 | ||
1554 | #if ENABLE_SELINUX | 1553 | #if ENABLE_SELINUX |
1555 | extern void renew_current_security_context(void) FAST_FUNC; | 1554 | extern void renew_current_security_context(void) FAST_FUNC; |
@@ -2006,6 +2005,7 @@ typedef struct bb_progress_t { | |||
2006 | unsigned last_update_sec; | 2005 | unsigned last_update_sec; |
2007 | unsigned last_change_sec; | 2006 | unsigned last_change_sec; |
2008 | unsigned start_sec; | 2007 | unsigned start_sec; |
2008 | /*unsigned last_eta;*/ | ||
2009 | const char *curfile; | 2009 | const char *curfile; |
2010 | } bb_progress_t; | 2010 | } bb_progress_t; |
2011 | 2011 | ||
diff --git a/init/halt.c b/init/halt.c index c6c857f08..e6dcb1c67 100644 --- a/init/halt.c +++ b/init/halt.c | |||
@@ -24,6 +24,17 @@ | |||
24 | //config: help | 24 | //config: help |
25 | //config: Stop all processes and reboot the system. | 25 | //config: Stop all processes and reboot the system. |
26 | //config: | 26 | //config: |
27 | //config:config FEATURE_WAIT_FOR_INIT | ||
28 | //config: bool "Before signaling init, make sure it is ready for it" | ||
29 | //config: default y | ||
30 | //config: depends on HALT || POWEROFF || REBOOT | ||
31 | //config: help | ||
32 | //config: In rare cases, poweroff may be commanded by firmware to OS | ||
33 | //config: even before init process exists. On Linux, this spawns | ||
34 | //config: "/sbin/poweroff" very early. This option adds code | ||
35 | //config: which checks that init is ready to receive poweroff | ||
36 | //config: commands. Code size increase of ~80 bytes. | ||
37 | //config: | ||
27 | //config:config FEATURE_CALL_TELINIT | 38 | //config:config FEATURE_CALL_TELINIT |
28 | //config: bool "Call telinit on shutdown and reboot" | 39 | //config: bool "Call telinit on shutdown and reboot" |
29 | //config: default y | 40 | //config: default y |
@@ -108,6 +119,47 @@ static void write_wtmp(void) | |||
108 | #define write_wtmp() ((void)0) | 119 | #define write_wtmp() ((void)0) |
109 | #endif | 120 | #endif |
110 | 121 | ||
122 | #if ENABLE_FEATURE_WAIT_FOR_INIT | ||
123 | /* In Linux, "poweroff" may be spawned even before init. | ||
124 | * For example, with ACPI: | ||
125 | * linux/drivers/acpi/bus.c: | ||
126 | * static void sb_notify_work(struct work_struct *dummy) | ||
127 | * orderly_poweroff(true); | ||
128 | * linux/kernel/reboot.c: | ||
129 | * static int run_cmd(const char *cmd) | ||
130 | * ret = call_usermodehelper(argv[0], argv, envp, UMH_WAIT_EXEC); | ||
131 | * poweroff_cmd[] = "/sbin/poweroff"; | ||
132 | * static int __orderly_poweroff(bool force) | ||
133 | * ret = run_cmd(poweroff_cmd); | ||
134 | * | ||
135 | * We want to make sure init exists and listens to signals. | ||
136 | */ | ||
137 | static int init_was_not_there(void) | ||
138 | { | ||
139 | enum { initial = 5 }; /* 5 seconds should be plenty for timeout */ | ||
140 | int cnt = initial - 1; | ||
141 | |||
142 | /* Just existence of PID 1 does not mean it installed | ||
143 | * the handlers already. | ||
144 | */ | ||
145 | #if 0 | ||
146 | while (kill(1, 0) != 0 && --cnt >= 0) | ||
147 | sleep(1); | ||
148 | #endif | ||
149 | /* ... so let's wait for some evidence a usual startup event, | ||
150 | * mounting of /proc, happened. By that time init should be ready | ||
151 | * for signals. | ||
152 | */ | ||
153 | while (access("/proc/meminfo", F_OK) != 0 && --cnt >= 0) | ||
154 | sleep(1); | ||
155 | |||
156 | /* Does it look like init wasn't there? */ | ||
157 | return (cnt != initial - 1); | ||
158 | } | ||
159 | #else | ||
160 | /* Assume it's always there */ | ||
161 | # define init_was_not_there() 0 | ||
162 | #endif | ||
111 | 163 | ||
112 | int halt_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | 164 | int halt_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
113 | int halt_main(int argc UNUSED_PARAM, char **argv) | 165 | int halt_main(int argc UNUSED_PARAM, char **argv) |
@@ -171,6 +223,8 @@ int halt_main(int argc UNUSED_PARAM, char **argv) | |||
171 | if (!ENABLE_FEATURE_CALL_TELINIT) { | 223 | if (!ENABLE_FEATURE_CALL_TELINIT) { |
172 | /* bbox init assumed */ | 224 | /* bbox init assumed */ |
173 | rc = kill(1, signals[which]); | 225 | rc = kill(1, signals[which]); |
226 | if (init_was_not_there()) | ||
227 | rc = kill(1, signals[which]); | ||
174 | } else { | 228 | } else { |
175 | /* SysV style init assumed */ | 229 | /* SysV style init assumed */ |
176 | /* runlevels: | 230 | /* runlevels: |
diff --git a/libbb/capability.c b/libbb/capability.c index f60062bfc..6587dcbf7 100644 --- a/libbb/capability.c +++ b/libbb/capability.c | |||
@@ -3,7 +3,8 @@ | |||
3 | * | 3 | * |
4 | * Licensed under GPLv2 or later, see file LICENSE in this source tree. | 4 | * Licensed under GPLv2 or later, see file LICENSE in this source tree. |
5 | */ | 5 | */ |
6 | //kbuild:lib-$(CONFIG_PLATFORM_LINUX) += capability.o | 6 | //kbuild:lib-$(CONFIG_FEATURE_SETPRIV_CAPABILITIES) += capability.o |
7 | //kbuild:lib-$(CONFIG_RUN_INIT) += capability.o | ||
7 | 8 | ||
8 | #include <linux/capability.h> | 9 | #include <linux/capability.h> |
9 | // #include <sys/capability.h> | 10 | // #include <sys/capability.h> |
diff --git a/libbb/progress.c b/libbb/progress.c index f1d980d68..23e974ce7 100644 --- a/libbb/progress.c +++ b/libbb/progress.c | |||
@@ -57,6 +57,9 @@ void FAST_FUNC bb_progress_init(bb_progress_t *p, const char *curfile) | |||
57 | p->last_update_sec = p->start_sec; | 57 | p->last_update_sec = p->start_sec; |
58 | p->last_change_sec = p->start_sec; | 58 | p->last_change_sec = p->start_sec; |
59 | p->last_size = 0; | 59 | p->last_size = 0; |
60 | #if 0 | ||
61 | p->last_eta = INT_MAX; | ||
62 | #endif | ||
60 | } | 63 | } |
61 | 64 | ||
62 | /* File already had beg_size bytes. | 65 | /* File already had beg_size bytes. |
@@ -192,6 +195,16 @@ void FAST_FUNC bb_progress_update(bb_progress_t *p, | |||
192 | /* if 32bit, can overflow ^^^^^^^^^^, but this would only show bad ETA */ | 195 | /* if 32bit, can overflow ^^^^^^^^^^, but this would only show bad ETA */ |
193 | if (eta >= 1000*60*60) | 196 | if (eta >= 1000*60*60) |
194 | eta = 1000*60*60 - 1; | 197 | eta = 1000*60*60 - 1; |
198 | #if 0 | ||
199 | /* To prevent annoying "back-and-forth" estimation jitter, | ||
200 | * if new ETA is larger than the last just by a few seconds, | ||
201 | * disregard it, and show last one. The end result is that | ||
202 | * ETA usually only decreases, unless download slows down a lot. | ||
203 | */ | ||
204 | if ((unsigned)(eta - p->last_eta) < 10) | ||
205 | eta = p->last_eta; | ||
206 | p->last_eta = eta; | ||
207 | #endif | ||
195 | secs = eta % 3600; | 208 | secs = eta % 3600; |
196 | hours = eta / 3600; | 209 | hours = eta / 3600; |
197 | fprintf(stderr, "%3u:%02u:%02u ETA", hours, secs / 60, secs % 60); | 210 | fprintf(stderr, "%3u:%02u:%02u ETA", hours, secs / 60, secs % 60); |
diff --git a/libbb/skip_whitespace.c b/libbb/skip_whitespace.c index 4df5d9e4d..37a24d3be 100644 --- a/libbb/skip_whitespace.c +++ b/libbb/skip_whitespace.c | |||
@@ -32,7 +32,6 @@ char* FAST_FUNC skip_non_whitespace(const char *s) | |||
32 | 32 | ||
33 | char* FAST_FUNC skip_dev_pfx(const char *tty_name) | 33 | char* FAST_FUNC skip_dev_pfx(const char *tty_name) |
34 | { | 34 | { |
35 | if (is_prefixed_with(tty_name, "/dev/")) | 35 | char *unprefixed = is_prefixed_with(tty_name, "/dev/"); |
36 | tty_name += 5; | 36 | return unprefixed ? unprefixed : (char*)tty_name; |
37 | return (char*)tty_name; | ||
38 | } | 37 | } |
diff --git a/miscutils/less.c b/miscutils/less.c index 41e4989ad..e4885db77 100644 --- a/miscutils/less.c +++ b/miscutils/less.c | |||
@@ -1890,7 +1890,11 @@ int less_main(int argc, char **argv) | |||
1890 | * -s: condense many empty lines to one | 1890 | * -s: condense many empty lines to one |
1891 | * (used by some setups for manpage display) | 1891 | * (used by some setups for manpage display) |
1892 | */ | 1892 | */ |
1893 | getopt32(argv, "EMmN~I" IF_FEATURE_LESS_TRUNCATE("S") /*ignored:*/"s"); | 1893 | getopt32(argv, "EMmN~I" |
1894 | IF_FEATURE_LESS_TRUNCATE("S") | ||
1895 | IF_FEATURE_LESS_RAW("R") | ||
1896 | /*ignored:*/"s" | ||
1897 | ); | ||
1894 | argv += optind; | 1898 | argv += optind; |
1895 | num_files = argc - optind; | 1899 | num_files = argc - optind; |
1896 | files = argv; | 1900 | files = argv; |
diff --git a/networking/inetd.c b/networking/inetd.c index 6843845fb..ca1a97268 100644 --- a/networking/inetd.c +++ b/networking/inetd.c | |||
@@ -246,7 +246,11 @@ | |||
246 | #if ENABLE_FEATURE_INETD_RPC | 246 | #if ENABLE_FEATURE_INETD_RPC |
247 | # if defined(__UCLIBC__) && ! defined(__UCLIBC_HAS_RPC__) | 247 | # if defined(__UCLIBC__) && ! defined(__UCLIBC_HAS_RPC__) |
248 | # warning "You probably need to build uClibc with UCLIBC_HAS_RPC for NFS support" | 248 | # warning "You probably need to build uClibc with UCLIBC_HAS_RPC for NFS support" |
249 | /* not #error, since user may be using e.g. libtirpc instead */ | 249 | /* not #error, since user may be using e.g. libtirpc instead. |
250 | * This might work: | ||
251 | * CONFIG_EXTRA_CFLAGS="-I/usr/include/tirpc" | ||
252 | * CONFIG_EXTRA_LDLIBS="tirpc" | ||
253 | */ | ||
250 | # endif | 254 | # endif |
251 | # include <rpc/rpc.h> | 255 | # include <rpc/rpc.h> |
252 | # include <rpc/pmap_clnt.h> | 256 | # include <rpc/pmap_clnt.h> |
diff --git a/networking/ip.c b/networking/ip.c index 0bc0edc57..accf90759 100644 --- a/networking/ip.c +++ b/networking/ip.c | |||
@@ -155,11 +155,61 @@ | |||
155 | //usage: | 155 | //usage: |
156 | //--------------123456789.123456789.123456789.123456789.123456789.123456789.123456789.123....79 | 156 | //--------------123456789.123456789.123456789.123456789.123456789.123456789.123456789.123....79 |
157 | //usage:#define iplink_trivial_usage | 157 | //usage:#define iplink_trivial_usage |
158 | //usage: "set IFACE [up|down] [arp on|off] | show [IFACE]" | 158 | //usage: /*Usage:iplink*/"set IFACE [up|down] [arp on|off] [multicast on|off]\n" |
159 | //usage:#define iplink_full_usage "\n\n" | 159 | //usage: " [promisc on|off] [mtu NUM] [name NAME] [qlen NUM] [address MAC]\n" |
160 | //usage: "iplink set IFACE [up|down] [arp on|off] [multicast on|off] [promisc on|off]\n" | 160 | //usage: " [master IFACE | nomaster]\n" |
161 | //usage: " [mtu NUM] [name NAME] [qlen NUM] [address MAC]\n" | 161 | // * short help shows only "set" command, long help continues (with just one "\n") |
162 | // * and shows all other commands: | ||
163 | //usage:#define iplink_full_usage "\n" | ||
164 | //usage: "iplink add [link IFACE] IFACE [address MAC] type TYPE [ARGS]\n" | ||
165 | //usage: "iplink delete IFACE type TYPE [ARGS]\n" | ||
166 | //usage: " TYPE ARGS := vlan VLANARGS | vrf table NUM\n" | ||
167 | //usage: " VLANARGS := id VLANID [protocol 802.1q|802.1ad] [reorder_hdr on|off]\n" | ||
168 | //usage: " [gvrp on|off] [mvrp on|off] [loose_binding on|off]\n" | ||
162 | //usage: "iplink show [IFACE]" | 169 | //usage: "iplink show [IFACE]" |
170 | //upstream man ip-link: | ||
171 | //===================== | ||
172 | //ip link add [link DEV] [ name ] NAME | ||
173 | // [ txqueuelen PACKETS ] | ||
174 | // [ address LLADDR ] | ||
175 | // [ broadcast LLADDR ] | ||
176 | // [ mtu MTU ] [index IDX ] | ||
177 | // [ numtxqueues QUEUE_COUNT ] | ||
178 | // [ numrxqueues QUEUE_COUNT ] | ||
179 | // type TYPE [ ARGS ] | ||
180 | // ip link delete { DEVICE | dev DEVICE | group DEVGROUP } type TYPE [ ARGS ] | ||
181 | // ip link set { DEVICE | dev DEVICE | group DEVGROUP } [ { up | down } ] | ||
182 | // [ arp { on | off } ] | ||
183 | // [ dynamic { on | off } ] | ||
184 | // [ multicast { on | off } ] | ||
185 | // [ allmulticast { on | off } ] | ||
186 | // [ promisc { on | off } ] | ||
187 | // [ trailers { on | off } ] | ||
188 | // [ txqueuelen PACKETS ] | ||
189 | // [ name NEWNAME ] | ||
190 | // [ address LLADDR ] | ||
191 | // [ broadcast LLADDR ] | ||
192 | // [ mtu MTU ] | ||
193 | // [ netns { PID | NAME } ] | ||
194 | // [ link-netnsid ID ] | ||
195 | // [ alias NAME ] | ||
196 | // [ vf NUM [ mac LLADDR ] | ||
197 | // [ vlan VLANID [ qos VLAN-QOS ] ] | ||
198 | // [ rate TXRATE ] | ||
199 | // [ spoofchk { on | off} ] | ||
200 | // [ query_rss { on | off} ] | ||
201 | // [ state { auto | enable | disable} ] ] | ||
202 | // [ trust { on | off} ] ] | ||
203 | // [ master DEVICE ] | ||
204 | // [ nomaster ] | ||
205 | // [ addrgenmode { eui64 | none | stable_secret | random } ] | ||
206 | // [ protodown { on | off } ] | ||
207 | // ip link show [ DEVICE | group GROUP ] [up] [master DEV] [type TYPE] | ||
208 | // ip link help [ TYPE ] | ||
209 | //TYPE := { vlan | veth | vcan | dummy | ifb | macvlan | macvtap | | ||
210 | // bridge | bond | ipoib | ip6tnl | ipip | sit | vxlan | | ||
211 | // gre | gretap | ip6gre | ip6gretap | vti | nlmon | | ||
212 | // bond_slave | ipvlan | geneve | bridge_slave | vrf } | ||
163 | //usage: | 213 | //usage: |
164 | //--------------123456789.123456789.123456789.123456789.123456789.123456789.123456789.123....79 | 214 | //--------------123456789.123456789.123456789.123456789.123456789.123456789.123456789.123....79 |
165 | //usage:#define iproute_trivial_usage | 215 | //usage:#define iproute_trivial_usage |
diff --git a/networking/libiproute/iplink.c b/networking/libiproute/iplink.c index f38fba055..2aa8b683b 100644 --- a/networking/libiproute/iplink.c +++ b/networking/libiproute/iplink.c | |||
@@ -128,6 +128,31 @@ static void set_mtu(char *dev, int mtu) | |||
128 | } | 128 | } |
129 | 129 | ||
130 | /* Exits on error */ | 130 | /* Exits on error */ |
131 | static void set_master(char *dev, int master) | ||
132 | { | ||
133 | struct rtnl_handle rth; | ||
134 | struct { | ||
135 | struct nlmsghdr n; | ||
136 | struct ifinfomsg i; | ||
137 | char buf[1024]; | ||
138 | } req; | ||
139 | |||
140 | memset(&req, 0, sizeof(req)); | ||
141 | |||
142 | req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)); | ||
143 | req.n.nlmsg_flags = NLM_F_REQUEST; | ||
144 | req.n.nlmsg_type = RTM_NEWLINK; | ||
145 | req.i.ifi_family = preferred_family; | ||
146 | |||
147 | xrtnl_open(&rth); | ||
148 | req.i.ifi_index = xll_name_to_index(dev); | ||
149 | //printf("master %i for %i\n", master, req.i.ifi_index); | ||
150 | addattr_l(&req.n, sizeof(req), IFLA_MASTER, &master, 4); | ||
151 | if (rtnl_talk(&rth, &req.n, 0, 0, NULL, NULL, NULL) < 0) | ||
152 | xfunc_die(); | ||
153 | } | ||
154 | |||
155 | /* Exits on error */ | ||
131 | static int get_address(char *dev, int *htype) | 156 | static int get_address(char *dev, int *htype) |
132 | { | 157 | { |
133 | struct ifreq ifr; | 158 | struct ifreq ifr; |
@@ -200,6 +225,7 @@ static int do_set(char **argv) | |||
200 | uint32_t flags = 0; | 225 | uint32_t flags = 0; |
201 | int qlen = -1; | 226 | int qlen = -1; |
202 | int mtu = -1; | 227 | int mtu = -1; |
228 | int master = -1; | ||
203 | char *newaddr = NULL; | 229 | char *newaddr = NULL; |
204 | char *newbrd = NULL; | 230 | char *newbrd = NULL; |
205 | struct ifreq ifr0, ifr1; | 231 | struct ifreq ifr0, ifr1; |
@@ -209,9 +235,11 @@ static int do_set(char **argv) | |||
209 | static const char keywords[] ALIGN1 = | 235 | static const char keywords[] ALIGN1 = |
210 | "up\0""down\0""name\0""mtu\0""qlen\0""multicast\0" | 236 | "up\0""down\0""name\0""mtu\0""qlen\0""multicast\0" |
211 | "arp\0""promisc\0""address\0" | 237 | "arp\0""promisc\0""address\0" |
238 | "master\0""nomaster\0" | ||
212 | "dev\0" /* must be last */; | 239 | "dev\0" /* must be last */; |
213 | enum { ARG_up = 0, ARG_down, ARG_name, ARG_mtu, ARG_qlen, ARG_multicast, | 240 | enum { ARG_up = 0, ARG_down, ARG_name, ARG_mtu, ARG_qlen, ARG_multicast, |
214 | ARG_arp, ARG_promisc, ARG_addr, | 241 | ARG_arp, ARG_promisc, ARG_addr, |
242 | ARG_master, ARG_nomaster, | ||
215 | ARG_dev }; | 243 | ARG_dev }; |
216 | enum { PARM_on = 0, PARM_off }; | 244 | enum { PARM_on = 0, PARM_off }; |
217 | smalluint key; | 245 | smalluint key; |
@@ -243,6 +271,11 @@ static int do_set(char **argv) | |||
243 | } else if (key == ARG_addr) { | 271 | } else if (key == ARG_addr) { |
244 | NEXT_ARG(); | 272 | NEXT_ARG(); |
245 | newaddr = *argv; | 273 | newaddr = *argv; |
274 | } else if (key == ARG_master) { | ||
275 | NEXT_ARG(); | ||
276 | master = xll_name_to_index(*argv); | ||
277 | } else if (key == ARG_nomaster) { | ||
278 | master = 0; | ||
246 | } else if (key >= ARG_dev) { | 279 | } else if (key >= ARG_dev) { |
247 | /* ^^^^^^ ">=" here results in "dev IFACE" treated as default */ | 280 | /* ^^^^^^ ">=" here results in "dev IFACE" treated as default */ |
248 | if (key == ARG_dev) { | 281 | if (key == ARG_dev) { |
@@ -427,6 +460,9 @@ static int do_set(char **argv) | |||
427 | if (mtu != -1) { | 460 | if (mtu != -1) { |
428 | set_mtu(dev, mtu); | 461 | set_mtu(dev, mtu); |
429 | } | 462 | } |
463 | if (master != -1) { | ||
464 | set_master(dev, master); | ||
465 | } | ||
430 | if (mask) | 466 | if (mask) |
431 | do_chflags(dev, flags, mask); | 467 | do_chflags(dev, flags, mask); |
432 | return 0; | 468 | return 0; |
@@ -525,6 +561,24 @@ static void vlan_parse_opt(char **argv, struct nlmsghdr *n, unsigned int size) | |||
525 | addattr_l(n, size, IFLA_VLAN_FLAGS, &flags, sizeof(flags)); | 561 | addattr_l(n, size, IFLA_VLAN_FLAGS, &flags, sizeof(flags)); |
526 | } | 562 | } |
527 | 563 | ||
564 | static void vrf_parse_opt(char **argv, struct nlmsghdr *n, unsigned int size) | ||
565 | { | ||
566 | /* IFLA_VRF_TABLE is an enum, not a define - | ||
567 | * can't test "defined(IFLA_VRF_TABLE)". | ||
568 | */ | ||
569 | #if !defined(IFLA_VRF_MAX) | ||
570 | # define IFLA_VRF_TABLE 1 | ||
571 | #endif | ||
572 | uint32_t table; | ||
573 | |||
574 | if (strcmp(*argv, "table") != 0) | ||
575 | invarg_1_to_2(*argv, "type vrf"); | ||
576 | |||
577 | NEXT_ARG(); | ||
578 | table = get_u32(*argv, "table"); | ||
579 | addattr_l(n, size, IFLA_VRF_TABLE, &table, sizeof(table)); | ||
580 | } | ||
581 | |||
528 | #ifndef NLMSG_TAIL | 582 | #ifndef NLMSG_TAIL |
529 | #define NLMSG_TAIL(nmsg) \ | 583 | #define NLMSG_TAIL(nmsg) \ |
530 | ((struct rtattr *) (((void *) (nmsg)) + NLMSG_ALIGN((nmsg)->nlmsg_len))) | 584 | ((struct rtattr *) (((void *) (nmsg)) + NLMSG_ALIGN((nmsg)->nlmsg_len))) |
@@ -563,6 +617,8 @@ static int do_add_or_delete(char **argv, const unsigned rtm) | |||
563 | if (rtm == RTM_NEWLINK) | 617 | if (rtm == RTM_NEWLINK) |
564 | req.n.nlmsg_flags |= NLM_F_CREATE|NLM_F_EXCL; | 618 | req.n.nlmsg_flags |= NLM_F_CREATE|NLM_F_EXCL; |
565 | 619 | ||
620 | /* NB: update iplink_full_usage if you extend this code */ | ||
621 | |||
566 | while (*argv) { | 622 | while (*argv) { |
567 | arg = index_in_substrings(keywords, *argv); | 623 | arg = index_in_substrings(keywords, *argv); |
568 | if (arg == ARG_type) { | 624 | if (arg == ARG_type) { |
@@ -582,7 +638,7 @@ static int do_add_or_delete(char **argv, const unsigned rtm) | |||
582 | } else if (arg == ARG_address) { | 638 | } else if (arg == ARG_address) { |
583 | NEXT_ARG(); | 639 | NEXT_ARG(); |
584 | address_str = *argv; | 640 | address_str = *argv; |
585 | dbg("address_str:'%s'", name_str); | 641 | dbg("address_str:'%s'", address_str); |
586 | } else { | 642 | } else { |
587 | if (arg == ARG_dev) { | 643 | if (arg == ARG_dev) { |
588 | if (dev_str) | 644 | if (dev_str) |
@@ -609,6 +665,8 @@ static int do_add_or_delete(char **argv, const unsigned rtm) | |||
609 | 665 | ||
610 | if (strcmp(type_str, "vlan") == 0) | 666 | if (strcmp(type_str, "vlan") == 0) |
611 | vlan_parse_opt(argv, &req.n, sizeof(req)); | 667 | vlan_parse_opt(argv, &req.n, sizeof(req)); |
668 | else if (strcmp(type_str, "vrf") == 0) | ||
669 | vrf_parse_opt(argv, &req.n, sizeof(req)); | ||
612 | 670 | ||
613 | data->rta_len = (void *)NLMSG_TAIL(&req.n) - (void *)data; | 671 | data->rta_len = (void *)NLMSG_TAIL(&req.n) - (void *)data; |
614 | } | 672 | } |
@@ -651,6 +709,8 @@ int FAST_FUNC do_iplink(char **argv) | |||
651 | { | 709 | { |
652 | static const char keywords[] ALIGN1 = | 710 | static const char keywords[] ALIGN1 = |
653 | "add\0""delete\0""set\0""show\0""lst\0""list\0"; | 711 | "add\0""delete\0""set\0""show\0""lst\0""list\0"; |
712 | |||
713 | xfunc_error_retval = 2; //TODO: move up to "ip"? Is it the common rule for all "ip" tools? | ||
654 | if (*argv) { | 714 | if (*argv) { |
655 | int key = index_in_substrings(keywords, *argv); | 715 | int key = index_in_substrings(keywords, *argv); |
656 | if (key < 0) /* invalid argument */ | 716 | if (key < 0) /* invalid argument */ |
diff --git a/networking/ping.c b/networking/ping.c index d1d59d545..8f85d3ec2 100644 --- a/networking/ping.c +++ b/networking/ping.c | |||
@@ -74,6 +74,7 @@ | |||
74 | //usage: ) | 74 | //usage: ) |
75 | //usage: "\n -c CNT Send only CNT pings" | 75 | //usage: "\n -c CNT Send only CNT pings" |
76 | //usage: "\n -s SIZE Send SIZE data bytes in packets (default 56)" | 76 | //usage: "\n -s SIZE Send SIZE data bytes in packets (default 56)" |
77 | //usage: "\n -A Ping as soon as reply is recevied" | ||
77 | //usage: "\n -t TTL Set TTL" | 78 | //usage: "\n -t TTL Set TTL" |
78 | //usage: "\n -I IFACE/IP Source interface or IP address" | 79 | //usage: "\n -I IFACE/IP Source interface or IP address" |
79 | //usage: "\n -W SEC Seconds to wait for the first response (default 10)" | 80 | //usage: "\n -W SEC Seconds to wait for the first response (default 10)" |
@@ -90,6 +91,7 @@ | |||
90 | //usage: "Send ICMP ECHO_REQUEST packets to network hosts\n" | 91 | //usage: "Send ICMP ECHO_REQUEST packets to network hosts\n" |
91 | //usage: "\n -c CNT Send only CNT pings" | 92 | //usage: "\n -c CNT Send only CNT pings" |
92 | //usage: "\n -s SIZE Send SIZE data bytes in packets (default 56)" | 93 | //usage: "\n -s SIZE Send SIZE data bytes in packets (default 56)" |
94 | //usage: "\n -A Ping as soon as reply is recevied" | ||
93 | //usage: "\n -I IFACE/IP Source interface or IP address" | 95 | //usage: "\n -I IFACE/IP Source interface or IP address" |
94 | //usage: "\n -q Quiet, only display output at start" | 96 | //usage: "\n -q Quiet, only display output at start" |
95 | //usage: "\n and when finished" | 97 | //usage: "\n and when finished" |
@@ -348,20 +350,21 @@ static int common_ping_main(sa_family_t af, char **argv) | |||
348 | /* Full(er) version */ | 350 | /* Full(er) version */ |
349 | 351 | ||
350 | /* -c NUM, -t NUM, -w NUM, -W NUM */ | 352 | /* -c NUM, -t NUM, -w NUM, -W NUM */ |
351 | #define OPT_STRING "qvc:+s:t:+w:+W:+I:np:4"IF_PING6("6") | 353 | #define OPT_STRING "qvAc:+s:t:+w:+W:+I:np:4"IF_PING6("6") |
352 | enum { | 354 | enum { |
353 | OPT_QUIET = 1 << 0, | 355 | OPT_QUIET = 1 << 0, |
354 | OPT_VERBOSE = 1 << 1, | 356 | OPT_VERBOSE = 1 << 1, |
355 | OPT_c = 1 << 2, | 357 | OPT_A = 1 << 2, |
356 | OPT_s = 1 << 3, | 358 | OPT_c = 1 << 3, |
357 | OPT_t = 1 << 4, | 359 | OPT_s = 1 << 4, |
358 | OPT_w = 1 << 5, | 360 | OPT_t = 1 << 5, |
359 | OPT_W = 1 << 6, | 361 | OPT_w = 1 << 6, |
360 | OPT_I = 1 << 7, | 362 | OPT_W = 1 << 7, |
361 | /*OPT_n = 1 << 8, - ignored */ | 363 | OPT_I = 1 << 8, |
362 | OPT_p = 1 << 9, | 364 | /*OPT_n = 1 << 9, - ignored */ |
363 | OPT_IPV4 = 1 << 10, | 365 | OPT_p = 1 << 10, |
364 | OPT_IPV6 = (1 << 11) * ENABLE_PING6, | 366 | OPT_IPV4 = 1 << 11, |
367 | OPT_IPV6 = (1 << 12) * ENABLE_PING6, | ||
365 | }; | 368 | }; |
366 | 369 | ||
367 | 370 | ||
@@ -377,9 +380,9 @@ struct globals { | |||
377 | uint8_t pattern; | 380 | uint8_t pattern; |
378 | unsigned tmin, tmax; /* in us */ | 381 | unsigned tmin, tmax; /* in us */ |
379 | unsigned long long tsum; /* in us, sum of all times */ | 382 | unsigned long long tsum; /* in us, sum of all times */ |
380 | unsigned deadline; | 383 | unsigned cur_us; /* low word only, we don't need more */ |
384 | unsigned deadline_us; | ||
381 | unsigned timeout; | 385 | unsigned timeout; |
382 | unsigned total_secs; | ||
383 | unsigned sizeof_rcv_packet; | 386 | unsigned sizeof_rcv_packet; |
384 | char *rcv_packet; /* [datalen + MAXIPLEN + MAXICMPLEN] */ | 387 | char *rcv_packet; /* [datalen + MAXIPLEN + MAXICMPLEN] */ |
385 | void *snd_packet; /* [datalen + ipv4/ipv6_const] */ | 388 | void *snd_packet; /* [datalen + ipv4/ipv6_const] */ |
@@ -405,9 +408,7 @@ struct globals { | |||
405 | #define tmin (G.tmin ) | 408 | #define tmin (G.tmin ) |
406 | #define tmax (G.tmax ) | 409 | #define tmax (G.tmax ) |
407 | #define tsum (G.tsum ) | 410 | #define tsum (G.tsum ) |
408 | #define deadline (G.deadline ) | ||
409 | #define timeout (G.timeout ) | 411 | #define timeout (G.timeout ) |
410 | #define total_secs (G.total_secs ) | ||
411 | #define hostname (G.hostname ) | 412 | #define hostname (G.hostname ) |
412 | #define dotted (G.dotted ) | 413 | #define dotted (G.dotted ) |
413 | #define pingaddr (G.pingaddr ) | 414 | #define pingaddr (G.pingaddr ) |
@@ -455,7 +456,7 @@ static void print_stats_and_exit(int junk UNUSED_PARAM) | |||
455 | tmax / 1000, tmax % 1000); | 456 | tmax / 1000, tmax % 1000); |
456 | } | 457 | } |
457 | /* if condition is true, exit with 1 -- 'failure' */ | 458 | /* if condition is true, exit with 1 -- 'failure' */ |
458 | exit(nrecv == 0 || (deadline && nrecv < pingcount)); | 459 | exit(nrecv == 0 || (G.deadline_us && nrecv < pingcount)); |
459 | } | 460 | } |
460 | 461 | ||
461 | static void sendping_tail(void (*sp)(int), int size_pkt) | 462 | static void sendping_tail(void (*sp)(int), int size_pkt) |
@@ -467,22 +468,23 @@ static void sendping_tail(void (*sp)(int), int size_pkt) | |||
467 | 468 | ||
468 | size_pkt += datalen; | 469 | size_pkt += datalen; |
469 | 470 | ||
471 | if (G.deadline_us) { | ||
472 | unsigned n = G.cur_us - G.deadline_us; | ||
473 | if ((int)n >= 0) | ||
474 | print_stats_and_exit(0); | ||
475 | } | ||
476 | |||
470 | /* sizeof(pingaddr) can be larger than real sa size, but I think | 477 | /* sizeof(pingaddr) can be larger than real sa size, but I think |
471 | * it doesn't matter */ | 478 | * it doesn't matter */ |
472 | sz = xsendto(pingsock, G.snd_packet, size_pkt, &pingaddr.sa, sizeof(pingaddr)); | 479 | sz = xsendto(pingsock, G.snd_packet, size_pkt, &pingaddr.sa, sizeof(pingaddr)); |
473 | if (sz != size_pkt) | 480 | if (sz != size_pkt) |
474 | bb_error_msg_and_die(bb_msg_write_error); | 481 | bb_error_msg_and_die(bb_msg_write_error); |
475 | 482 | ||
476 | if (pingcount == 0 || deadline || G.ntransmitted < pingcount) { | 483 | if (pingcount == 0 || G.ntransmitted < pingcount) { |
477 | /* Didn't send all pings yet - schedule next in 1s */ | 484 | /* Didn't send all pings yet - schedule next in 1s */ |
478 | signal(SIGALRM, sp); | 485 | signal(SIGALRM, sp); |
479 | if (deadline) { | ||
480 | total_secs += PINGINTERVAL; | ||
481 | if (total_secs >= deadline) | ||
482 | signal(SIGALRM, print_stats_and_exit); | ||
483 | } | ||
484 | alarm(PINGINTERVAL); | 486 | alarm(PINGINTERVAL); |
485 | } else { /* -c NN, and all NN are sent (and no deadline) */ | 487 | } else { /* -c NN, and all NN are sent */ |
486 | /* Wait for the last ping to come back. | 488 | /* Wait for the last ping to come back. |
487 | * -W timeout: wait for a response in seconds. | 489 | * -W timeout: wait for a response in seconds. |
488 | * Affects only timeout in absence of any responses, | 490 | * Affects only timeout in absence of any responses, |
@@ -516,7 +518,7 @@ static void sendping4(int junk UNUSED_PARAM) | |||
516 | */ | 518 | */ |
517 | /*if (datalen >= 4)*/ | 519 | /*if (datalen >= 4)*/ |
518 | /* No hton: we'll read it back on the same machine */ | 520 | /* No hton: we'll read it back on the same machine */ |
519 | *(uint32_t*)&pkt->icmp_dun = monotonic_us(); | 521 | *(uint32_t*)&pkt->icmp_dun = G.cur_us = monotonic_us(); |
520 | 522 | ||
521 | pkt->icmp_cksum = inet_cksum((uint16_t *) pkt, datalen + ICMP_MINLEN); | 523 | pkt->icmp_cksum = inet_cksum((uint16_t *) pkt, datalen + ICMP_MINLEN); |
522 | 524 | ||
@@ -535,7 +537,7 @@ static void sendping6(int junk UNUSED_PARAM) | |||
535 | pkt->icmp6_id = myid; | 537 | pkt->icmp6_id = myid; |
536 | 538 | ||
537 | /*if (datalen >= 4)*/ | 539 | /*if (datalen >= 4)*/ |
538 | *(bb__aliased_uint32_t*)(&pkt->icmp6_data8[4]) = monotonic_us(); | 540 | *(bb__aliased_uint32_t*)(&pkt->icmp6_data8[4]) = G.cur_us = monotonic_us(); |
539 | 541 | ||
540 | //TODO? pkt->icmp_cksum = inet_cksum(...); | 542 | //TODO? pkt->icmp_cksum = inet_cksum(...); |
541 | 543 | ||
@@ -632,7 +634,7 @@ static void unpack_tail(int sz, uint32_t *tp, | |||
632 | puts(dupmsg); | 634 | puts(dupmsg); |
633 | fflush_all(); | 635 | fflush_all(); |
634 | } | 636 | } |
635 | static void unpack4(char *buf, int sz, struct sockaddr_in *from) | 637 | static int unpack4(char *buf, int sz, struct sockaddr_in *from) |
636 | { | 638 | { |
637 | struct icmp *icmppkt; | 639 | struct icmp *icmppkt; |
638 | struct iphdr *iphdr; | 640 | struct iphdr *iphdr; |
@@ -640,7 +642,7 @@ static void unpack4(char *buf, int sz, struct sockaddr_in *from) | |||
640 | 642 | ||
641 | /* discard if too short */ | 643 | /* discard if too short */ |
642 | if (sz < (datalen + ICMP_MINLEN)) | 644 | if (sz < (datalen + ICMP_MINLEN)) |
643 | return; | 645 | return 0; |
644 | 646 | ||
645 | /* check IP header */ | 647 | /* check IP header */ |
646 | iphdr = (struct iphdr *) buf; | 648 | iphdr = (struct iphdr *) buf; |
@@ -648,7 +650,7 @@ static void unpack4(char *buf, int sz, struct sockaddr_in *from) | |||
648 | sz -= hlen; | 650 | sz -= hlen; |
649 | icmppkt = (struct icmp *) (buf + hlen); | 651 | icmppkt = (struct icmp *) (buf + hlen); |
650 | if (icmppkt->icmp_id != myid) | 652 | if (icmppkt->icmp_id != myid) |
651 | return; /* not our ping */ | 653 | return 0; /* not our ping */ |
652 | 654 | ||
653 | if (icmppkt->icmp_type == ICMP_ECHOREPLY) { | 655 | if (icmppkt->icmp_type == ICMP_ECHOREPLY) { |
654 | uint16_t recv_seq = ntohs(icmppkt->icmp_seq); | 656 | uint16_t recv_seq = ntohs(icmppkt->icmp_seq); |
@@ -659,25 +661,28 @@ static void unpack4(char *buf, int sz, struct sockaddr_in *from) | |||
659 | unpack_tail(sz, tp, | 661 | unpack_tail(sz, tp, |
660 | inet_ntoa(*(struct in_addr *) &from->sin_addr.s_addr), | 662 | inet_ntoa(*(struct in_addr *) &from->sin_addr.s_addr), |
661 | recv_seq, iphdr->ttl); | 663 | recv_seq, iphdr->ttl); |
662 | } else if (icmppkt->icmp_type != ICMP_ECHO) { | 664 | return 1; |
665 | } | ||
666 | if (icmppkt->icmp_type != ICMP_ECHO) { | ||
663 | bb_error_msg("warning: got ICMP %d (%s)", | 667 | bb_error_msg("warning: got ICMP %d (%s)", |
664 | icmppkt->icmp_type, | 668 | icmppkt->icmp_type, |
665 | icmp_type_name(icmppkt->icmp_type)); | 669 | icmp_type_name(icmppkt->icmp_type)); |
666 | } | 670 | } |
671 | return 0; | ||
667 | } | 672 | } |
668 | #if ENABLE_PING6 | 673 | #if ENABLE_PING6 |
669 | static void unpack6(char *packet, int sz, struct sockaddr_in6 *from, int hoplimit) | 674 | static int unpack6(char *packet, int sz, struct sockaddr_in6 *from, int hoplimit) |
670 | { | 675 | { |
671 | struct icmp6_hdr *icmppkt; | 676 | struct icmp6_hdr *icmppkt; |
672 | char buf[INET6_ADDRSTRLEN]; | 677 | char buf[INET6_ADDRSTRLEN]; |
673 | 678 | ||
674 | /* discard if too short */ | 679 | /* discard if too short */ |
675 | if (sz < (datalen + sizeof(struct icmp6_hdr))) | 680 | if (sz < (datalen + sizeof(struct icmp6_hdr))) |
676 | return; | 681 | return 0; |
677 | 682 | ||
678 | icmppkt = (struct icmp6_hdr *) packet; | 683 | icmppkt = (struct icmp6_hdr *) packet; |
679 | if (icmppkt->icmp6_id != myid) | 684 | if (icmppkt->icmp6_id != myid) |
680 | return; /* not our ping */ | 685 | return 0; /* not our ping */ |
681 | 686 | ||
682 | if (icmppkt->icmp6_type == ICMP6_ECHO_REPLY) { | 687 | if (icmppkt->icmp6_type == ICMP6_ECHO_REPLY) { |
683 | uint16_t recv_seq = ntohs(icmppkt->icmp6_seq); | 688 | uint16_t recv_seq = ntohs(icmppkt->icmp6_seq); |
@@ -689,11 +694,14 @@ static void unpack6(char *packet, int sz, struct sockaddr_in6 *from, int hoplimi | |||
689 | inet_ntop(AF_INET6, &from->sin6_addr, | 694 | inet_ntop(AF_INET6, &from->sin6_addr, |
690 | buf, sizeof(buf)), | 695 | buf, sizeof(buf)), |
691 | recv_seq, hoplimit); | 696 | recv_seq, hoplimit); |
692 | } else if (icmppkt->icmp6_type != ICMP6_ECHO_REQUEST) { | 697 | return 1; |
698 | } | ||
699 | if (icmppkt->icmp6_type != ICMP6_ECHO_REQUEST) { | ||
693 | bb_error_msg("warning: got ICMP %d (%s)", | 700 | bb_error_msg("warning: got ICMP %d (%s)", |
694 | icmppkt->icmp6_type, | 701 | icmppkt->icmp6_type, |
695 | icmp6_type_name(icmppkt->icmp6_type)); | 702 | icmp6_type_name(icmppkt->icmp6_type)); |
696 | } | 703 | } |
704 | return 0; | ||
697 | } | 705 | } |
698 | #endif | 706 | #endif |
699 | 707 | ||
@@ -726,6 +734,7 @@ static void ping4(len_and_sockaddr *lsa) | |||
726 | signal(SIGINT, print_stats_and_exit); | 734 | signal(SIGINT, print_stats_and_exit); |
727 | 735 | ||
728 | /* start the ping's going ... */ | 736 | /* start the ping's going ... */ |
737 | send_ping: | ||
729 | sendping4(0); | 738 | sendping4(0); |
730 | 739 | ||
731 | /* listen for replies */ | 740 | /* listen for replies */ |
@@ -741,9 +750,12 @@ static void ping4(len_and_sockaddr *lsa) | |||
741 | bb_perror_msg("recvfrom"); | 750 | bb_perror_msg("recvfrom"); |
742 | continue; | 751 | continue; |
743 | } | 752 | } |
744 | unpack4(G.rcv_packet, c, &from); | 753 | c = unpack4(G.rcv_packet, c, &from); |
745 | if (pingcount && G.nreceived >= pingcount) | 754 | if (pingcount && G.nreceived >= pingcount) |
746 | break; | 755 | break; |
756 | if (c && (option_mask32 & OPT_A)) { | ||
757 | goto send_ping; | ||
758 | } | ||
747 | } | 759 | } |
748 | } | 760 | } |
749 | #if ENABLE_PING6 | 761 | #if ENABLE_PING6 |
@@ -794,10 +806,6 @@ static void ping6(len_and_sockaddr *lsa) | |||
794 | 806 | ||
795 | signal(SIGINT, print_stats_and_exit); | 807 | signal(SIGINT, print_stats_and_exit); |
796 | 808 | ||
797 | /* start the ping's going ... */ | ||
798 | sendping6(0); | ||
799 | |||
800 | /* listen for replies */ | ||
801 | msg.msg_name = &from; | 809 | msg.msg_name = &from; |
802 | msg.msg_namelen = sizeof(from); | 810 | msg.msg_namelen = sizeof(from); |
803 | msg.msg_iov = &iov; | 811 | msg.msg_iov = &iov; |
@@ -805,12 +813,18 @@ static void ping6(len_and_sockaddr *lsa) | |||
805 | msg.msg_control = control_buf; | 813 | msg.msg_control = control_buf; |
806 | iov.iov_base = G.rcv_packet; | 814 | iov.iov_base = G.rcv_packet; |
807 | iov.iov_len = G.sizeof_rcv_packet; | 815 | iov.iov_len = G.sizeof_rcv_packet; |
816 | |||
817 | /* start the ping's going ... */ | ||
818 | send_ping: | ||
819 | sendping6(0); | ||
820 | |||
821 | /* listen for replies */ | ||
808 | while (1) { | 822 | while (1) { |
809 | int c; | 823 | int c; |
810 | struct cmsghdr *mp; | 824 | struct cmsghdr *mp; |
811 | int hoplimit = -1; | 825 | int hoplimit = -1; |
812 | msg.msg_controllen = sizeof(control_buf); | ||
813 | 826 | ||
827 | msg.msg_controllen = sizeof(control_buf); | ||
814 | c = recvmsg(pingsock, &msg, 0); | 828 | c = recvmsg(pingsock, &msg, 0); |
815 | if (c < 0) { | 829 | if (c < 0) { |
816 | if (errno != EINTR) | 830 | if (errno != EINTR) |
@@ -827,9 +841,12 @@ static void ping6(len_and_sockaddr *lsa) | |||
827 | move_from_unaligned_int(hoplimit, CMSG_DATA(mp)); | 841 | move_from_unaligned_int(hoplimit, CMSG_DATA(mp)); |
828 | } | 842 | } |
829 | } | 843 | } |
830 | unpack6(G.rcv_packet, c, &from, hoplimit); | 844 | c = unpack6(G.rcv_packet, c, &from, hoplimit); |
831 | if (pingcount && G.nreceived >= pingcount) | 845 | if (pingcount && G.nreceived >= pingcount) |
832 | break; | 846 | break; |
847 | if (c && (option_mask32 & OPT_A)) { | ||
848 | goto send_ping; | ||
849 | } | ||
833 | } | 850 | } |
834 | } | 851 | } |
835 | #endif | 852 | #endif |
@@ -875,7 +892,7 @@ static int common_ping_main(int opt, char **argv) | |||
875 | OPT_STRING | 892 | OPT_STRING |
876 | /* exactly one arg; -v and -q don't mix */ | 893 | /* exactly one arg; -v and -q don't mix */ |
877 | "\0" "=1:q--v:v--q", | 894 | "\0" "=1:q--v:v--q", |
878 | &pingcount, &str_s, &opt_ttl, &deadline, &timeout, &str_I, &str_p | 895 | &pingcount, &str_s, &opt_ttl, &G.deadline_us, &timeout, &str_I, &str_p |
879 | ); | 896 | ); |
880 | if (opt & OPT_s) | 897 | if (opt & OPT_s) |
881 | datalen = xatou16(str_s); // -s | 898 | datalen = xatou16(str_s); // -s |
@@ -889,6 +906,10 @@ static int common_ping_main(int opt, char **argv) | |||
889 | } | 906 | } |
890 | if (opt & OPT_p) | 907 | if (opt & OPT_p) |
891 | G.pattern = xstrtou_range(str_p, 16, 0, 255); | 908 | G.pattern = xstrtou_range(str_p, 16, 0, 255); |
909 | if (G.deadline_us) { | ||
910 | unsigned d = G.deadline_us < INT_MAX/1000000 ? G.deadline_us : INT_MAX/1000000; | ||
911 | G.deadline_us = 1 | ((d * 1000000) + monotonic_us()); | ||
912 | } | ||
892 | 913 | ||
893 | myid = (uint16_t) getpid(); | 914 | myid = (uint16_t) getpid(); |
894 | hostname = argv[optind]; | 915 | hostname = argv[optind]; |
@@ -911,7 +932,7 @@ static int common_ping_main(int opt, char **argv) | |||
911 | 932 | ||
912 | dotted = xmalloc_sockaddr2dotted_noport(&lsa->u.sa); | 933 | dotted = xmalloc_sockaddr2dotted_noport(&lsa->u.sa); |
913 | ping(lsa); | 934 | ping(lsa); |
914 | print_stats_and_exit(EXIT_SUCCESS); | 935 | print_stats_and_exit(0); |
915 | /*return EXIT_SUCCESS;*/ | 936 | /*return EXIT_SUCCESS;*/ |
916 | } | 937 | } |
917 | #endif /* FEATURE_FANCY_PING */ | 938 | #endif /* FEATURE_FANCY_PING */ |
diff --git a/networking/tcpudp.c b/networking/tcpudp.c index d4c69e0f7..a90e3f80a 100644 --- a/networking/tcpudp.c +++ b/networking/tcpudp.c | |||
@@ -127,6 +127,7 @@ struct globals { | |||
127 | unsigned cur_per_host; | 127 | unsigned cur_per_host; |
128 | unsigned cnum; | 128 | unsigned cnum; |
129 | unsigned cmax; | 129 | unsigned cmax; |
130 | struct hcc *cc; | ||
130 | char **env_cur; | 131 | char **env_cur; |
131 | char *env_var[1]; /* actually bigger */ | 132 | char *env_var[1]; /* actually bigger */ |
132 | } FIX_ALIASING; | 133 | } FIX_ALIASING; |
@@ -229,7 +230,7 @@ static void sig_child_handler(int sig UNUSED_PARAM) | |||
229 | 230 | ||
230 | while ((pid = wait_any_nohang(&wstat)) > 0) { | 231 | while ((pid = wait_any_nohang(&wstat)) > 0) { |
231 | if (max_per_host) | 232 | if (max_per_host) |
232 | ipsvd_perhost_remove(pid); | 233 | ipsvd_perhost_remove(G.cc, pid); |
233 | if (cnum) | 234 | if (cnum) |
234 | cnum--; | 235 | cnum--; |
235 | if (verbose) | 236 | if (verbose) |
@@ -318,7 +319,7 @@ int tcpudpsvd_main(int argc UNUSED_PARAM, char **argv) | |||
318 | sslser = user; | 319 | sslser = user; |
319 | client = 0; | 320 | client = 0; |
320 | if ((getuid() == 0) && !(opts & OPT_u)) { | 321 | if ((getuid() == 0) && !(opts & OPT_u)) { |
321 | xfunc_exitcode = 100; | 322 | xfunc_error_retval = 100; |
322 | bb_error_msg_and_die(bb_msg_you_must_be_root); | 323 | bb_error_msg_and_die(bb_msg_you_must_be_root); |
323 | } | 324 | } |
324 | if (opts & OPT_u) | 325 | if (opts & OPT_u) |
@@ -347,7 +348,7 @@ int tcpudpsvd_main(int argc UNUSED_PARAM, char **argv) | |||
347 | signal(SIGPIPE, SIG_IGN); | 348 | signal(SIGPIPE, SIG_IGN); |
348 | 349 | ||
349 | if (max_per_host) | 350 | if (max_per_host) |
350 | ipsvd_perhost_init(cmax); | 351 | G.cc = ipsvd_perhost_init(cmax); |
351 | 352 | ||
352 | local_port = bb_lookup_port(argv[1], tcp ? "tcp" : "udp", 0); | 353 | local_port = bb_lookup_port(argv[1], tcp ? "tcp" : "udp", 0); |
353 | lsa = xhost2sockaddr(argv[0], local_port); | 354 | lsa = xhost2sockaddr(argv[0], local_port); |
@@ -422,7 +423,7 @@ int tcpudpsvd_main(int argc UNUSED_PARAM, char **argv) | |||
422 | /* Drop connection immediately if cur_per_host > max_per_host | 423 | /* Drop connection immediately if cur_per_host > max_per_host |
423 | * (minimizing load under SYN flood) */ | 424 | * (minimizing load under SYN flood) */ |
424 | remote_addr = xmalloc_sockaddr2dotted_noport(&remote.u.sa); | 425 | remote_addr = xmalloc_sockaddr2dotted_noport(&remote.u.sa); |
425 | cur_per_host = ipsvd_perhost_add(remote_addr, max_per_host, &hccp); | 426 | cur_per_host = ipsvd_perhost_add(G.cc, remote_addr, max_per_host, &hccp); |
426 | if (cur_per_host > max_per_host) { | 427 | if (cur_per_host > max_per_host) { |
427 | /* ipsvd_perhost_add detected that max is exceeded | 428 | /* ipsvd_perhost_add detected that max is exceeded |
428 | * (and did not store ip in connection table) */ | 429 | * (and did not store ip in connection table) */ |
diff --git a/networking/tcpudp_perhost.c b/networking/tcpudp_perhost.c index 105410883..2643f8d16 100644 --- a/networking/tcpudp_perhost.c +++ b/networking/tcpudp_perhost.c | |||
@@ -10,25 +10,21 @@ | |||
10 | #include "libbb.h" | 10 | #include "libbb.h" |
11 | #include "tcpudp_perhost.h" | 11 | #include "tcpudp_perhost.h" |
12 | 12 | ||
13 | static struct hcc *cc; | 13 | struct hcc* FAST_FUNC ipsvd_perhost_init(unsigned c) |
14 | static unsigned cclen; | ||
15 | |||
16 | /* to be optimized */ | ||
17 | |||
18 | void ipsvd_perhost_init(unsigned c) | ||
19 | { | 14 | { |
20 | // free(cc); | 15 | // free(cc); |
21 | cc = xzalloc(c * sizeof(*cc)); | 16 | struct hcc *cc = xzalloc((c + 1) * sizeof(*cc)); |
22 | cclen = c; | 17 | cc[c].pid = -1; /* "end" marker */ |
18 | return cc; | ||
23 | } | 19 | } |
24 | 20 | ||
25 | unsigned ipsvd_perhost_add(char *ip, unsigned maxconn, struct hcc **hccpp) | 21 | unsigned FAST_FUNC ipsvd_perhost_add(struct hcc *cc, char *ip, unsigned maxconn, struct hcc **hccpp) |
26 | { | 22 | { |
27 | unsigned i; | 23 | unsigned i; |
28 | unsigned conn = 1; | 24 | unsigned conn = 1; |
29 | int freepos = -1; | 25 | int freepos = -1; |
30 | 26 | ||
31 | for (i = 0; i < cclen; ++i) { | 27 | for (i = 0; cc[i].pid >= 0; ++i) { |
32 | if (!cc[i].ip) { | 28 | if (!cc[i].ip) { |
33 | freepos = i; | 29 | freepos = i; |
34 | continue; | 30 | continue; |
@@ -46,10 +42,10 @@ unsigned ipsvd_perhost_add(char *ip, unsigned maxconn, struct hcc **hccpp) | |||
46 | return conn; | 42 | return conn; |
47 | } | 43 | } |
48 | 44 | ||
49 | void ipsvd_perhost_remove(int pid) | 45 | void FAST_FUNC ipsvd_perhost_remove(struct hcc *cc, int pid) |
50 | { | 46 | { |
51 | unsigned i; | 47 | unsigned i; |
52 | for (i = 0; i < cclen; ++i) { | 48 | for (i = 0; cc[i].pid >= 0; ++i) { |
53 | if (cc[i].pid == pid) { | 49 | if (cc[i].pid == pid) { |
54 | free(cc[i].ip); | 50 | free(cc[i].ip); |
55 | cc[i].ip = NULL; | 51 | cc[i].ip = NULL; |
@@ -59,7 +55,7 @@ void ipsvd_perhost_remove(int pid) | |||
59 | } | 55 | } |
60 | } | 56 | } |
61 | 57 | ||
62 | //void ipsvd_perhost_free(void) | 58 | //void ipsvd_perhost_free(struct hcc *cc) |
63 | //{ | 59 | //{ |
64 | // free(cc); | 60 | // free(cc); |
65 | //} | 61 | //} |
diff --git a/networking/tcpudp_perhost.h b/networking/tcpudp_perhost.h index 3e5757678..3b14d9a57 100644 --- a/networking/tcpudp_perhost.h +++ b/networking/tcpudp_perhost.h | |||
@@ -14,7 +14,7 @@ struct hcc { | |||
14 | int pid; | 14 | int pid; |
15 | }; | 15 | }; |
16 | 16 | ||
17 | void ipsvd_perhost_init(unsigned); | 17 | struct hcc* FAST_FUNC ipsvd_perhost_init(unsigned); |
18 | 18 | ||
19 | /* Returns number of already opened connects to this ips, including this one. | 19 | /* Returns number of already opened connects to this ips, including this one. |
20 | * ip should be a malloc'ed ptr. | 20 | * ip should be a malloc'ed ptr. |
@@ -22,12 +22,12 @@ void ipsvd_perhost_init(unsigned); | |||
22 | * and pointer to table entry if stored in *hccpp | 22 | * and pointer to table entry if stored in *hccpp |
23 | * (useful for storing pid later). | 23 | * (useful for storing pid later). |
24 | * Else ip is NOT inserted (you must take care of it - free() etc) */ | 24 | * Else ip is NOT inserted (you must take care of it - free() etc) */ |
25 | unsigned ipsvd_perhost_add(char *ip, unsigned maxconn, struct hcc **hccpp); | 25 | unsigned FAST_FUNC ipsvd_perhost_add(struct hcc *cc, char *ip, unsigned maxconn, struct hcc **hccpp); |
26 | 26 | ||
27 | /* Finds and frees element with pid */ | 27 | /* Finds and frees element with pid */ |
28 | void ipsvd_perhost_remove(int pid); | 28 | void FAST_FUNC ipsvd_perhost_remove(struct hcc *cc, int pid); |
29 | 29 | ||
30 | //unsigned ipsvd_perhost_setpid(int pid); | 30 | //unsigned ipsvd_perhost_setpid(int pid); |
31 | //void ipsvd_perhost_free(void); | 31 | //void ipsvd_perhost_free(struct hcc *cc); |
32 | 32 | ||
33 | POP_SAVED_FUNCTION_VISIBILITY | 33 | POP_SAVED_FUNCTION_VISIBILITY |
diff --git a/networking/tls.c b/networking/tls.c index da7b6058f..99722cfb4 100644 --- a/networking/tls.c +++ b/networking/tls.c | |||
@@ -806,8 +806,6 @@ static int tls_xread_record(tls_state_t *tls, const char *expected) | |||
806 | || xhdr->proto_min != TLS_MIN | 806 | || xhdr->proto_min != TLS_MIN |
807 | ) { | 807 | ) { |
808 | sz = total < target ? total : target; | 808 | sz = total < target ? total : target; |
809 | if (sz > 24) | ||
810 | sz = 24; /* don't flood */ | ||
811 | bad_record_die(tls, expected, sz); | 809 | bad_record_die(tls, expected, sz); |
812 | } | 810 | } |
813 | dbg("xhdr type:%d ver:%d.%d len:%d\n", | 811 | dbg("xhdr type:%d ver:%d.%d len:%d\n", |
diff --git a/shell/ash.c b/shell/ash.c index 88834f49a..394022df9 100644 --- a/shell/ash.c +++ b/shell/ash.c | |||
@@ -6517,12 +6517,12 @@ rmescapes(char *str, int flag, int *slash_position) | |||
6517 | if (*p == '*' | 6517 | if (*p == '*' |
6518 | || *p == '?' | 6518 | || *p == '?' |
6519 | || *p == '[' | 6519 | || *p == '[' |
6520 | || *p == '\\' /* case '\' in \\ ) echo ok;; *) echo WRONG;; esac */ | 6520 | || *p == '\\' /* case '\' in \\ ) echo ok;; *) echo WRONG;; esac */ |
6521 | || *p == ']' /* case ']' in [a\]] ) echo ok;; *) echo WRONG;; esac */ | 6521 | || *p == ']' /* case ']' in [a\]] ) echo ok;; *) echo WRONG;; esac */ |
6522 | || *p == '-' /* case '-' in [a\-c]) echo ok;; *) echo WRONG;; esac */ | 6522 | || *p == '-' /* case '-' in [a\-c]) echo ok;; *) echo WRONG;; esac */ |
6523 | || *p == '!' /* case '!' in [\!] ) echo ok;; *) echo WRONG;; esac */ | 6523 | || *p == '!' /* case '!' in [\!] ) echo ok;; *) echo WRONG;; esac */ |
6524 | /* Some libc support [^negate], that's why "^" also needs love */ | 6524 | /* Some libc support [^negate], that's why "^" also needs love */ |
6525 | || *p == '^' /* case '^' in [\^] ) echo ok;; *) echo WRONG;; esac */ | 6525 | || *p == '^' /* case '^' in [\^] ) echo ok;; *) echo WRONG;; esac */ |
6526 | ) { | 6526 | ) { |
6527 | *q++ = '\\'; | 6527 | *q++ = '\\'; |
6528 | } | 6528 | } |
@@ -12512,13 +12512,24 @@ readtoken1(int c, int syntax, char *eofmark, int striptabs) | |||
12512 | USTPUTC(CTLESC, out); | 12512 | USTPUTC(CTLESC, out); |
12513 | USTPUTC('\\', out); | 12513 | USTPUTC('\\', out); |
12514 | } | 12514 | } |
12515 | /* Backslash is retained if we are in "str" and next char isn't special */ | 12515 | /* Backslash is retained if we are in "str" |
12516 | * and next char isn't dquote-special. | ||
12517 | */ | ||
12516 | if (dblquote | 12518 | if (dblquote |
12517 | && c != '\\' | 12519 | && c != '\\' |
12518 | && c != '`' | 12520 | && c != '`' |
12519 | && c != '$' | 12521 | && c != '$' |
12520 | && (c != '"' || eofmark != NULL) | 12522 | && (c != '"' || eofmark != NULL) |
12521 | ) { | 12523 | ) { |
12524 | //dash survives not doing USTPUTC(CTLESC), but merely by chance: | ||
12525 | //Example: "\z" gets encoded as "\<CTLESC>z". | ||
12526 | //rmescapes() then emits "\", "\z", protecting z from globbing. | ||
12527 | //But it's wrong, should protect _both_ from globbing: | ||
12528 | //everything in double quotes is not globbed. | ||
12529 | //Unlike dash, we have a fix in rmescapes() which emits bare "z" | ||
12530 | //for "<CTLESC>z" since "z" is not glob-special (else unicode may break), | ||
12531 | //and glob would see "\z" and eat "\". Thus: | ||
12532 | USTPUTC(CTLESC, out); /* protect '\' from glob */ | ||
12522 | USTPUTC('\\', out); | 12533 | USTPUTC('\\', out); |
12523 | } | 12534 | } |
12524 | USTPUTC(CTLESC, out); | 12535 | USTPUTC(CTLESC, out); |
diff --git a/shell/ash_test/ash-quoting/bkslash_case1.right b/shell/ash_test/ash-quoting/bkslash_case1.right new file mode 100644 index 000000000..1b52491f7 --- /dev/null +++ b/shell/ash_test/ash-quoting/bkslash_case1.right | |||
@@ -0,0 +1,10 @@ | |||
1 | ok1 | ||
2 | ok2 | ||
3 | ok3 | ||
4 | ok4 | ||
5 | ok5 | ||
6 | Ok:0 | ||
7 | ok6 | ||
8 | ok7 | ||
9 | ok8 | ||
10 | Ok:0 | ||
diff --git a/shell/ash_test/ash-quoting/bkslash_case1.tests b/shell/ash_test/ash-quoting/bkslash_case1.tests new file mode 100755 index 000000000..d0c359927 --- /dev/null +++ b/shell/ash_test/ash-quoting/bkslash_case1.tests | |||
@@ -0,0 +1,38 @@ | |||
1 | # Case argument is globbed, match patterns are not. | ||
2 | # This caught some bugs in the past. | ||
3 | |||
4 | case z in | ||
5 | \z ) echo ok1 ;; | ||
6 | * ) echo BUG ;; | ||
7 | esac | ||
8 | case \z in | ||
9 | z ) echo ok2 ;; | ||
10 | * ) echo BUG ;; | ||
11 | esac | ||
12 | case \z in | ||
13 | \z ) echo ok3 ;; | ||
14 | * ) echo BUG ;; | ||
15 | esac | ||
16 | case z in | ||
17 | \z ) echo ok4 ;; | ||
18 | * ) echo BUG ;; | ||
19 | esac | ||
20 | case \\z in | ||
21 | \\z ) echo ok5 ;; | ||
22 | * ) echo BUG ;; | ||
23 | esac | ||
24 | echo Ok:$? | ||
25 | |||
26 | case "\z" in | ||
27 | "\z" ) echo ok6 ;; | ||
28 | * ) echo BUG ;; | ||
29 | esac | ||
30 | case "\\z" in | ||
31 | "\\z" ) echo ok7 ;; | ||
32 | * ) echo BUG ;; | ||
33 | esac | ||
34 | case "\\\z" in | ||
35 | "\\\z") echo ok8 ;; | ||
36 | * ) echo BUG ;; | ||
37 | esac | ||
38 | echo Ok:$? | ||
diff --git a/shell/hush.c b/shell/hush.c index 8f1017e3c..e005b0a20 100644 --- a/shell/hush.c +++ b/shell/hush.c | |||
@@ -6614,24 +6614,22 @@ static void parse_and_run_stream(struct in_str *inp, int end_trigger) | |||
6614 | static void parse_and_run_string(const char *s) | 6614 | static void parse_and_run_string(const char *s) |
6615 | { | 6615 | { |
6616 | struct in_str input; | 6616 | struct in_str input; |
6617 | //IF_HUSH_LINENO_VAR(unsigned sv = G.lineno;) | ||
6618 | |||
6617 | setup_string_in_str(&input, s); | 6619 | setup_string_in_str(&input, s); |
6618 | parse_and_run_stream(&input, '\0'); | 6620 | parse_and_run_stream(&input, '\0'); |
6621 | //IF_HUSH_LINENO_VAR(G.lineno = sv;) | ||
6619 | } | 6622 | } |
6620 | 6623 | ||
6621 | static void parse_and_run_file(FILE *f) | 6624 | static void parse_and_run_file(FILE *f) |
6622 | { | 6625 | { |
6623 | struct in_str input; | 6626 | struct in_str input; |
6624 | #if ENABLE_HUSH_LINENO_VAR | 6627 | IF_HUSH_LINENO_VAR(unsigned sv = G.lineno;) |
6625 | unsigned sv; | ||
6626 | 6628 | ||
6627 | sv = G.lineno; | 6629 | IF_HUSH_LINENO_VAR(G.lineno = 1;) |
6628 | G.lineno = 1; | ||
6629 | #endif | ||
6630 | setup_file_in_str(&input, f); | 6630 | setup_file_in_str(&input, f); |
6631 | parse_and_run_stream(&input, ';'); | 6631 | parse_and_run_stream(&input, ';'); |
6632 | #if ENABLE_HUSH_LINENO_VAR | 6632 | IF_HUSH_LINENO_VAR(G.lineno = sv;) |
6633 | G.lineno = sv; | ||
6634 | #endif | ||
6635 | } | 6633 | } |
6636 | 6634 | ||
6637 | #if ENABLE_HUSH_TICK | 6635 | #if ENABLE_HUSH_TICK |
@@ -6744,16 +6742,16 @@ static FILE *generate_stream_from_string(const char *s, pid_t *pid_p) | |||
6744 | static int process_command_subs(o_string *dest, const char *s) | 6742 | static int process_command_subs(o_string *dest, const char *s) |
6745 | { | 6743 | { |
6746 | FILE *fp; | 6744 | FILE *fp; |
6747 | struct in_str pipe_str; | ||
6748 | pid_t pid; | 6745 | pid_t pid; |
6749 | int status, ch, eol_cnt; | 6746 | int status, ch, eol_cnt; |
6750 | 6747 | ||
6751 | fp = generate_stream_from_string(s, &pid); | 6748 | fp = generate_stream_from_string(s, &pid); |
6752 | 6749 | ||
6753 | /* Now send results of command back into original context */ | 6750 | /* Now send results of command back into original context */ |
6754 | setup_file_in_str(&pipe_str, fp); | ||
6755 | eol_cnt = 0; | 6751 | eol_cnt = 0; |
6756 | while ((ch = i_getch(&pipe_str)) != EOF) { | 6752 | while ((ch = getc(fp)) != EOF) { |
6753 | if (ch == '\0') | ||
6754 | continue; | ||
6757 | if (ch == '\n') { | 6755 | if (ch == '\n') { |
6758 | eol_cnt++; | 6756 | eol_cnt++; |
6759 | continue; | 6757 | continue; |
diff --git a/shell/hush_test/hush-quoting/bkslash_case1.right b/shell/hush_test/hush-quoting/bkslash_case1.right new file mode 100644 index 000000000..1b52491f7 --- /dev/null +++ b/shell/hush_test/hush-quoting/bkslash_case1.right | |||
@@ -0,0 +1,10 @@ | |||
1 | ok1 | ||
2 | ok2 | ||
3 | ok3 | ||
4 | ok4 | ||
5 | ok5 | ||
6 | Ok:0 | ||
7 | ok6 | ||
8 | ok7 | ||
9 | ok8 | ||
10 | Ok:0 | ||
diff --git a/shell/hush_test/hush-quoting/bkslash_case1.tests b/shell/hush_test/hush-quoting/bkslash_case1.tests new file mode 100755 index 000000000..d0c359927 --- /dev/null +++ b/shell/hush_test/hush-quoting/bkslash_case1.tests | |||
@@ -0,0 +1,38 @@ | |||
1 | # Case argument is globbed, match patterns are not. | ||
2 | # This caught some bugs in the past. | ||
3 | |||
4 | case z in | ||
5 | \z ) echo ok1 ;; | ||
6 | * ) echo BUG ;; | ||
7 | esac | ||
8 | case \z in | ||
9 | z ) echo ok2 ;; | ||
10 | * ) echo BUG ;; | ||
11 | esac | ||
12 | case \z in | ||
13 | \z ) echo ok3 ;; | ||
14 | * ) echo BUG ;; | ||
15 | esac | ||
16 | case z in | ||
17 | \z ) echo ok4 ;; | ||
18 | * ) echo BUG ;; | ||
19 | esac | ||
20 | case \\z in | ||
21 | \\z ) echo ok5 ;; | ||
22 | * ) echo BUG ;; | ||
23 | esac | ||
24 | echo Ok:$? | ||
25 | |||
26 | case "\z" in | ||
27 | "\z" ) echo ok6 ;; | ||
28 | * ) echo BUG ;; | ||
29 | esac | ||
30 | case "\\z" in | ||
31 | "\\z" ) echo ok7 ;; | ||
32 | * ) echo BUG ;; | ||
33 | esac | ||
34 | case "\\\z" in | ||
35 | "\\\z") echo ok8 ;; | ||
36 | * ) echo BUG ;; | ||
37 | esac | ||
38 | echo Ok:$? | ||
diff --git a/testsuite/tar.tests b/testsuite/tar.tests index b7cd74ca5..1675b07b1 100755 --- a/testsuite/tar.tests +++ b/testsuite/tar.tests | |||
@@ -279,7 +279,7 @@ optional UUDECODE FEATURE_TAR_AUTODETECT FEATURE_SEAMLESS_BZ2 | |||
279 | testing "tar does not extract into symlinks" "\ | 279 | testing "tar does not extract into symlinks" "\ |
280 | >>/tmp/passwd && uudecode -o input && tar xf input 2>&1 && rm passwd; cat /tmp/passwd; echo \$? | 280 | >>/tmp/passwd && uudecode -o input && tar xf input 2>&1 && rm passwd; cat /tmp/passwd; echo \$? |
281 | " "\ | 281 | " "\ |
282 | tar: skipping unsafe symlink to '/tmp/passwd' in archive, set EXTRACT_UNSAFE_SYMLINKS=1 to extract | 282 | tar: can't create symlink 'passwd' to '/tmp/passwd' |
283 | 0 | 283 | 0 |
284 | " \ | 284 | " \ |
285 | "" "\ | 285 | "" "\ |
@@ -299,7 +299,7 @@ optional UUDECODE FEATURE_TAR_AUTODETECT FEATURE_SEAMLESS_BZ2 | |||
299 | testing "tar -k does not extract into symlinks" "\ | 299 | testing "tar -k does not extract into symlinks" "\ |
300 | >>/tmp/passwd && uudecode -o input && tar xf input -k 2>&1 && rm passwd; cat /tmp/passwd; echo \$? | 300 | >>/tmp/passwd && uudecode -o input && tar xf input -k 2>&1 && rm passwd; cat /tmp/passwd; echo \$? |
301 | " "\ | 301 | " "\ |
302 | tar: skipping unsafe symlink to '/tmp/passwd' in archive, set EXTRACT_UNSAFE_SYMLINKS=1 to extract | 302 | tar: can't create symlink 'passwd' to '/tmp/passwd' |
303 | 0 | 303 | 0 |
304 | " \ | 304 | " \ |
305 | "" "\ | 305 | "" "\ |
@@ -324,11 +324,11 @@ rm -rf etc usr | |||
324 | ' "\ | 324 | ' "\ |
325 | etc/ssl/certs/3b2716e5.0 | 325 | etc/ssl/certs/3b2716e5.0 |
326 | etc/ssl/certs/EBG_Elektronik_Sertifika_Hizmet_Sağlayıcısı.pem | 326 | etc/ssl/certs/EBG_Elektronik_Sertifika_Hizmet_Sağlayıcısı.pem |
327 | tar: skipping unsafe symlink to '/usr/share/ca-certificates/mozilla/EBG_Elektronik_Sertifika_Hizmet_Sağlayıcısı.crt' in archive, set EXTRACT_UNSAFE_SYMLINKS=1 to extract | ||
328 | etc/ssl/certs/f80cc7f6.0 | 327 | etc/ssl/certs/f80cc7f6.0 |
329 | usr/share/ca-certificates/mozilla/EBG_Elektronik_Sertifika_Hizmet_Sağlayıcısı.crt | 328 | usr/share/ca-certificates/mozilla/EBG_Elektronik_Sertifika_Hizmet_Sağlayıcısı.crt |
330 | 0 | 329 | 0 |
331 | etc/ssl/certs/3b2716e5.0 -> EBG_Elektronik_Sertifika_Hizmet_Sağlayıcısı.pem | 330 | etc/ssl/certs/3b2716e5.0 -> EBG_Elektronik_Sertifika_Hizmet_Sağlayıcısı.pem |
331 | etc/ssl/certs/EBG_Elektronik_Sertifika_Hizmet_Sağlayıcısı.pem -> /usr/share/ca-certificates/mozilla/EBG_Elektronik_Sertifika_Hizmet_Sağlayıcısı.crt | ||
332 | etc/ssl/certs/f80cc7f6.0 -> EBG_Elektronik_Sertifika_Hizmet_Sağlayıcısı.pem | 332 | etc/ssl/certs/f80cc7f6.0 -> EBG_Elektronik_Sertifika_Hizmet_Sağlayıcısı.pem |
333 | " \ | 333 | " \ |
334 | "" "" | 334 | "" "" |
@@ -346,9 +346,9 @@ ls symlink/bb_test_evilfile | |||
346 | ' "\ | 346 | ' "\ |
347 | anything.txt | 347 | anything.txt |
348 | symlink | 348 | symlink |
349 | tar: skipping unsafe symlink to '/tmp' in archive, set EXTRACT_UNSAFE_SYMLINKS=1 to extract | ||
350 | symlink/bb_test_evilfile | 349 | symlink/bb_test_evilfile |
351 | 0 | 350 | tar: can't create symlink 'symlink' to '/tmp' |
351 | 1 | ||
352 | ls: /tmp/bb_test_evilfile: No such file or directory | 352 | ls: /tmp/bb_test_evilfile: No such file or directory |
353 | ls: bb_test_evilfile: No such file or directory | 353 | ls: bb_test_evilfile: No such file or directory |
354 | symlink/bb_test_evilfile | 354 | symlink/bb_test_evilfile |
diff --git a/util-linux/mkfs_ext2.c b/util-linux/mkfs_ext2.c index 8434dd6ad..f524bc239 100644 --- a/util-linux/mkfs_ext2.c +++ b/util-linux/mkfs_ext2.c | |||
@@ -83,11 +83,11 @@ char BUG_wrong_field_size(void); | |||
83 | #define STORE_LE(field, value) \ | 83 | #define STORE_LE(field, value) \ |
84 | do { \ | 84 | do { \ |
85 | if (sizeof(field) == 4) \ | 85 | if (sizeof(field) == 4) \ |
86 | field = SWAP_LE32(value); \ | 86 | field = SWAP_LE32((uint32_t)(value)); \ |
87 | else if (sizeof(field) == 2) \ | 87 | else if (sizeof(field) == 2) \ |
88 | field = SWAP_LE16(value); \ | 88 | field = SWAP_LE16((uint16_t)(value)); \ |
89 | else if (sizeof(field) == 1) \ | 89 | else if (sizeof(field) == 1) \ |
90 | field = (value); \ | 90 | field = (uint8_t)(value); \ |
91 | else \ | 91 | else \ |
92 | BUG_wrong_field_size(); \ | 92 | BUG_wrong_field_size(); \ |
93 | } while (0) | 93 | } while (0) |
diff --git a/util-linux/mkfs_vfat.c b/util-linux/mkfs_vfat.c index 426854b1e..26a919536 100644 --- a/util-linux/mkfs_vfat.c +++ b/util-linux/mkfs_vfat.c | |||
@@ -210,11 +210,11 @@ void BUG_unsupported_field_size(void); | |||
210 | #define STORE_LE(field, value) \ | 210 | #define STORE_LE(field, value) \ |
211 | do { \ | 211 | do { \ |
212 | if (sizeof(field) == 4) \ | 212 | if (sizeof(field) == 4) \ |
213 | field = SWAP_LE32(value); \ | 213 | field = SWAP_LE32((uint32_t)(value)); \ |
214 | else if (sizeof(field) == 2) \ | 214 | else if (sizeof(field) == 2) \ |
215 | field = SWAP_LE16(value); \ | 215 | field = SWAP_LE16((uint16_t)(value)); \ |
216 | else if (sizeof(field) == 1) \ | 216 | else if (sizeof(field) == 1) \ |
217 | field = (value); \ | 217 | field = (uint8_t)(value); \ |
218 | else \ | 218 | else \ |
219 | BUG_unsupported_field_size(); \ | 219 | BUG_unsupported_field_size(); \ |
220 | } while (0) | 220 | } while (0) |
diff --git a/util-linux/mount.c b/util-linux/mount.c index 4eade0869..fa2e7b114 100644 --- a/util-linux/mount.c +++ b/util-linux/mount.c | |||
@@ -244,7 +244,11 @@ | |||
244 | * uclibc faq entry rather than in busybox... */ | 244 | * uclibc faq entry rather than in busybox... */ |
245 | # if defined(__UCLIBC__) && ! defined(__UCLIBC_HAS_RPC__) | 245 | # if defined(__UCLIBC__) && ! defined(__UCLIBC_HAS_RPC__) |
246 | # warning "You probably need to build uClibc with UCLIBC_HAS_RPC for NFS support" | 246 | # warning "You probably need to build uClibc with UCLIBC_HAS_RPC for NFS support" |
247 | /* not #error, since user may be using e.g. libtirpc instead */ | 247 | /* not #error, since user may be using e.g. libtirpc instead. |
248 | * This might work: | ||
249 | * CONFIG_EXTRA_CFLAGS="-I/usr/include/tirpc" | ||
250 | * CONFIG_EXTRA_LDLIBS="tirpc" | ||
251 | */ | ||
248 | # endif | 252 | # endif |
249 | # include <rpc/rpc.h> | 253 | # include <rpc/rpc.h> |
250 | # include <rpc/pmap_prot.h> | 254 | # include <rpc/pmap_prot.h> |
diff --git a/util-linux/switch_root.c b/util-linux/switch_root.c index 2d1802b79..947dd0cdc 100644 --- a/util-linux/switch_root.c +++ b/util-linux/switch_root.c | |||
@@ -39,6 +39,12 @@ | |||
39 | #include <sys/mount.h> | 39 | #include <sys/mount.h> |
40 | #if ENABLE_RUN_INIT | 40 | #if ENABLE_RUN_INIT |
41 | # include <sys/prctl.h> | 41 | # include <sys/prctl.h> |
42 | # ifndef PR_CAPBSET_READ | ||
43 | # define PR_CAPBSET_READ 23 | ||
44 | # endif | ||
45 | # ifndef PR_CAPBSET_DROP | ||
46 | # define PR_CAPBSET_DROP 24 | ||
47 | # endif | ||
42 | # include <linux/capability.h> | 48 | # include <linux/capability.h> |
43 | // #include <sys/capability.h> | 49 | // #include <sys/capability.h> |
44 | // This header is in libcap, but the functions are in libc. | 50 | // This header is in libcap, but the functions are in libc. |
diff --git a/util-linux/umount.c b/util-linux/umount.c index b45cd8a6b..e2329f8b3 100644 --- a/util-linux/umount.c +++ b/util-linux/umount.c | |||
@@ -57,6 +57,7 @@ | |||
57 | //usage: IF_FEATURE_MOUNT_LOOP( | 57 | //usage: IF_FEATURE_MOUNT_LOOP( |
58 | //usage: "\n -d Free loop device if it has been used" | 58 | //usage: "\n -d Free loop device if it has been used" |
59 | //usage: ) | 59 | //usage: ) |
60 | //usage: "\n -t FSTYPE[,...] Unmount only these filesystem type(s)" | ||
60 | //usage: | 61 | //usage: |
61 | //usage:#define umount_example_usage | 62 | //usage:#define umount_example_usage |
62 | //usage: "$ umount /dev/hdc1\n" | 63 | //usage: "$ umount /dev/hdc1\n" |
@@ -81,8 +82,8 @@ static struct mntent *getmntent_r(FILE* stream, struct mntent* result, | |||
81 | } | 82 | } |
82 | #endif | 83 | #endif |
83 | 84 | ||
84 | /* ignored: -c -v -t -i */ | 85 | /* ignored: -c -v -i */ |
85 | #define OPTION_STRING "fldnra" "cvt:i" | 86 | #define OPTION_STRING "fldnrat:" "cvi" |
86 | #define OPT_FORCE (1 << 0) // Same as MNT_FORCE | 87 | #define OPT_FORCE (1 << 0) // Same as MNT_FORCE |
87 | #define OPT_LAZY (1 << 1) // Same as MNT_DETACH | 88 | #define OPT_LAZY (1 << 1) // Same as MNT_DETACH |
88 | #define OPT_FREELOOP (1 << 2) | 89 | #define OPT_FREELOOP (1 << 2) |
@@ -143,7 +144,8 @@ int umount_main(int argc UNUSED_PARAM, char **argv) | |||
143 | } | 144 | } |
144 | 145 | ||
145 | // If we're not umounting all, we need at least one argument. | 146 | // If we're not umounting all, we need at least one argument. |
146 | if (!(opt & OPT_ALL) && !fstype) { | 147 | // Note: "-t FSTYPE" does not imply -a. |
148 | if (!(opt & OPT_ALL)) { | ||
147 | if (!argv[0]) | 149 | if (!argv[0]) |
148 | bb_show_usage(); | 150 | bb_show_usage(); |
149 | m = NULL; | 151 | m = NULL; |