extend fractional duration support to "top -d N.N" and "timeout"
No binary (build failed)
—
(log yok)
| @@ -32,13 +32,6 @@ | |
| //config: depends on SLEEP | |
| //config: help | |
| //config: Allow sleep to pause for specified minutes, hours, and days. | |
| - | //config: |
| - | //config:config FEATURE_FLOAT_SLEEP |
| - | //config: bool "Enable fractional arguments" |
| - | //config: default y |
| - | //config: depends on FEATURE_FANCY_SLEEP |
| - | //config: help |
| - | //config: Allow for fractional numeric parameters. |
| /* Do not make this applet NOFORK. It breaks ^C-ing of pauses in shells */ | |
| //applet:IF_SLEEP(APPLET(sleep, BB_DIR_BIN, BB_SUID_DROP)) | |
| @@ -66,89 +59,28 @@ | |
| #include "libbb.h" | |
| - | #if ENABLE_FEATURE_FANCY_SLEEP || ENABLE_FEATURE_FLOAT_SLEEP |
| - | static const struct suffix_mult sfx[] = { |
| - | { "s", 1 }, |
| - | { "m", 60 }, |
| - | { "h", 60*60 }, |
| - | { "d", 24*60*60 }, |
| - | { "", 0 } |
| - | }; |
| - | #endif |
| - | |
| int sleep_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | |
| int sleep_main(int argc UNUSED_PARAM, char **argv) | |
| { | |
| - | #if ENABLE_FEATURE_FLOAT_SLEEP |
| - | double duration; |
| - | struct timespec ts; |
| - | #else |
| - | unsigned duration; |
| - | #endif |
| + | duration_t duration; |
| ++argv; | |
| if (!*argv) | |
| bb_show_usage(); | |
| - | #if ENABLE_FEATURE_FLOAT_SLEEP |
| - | |
| - | # if ENABLE_LOCALE_SUPPORT |
| + | #if ENABLE_FEATURE_FANCY_SLEEP |
| + | # if ENABLE_FLOAT_DURATION |
| /* undo busybox.c setlocale */ | |
| setlocale(LC_NUMERIC, "C"); | |
| # endif | |
| duration = 0; | |
| do { | |
| - | char *arg = *argv; |
| - | if (strchr(arg, '.')) { |
| - | double d; |
| - | char *pp; |
| - | int len = strspn(arg, "0123456789."); |
| - | char sv = arg[len]; |
| - | arg[len] = '\0'; |
| - | errno = 0; |
| - | d = strtod(arg, &pp); |
| - | if (errno || *pp) |
| - | bb_show_usage(); |
| - | arg += len; |
| - | *arg-- = sv; |
| - | sv = *arg; |
| - | *arg = '1'; |
| - | duration += d * xatoul_sfx(arg, sfx); |
| - | *arg = sv; |
| - | } else { |
| - | duration += xatoul_sfx(arg, sfx); |
| - | } |
| + | duration += parse_duration_str(*argv); |
| } while (*++argv); | |
| - | |
| - | ts.tv_sec = MAXINT(typeof(ts.tv_sec)); |
| - | ts.tv_nsec = 0; |
| - | if (duration >= 0 && duration < ts.tv_sec) { |
| - | ts.tv_sec = duration; |
| - | ts.tv_nsec = (duration - ts.tv_sec) * 1000000000; |
| - | } |
| - | do { |
| - | errno = 0; |
| - | nanosleep(&ts, &ts); |
| - | } while (errno == EINTR); |
| - | |
| - | #elif ENABLE_FEATURE_FANCY_SLEEP |
| - | |
| - | duration = 0; |
| - | do { |
| - | duration += xatou_range_sfx(*argv, 0, UINT_MAX - duration, sfx); |
| - | } while (*++argv); |
| - | sleep(duration); |
| - | |
| + | sleep_for_duration(duration); |
| #else /* simple */ | |
| - | |
| duration = xatou(*argv); | |
| sleep(duration); | |
| - | // Off. If it's really needed, provide example why |
| - | //if (sleep(duration)) { |
| - | // bb_perror_nomsg_and_die(); |
| - | //} |
| - | |
| #endif | |
| - | |
| return EXIT_SUCCESS; | |
| } | |
| @@ -52,7 +52,8 @@ int timeout_main(int argc UNUSED_PARAM, char **argv) | |
| int signo; | |
| int status; | |
| int parent = 0; | |
| - | int timeout = 10; |
| + | unsigned timeout; |
| + | const char *timeout_s = "10"; |
| pid_t pid; | |
| #if !BB_MMU | |
| char *sv1, *sv2; | |
| @@ -63,11 +64,12 @@ int timeout_main(int argc UNUSED_PARAM, char **argv) | |
| /* -t SECONDS; -p PARENT_PID */ | |
| /* '+': stop at first non-option */ | |
| - | getopt32(argv, "+s:t:+" USE_FOR_NOMMU("p:+"), &opt_s, &timeout, &parent); |
| + | getopt32(argv, "+s:t:" USE_FOR_NOMMU("p:+"), &opt_s, &timeout_s, &parent); |
| /*argv += optind; - no, wait for bb_daemonize_or_rexec! */ | |
| signo = get_signum(opt_s); | |
| if (signo < 0) | |
| bb_error_msg_and_die("unknown signal '%s'", opt_s); | |
| + | timeout = parse_duration_str((char*)timeout_s); |
| /* We want to create a grandchild which will watch | |
| * and kill the grandparent. Other methods: | |
| @@ -1018,6 +1018,14 @@ int xatoi_positive(const char *numstr) FAST_FUNC; | |
| /* Useful for reading port numbers */ | |
| uint16_t xatou16(const char *numstr) FAST_FUNC; | |
| + | #if ENABLE_FLOAT_DURATION |
| + | typedef double duration_t; |
| + | void sleep_for_duration(duration_t duration) FAST_FUNC; |
| + | #else |
| + | typedef unsigned duration_t; |
| + | #define sleep_for_duration(duration) sleep(duration) |
| + | #endif |
| + | duration_t parse_duration_str(char *str) FAST_FUNC; |
| /* These parse entries in /etc/passwd and /etc/group. This is desirable | |
| * for BusyBox since we want to avoid using the glibc NSS stuff, which | |
| @@ -0,0 +1,76 @@ | |
| + | /* vi: set sw=4 ts=4: */ |
| + | /* |
| + | * Utility routines. |
| + | * |
| + | * Copyright (C) 2018 Denys Vlasenko |
| + | * |
| + | * Licensed under GPLv2, see file LICENSE in this source tree. |
| + | */ |
| + | //config:config FLOAT_DURATION |
| + | //config: bool "Enable fractional duration arguments" |
| + | //config: default y |
| + | //config: help |
| + | //config: Allow sleep N.NNN, top -d N.NNN etc. |
| + | |
| + | //kbuild:lib-$(CONFIG_SLEEP) += duration.o |
| + | //kbuild:lib-$(CONFIG_TOP) += duration.o |
| + | //kbuild:lib-$(CONFIG_TIMEOUT) += duration.o |
| + | |
| + | #include "libbb.h" |
| + | |
| + | static const struct suffix_mult duration_suffixes[] = { |
| + | { "s", 1 }, |
| + | { "m", 60 }, |
| + | { "h", 60*60 }, |
| + | { "d", 24*60*60 }, |
| + | { "", 0 } |
| + | }; |
| + | |
| + | #if ENABLE_FLOAT_DURATION |
| + | duration_t FAST_FUNC parse_duration_str(char *str) |
| + | { |
| + | duration_t duration; |
| + | |
| + | if (strchr(str, '.')) { |
| + | double d; |
| + | char *pp; |
| + | int len = strspn(str, "0123456789."); |
| + | char sv = str[len]; |
| + | str[len] = '\0'; |
| + | errno = 0; |
| + | d = strtod(str, &pp); |
| + | if (errno || *pp) |
| + | bb_show_usage(); |
| + | str += len; |
| + | *str-- = sv; |
| + | sv = *str; |
| + | *str = '1'; |
| + | duration = d * xatoul_sfx(str, duration_suffixes); |
| + | *str = sv; |
| + | } else { |
| + | duration = xatoul_sfx(str, duration_suffixes); |
| + | } |
| + | |
| + | return duration; |
| + | } |
| + | void FAST_FUNC sleep_for_duration(duration_t duration) |
| + | { |
| + | struct timespec ts; |
| + | |
| + | ts.tv_sec = MAXINT(typeof(ts.tv_sec)); |
| + | ts.tv_nsec = 0; |
| + | if (duration >= 0 && duration < ts.tv_sec) { |
| + | ts.tv_sec = duration; |
| + | ts.tv_nsec = (duration - ts.tv_sec) * 1000000000; |
| + | } |
| + | do { |
| + | errno = 0; |
| + | nanosleep(&ts, &ts); |
| + | } while (errno == EINTR); |
| + | } |
| + | #else |
| + | duration_t FAST_FUNC parse_duration_str(char *str) |
| + | { |
| + | return xatou_range_sfx(*argv, 0, UINT_MAX, duration_suffixes); |
| + | } |
| + | #endif |
| @@ -901,11 +901,11 @@ enum { | |
| }; | |
| #if ENABLE_FEATURE_TOP_INTERACTIVE | |
| - | static unsigned handle_input(unsigned scan_mask, unsigned interval) |
| + | static unsigned handle_input(unsigned scan_mask, duration_t interval) |
| { | |
| if (option_mask32 & OPT_EOF) { | |
| /* EOF on stdin ("top </dev/null") */ | |
| - | sleep(interval); |
| + | sleep_for_duration(interval); |
| return scan_mask; | |
| } | |
| @@ -1092,9 +1092,9 @@ static unsigned handle_input(unsigned scan_mask, unsigned interval) | |
| int top_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | |
| int top_main(int argc UNUSED_PARAM, char **argv) | |
| { | |
| + | duration_t interval; |
| int iterations; | |
| unsigned col; | |
| - | unsigned interval; |
| char *str_interval, *str_iterations; | |
| unsigned scan_mask = TOP_MASK; | |
| @@ -1120,8 +1120,10 @@ int top_main(int argc UNUSED_PARAM, char **argv) | |
| /* work around for "-d 1" -> "-d -1" done by make_all_argv_opts() */ | |
| if (str_interval[0] == '-') | |
| str_interval++; | |
| + | interval = parse_duration_str(str_interval); |
| /* Need to limit it to not overflow poll timeout */ | |
| - | interval = xatou16(str_interval); |
| + | if (interval > INT_MAX / 1000) |
| + | interval = INT_MAX / 1000; |
| } | |
| if (col & OPT_n) { | |
| if (str_iterations[0] == '-') | |
| @@ -1169,7 +1171,7 @@ int top_main(int argc UNUSED_PARAM, char **argv) | |
| /* We output to stdout, we need size of stdout (not stdin)! */ | |
| get_terminal_width_height(STDOUT_FILENO, &col, &G.lines); | |
| if (G.lines < 5 || col < 10) { | |
| - | sleep(interval); |
| + | sleep_for_duration(interval); |
| continue; | |
| } | |
| if (col > LINE_BUF_SIZE - 2) | |
| @@ -1254,7 +1256,7 @@ int top_main(int argc UNUSED_PARAM, char **argv) | |
| break; | |
| #if !ENABLE_FEATURE_TOP_INTERACTIVE | |
| clearmems(); | |
| - | sleep(interval); |
| + | sleep_for_duration(interval); |
| #else | |
| new_mask = handle_input(scan_mask, interval); | |
| if (new_mask == NO_RESCAN_MASK) | |