From 163e637ffb47425d9f5027755b7cb4877d77b0cb Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Wed, 25 Jul 2018 12:08:26 +0200 Subject: [PATCH 01/62] fdisk: use strtoul[l] instead of atoi, closes 11176 Couldn't create partitions bigger than 1TB (when using 512 bytes sectors, on 32 bits architectures). Signed-off-by: Denys Vlasenko --- util-linux/fdisk.c | 9 ++++++--- util-linux/fdisk_sun.c | 2 +- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/util-linux/fdisk.c b/util-linux/fdisk.c index cdcba0a037..e47bc7324c 100644 --- a/util-linux/fdisk.c +++ b/util-linux/fdisk.c @@ -169,9 +169,9 @@ typedef unsigned long long ullong; * do not support more than 2^32 sectors */ typedef uint32_t sector_t; -#if UINT_MAX == 4294967295 +#if UINT_MAX == 0xffffffff # define SECT_FMT "" -#elif ULONG_MAX == 4294967295 +#elif ULONG_MAX == 0xffffffff # define SECT_FMT "l" #else # error Cant detect sizeof(uint32_t) @@ -1616,7 +1616,10 @@ read_int(sector_t low, sector_t dflt, sector_t high, sector_t base, const char * int minus = (*line_ptr == '-'); int absolute = 0; - value = atoi(line_ptr + 1); + if (sizeof(value) <= sizeof(long)) + value = strtoul(line_ptr + 1, NULL, 10); + else + value = strtoull(line_ptr + 1, NULL, 10); /* (1) if 2nd char is digit, use_default = 0. * (2) move line_ptr to first non-digit. */ diff --git a/util-linux/fdisk_sun.c b/util-linux/fdisk_sun.c index e32740dea4..f62a53ac6a 100644 --- a/util-linux/fdisk_sun.c +++ b/util-linux/fdisk_sun.c @@ -606,7 +606,7 @@ sun_change_sysid(int i, int sys) "there may destroy your partition table and bootblock.\n" "Type YES if you're very sure you would like that partition\n" "tagged with 82 (Linux swap): "); - if (strcmp (line_ptr, "YES\n")) + if (strcmp(line_ptr, "YES\n")) return; } switch (sys) { From c104549b483c83acdf3e12cfe3df2c6437fa6926 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Wed, 25 Jul 2018 13:45:36 +0200 Subject: [PATCH 02/62] fdisk: sync up with some util-linux 2.31 behavior = display default is no longer in cylinders = +sizeC is no longer supported = add +sizeT suffix - terabytes are here = K,M,G,T and k,m,g,t suffixes all are binary, not decimal = +sizeM results in last sector +(size * 1Mbyte - 1), not +(size * 1Mbyte) = fix comparison to "YES" answer for sgi/sun function old new delta read_int 492 519 +27 fdisk_main 2644 2640 -4 ------------------------------------------------------------------------------ (add/remove: 0/0 grow/shrink: 1/1 up/down: 27/-4) Total: 23 bytes Signed-off-by: Denys Vlasenko --- util-linux/fdisk.c | 90 +++++++++++++++++++++--------------------- util-linux/fdisk_sgi.c | 2 +- util-linux/fdisk_sun.c | 2 +- 3 files changed, 46 insertions(+), 48 deletions(-) diff --git a/util-linux/fdisk.c b/util-linux/fdisk.c index e47bc7324c..2967165750 100644 --- a/util-linux/fdisk.c +++ b/util-linux/fdisk.c @@ -426,7 +426,7 @@ struct globals { unsigned sector_offset; // = 1; unsigned g_heads, g_sectors, g_cylinders; smallint /* enum label_type */ current_label_type; - smallint display_in_cyl_units; // = 1; + smallint display_in_cyl_units; #if ENABLE_FEATURE_OSF_LABEL smallint possibly_osf_label; #endif @@ -488,7 +488,6 @@ struct globals { sector_size = DEFAULT_SECTOR_SIZE; \ sector_offset = 1; \ g_partitions = 4; \ - display_in_cyl_units = 1; \ units_per_sector = 1; \ dos_compatible_flag = 1; \ } while (0) @@ -639,25 +638,6 @@ seek_sector(sector_t secno) } #if ENABLE_FEATURE_FDISK_WRITABLE -/* Read line; return 0 or first printable char */ -static int -read_line(const char *prompt) -{ - int sz; - - sz = read_line_input(NULL, prompt, line_buffer, sizeof(line_buffer)); - if (sz <= 0) - exit(EXIT_SUCCESS); /* Ctrl-D or Ctrl-C */ - - if (line_buffer[sz-1] == '\n') - line_buffer[--sz] = '\0'; - - line_ptr = line_buffer; - while (*line_ptr != '\0' && (unsigned char)*line_ptr <= ' ') - line_ptr++; - return *line_ptr; -} - static void set_all_unchanged(void) { @@ -680,6 +660,25 @@ write_part_table_flag(char *b) b[511] = 0xaa; } +/* Read line; return 0 or first printable non-space char */ +static int +read_line(const char *prompt) +{ + int sz; + + sz = read_line_input(NULL, prompt, line_buffer, sizeof(line_buffer)); + if (sz <= 0) + exit(EXIT_SUCCESS); /* Ctrl-D or Ctrl-C */ + + if (line_buffer[sz-1] == '\n') + line_buffer[--sz] = '\0'; + + line_ptr = line_buffer; + while (*line_ptr != '\0' && (unsigned char)*line_ptr <= ' ') + line_ptr++; + return *line_ptr; +} + static char read_nonempty(const char *mesg) { @@ -1614,7 +1613,7 @@ read_int(sector_t low, sector_t dflt, sector_t high, sector_t base, const char * if (*line_ptr == '+' || *line_ptr == '-') { int minus = (*line_ptr == '-'); - int absolute = 0; + unsigned scale_shift; if (sizeof(value) <= sizeof(long)) value = strtoul(line_ptr + 1, NULL, 10); @@ -1622,48 +1621,46 @@ read_int(sector_t low, sector_t dflt, sector_t high, sector_t base, const char * value = strtoull(line_ptr + 1, NULL, 10); /* (1) if 2nd char is digit, use_default = 0. - * (2) move line_ptr to first non-digit. */ + * (2) move line_ptr to first non-digit. + */ while (isdigit(*++line_ptr)) use_default = 0; - switch (*line_ptr) { - case 'c': - case 'C': - if (!display_in_cyl_units) - value *= g_heads * g_sectors; - break; - case 'K': - absolute = 1024; - break; + scale_shift = 0; + switch (*line_ptr | 0x20) { case 'k': - absolute = 1000; + scale_shift = 10; /* 1024 */ break; case 'm': - case 'M': - absolute = 1000000; + scale_shift = 20; /* 1024*1024 */ break; case 'g': - case 'G': - absolute = 1000000000; + scale_shift = 30; /* 1024*1024*1024 */ + break; + case 't': + scale_shift = 40; /* 1024*1024*1024*1024 */ break; default: break; } - if (absolute) { + if (scale_shift) { ullong bytes; unsigned long unit; - bytes = (ullong) value * absolute; + bytes = (ullong) value << scale_shift; unit = sector_size * units_per_sector; bytes += unit/2; /* round */ bytes /= unit; - value = bytes; + value = (bytes != 0 ? bytes - 1 : 0); } if (minus) value = -value; value += base; } else { - value = atoi(line_ptr); + if (sizeof(value) <= sizeof(long)) + value = strtoul(line_ptr, NULL, 10); + else + value = strtoull(line_ptr, NULL, 10); while (isdigit(*line_ptr)) { line_ptr++; use_default = 0; @@ -2529,8 +2526,9 @@ add_partition(int n, int sys) stop = limit; } else { snprintf(mesg, sizeof(mesg), - "Last %s or +size or +sizeM or +sizeK", - str_units(SINGULAR)); + "Last %s or +size{,K,M,G,T}", + str_units(SINGULAR) + ); stop = read_int(cround(start), cround(limit), cround(limit), cround(start), mesg); if (display_in_cyl_units) { stop = stop * units_per_sector - 1; @@ -2614,9 +2612,9 @@ new_partition(void) } else { char c, line[80]; snprintf(line, sizeof(line), - "Command action\n" - " %s\n" - " p primary partition (1-4)\n", + "Partition type\n" + " p primary partition (1-4)\n" + " %s\n", (extended_offset ? "l logical (5 or over)" : "e extended")); while (1) { diff --git a/util-linux/fdisk_sgi.c b/util-linux/fdisk_sgi.c index 1cf0af5cc3..0e5491a19c 100644 --- a/util-linux/fdisk_sgi.c +++ b/util-linux/fdisk_sgi.c @@ -623,7 +623,7 @@ sgi_change_sysid(int i, int sys) "retrieve from its directory standalone tools like sash and fx.\n" "Only the \"SGI volume\" entire disk section may violate this.\n" "Type YES if you are sure about tagging this partition differently.\n"); - if (strcmp(line_ptr, "YES\n") != 0) + if (strcmp(line_ptr, "YES") != 0) return; } sgilabel->partitions[i].id = SGI_SSWAP32(sys); diff --git a/util-linux/fdisk_sun.c b/util-linux/fdisk_sun.c index f62a53ac6a..3697a69b95 100644 --- a/util-linux/fdisk_sun.c +++ b/util-linux/fdisk_sun.c @@ -606,7 +606,7 @@ sun_change_sysid(int i, int sys) "there may destroy your partition table and bootblock.\n" "Type YES if you're very sure you would like that partition\n" "tagged with 82 (Linux swap): "); - if (strcmp(line_ptr, "YES\n")) + if (strcmp(line_ptr, "YES") != 0) return; } switch (sys) { From 1ab3899d49bda113671bf787a43d90c683faf2a8 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Wed, 25 Jul 2018 14:42:53 +0200 Subject: [PATCH 03/62] fdisk: code shrink function old new delta get_nonexisting_partition 119 104 -15 fdisk_main 2640 2622 -18 ------------------------------------------------------------------------------ (add/remove: 0/0 grow/shrink: 0/2 up/down: 0/-33) Total: -33 bytes Signed-off-by: Denys Vlasenko --- util-linux/fdisk.c | 32 +++++++++++++++++++++++++++----- 1 file changed, 27 insertions(+), 5 deletions(-) diff --git a/util-linux/fdisk.c b/util-linux/fdisk.c index 2967165750..288b9235f8 100644 --- a/util-linux/fdisk.c +++ b/util-linux/fdisk.c @@ -1631,6 +1631,26 @@ read_int(sector_t low, sector_t dflt, sector_t high, sector_t base, const char * case 'k': scale_shift = 10; /* 1024 */ break; +/* + * fdisk from util-linux 2.31 seems to round '+NNNk' and '+NNNK' to megabytes, + * (512-byte) sector count of the partition does not equal NNN*2: + * + * Last sector, +sectors or +size{K,M,G,T,P} (1953792-1000215215, default 1000215215): +9727k + * Device Boot Start End Sectors Size Id Type + * /dev/sdaN 1953792 1972223 18432 9M 83 Linux <-- size exactly 9*1024*1024 bytes + * + * Last sector, +sectors or +size{K,M,G,T,P} (1953792-1000215215, default 1000215215): +9728k + * /dev/sdaN 1953792 1974271 20480 10M 83 Linux <-- size exactly 10*1024*1024 bytes + * + * If 'k' means 1000 bytes (not 1024), then 9728k = 9728*1000 = 9500*1024, + * exactly halfway from 9000 to 10000, which explains why it jumps to next mbyte + * at this value. + * + * 'm' does not seem to behave this way: it means 1024*1024 bytes. + * + * Not sure we want to copy this. If user says he wants 1234kbyte partition, + * we do _exactly that_: 1234kbytes = 2468 sectors. + */ case 'm': scale_shift = 20; /* 1024*1024 */ break; @@ -1725,8 +1745,9 @@ get_existing_partition(int warn, unsigned max) } static int -get_nonexisting_partition(int warn, unsigned max) +get_nonexisting_partition(void) { + const int max = 4; int pno = -1; unsigned i; @@ -1748,7 +1769,7 @@ get_nonexisting_partition(int warn, unsigned max) return -1; not_unique: - return get_partition(warn, max); + return get_partition(/*warn*/ 0, max); } @@ -2619,8 +2640,9 @@ new_partition(void) "l logical (5 or over)" : "e extended")); while (1) { c = read_nonempty(line); - if ((c | 0x20) == 'p') { - i = get_nonexisting_partition(0, 4); + c |= 0x20; /* lowercase */ + if (c == 'p') { + i = get_nonexisting_partition(); if (i >= 0) add_partition(i, LINUX_NATIVE); return; @@ -2630,7 +2652,7 @@ new_partition(void) return; } if (c == 'e' && !extended_offset) { - i = get_nonexisting_partition(0, 4); + i = get_nonexisting_partition(); if (i >= 0) add_partition(i, EXTENDED); return; From 79153cb6bc5a39b00e3c43988a265804ac609837 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Wed, 25 Jul 2018 14:58:49 +0200 Subject: [PATCH 04/62] htpd: increase MIN_FREQHOLD by 3 This means we'll start correcting frequency ~5 minutes after start, not ~3.5 ones. With previos settings I still often see largish ~0.7s initial offsets only about 1/2 corrected before frequency correction kicks in, resulting in ~200ppm "correction" which is then slowly undone. Signed-off-by: Denys Vlasenko --- networking/ntpd.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/networking/ntpd.c b/networking/ntpd.c index 7b800369eb..7462113ce0 100644 --- a/networking/ntpd.c +++ b/networking/ntpd.c @@ -149,7 +149,7 @@ */ #define INITIAL_SAMPLES 4 /* how many samples do we want for init */ -#define MIN_FREQHOLD 10 /* adjust offset, but not freq in this many first adjustments */ +#define MIN_FREQHOLD 12 /* adjust offset, but not freq in this many first adjustments */ #define BAD_DELAY_GROWTH 4 /* drop packet if its delay grew by more than this */ #define RETRY_INTERVAL 32 /* on send/recv error, retry in N secs (need to be power of 2) */ @@ -1777,9 +1777,9 @@ update_local_clock(peer_t *p) //15:31:53.473 update from: offset:+0.000007 delay:0.158142 jitter:0.010922 clock drift:+9.343ppm tc:6 //15:32:58.902 update from: offset:-0.000728 delay:0.158222 jitter:0.009454 clock drift:+9.298ppm tc:6 /* - * This expression would choose MIN_FREQHOLD + 7 in the above example. + * This expression would choose MIN_FREQHOLD + 8 in the above example. */ - G.FREQHOLD_cnt = MIN_FREQHOLD + ((unsigned)(abs(tmx.offset)) >> 16); + G.FREQHOLD_cnt = 1 + MIN_FREQHOLD + ((unsigned)(abs(tmx.offset)) >> 16); } G.FREQHOLD_cnt--; tmx.status |= STA_FREQHOLD; From 871bd2abaccd042cba757831fc4461806e8f446d Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Thu, 26 Jul 2018 13:45:33 +0200 Subject: [PATCH 05/62] examples/var_service: add /var/run flag file to ntp.script Signed-off-by: Denys Vlasenko --- examples/var_service/ntpd/ntp.script | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/examples/var_service/ntpd/ntp.script b/examples/var_service/ntpd/ntp.script index 8542181eb4..6cd81014d5 100755 --- a/examples/var_service/ntpd/ntp.script +++ b/examples/var_service/ntpd/ntp.script @@ -1,5 +1,4 @@ #!/bin/sh - # Note that there is no provision to prevent several copies of the script # to be run in quick succession. In fact, it happens rather often # if initial syncronization results in a step. @@ -8,6 +7,9 @@ # # Script should be ready to deal with this. +# For other parts of the system which prefer to run only on the stable clock +echo "$1" >rundir/sync_status + dt=`date '+%Y-%m-%d %H:%M:%S'` echo "`tail -n 199 -- "$0.log" 2>/dev/null`" >"$0.log.$$" From 7c5f18a3bab721cdfa515220ad8d481643aaae23 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Thu, 26 Jul 2018 15:21:50 +0200 Subject: [PATCH 06/62] hush: improve set -x: make "+++" indent level increase in `cmd` and eval. function old new delta dump_cmd_in_x_mode 126 144 +18 run_pipe 1873 1883 +10 builtin_eval 119 127 +8 expand_vars_to_list 1100 1103 +3 ------------------------------------------------------------------------------ (add/remove: 0/0 grow/shrink: 4/0 up/down: 39/0) Total: 39 bytes Signed-off-by: Denys Vlasenko --- shell/hush.c | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/shell/hush.c b/shell/hush.c index e3c6e2de99..02fb1b5efa 100644 --- a/shell/hush.c +++ b/shell/hush.c @@ -896,6 +896,7 @@ struct globals { char o_opt[NUM_OPT_O]; #if ENABLE_HUSH_MODE_X + smalluint x_mode_depth; # define G_x_mode (G.o_opt[OPT_O_XTRACE]) #else # define G_x_mode 0 @@ -7182,11 +7183,8 @@ static int generate_stream_from_string(const char *s, pid_t *pid_p) + (1 << SIGTTIN) + (1 << SIGTTOU) , SIG_IGN); - CLEAR_RANDOM_T(&G.random_gen); /* or else $RANDOM repeats in child */ close(channel[0]); /* NB: close _first_, then move fd! */ xmove_fd(channel[1], 1); - /* Prevent it from trying to handle ctrl-z etc */ - IF_HUSH_JOB(G.run_list_level = 1;) # if ENABLE_HUSH_TRAP /* Awful hack for `trap` or $(trap). * @@ -7233,7 +7231,11 @@ static int generate_stream_from_string(const char *s, pid_t *pid_p) } # endif # if BB_MMU + /* Prevent it from trying to handle ctrl-z etc */ + IF_HUSH_JOB(G.run_list_level = 1;) + CLEAR_RANDOM_T(&G.random_gen); /* or else $RANDOM repeats in child */ reset_traps_to_defaults(); + IF_HUSH_MODE_X(G.x_mode_depth++;) parse_and_run_string(s); _exit(G.last_exitcode); # else @@ -8022,13 +8024,14 @@ static void dump_cmd_in_x_mode(char **argv) int len; int n; - len = 3; + len = G.x_mode_depth + 3; /* "+[+++...]\n\0" */ n = 0; while (argv[n]) len += strlen(argv[n++]) + 1; - buf = xmalloc(len); - buf[0] = '+'; - p = buf + 1; + p = buf = xmalloc(len); + n = G.x_mode_depth; + while (n-- >= 0) + *p++ = '+'; n = 0; while (argv[n]) p += sprintf(p, " %s", argv[n++]); @@ -8821,8 +8824,13 @@ static NOINLINE int run_pipe(struct pipe *pi) restore_redirects(squirrel); /* Set shell variables */ - if (G_x_mode) - bb_putchar_stderr('+'); +#if ENABLE_HUSH_MODE_X + if (G_x_mode) { + int n = G.x_mode_depth; + while (n-- >= 0) + bb_putchar_stderr('+'); + } +#endif i = 0; while (i < command->assignment_cnt) { char *p = expand_string_to_string(argv[i], @@ -10226,6 +10234,7 @@ static int FAST_FUNC builtin_eval(char **argv) if (!argv[0]) return EXIT_SUCCESS; + IF_HUSH_MODE_X(G.x_mode_depth++;) if (!argv[1]) { /* bash: * eval "echo Hi; done" ("done" is syntax error): @@ -10255,6 +10264,7 @@ static int FAST_FUNC builtin_eval(char **argv) parse_and_run_string(str); free(str); } + IF_HUSH_MODE_X(G.x_mode_depth--;) return G.last_exitcode; } From 186cf4976768029113cf8438734a65bf2c489c5c Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Fri, 27 Jul 2018 12:14:39 +0200 Subject: [PATCH 07/62] hush: in some cases, expand_on_ifs() relied of uninitialized memory The n > 0 check to prevent access to the last byte of non-existing argv[-1] wasn't enough. Switched to making sure there are initialized (zero) bytes there. A predictable testcase is rather hard to construct, unfortunately, contents of memory depends on allocator behavior and whatnot. function old new delta o_save_ptr_helper 119 137 +18 expand_on_ifs 345 339 -6 ------------------------------------------------------------------------------ (add/remove: 0/0 grow/shrink: 1/1 up/down: 18/-6) Total: 12 bytes Signed-off-by: Denys Vlasenko --- shell/hush.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/shell/hush.c b/shell/hush.c index 02fb1b5efa..1ac2db3814 100644 --- a/shell/hush.c +++ b/shell/hush.c @@ -3069,6 +3069,13 @@ static int o_save_ptr_helper(o_string *o, int n) o->data = xrealloc(o->data, o->maxlen + 1); list = (char**)o->data; memmove(list + n + 0x10, list + n, string_len); + /* + * expand_on_ifs() has a "previous argv[] ends in IFS?" + * check. (grep for -prev-ifs-check-). + * Ensure that argv[-1][last] is not garbage + * but zero bytes, to save index check there. + */ + list[n + 0x10 - 1] = 0; o->length += 0x10 * sizeof(list[0]); } else { debug_printf_list("list[%d]=%d string_start=%d\n", @@ -5797,12 +5804,16 @@ static int expand_on_ifs(o_string *output, int n, const char *str) /* Start new word... but not always! */ /* Case "v=' a'; echo ''$v": we do need to finalize empty word: */ if (output->has_quoted_part - /* Case "v=' a'; echo $v": + /* + * Case "v=' a'; echo $v": * here nothing precedes the space in $v expansion, * therefore we should not finish the word * (IOW: if there *is* word to finalize, only then do it): + * It's okay if this accesses the byte before first argv[]: + * past call to o_save_ptr() cleared it to zero byte + * (grep for -prev-ifs-check-). */ - || (n > 0 && output->data[output->length - 1]) + || output->data[output->length - 1] ) { new_word: o_addchr(output, '\0'); From 9dda9270df3108bb85b29c6d382a3477aeb3344b Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Fri, 27 Jul 2018 14:12:05 +0200 Subject: [PATCH 08/62] hush: fix "set -x" output prefix overlapping for v="..`cmd`.." case Was printing initial "+" prefix for the assignment, that printing "+ cmd" then printing the expanded " v=VAL" string. Delay printing of "+" prefix for the assignment to after expansion. function old new delta run_pipe 1883 1902 +19 builtin_eval 127 133 +6 expand_vars_to_list 1103 1106 +3 dump_cmd_in_x_mode 144 142 -2 ------------------------------------------------------------------------------ (add/remove: 0/0 grow/shrink: 3/1 up/down: 28/-2) Total: 26 bytes Signed-off-by: Denys Vlasenko --- shell/hush.c | 34 ++++++++++++++++++++-------------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/shell/hush.c b/shell/hush.c index 1ac2db3814..e9212cefca 100644 --- a/shell/hush.c +++ b/shell/hush.c @@ -896,7 +896,6 @@ struct globals { char o_opt[NUM_OPT_O]; #if ENABLE_HUSH_MODE_X - smalluint x_mode_depth; # define G_x_mode (G.o_opt[OPT_O_XTRACE]) #else # define G_x_mode 0 @@ -955,6 +954,9 @@ struct globals { unsigned func_nest_level; /* solely to prevent "local v" in non-functions */ # endif struct function *top_func; +#endif +#if ENABLE_HUSH_MODE_X + unsigned x_mode_depth; #endif /* Signal and trap handling */ #if ENABLE_HUSH_FAST @@ -7247,6 +7249,7 @@ static int generate_stream_from_string(const char *s, pid_t *pid_p) CLEAR_RANDOM_T(&G.random_gen); /* or else $RANDOM repeats in child */ reset_traps_to_defaults(); IF_HUSH_MODE_X(G.x_mode_depth++;) + //bb_error_msg("%s: ++x_mode_depth=%d", __func__, G.x_mode_depth); parse_and_run_string(s); _exit(G.last_exitcode); # else @@ -8032,17 +8035,16 @@ static void dump_cmd_in_x_mode(char **argv) if (G_x_mode && argv) { /* We want to output the line in one write op */ char *buf, *p; - int len; - int n; + unsigned len; + unsigned n; - len = G.x_mode_depth + 3; /* "+[+++...]\n\0" */ + len = G.x_mode_depth + 3; /* "+[+++...][ cmd...]\n\0" */ n = 0; while (argv[n]) len += strlen(argv[n++]) + 1; p = buf = xmalloc(len); n = G.x_mode_depth; - while (n-- >= 0) - *p++ = '+'; + do *p++ = '+'; while ((int)(--n) >= 0); n = 0; while (argv[n]) p += sprintf(p, " %s", argv[n++]); @@ -8835,21 +8837,23 @@ static NOINLINE int run_pipe(struct pipe *pi) restore_redirects(squirrel); /* Set shell variables */ -#if ENABLE_HUSH_MODE_X - if (G_x_mode) { - int n = G.x_mode_depth; - while (n-- >= 0) - bb_putchar_stderr('+'); - } -#endif i = 0; while (i < command->assignment_cnt) { char *p = expand_string_to_string(argv[i], EXP_FLAG_ESC_GLOB_CHARS, /*unbackslash:*/ 1 ); - if (G_x_mode) +#if ENABLE_HUSH_MODE_X + if (G_x_mode) { + if (i == 0) { + unsigned n = G.x_mode_depth; + do + bb_putchar_stderr('+'); + while ((int)(--n) >= 0); + } fprintf(stderr, " %s", p); + } +#endif debug_printf_env("set shell var:'%s'->'%s'\n", *argv, p); if (set_local_var(p, /*flag:*/ 0)) { /* assignment to readonly var / putenv error? */ @@ -10246,6 +10250,7 @@ static int FAST_FUNC builtin_eval(char **argv) return EXIT_SUCCESS; IF_HUSH_MODE_X(G.x_mode_depth++;) + //bb_error_msg("%s: ++x_mode_depth=%d", __func__, G.x_mode_depth); if (!argv[1]) { /* bash: * eval "echo Hi; done" ("done" is syntax error): @@ -10276,6 +10281,7 @@ static int FAST_FUNC builtin_eval(char **argv) free(str); } IF_HUSH_MODE_X(G.x_mode_depth--;) + //bb_error_msg("%s: --x_mode_depth=%d", __func__, G.x_mode_depth); return G.last_exitcode; } From 4b70c926bcbfaf6df6e21c98ea096b0db8629095 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Fri, 27 Jul 2018 17:42:38 +0200 Subject: [PATCH 09/62] hush: make "set -x" output closer to bash function old new delta print_optionally_squoted - 145 +145 run_pipe 1902 1919 +17 dump_cmd_in_x_mode 142 110 -32 ------------------------------------------------------------------------------ (add/remove: 1/0 grow/shrink: 1/1 up/down: 162/-32) Total: 130 bytes Signed-off-by: Denys Vlasenko --- shell/hush.c | 78 ++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 60 insertions(+), 18 deletions(-) diff --git a/shell/hush.c b/shell/hush.c index e9212cefca..ac8467fb44 100644 --- a/shell/hush.c +++ b/shell/hush.c @@ -8030,28 +8030,67 @@ static void execvp_or_die(char **argv) } #if ENABLE_HUSH_MODE_X +static void print_optionally_squoted(FILE *fp, const char *str) +{ + unsigned len; + const char *cp; + + cp = str; + if (str[0] != '{' && str[0] != '(') for (;;) { + if (!*cp) { + /* string has no special chars */ + fputs(str, fp); + return; + } + if (*cp == '\\') break; + if (*cp == '\'') break; + if (*cp == '"') break; + if (*cp == '$') break; + if (*cp == '!') break; + if (*cp == '*') break; + if (*cp == '[') break; + if (*cp == ']') break; +#if ENABLE_HUSH_TICK + if (*cp == '`') break; +#endif + if (isspace(*cp)) break; + cp++; + } + + cp = str; + for (;;) { + /* print '....' up to EOL or first squote */ + len = (int)(strchrnul(cp, '\'') - cp); + if (len != 0) { + fprintf(fp, "'%.*s'", len, cp); + cp += len; + } + if (*cp == '\0') + break; + /* string contains squote(s), print them as \' */ + fprintf(fp, "\\'"); + cp++; + } +} static void dump_cmd_in_x_mode(char **argv) { if (G_x_mode && argv) { - /* We want to output the line in one write op */ - char *buf, *p; - unsigned len; unsigned n; - len = G.x_mode_depth + 3; /* "+[+++...][ cmd...]\n\0" */ - n = 0; - while (argv[n]) - len += strlen(argv[n++]) + 1; - p = buf = xmalloc(len); + /* "+[+++...][ cmd...]\n\0" */ n = G.x_mode_depth; - do *p++ = '+'; while ((int)(--n) >= 0); + do bb_putchar_stderr('+'); while ((int)(--n) >= 0); n = 0; - while (argv[n]) - p += sprintf(p, " %s", argv[n++]); - *p++ = '\n'; - *p = '\0'; - fputs(buf, stderr); - free(buf); + while (argv[n]) { + if (argv[n][0] == '\0') + fputs(" ''", stderr); + else { + bb_putchar_stderr(' '); + print_optionally_squoted(stderr, argv[n]); + } + n++; + } + bb_putchar_stderr('\n'); } } #else @@ -8845,13 +8884,18 @@ static NOINLINE int run_pipe(struct pipe *pi) ); #if ENABLE_HUSH_MODE_X if (G_x_mode) { + char *eq; if (i == 0) { unsigned n = G.x_mode_depth; do bb_putchar_stderr('+'); while ((int)(--n) >= 0); } - fprintf(stderr, " %s", p); + eq = strchrnul(p, '='); + fprintf(stderr, " %.*s=", (int)(eq - p), p); + if (*eq) + print_optionally_squoted(stderr, eq + 1); + bb_putchar_stderr('\n'); } #endif debug_printf_env("set shell var:'%s'->'%s'\n", *argv, p); @@ -8861,8 +8905,6 @@ static NOINLINE int run_pipe(struct pipe *pi) } i++; } - if (G_x_mode) - bb_putchar_stderr('\n'); /* Redirect error sets $? to 1. Otherwise, * if evaluating assignment value set $?, retain it. * Else, clear $?: From aa449c927d1d84092f9654e45ab9f68847e81226 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Sat, 28 Jul 2018 12:13:58 +0200 Subject: [PATCH 10/62] hush: make "set -x" output don't redirectable when fd#2 redirected function old new delta x_mode_print_optionally_squoted - 120 +120 x_mode_flush - 68 +68 save_fd_on_redirect 208 243 +35 x_mode_prefix - 27 +27 x_mode_addblock - 23 +23 x_mode_addchr - 17 +17 dump_cmd_in_x_mode 110 85 -25 run_pipe 1919 1890 -29 print_optionally_squoted 145 - -145 ------------------------------------------------------------------------------ (add/remove: 5/1 grow/shrink: 1/2 up/down: 290/-199) Total: 91 bytes Signed-off-by: Denys Vlasenko --- shell/hush.c | 129 +++++++++++++++++++++++++++++++++------------------ 1 file changed, 84 insertions(+), 45 deletions(-) diff --git a/shell/hush.c b/shell/hush.c index ac8467fb44..9676819fa1 100644 --- a/shell/hush.c +++ b/shell/hush.c @@ -954,9 +954,6 @@ struct globals { unsigned func_nest_level; /* solely to prevent "local v" in non-functions */ # endif struct function *top_func; -#endif -#if ENABLE_HUSH_MODE_X - unsigned x_mode_depth; #endif /* Signal and trap handling */ #if ENABLE_HUSH_FAST @@ -993,6 +990,15 @@ struct globals { #if ENABLE_HUSH_MEMLEAK unsigned long memleak_value; #endif +#if ENABLE_HUSH_MODE_X + unsigned x_mode_depth; + /* "set -x" output should not be redirectable with subsequent 2>FILE. + * We dup fd#2 to x_mode_fd when "set -x" is executed, and use it + * for all subsequent output. + */ + int x_mode_fd; + o_string x_mode_buf; +#endif #if HUSH_DEBUG int debug_indent; #endif @@ -1660,6 +1666,12 @@ static int move_HFILEs_on_redirect(int fd, int avoid_fd) } fl = fl->next_hfile; } +#if ENABLE_HUSH_MODE_X + if (G.x_mode_fd > 0 && fd == G.x_mode_fd) { + G.x_mode_fd = xdup_CLOEXEC_and_close(fd, avoid_fd); + return 1; /* "found and moved" */ + } +#endif return 0; /* "not in the list" */ } #if ENABLE_FEATURE_SH_STANDALONE && BB_MMU @@ -2903,6 +2915,11 @@ static void o_addstr(o_string *o, const char *str) o_addblock(o, str, strlen(str)); } +static void o_addstr_with_NUL(o_string *o, const char *str) +{ + o_addblock(o, str, strlen(str) + 1); +} + #if !BB_MMU static void nommu_addchr(o_string *o, int ch) { @@ -2913,10 +2930,36 @@ static void nommu_addchr(o_string *o, int ch) # define nommu_addchr(o, str) ((void)0) #endif -static void o_addstr_with_NUL(o_string *o, const char *str) +#if ENABLE_HUSH_MODE_X +static void x_mode_addchr(int ch) { - o_addblock(o, str, strlen(str) + 1); + o_addchr(&G.x_mode_buf, ch); } +static void x_mode_addstr(const char *str) +{ + o_addstr(&G.x_mode_buf, str); +} +static void x_mode_addblock(const char *str, int len) +{ + o_addblock(&G.x_mode_buf, str, len); +} +static void x_mode_prefix(void) +{ + int n = G.x_mode_depth; + do x_mode_addchr('+'); while (--n >= 0); +} +static void x_mode_flush(void) +{ + int len = G.x_mode_buf.length; + if (len <= 0) + return; + if (G.x_mode_fd > 0) { + G.x_mode_buf.data[len] = '\n'; + full_write(G.x_mode_fd, G.x_mode_buf.data, len + 1); + } + G.x_mode_buf.length = 0; +} +#endif /* * HUSH_BRACE_EXPANSION code needs corresponding quoting on variable expansion side. @@ -8030,31 +8073,26 @@ static void execvp_or_die(char **argv) } #if ENABLE_HUSH_MODE_X -static void print_optionally_squoted(FILE *fp, const char *str) +static void x_mode_print_optionally_squoted(const char *str) { unsigned len; const char *cp; cp = str; - if (str[0] != '{' && str[0] != '(') for (;;) { - if (!*cp) { - /* string has no special chars */ - fputs(str, fp); - return; - } - if (*cp == '\\') break; - if (*cp == '\'') break; - if (*cp == '"') break; - if (*cp == '$') break; - if (*cp == '!') break; - if (*cp == '*') break; - if (*cp == '[') break; - if (*cp == ']') break; -#if ENABLE_HUSH_TICK - if (*cp == '`') break; -#endif - if (isspace(*cp)) break; - cp++; + + /* the set of chars which-cause-string-to-be-squoted mimics bash */ + /* test a char with: bash -c 'set -x; echo "CH"' */ + if (str[strcspn(str, "\\\"'`$(){}[]<>;#&|~*?!^" + " " "\001\002\003\004\005\006\007" + "\010\011\012\013\014\015\016\017" + "\020\021\022\023\024\025\026\027" + "\030\031\032\033\034\035\036\037" + ) + ] == '\0' + ) { + /* string has no special chars */ + x_mode_addstr(str); + return; } cp = str; @@ -8062,13 +8100,16 @@ static void print_optionally_squoted(FILE *fp, const char *str) /* print '....' up to EOL or first squote */ len = (int)(strchrnul(cp, '\'') - cp); if (len != 0) { - fprintf(fp, "'%.*s'", len, cp); + x_mode_addchr('\''); + x_mode_addblock(cp, len); + x_mode_addchr('\''); cp += len; } if (*cp == '\0') break; /* string contains squote(s), print them as \' */ - fprintf(fp, "\\'"); + x_mode_addchr('\\'); + x_mode_addchr('\''); cp++; } } @@ -8078,19 +8119,19 @@ static void dump_cmd_in_x_mode(char **argv) unsigned n; /* "+[+++...][ cmd...]\n\0" */ - n = G.x_mode_depth; - do bb_putchar_stderr('+'); while ((int)(--n) >= 0); + x_mode_prefix(); n = 0; while (argv[n]) { - if (argv[n][0] == '\0') - fputs(" ''", stderr); - else { - bb_putchar_stderr(' '); - print_optionally_squoted(stderr, argv[n]); + x_mode_addchr(' '); + if (argv[n][0] == '\0') { + x_mode_addchr('\''); + x_mode_addchr('\''); + } else { + x_mode_print_optionally_squoted(argv[n]); } n++; } - bb_putchar_stderr('\n'); + x_mode_flush(); } } #else @@ -8885,17 +8926,14 @@ static NOINLINE int run_pipe(struct pipe *pi) #if ENABLE_HUSH_MODE_X if (G_x_mode) { char *eq; - if (i == 0) { - unsigned n = G.x_mode_depth; - do - bb_putchar_stderr('+'); - while ((int)(--n) >= 0); - } + if (i == 0) + x_mode_prefix(); + x_mode_addchr(' '); eq = strchrnul(p, '='); - fprintf(stderr, " %.*s=", (int)(eq - p), p); - if (*eq) - print_optionally_squoted(stderr, eq + 1); - bb_putchar_stderr('\n'); + if (*eq) eq++; + x_mode_addblock(p, (eq - p)); + x_mode_print_optionally_squoted(eq); + x_mode_flush(); } #endif debug_printf_env("set shell var:'%s'->'%s'\n", *argv, p); @@ -9691,6 +9729,7 @@ static int set_mode(int state, char mode, const char *o_opt) break; case 'x': IF_HUSH_MODE_X(G_x_mode = state;) + IF_HUSH_MODE_X(if (G.x_mode_fd <= 0) G.x_mode_fd = dup_CLOEXEC(2, 10);) break; case 'o': if (!o_opt) { From a8e7441176ec945a1bfb117a1067ac3a6680a19c Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Sat, 28 Jul 2018 12:16:30 +0200 Subject: [PATCH 11/62] hush: disable debug_indent increment/decrement for HUSH_DEBUG < 2 builds function old new delta run_list 1063 1046 -17 parse_stream 2296 2249 -47 run_pipe 1890 1840 -50 ------------------------------------------------------------------------------ (add/remove: 0/0 grow/shrink: 0/3 up/down: 0/-114) Total: -114 bytes Signed-off-by: Denys Vlasenko --- shell/hush.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/shell/hush.c b/shell/hush.c index 9676819fa1..14681aa48d 100644 --- a/shell/hush.c +++ b/shell/hush.c @@ -999,7 +999,7 @@ struct globals { int x_mode_fd; o_string x_mode_buf; #endif -#if HUSH_DEBUG +#if HUSH_DEBUG >= 2 int debug_indent; #endif struct sigaction sa; @@ -1221,7 +1221,7 @@ static const struct built_in_command bltins2[] = { /* Debug printouts. */ -#if HUSH_DEBUG +#if HUSH_DEBUG >= 2 /* prevent disasters with G.debug_indent < 0 */ # define indent() fdprintf(2, "%*s", (G.debug_indent * 2) & 0xff, "") # define debug_enter() (G.debug_indent++) From a5db1d7354977fe295882ff557e8f5fb23741b13 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Sat, 28 Jul 2018 12:42:08 +0200 Subject: [PATCH 12/62] hush: fix another case where empty "for" wasn't setting exitcode to 0 Signed-off-by: Denys Vlasenko --- shell/ash_test/ash-misc/empty_for1.right | 1 + shell/ash_test/ash-misc/empty_for1.tests | 5 +++++ shell/hush.c | 2 +- shell/hush_test/hush-misc/empty_for1.right | 1 + shell/hush_test/hush-misc/empty_for1.tests | 5 +++++ 5 files changed, 13 insertions(+), 1 deletion(-) create mode 100644 shell/ash_test/ash-misc/empty_for1.right create mode 100755 shell/ash_test/ash-misc/empty_for1.tests create mode 100644 shell/hush_test/hush-misc/empty_for1.right create mode 100755 shell/hush_test/hush-misc/empty_for1.tests diff --git a/shell/ash_test/ash-misc/empty_for1.right b/shell/ash_test/ash-misc/empty_for1.right new file mode 100644 index 0000000000..46ffcece7d --- /dev/null +++ b/shell/ash_test/ash-misc/empty_for1.right @@ -0,0 +1 @@ +Zero:0 diff --git a/shell/ash_test/ash-misc/empty_for1.tests b/shell/ash_test/ash-misc/empty_for1.tests new file mode 100755 index 0000000000..5a2554d545 --- /dev/null +++ b/shell/ash_test/ash-misc/empty_for1.tests @@ -0,0 +1,5 @@ +false +for v; do + exit 2 +done +echo Zero:$? diff --git a/shell/hush.c b/shell/hush.c index 14681aa48d..d17f7f29e9 100644 --- a/shell/hush.c +++ b/shell/hush.c @@ -9391,11 +9391,11 @@ static int run_list(struct pipe *pi) }; /* argv list with one element: "$@" */ char **vals; + G.last_exitcode = rcode = EXIT_SUCCESS; vals = (char**)encoded_dollar_at_argv; if (pi->next->res_word == RES_IN) { /* if no variable values after "in" we skip "for" */ if (!pi->next->cmds[0].argv) { - G.last_exitcode = rcode = EXIT_SUCCESS; debug_printf_exec(": null FOR: exitcode EXIT_SUCCESS\n"); break; } diff --git a/shell/hush_test/hush-misc/empty_for1.right b/shell/hush_test/hush-misc/empty_for1.right new file mode 100644 index 0000000000..46ffcece7d --- /dev/null +++ b/shell/hush_test/hush-misc/empty_for1.right @@ -0,0 +1 @@ +Zero:0 diff --git a/shell/hush_test/hush-misc/empty_for1.tests b/shell/hush_test/hush-misc/empty_for1.tests new file mode 100755 index 0000000000..5a2554d545 --- /dev/null +++ b/shell/hush_test/hush-misc/empty_for1.tests @@ -0,0 +1,5 @@ +false +for v; do + exit 2 +done +echo Zero:$? From 54fdabda3b953087f669bfcba99b9ae3b0c09fec Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Tue, 31 Jul 2018 10:36:29 +0200 Subject: [PATCH 13/62] hush: speed up ${var:+ARG} for literal ARGs function old new delta first_special_char_in_vararg - 52 +52 expand_one_var 2248 2296 +48 encode_then_expand_vararg 357 336 -21 ------------------------------------------------------------------------------ (add/remove: 1/0 grow/shrink: 1/1 up/down: 100/-21) Total: 79 bytes Signed-off-by: Denys Vlasenko --- shell/hush.c | 58 ++++++++++++++++++++++++++++------------------------ 1 file changed, 31 insertions(+), 27 deletions(-) diff --git a/shell/hush.c b/shell/hush.c index d17f7f29e9..6852b5f79c 100644 --- a/shell/hush.c +++ b/shell/hush.c @@ -5913,6 +5913,26 @@ static char *encode_then_expand_string(const char *str) return exp_str; } +static const char *first_special_char_in_vararg(const char *cp) +{ + for (;;) { + if (!*cp) return NULL; /* string has no special chars */ + if (*cp == '$') return cp; + if (*cp == '\\') return cp; + if (*cp == '\'') return cp; + if (*cp == '"') return cp; +#if ENABLE_HUSH_TICK + if (*cp == '`') return cp; +#endif + /* dquoted "${x:+ARG}" should not glob, therefore + * '*' et al require some non-literal processing: */ + if (*cp == '*') return cp; + if (*cp == '?') return cp; + if (*cp == '[') return cp; + cp++; + } +} + /* Expanding ARG in ${var#ARG}, ${var%ARG}, or ${var/ARG/ARG}. * These can contain single- and double-quoted strings, * and treated as if the ARG string is initially unquoted. IOW: @@ -5932,19 +5952,10 @@ static char *encode_then_expand_vararg(const char *str, int handle_squotes, int char *exp_str; struct in_str input; o_string dest = NULL_O_STRING; - const char *cp; - cp = str; - for (;;) { - if (!*cp) return NULL; /* string has no special chars */ - if (*cp == '$') break; - if (*cp == '\\') break; - if (*cp == '\'') break; - if (*cp == '"') break; -#if ENABLE_HUSH_TICK - if (*cp == '`') break; -#endif - cp++; + if (!first_special_char_in_vararg(str)) { + /* string has no special chars */ + return NULL; } setup_string_in_str(&input, str); @@ -6025,26 +6036,19 @@ static char *encode_then_expand_vararg(const char *str, int handle_squotes, int /* Expanding ARG in ${var+ARG}, ${var-ARG} */ static int encode_then_append_var_plusminus(o_string *output, int n, - const char *str, int dquoted) + char *str, int dquoted) { struct in_str input; o_string dest = NULL_O_STRING; -#if 0 //todo? - const char *cp; - cp = str; - for (;;) { - if (!*cp) return NULL; /* string has no special chars */ - if (*cp == '$') break; - if (*cp == '\\') break; - if (*cp == '\'') break; - if (*cp == '"') break; -#if ENABLE_HUSH_TICK - if (*cp == '`') break; -#endif - cp++; + if (!first_special_char_in_vararg(str) + && '\0' == str[strcspn(str, G.ifs)] + ) { + /* string has no special chars + * && string has no $IFS chars + */ + return expand_vars_to_list(output, n, str); } -#endif setup_string_in_str(&input, str); From e8073519d8d851d5f7e7c1fde8c9b9e65c4ab458 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Tue, 31 Jul 2018 15:25:00 +0200 Subject: [PATCH 14/62] klogd: better help text Signed-off-by: Denys Vlasenko --- sysklogd/klogd.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sysklogd/klogd.c b/sysklogd/klogd.c index 728cc8d38a..a1552ed13e 100644 --- a/sysklogd/klogd.c +++ b/sysklogd/klogd.c @@ -53,7 +53,7 @@ //usage:#define klogd_trivial_usage //usage: "[-c N] [-n]" //usage:#define klogd_full_usage "\n\n" -//usage: "Kernel logger\n" +//usage: "Log kernel messages to syslog\n" //usage: "\n -c N Print to console messages more urgent than prio N (1-8)" //usage: "\n -n Run in foreground" @@ -85,6 +85,7 @@ static void klogd_setloglevel(int lvl) static int klogd_read(char *bufp, int len) { + /* "2 -- Read from the log." */ return klogctl(2, bufp, len); } # define READ_ERROR "klogctl(2) error" @@ -238,7 +239,6 @@ int klogd_main(int argc UNUSED_PARAM, char **argv) int priority; char *start; - /* "2 -- Read from the log." */ start = log_buffer + used; n = klogd_read(start, KLOGD_LOGBUF_SIZE-1 - used); if (n < 0) { From 3d07446cef196c8c6f92a4d8006a56d348fca062 Mon Sep 17 00:00:00 2001 From: Baruch Siach Date: Tue, 17 Jul 2018 13:40:45 +0300 Subject: [PATCH 15/62] install.sh: don't clobber dangling symlinks Symlinks in a subdirectory that is to become target rootfs are sometimes dangling because they link to canonical file names that are not present on the host, but are present relative to the target rootfs root. Don't copy over dangling symlinks when noclobber is enabled The -e test treats dangling symlinks as non-existent files. Add -h test that returns true for all symlinks. Cc: Yann E. MORIN Signed-off-by: Baruch Siach Signed-off-by: Denys Vlasenko --- applets/install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/applets/install.sh b/applets/install.sh index 9aede0f530..415896893e 100755 --- a/applets/install.sh +++ b/applets/install.sh @@ -83,7 +83,7 @@ install -m 755 busybox "$prefix/bin/busybox" || exit 1 for i in $h; do appdir=`dirname "$i"` app=`basename "$i"` - if [ x"$noclobber" = x"1" ] && [ -e "$prefix/$i" ]; then + if [ x"$noclobber" = x"1" ] && ([ -e "$prefix/$i" ] || [ -h "$prefix/$i" ]); then echo " $prefix/$i already exists" continue fi From c16ae469ef132d7250a509ef5ccf3420e2fc567f Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Tue, 31 Jul 2018 16:50:26 +0200 Subject: [PATCH 16/62] klogd: code shrink function old new delta klogd_main 511 486 -25 Signed-off-by: Denys Vlasenko --- sysklogd/klogd.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/sysklogd/klogd.c b/sysklogd/klogd.c index a1552ed13e..25ddf3172f 100644 --- a/sysklogd/klogd.c +++ b/sysklogd/klogd.c @@ -275,10 +275,13 @@ int klogd_main(int argc UNUSED_PARAM, char **argv) priority = LOG_INFO; if (*start == '<') { start++; - if (*start) - priority = strtoul(start, &start, 10); - if (*start == '>') - start++; + if (*start) { + char *end; + priority = strtoul(start, &end, 10); + if (*end == '>') + end++; + start = end; + } } /* Log (only non-empty lines) */ if (*start) From f28b8857a9fa7b2b137a19ce7069077da5706d78 Mon Sep 17 00:00:00 2001 From: "Raffaello D. Di Napoli" Date: Tue, 26 Jun 2018 19:17:45 -0400 Subject: [PATCH 17/62] sendmail: support AUTH PLAIN in addition to AUTH LOGIN Implement the -am argument to allow choosing an AUTH method. For now only PLAIN and LOGIN are supported, but others can be added easily in the future. AUTH PLAIN required adding a new variant of encode_base64() capable of handling NUL characters in the input string; the old function is now a wrapper for the newer one. function old new delta encode_n_base64 - 236 +236 sendmail_main 1199 1380 +181 packed_usage 32873 32877 +4 encode_base64 242 36 -206 ------------------------------------------------------------------------------ (add/remove: 1/0 grow/shrink: 2/1 up/down: 421/-206) Total: 215 bytes Signed-off-by: Raffaello D. Di Napoli Signed-off-by: Denys Vlasenko --- mailutils/mail.c | 18 ++++++++++++------ mailutils/mail.h | 1 + mailutils/sendmail.c | 40 ++++++++++++++++++++++++++++++++-------- 3 files changed, 45 insertions(+), 14 deletions(-) diff --git a/mailutils/mail.c b/mailutils/mail.c index 7af7edd6c9..2ad959f7de 100644 --- a/mailutils/mail.c +++ b/mailutils/mail.c @@ -108,6 +108,17 @@ static char* FAST_FUNC parse_url(char *url, char **user, char **pass) */ void FAST_FUNC encode_base64(char *fname, const char *text, const char *eol) +{ + size_t len = len; + if (text) { + // though we do not call uuencode(NULL, NULL) explicitly + // still we do not want to break things suddenly + len = strlen(text); + } + encode_n_base64(fname, text, len, eol); +} + +void FAST_FUNC encode_n_base64(char *fname, const char *text, size_t len, const char *eol) { enum { SRC_BUF_SIZE = 57, /* This *MUST* be a multiple of 3 */ @@ -116,17 +127,12 @@ void FAST_FUNC encode_base64(char *fname, const char *text, const char *eol) #define src_buf text char src[SRC_BUF_SIZE]; FILE *fp = fp; - ssize_t len = len; char dst_buf[DST_BUF_SIZE + 1]; if (fname) { fp = (NOT_LONE_DASH(fname)) ? xfopen_for_read(fname) : (FILE *)text; src_buf = src; - } else if (text) { - // though we do not call uuencode(NULL, NULL) explicitly - // still we do not want to break things suddenly - len = strlen(text); - } else + } else if (!text) return; while (1) { diff --git a/mailutils/mail.h b/mailutils/mail.h index fa0c5b3785..4eb2bc2c01 100644 --- a/mailutils/mail.h +++ b/mailutils/mail.h @@ -35,3 +35,4 @@ void get_cred_or_die(int fd) FAST_FUNC; char *send_mail_command(const char *fmt, const char *param) FAST_FUNC; void encode_base64(char *fname, const char *text, const char *eol) FAST_FUNC; +void encode_n_base64(char *fname, const char *text, size_t size, const char *eol) FAST_FUNC; diff --git a/mailutils/sendmail.c b/mailutils/sendmail.c index 0170f28703..1dbaf595cb 100644 --- a/mailutils/sendmail.c +++ b/mailutils/sendmail.c @@ -36,7 +36,9 @@ //usage: "\n openssl s_client -quiet -tls1 -connect smtp.gmail.com:465" //usage: "\n $SMTP_ANTISPAM_DELAY: seconds to wait after helper connect" //usage: "\n -S HOST[:PORT] Server (default $SMTPHOST or 127.0.0.1)" -//usage: "\n -amLOGIN Log in using AUTH LOGIN (-amCRAM-MD5 not supported)" +//usage: "\n -amLOGIN Log in using AUTH LOGIN" +//usage: "\n -amPLAIN or AUTH PLAIN" +//usage: "\n (-amCRAM-MD5 not supported)" //usage: "\n -auUSER Username for AUTH" //usage: "\n -apPASS Password for AUTH" //usage: "\n" @@ -248,6 +250,10 @@ int sendmail_main(int argc UNUSED_PARAM, char **argv) OPT_S = 1 << 6, // specify connection string OPT_a = 1 << 7, // authentication tokens OPT_v = 1 << 8, // verbosity + //--- from -am + OPT_am_mask = 3 << 14, // AUTH method + OPT_am_login = 0 << 14, // AUTH LOGIN (default) + OPT_am_plain = 1 << 14, // AUTH PLAIN }; // init global variables @@ -286,9 +292,12 @@ int sendmail_main(int argc UNUSED_PARAM, char **argv) G.user = xstrdup(a+1); if ('p' == a[0]) G.pass = xstrdup(a+1); - // N.B. we support only AUTH LOGIN so far - //if ('m' == a[0]) - // G.method = xstrdup(a+1); + if ('m' == a[0]) { + if (strcasecmp("plain", a+1) == 0) + opts |= OPT_am_plain; + else if (strcasecmp("login", a+1) != 0) + bb_error_msg_and_die("unsupported AUTH method %s", a+1); + } } // N.B. list == NULL here //bb_error_msg("OPT[%x] AU[%s], AP[%s], AM[%s], ARGV[%s]", opts, au, ap, am, *argv); @@ -348,13 +357,28 @@ int sendmail_main(int argc UNUSED_PARAM, char **argv) // perform authentication if (opts & OPT_a) { - smtp_check("AUTH LOGIN", 334); // we must read credentials unless they are given via -a[up] options if (!G.user || !G.pass) get_cred_or_die(4); - encode_base64(NULL, G.user, NULL); - smtp_check("", 334); - encode_base64(NULL, G.pass, NULL); + if ((opts & OPT_am_mask) == OPT_am_plain) { + char *plain_auth; + size_t user_len, pass_len; + user_len = strlen(G.user); + pass_len = strlen(G.pass); + smtp_check("AUTH PLAIN", 334); + // use \1 as placeholders for \0 (format string is NUL-terminated) + plain_auth = xasprintf("\1%s\1%s", G.user, G.pass); + // substitute placeholders + plain_auth[0] = '\0'; + plain_auth[1 + user_len] = '\0'; + encode_n_base64(NULL, plain_auth, 1 + user_len + 1 + pass_len, NULL); + free(plain_auth); + } else if ((opts & OPT_am_mask) == OPT_am_login) { + smtp_check("AUTH LOGIN", 334); + encode_base64(NULL, G.user, NULL); + smtp_check("", 334); + encode_base64(NULL, G.pass, NULL); + } smtp_check("", 235); } From 8d634a08c4164da3a0d93caa9de825384e59d27d Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Tue, 31 Jul 2018 17:30:08 +0200 Subject: [PATCH 18/62] sendfile: code shrink function old new delta printstr_base64 - 22 +22 printbuf_base64 - 11 +11 printfile_base64 - 9 +9 makemime_main 305 294 -11 encode_n_base64 236 223 -13 sendmail_main 1380 1366 -14 encode_base64 36 - -36 ------------------------------------------------------------------------------ (add/remove: 3/1 grow/shrink: 0/3 up/down: 42/-74) Total: -32 bytes Signed-off-by: Denys Vlasenko --- mailutils/mail.c | 35 +++++++++++++++++++---------------- mailutils/mail.h | 5 +++-- mailutils/makemime.c | 2 +- mailutils/sendmail.c | 6 +++--- 4 files changed, 26 insertions(+), 22 deletions(-) diff --git a/mailutils/mail.c b/mailutils/mail.c index 2ad959f7de..6726654f78 100644 --- a/mailutils/mail.c +++ b/mailutils/mail.c @@ -107,18 +107,7 @@ static char* FAST_FUNC parse_url(char *url, char **user, char **pass) } */ -void FAST_FUNC encode_base64(char *fname, const char *text, const char *eol) -{ - size_t len = len; - if (text) { - // though we do not call uuencode(NULL, NULL) explicitly - // still we do not want to break things suddenly - len = strlen(text); - } - encode_n_base64(fname, text, len, eol); -} - -void FAST_FUNC encode_n_base64(char *fname, const char *text, size_t len, const char *eol) +static void encode_n_base64(const char *fname, const char *text, size_t len) { enum { SRC_BUF_SIZE = 57, /* This *MUST* be a multiple of 3 */ @@ -130,10 +119,9 @@ void FAST_FUNC encode_n_base64(char *fname, const char *text, size_t len, const char dst_buf[DST_BUF_SIZE + 1]; if (fname) { - fp = (NOT_LONE_DASH(fname)) ? xfopen_for_read(fname) : (FILE *)text; + fp = (NOT_LONE_DASH(fname)) ? xfopen_for_read(fname) : stdin; src_buf = src; - } else if (!text) - return; + } while (1) { size_t size; @@ -151,7 +139,7 @@ void FAST_FUNC encode_n_base64(char *fname, const char *text, size_t len, const // encode the buffer we just read in bb_uuencode(dst_buf, src_buf, size, bb_uuenc_tbl_base64); if (fname) { - puts(eol); + puts(""); } else { src_buf += size; len -= size; @@ -163,6 +151,21 @@ void FAST_FUNC encode_n_base64(char *fname, const char *text, size_t len, const #undef src_buf } +void FAST_FUNC printstr_base64(const char *text) +{ + encode_n_base64(NULL, text, strlen(text)); +} + +void FAST_FUNC printbuf_base64(const char *text, unsigned len) +{ + encode_n_base64(NULL, text, len); +} + +void FAST_FUNC printfile_base64(const char *fname) +{ + encode_n_base64(fname, NULL, 0); +} + /* * get username and password from a file descriptor */ diff --git a/mailutils/mail.h b/mailutils/mail.h index 4eb2bc2c01..b14228a4a5 100644 --- a/mailutils/mail.h +++ b/mailutils/mail.h @@ -34,5 +34,6 @@ void get_cred_or_die(int fd) FAST_FUNC; char *send_mail_command(const char *fmt, const char *param) FAST_FUNC; -void encode_base64(char *fname, const char *text, const char *eol) FAST_FUNC; -void encode_n_base64(char *fname, const char *text, size_t size, const char *eol) FAST_FUNC; +void printbuf_base64(const char *buf, unsigned len) FAST_FUNC; +void printstr_base64(const char *buf) FAST_FUNC; +void printfile_base64(const char *fname) FAST_FUNC; diff --git a/mailutils/makemime.c b/mailutils/makemime.c index 577bcde399..7539d5134f 100644 --- a/mailutils/makemime.c +++ b/mailutils/makemime.c @@ -234,7 +234,7 @@ int makemime_main(int argc UNUSED_PARAM, char **argv) , G.opt_charset , bb_get_last_path_component_strip(*argv) ); - encode_base64(*argv++, (const char *)stdin, ""); + printfile_base64(*argv++); } // put multipart footer diff --git a/mailutils/sendmail.c b/mailutils/sendmail.c index 1dbaf595cb..2fbceaad2a 100644 --- a/mailutils/sendmail.c +++ b/mailutils/sendmail.c @@ -371,13 +371,13 @@ int sendmail_main(int argc UNUSED_PARAM, char **argv) // substitute placeholders plain_auth[0] = '\0'; plain_auth[1 + user_len] = '\0'; - encode_n_base64(NULL, plain_auth, 1 + user_len + 1 + pass_len, NULL); + printbuf_base64(plain_auth, 1 + user_len + 1 + pass_len); free(plain_auth); } else if ((opts & OPT_am_mask) == OPT_am_login) { smtp_check("AUTH LOGIN", 334); - encode_base64(NULL, G.user, NULL); + printstr_base64(G.user); smtp_check("", 334); - encode_base64(NULL, G.pass, NULL); + printstr_base64(G.pass); } smtp_check("", 235); } From 884ea1c172f022c362a3d99b11dbd2f2443ab786 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Tue, 31 Jul 2018 17:36:31 +0200 Subject: [PATCH 19/62] sendmail: code shrink 2 function old new delta sendmail_main 1366 1335 -31 Signed-off-by: Denys Vlasenko --- mailutils/sendmail.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/mailutils/sendmail.c b/mailutils/sendmail.c index 2fbceaad2a..7a4afb835d 100644 --- a/mailutils/sendmail.c +++ b/mailutils/sendmail.c @@ -250,10 +250,8 @@ int sendmail_main(int argc UNUSED_PARAM, char **argv) OPT_S = 1 << 6, // specify connection string OPT_a = 1 << 7, // authentication tokens OPT_v = 1 << 8, // verbosity - //--- from -am - OPT_am_mask = 3 << 14, // AUTH method - OPT_am_login = 0 << 14, // AUTH LOGIN (default) - OPT_am_plain = 1 << 14, // AUTH PLAIN + //--- for -amMETHOD + OPT_am_plain = 1 << 9, // AUTH PLAIN }; // init global variables @@ -293,9 +291,11 @@ int sendmail_main(int argc UNUSED_PARAM, char **argv) if ('p' == a[0]) G.pass = xstrdup(a+1); if ('m' == a[0]) { - if (strcasecmp("plain", a+1) == 0) + if ((a[1] | 0x20) == 'p') // PLAIN opts |= OPT_am_plain; - else if (strcasecmp("login", a+1) != 0) + else if ((a[1] | 0x20) == 'l') // LOGIN + ; /* do nothing (this is the default) */ + else bb_error_msg_and_die("unsupported AUTH method %s", a+1); } } @@ -357,10 +357,10 @@ int sendmail_main(int argc UNUSED_PARAM, char **argv) // perform authentication if (opts & OPT_a) { - // we must read credentials unless they are given via -a[up] options + // read credentials unless they are given via -a[up] options if (!G.user || !G.pass) get_cred_or_die(4); - if ((opts & OPT_am_mask) == OPT_am_plain) { + if (opts & OPT_am_plain) { char *plain_auth; size_t user_len, pass_len; user_len = strlen(G.user); @@ -373,7 +373,7 @@ int sendmail_main(int argc UNUSED_PARAM, char **argv) plain_auth[1 + user_len] = '\0'; printbuf_base64(plain_auth, 1 + user_len + 1 + pass_len); free(plain_auth); - } else if ((opts & OPT_am_mask) == OPT_am_login) { + } else { smtp_check("AUTH LOGIN", 334); printstr_base64(G.user); smtp_check("", 334); From 3550bc494d8fe51e8830929a4f543931030aaab0 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Tue, 31 Jul 2018 18:07:20 +0200 Subject: [PATCH 20/62] sendmail: use on-stack buffer for AUTH PLAIN function old new delta sendmail_main 1335 1307 -28 Signed-off-by: Denys Vlasenko --- mailutils/sendmail.c | 38 +++++++++++++++++++++++++++----------- 1 file changed, 27 insertions(+), 11 deletions(-) diff --git a/mailutils/sendmail.c b/mailutils/sendmail.c index 7a4afb835d..32c50ba840 100644 --- a/mailutils/sendmail.c +++ b/mailutils/sendmail.c @@ -361,19 +361,35 @@ int sendmail_main(int argc UNUSED_PARAM, char **argv) if (!G.user || !G.pass) get_cred_or_die(4); if (opts & OPT_am_plain) { - char *plain_auth; - size_t user_len, pass_len; - user_len = strlen(G.user); - pass_len = strlen(G.pass); + // C: AUTH PLAIN + // S: 334 + // C: base64encoded(authuserpass) + // S: 235 2.7.0 Authentication successful +//Note: a shorter format is allowed: +// C: AUTH PLAIN base64encoded(authuserpass) +// S: 235 2.7.0 Authentication successful smtp_check("AUTH PLAIN", 334); - // use \1 as placeholders for \0 (format string is NUL-terminated) - plain_auth = xasprintf("\1%s\1%s", G.user, G.pass); - // substitute placeholders - plain_auth[0] = '\0'; - plain_auth[1 + user_len] = '\0'; - printbuf_base64(plain_auth, 1 + user_len + 1 + pass_len); - free(plain_auth); + { + unsigned user_len = strlen(G.user); + unsigned pass_len = strlen(G.pass); + unsigned sz = 1 + user_len + 1 + pass_len; + char plain_auth[sz + 1]; + // the format is: + // "authorization identityusernamepassword" + // authorization identity is empty. + plain_auth[0] = '\0'; + strcpy(stpcpy(plain_auth + 1, G.user) + 1, G.pass); + printbuf_base64(plain_auth, sz); + } } else { + // C: AUTH LOGIN + // S: 334 VXNlcm5hbWU6 + // ^^^^^^^^^^^^ server says "Username:" + // C: base64encoded(user) + // S: 334 UGFzc3dvcmQ6 + // ^^^^^^^^^^^^ server says "Password:" + // C: base64encoded(pass) + // S: 235 2.7.0 Authentication successful smtp_check("AUTH LOGIN", 334); printstr_base64(G.user); smtp_check("", 334); From 48ec979ed1b3eb1441363240915ef0b9a91c545d Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Wed, 1 Aug 2018 13:16:05 +0200 Subject: [PATCH 21/62] udhcpc: do in fact not allow hostname components which start with dash function old new delta xmalloc_optname_optval 879 893 +14 Signed-off-by: Denys Vlasenko --- networking/udhcp/dhcpc.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/networking/udhcp/dhcpc.c b/networking/udhcp/dhcpc.c index c2805a0090..3afdb35fe3 100644 --- a/networking/udhcp/dhcpc.c +++ b/networking/udhcp/dhcpc.c @@ -160,8 +160,8 @@ static int mton(uint32_t mask) #if ENABLE_FEATURE_UDHCPC_SANITIZEOPT /* Check if a given label represents a valid DNS label - * Return pointer to the first character after the label upon success, - * NULL otherwise. + * Return pointer to the first character after the label + * (NUL or dot) upon success, NULL otherwise. * See RFC1035, 2.3.1 */ /* We don't need to be particularly anal. For example, allowing _, hyphen @@ -173,8 +173,10 @@ static int mton(uint32_t mask) static const char *valid_domain_label(const char *label) { unsigned char ch; - unsigned pos = 0; + //unsigned pos = 0; + if (label[0] == '-') + return NULL; for (;;) { ch = *label; if ((ch|0x20) < 'a' || (ch|0x20) > 'z') { @@ -187,7 +189,7 @@ static const char *valid_domain_label(const char *label) } } label++; - pos++; + //pos++; //Do we want this? //if (pos > 63) /* NS_MAXLABEL; labels must be 63 chars or less */ // return NULL; From 02cf149ed71b17326bdca69fcae8224d043fc7bb Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Wed, 1 Aug 2018 13:36:36 +0200 Subject: [PATCH 22/62] udhcpc: add a cooment about sanitizing DHCP_DOMAIN_NAME, no code changes Signed-off-by: Denys Vlasenko --- networking/udhcp/dhcpc.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/networking/udhcp/dhcpc.c b/networking/udhcp/dhcpc.c index 3afdb35fe3..0310663e0a 100644 --- a/networking/udhcp/dhcpc.c +++ b/networking/udhcp/dhcpc.c @@ -274,6 +274,12 @@ static NOINLINE char *xmalloc_optname_optval(uint8_t *option, const struct dhcp_ case OPTION_STRING_HOST: memcpy(dest, option, len); dest[len] = '\0'; +//TODO: it appears option 15 DHCP_DOMAIN_NAME is often abused +//by DHCP admins to contain a space-separated list of domains, +//not one domain name (presumably, to work as list of search domains, +//instead of using proper option 119 DHCP_DOMAIN_SEARCH). +//Currently, good_hostname() balks on strings containing spaces. +//Do we need to allow it? Only for DHCP_DOMAIN_NAME option? if (type == OPTION_STRING_HOST && !good_hostname(dest)) safe_strncpy(dest, "bad", len); return ret; From 4e30c67fa0eb0ddd9f68f783ee44806c4495cd82 Mon Sep 17 00:00:00 2001 From: Jon Kolb Date: Tue, 31 Jul 2018 21:30:24 -0400 Subject: [PATCH 23/62] Add chroot support to chpasswd function old new delta .rodata 170689 170724 +35 packed_usage 32850 32876 +26 chpasswd_main 411 436 +25 chpasswd_longopts 34 41 +7 ------------------------------------------------------------------------------ (add/remove: 0/0 grow/shrink: 4/0 up/down: 93/0) Total: 93 bytes Signed-off-by: Jon Kolb Signed-off-by: Denys Vlasenko --- loginutils/chpasswd.c | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/loginutils/chpasswd.c b/loginutils/chpasswd.c index 652e4f1275..4b3602e7a3 100644 --- a/loginutils/chpasswd.c +++ b/loginutils/chpasswd.c @@ -24,18 +24,20 @@ //kbuild:lib-$(CONFIG_CHPASSWD) += chpasswd.o //usage:#define chpasswd_trivial_usage -//usage: IF_LONG_OPTS("[--md5|--encrypted|--crypt-method]") IF_NOT_LONG_OPTS("[-m|-e|-c]") +//usage: IF_LONG_OPTS("[--md5|--encrypted|--crypt-method|--root]") IF_NOT_LONG_OPTS("[-m|-e|-c|-R]") //usage:#define chpasswd_full_usage "\n\n" //usage: "Read user:password from stdin and update /etc/passwd\n" //usage: IF_LONG_OPTS( //usage: "\n -e,--encrypted Supplied passwords are in encrypted form" -//usage: "\n -m,--md5 Eencrypt using md5, not des" +//usage: "\n -m,--md5 Encrypt using md5, not des" //usage: "\n -c,--crypt-method ALG "CRYPT_METHODS_HELP_STR +//usage: "\n -R,--root DIR Directory to chroot into" //usage: ) //usage: IF_NOT_LONG_OPTS( //usage: "\n -e Supplied passwords are in encrypted form" -//usage: "\n -m Eencrypt using md5, not des" +//usage: "\n -m Encrypt using md5, not des" //usage: "\n -c ALG "CRYPT_METHODS_HELP_STR +//usage: "\n -R DIR Directory to chroot into" //usage: ) #include "libbb.h" @@ -45,6 +47,7 @@ static const char chpasswd_longopts[] ALIGN1 = "encrypted\0" No_argument "e" "md5\0" No_argument "m" "crypt-method\0" Required_argument "c" + "root\0" Required_argument "R" ; #endif @@ -56,16 +59,21 @@ int chpasswd_main(int argc UNUSED_PARAM, char **argv) { char *name; const char *algo = CONFIG_FEATURE_DEFAULT_PASSWD_ALGO; + const char *root = NULL; int opt; if (getuid() != 0) bb_error_msg_and_die(bb_msg_perm_denied_are_you_root); - opt = getopt32long(argv, "^" "emc:" "\0" "m--ec:e--mc:c--em", + opt = getopt32long(argv, "^" "emc:R:" "\0" "m--ec:e--mc:c--em", chpasswd_longopts, - &algo + &algo, &root ); + if (root) { + xchroot(root); + } + while ((name = xmalloc_fgetline(stdin)) != NULL) { char *free_me; char *pass; From d01c9aa7ca949220e76d28972359f97dd0b040b1 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Wed, 1 Aug 2018 18:29:41 +0200 Subject: [PATCH 24/62] cp: placate gcc8 Signed-off-by: Denys Vlasenko --- coreutils/cp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/coreutils/cp.c b/coreutils/cp.c index ae60623d31..b26c0e9547 100644 --- a/coreutils/cp.c +++ b/coreutils/cp.c @@ -114,7 +114,7 @@ int cp_main(int argc, char **argv) # endif ); # if ENABLE_FEATURE_CP_REFLINK - BUILD_BUG_ON(OPT_reflink != FILEUTILS_REFLINK); + BUILD_BUG_ON((int)OPT_reflink != (int)FILEUTILS_REFLINK); if (flags & FILEUTILS_REFLINK) { if (!reflink) flags |= FILEUTILS_REFLINK_ALWAYS; From 9408978a438ac6c3becb2216d663216d27b59eab Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Wed, 1 Aug 2018 19:42:46 +0200 Subject: [PATCH 25/62] nslookup: add support for search domains, closes 11161 function old new delta parse_resolvconf - 311 +311 add_query_with_search - 105 +105 nslookup_main 873 757 -116 ------------------------------------------------------------------------------ (add/remove: 2/0 grow/shrink: 0/1 up/down: 416/-116) Total: 300 bytes Signed-off-by: Denys Vlasenko --- networking/nslookup.c | 104 +++++++++++++++++++++++++++++++++--------- 1 file changed, 82 insertions(+), 22 deletions(-) diff --git a/networking/nslookup.c b/networking/nslookup.c index 3a614b0c6c..e153eb585d 100644 --- a/networking/nslookup.c +++ b/networking/nslookup.c @@ -318,6 +318,8 @@ struct globals { unsigned serv_count; struct ns *server; struct query *query; + char *search; + smalluint have_search_directive; } FIX_ALIASING; #define G (*(struct globals*)bb_common_bufsiz1) #define INIT_G() do { \ @@ -667,24 +669,60 @@ static void parse_resolvconf(void) resolv = fopen("/etc/resolv.conf", "r"); if (resolv) { - char line[128], *p; + char line[512]; /* "search" is defined to be up to 256 chars */ while (fgets(line, sizeof(line), resolv)) { - p = strtok(line, " \t\n"); + char *p, *arg; - if (!p || strcmp(p, "nameserver") != 0) + p = strtok(line, " \t\n"); + if (!p) + continue; + dbg("resolv_key:'%s'\n", p); + arg = strtok(NULL, "\n"); + dbg("resolv_arg:'%s'\n", arg); + if (!arg) continue; - p = strtok(NULL, " \t\n"); + if (strcmp(p, "domain") == 0) { + /* domain DOM */ + if (!G.have_search_directive) + goto set_search; + continue; + } + if (strcmp(p, "search") == 0) { + /* search DOM1 DOM2... */ + G.have_search_directive = 1; + set_search: + free(G.search); + G.search = xstrdup(arg); + dbg("search='%s'\n", G.search); + continue; + } - if (!p) + if (strcmp(p, "nameserver") != 0) continue; - add_ns(xstrdup(p)); + /* nameserver DNS */ + add_ns(xstrdup(arg)); } fclose(resolv); } + + if (!G.search) { + /* default search domain is domain part of hostname */ + char *h = safe_gethostname(); + char *d = strchr(h, '.'); + if (d) { + G.search = d + 1; + dbg("search='%s' (from hostname)\n", G.search); + } + /* else free(h); */ + } + + /* Cater for case of "domain ." in resolv.conf */ + if (G.search && LONE_CHAR(G.search, '.')) + G.search = NULL; } static void add_query(int type, const char *dname) @@ -695,7 +733,7 @@ static void add_query(int type, const char *dname) count = G.query_count++; - G.query = xrealloc_vector(G.query, /*2=2^1:*/ 1, count); + G.query = xrealloc_vector(G.query, /*4=2^2:*/ 2, count); new_q = &G.query[count]; dbg("new query#%u type %u for '%s'\n", count, type, dname); @@ -709,6 +747,28 @@ static void add_query(int type, const char *dname) new_q->qlen = qlen; } +static void add_query_with_search(int type, const char *dname) +{ + char *s; + + if (type == T_PTR || !G.search || strchr(dname, '.')) { + add_query(type, dname); + return; + } + + s = G.search; + for (;;) { + char *fullname, *e; + + e = skip_non_whitespace(s); + fullname = xasprintf("%s.%.*s", dname, (int)(e - s), s); + add_query(type, fullname); + s = skip_whitespace(e); + if (!*s) + break; + } +} + static char *make_ptr(const char *addrstr) { unsigned char addr[16]; @@ -833,6 +893,18 @@ int nslookup_main(int argc UNUSED_PARAM, char **argv) } } + /* Use given DNS server if present */ + if (argv[1]) { + if (argv[2]) + bb_show_usage(); + add_ns(argv[1]); + } else { + parse_resolvconf(); + /* Fall back to localhost if we could not find NS in resolv.conf */ + if (G.serv_count == 0) + add_ns("127.0.0.1"); + } + if (types == 0) { /* No explicit type given, guess query type. * If we can convert the domain argument into a ptr (means that @@ -846,31 +918,19 @@ int nslookup_main(int argc UNUSED_PARAM, char **argv) if (ptr) { add_query(T_PTR, ptr); } else { - add_query(T_A, argv[0]); + add_query_with_search(T_A, argv[0]); #if ENABLE_FEATURE_IPV6 - add_query(T_AAAA, argv[0]); + add_query_with_search(T_AAAA, argv[0]); #endif } } else { int c; for (c = 0; c < ARRAY_SIZE(qtypes); c++) { if (types & (1 << c)) - add_query(qtypes[c].type, argv[0]); + add_query_with_search(qtypes[c].type, argv[0]); } } - /* Use given DNS server if present */ - if (argv[1]) { - if (argv[2]) - bb_show_usage(); - add_ns(argv[1]); - } else { - parse_resolvconf(); - /* Fall back to localhost if we could not find NS in resolv.conf */ - if (G.serv_count == 0) - add_ns("127.0.0.1"); - } - for (rc = 0; rc < G.serv_count;) { int c; From a380aacca61271e24656df237d73fb9930702ff1 Mon Sep 17 00:00:00 2001 From: Lauri Kasanen Date: Wed, 1 Aug 2018 19:51:17 +0300 Subject: [PATCH 26/62] ls: When -h is passed, the total should also be in human units Signed-off-by: Lauri Kasanen Signed-off-by: Denys Vlasenko --- coreutils/ls.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/coreutils/ls.c b/coreutils/ls.c index 4be4990880..b1c306809a 100644 --- a/coreutils/ls.c +++ b/coreutils/ls.c @@ -1016,7 +1016,15 @@ static void scan_and_display_dirs_recur(struct dnode **dn, int first) subdnp = scan_one_dir((*dn)->fullname, &nfiles); #if ENABLE_DESKTOP if (option_mask32 & (OPT_s|OPT_l)) { - printf("total %"OFF_FMT"u\n", calculate_blocks(subdnp)); + if (option_mask32 & OPT_h) { + printf("total %-"HUMAN_READABLE_MAX_WIDTH_STR"s\n", + /* print size, no fractions, use suffixes */ + make_human_readable_str(calculate_blocks(subdnp) * 1024, + 0, 0) + ); + } else { + printf("total %"OFF_FMT"u\n", calculate_blocks(subdnp)); + } } #endif if (nfiles > 0) { From 9b1c8bf89be668a533505e5fb4405bac6eed651c Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Fri, 3 Aug 2018 11:03:55 +0200 Subject: [PATCH 27/62] ntpd: show real, unclamped delays on low-latency networks On fast network, I've seen "delay:0.002000" shown for all packets, thus completely losing information on what real delays are. The new code is careful to not reject packets with tiny delays if the delay "grows a lot" but is still tiny: 0.000009 is "much larger" than 0.000001 (nine times larger), but is still very good small delay. function old new delta recv_and_process_peer_pkt 863 889 +26 Signed-off-by: Denys Vlasenko --- networking/ntpd.c | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/networking/ntpd.c b/networking/ntpd.c index 7462113ce0..991c518f6a 100644 --- a/networking/ntpd.c +++ b/networking/ntpd.c @@ -150,7 +150,7 @@ #define INITIAL_SAMPLES 4 /* how many samples do we want for init */ #define MIN_FREQHOLD 12 /* adjust offset, but not freq in this many first adjustments */ -#define BAD_DELAY_GROWTH 4 /* drop packet if its delay grew by more than this */ +#define BAD_DELAY_GROWTH 4 /* drop packet if its delay grew by more than this factor */ #define RETRY_INTERVAL 32 /* on send/recv error, retry in N secs (need to be power of 2) */ #define NOREPLY_INTERVAL 512 /* sent, but got no reply: cap next query by this many seconds */ @@ -1819,7 +1819,7 @@ update_local_clock(peer_t *p) VERB2 bb_error_msg("update from:%s offset:%+f delay:%f jitter:%f clock drift:%+.3fppm tc:%d", p->p_dotted, offset, - p->lastpkt_delay, + p->p_raw_delay, G.discipline_jitter, (double)tmx.freq / 65536, (int)tmx.constant @@ -1976,27 +1976,30 @@ recv_and_process_peer_pkt(peer_t *p) T2 = lfp_to_d(msg.m_rectime); T3 = lfp_to_d(msg.m_xmttime); T4 = G.cur_time; - - /* The delay calculation is a special case. In cases where the - * server and client clocks are running at different rates and - * with very fast networks, the delay can appear negative. In - * order to avoid violating the Principle of Least Astonishment, - * the delay is clamped not less than the system precision. - */ delay = (T4 - T1) - (T3 - T2); - if (delay < G_precision_sec) - delay = G_precision_sec; + /* * If this packet's delay is much bigger than the last one, * it's better to just ignore it than use its much less precise value. */ prev_delay = p->p_raw_delay; - p->p_raw_delay = delay; - if (p->reachable_bits && delay > prev_delay * BAD_DELAY_GROWTH) { + p->p_raw_delay = (delay < 0 ? 0.0 : delay); + if (p->reachable_bits + && delay > prev_delay * BAD_DELAY_GROWTH + && delay > 1.0 / (8 * 1024) /* larger than ~0.000122 */ + ) { bb_error_msg("reply from %s: delay %f is too high, ignoring", p->p_dotted, delay); goto pick_normal_interval; } + /* The delay calculation is a special case. In cases where the + * server and client clocks are running at different rates and + * with very fast networks, the delay can appear negative. In + * order to avoid violating the Principle of Least Astonishment, + * the delay is clamped not less than the system precision. + */ + if (delay < G_precision_sec) + delay = G_precision_sec; p->lastpkt_delay = delay; p->lastpkt_recv_time = T4; VERB6 bb_error_msg("%s->lastpkt_recv_time=%f", p->p_dotted, p->lastpkt_recv_time); @@ -2024,7 +2027,7 @@ recv_and_process_peer_pkt(peer_t *p) bb_error_msg("reply from %s: offset:%+f delay:%f status:0x%02x strat:%d refid:0x%08x rootdelay:%f reach:0x%02x", p->p_dotted, offset, - p->lastpkt_delay, + p->p_raw_delay, p->lastpkt_status, p->lastpkt_stratum, p->lastpkt_refid, From 4c20d9f2b0223874e2b5ac1235d5f33fdd02589b Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Fri, 3 Aug 2018 18:17:12 +0200 Subject: [PATCH 28/62] extend fractional duration support to "top -d N.N" and "timeout" function old new delta parse_duration_str - 168 +168 sleep_for_duration - 157 +157 top_main 885 928 +43 timeout_main 269 312 +43 handle_input 571 614 +43 duration_suffixes - 40 +40 sfx 40 - -40 sleep_main 364 79 -285 ------------------------------------------------------------------------------ (add/remove: 4/1 grow/shrink: 3/1 up/down: 494/-325) Total: 169 bytes Signed-off-by: Denys Vlasenko --- coreutils/sleep.c | 78 +++------------------------------------------ coreutils/timeout.c | 6 ++-- include/libbb.h | 8 +++++ libbb/duration.c | 76 +++++++++++++++++++++++++++++++++++++++++++ procps/top.c | 14 ++++---- 5 files changed, 101 insertions(+), 81 deletions(-) create mode 100644 libbb/duration.c diff --git a/coreutils/sleep.c b/coreutils/sleep.c index 9b9581ca9a..126665839a 100644 --- a/coreutils/sleep.c +++ b/coreutils/sleep.c @@ -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; } diff --git a/coreutils/timeout.c b/coreutils/timeout.c index 4a6117f594..663303c2de 100644 --- a/coreutils/timeout.c +++ b/coreutils/timeout.c @@ -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: diff --git a/include/libbb.h b/include/libbb.h index 94caba2bb2..7cad12c448 100644 --- a/include/libbb.h +++ b/include/libbb.h @@ -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 diff --git a/libbb/duration.c b/libbb/duration.c new file mode 100644 index 0000000000..765a1e9fe9 --- /dev/null +++ b/libbb/duration.c @@ -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 diff --git a/procps/top.c b/procps/top.c index 1b49a5e6b8..f016f55010 100644 --- a/procps/top.c +++ b/procps/top.c @@ -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 "-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) From c9720a761e88e83265b4d75808533cdfbc66075b Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Fri, 3 Aug 2018 18:27:00 +0200 Subject: [PATCH 29/62] timeout: fix arguments to match coreutils Was: timeout [-t SECS] [-s SIG] PROG ARGS Is: timeout [-s SIG] SECS PROG ARGS function old new delta timeout_main 312 319 +7 packed_usage 32882 32858 -24 ------------------------------------------------------------------------------ (add/remove: 0/0 grow/shrink: 1/1 up/down: 7/-24) Total: -17 bytes Signed-off-by: Denys Vlasenko --- coreutils/timeout.c | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/coreutils/timeout.c b/coreutils/timeout.c index 663303c2de..e42aba85c2 100644 --- a/coreutils/timeout.c +++ b/coreutils/timeout.c @@ -39,10 +39,10 @@ //kbuild:lib-$(CONFIG_TIMEOUT) += timeout.o //usage:#define timeout_trivial_usage -//usage: "[-t SECS] [-s SIG] PROG ARGS" +//usage: "[-s SIG] SECS PROG ARGS" //usage:#define timeout_full_usage "\n\n" //usage: "Runs PROG. Sends SIG to it if it is not gone in SECS seconds.\n" -//usage: "Defaults: SECS: 10, SIG: TERM." +//usage: "Default SIG: TERM." #include "libbb.h" @@ -52,8 +52,7 @@ int timeout_main(int argc UNUSED_PARAM, char **argv) int signo; int status; int parent = 0; - unsigned timeout; - const char *timeout_s = "10"; + int timeout; pid_t pid; #if !BB_MMU char *sv1, *sv2; @@ -64,12 +63,18 @@ 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_s, &parent); + getopt32(argv, "+s:" USE_FOR_NOMMU("p:+"), &opt_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); + + if (!argv[optind]) + bb_show_usage(); + timeout = parse_duration_str(argv[optind++]); + if (!argv[optind]) /* no PROG? */ + bb_show_usage(); /* We want to create a grandchild which will watch * and kill the grandparent. Other methods: @@ -77,12 +82,10 @@ int timeout_main(int argc UNUSED_PARAM, char **argv) * (example: "tcpsvd 0.0.0.0 1234 timeout service_prog" - * it's better if service_prog is a child of tcpsvd!), * making child watch parent results in programs having - * unexpected children. */ + * unexpected children. */ if (parent) /* we were re-execed, already grandchild */ goto grandchild; - if (!argv[optind]) /* no PROG? */ - bb_show_usage(); #if !BB_MMU sv1 = argv[optind]; From 36d198ec6dc8f214a1e9122b993581613e6d7f12 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Fri, 3 Aug 2018 18:50:50 +0200 Subject: [PATCH 30/62] ping: implement -i SEC function old new delta common_ping_main 1862 1968 +106 ualarm - 79 +79 packed_usage 32858 32897 +39 sendping_tail 204 218 +14 ------------------------------------------------------------------------------ (add/remove: 5/0 grow/shrink: 3/0 up/down: 320/0) Total: 320 bytes Signed-off-by: Denys Vlasenko --- networking/ping.c | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/networking/ping.c b/networking/ping.c index 8f85d3ec25..a579ea4aeb 100644 --- a/networking/ping.c +++ b/networking/ping.c @@ -74,6 +74,7 @@ //usage: ) //usage: "\n -c CNT Send only CNT pings" //usage: "\n -s SIZE Send SIZE data bytes in packets (default 56)" +//usage: "\n -i SECS Interval" //usage: "\n -A Ping as soon as reply is recevied" //usage: "\n -t TTL Set TTL" //usage: "\n -I IFACE/IP Source interface or IP address" @@ -91,6 +92,7 @@ //usage: "Send ICMP ECHO_REQUEST packets to network hosts\n" //usage: "\n -c CNT Send only CNT pings" //usage: "\n -s SIZE Send SIZE data bytes in packets (default 56)" +//usage: "\n -i SECS Interval" //usage: "\n -A Ping as soon as reply is recevied" //usage: "\n -I IFACE/IP Source interface or IP address" //usage: "\n -q Quiet, only display output at start" @@ -350,7 +352,7 @@ static int common_ping_main(sa_family_t af, char **argv) /* Full(er) version */ /* -c NUM, -t NUM, -w NUM, -W NUM */ -#define OPT_STRING "qvAc:+s:t:+w:+W:+I:np:4"IF_PING6("6") +#define OPT_STRING "qvAc:+s:t:+w:+W:+I:np:i:4"IF_PING6("6") enum { OPT_QUIET = 1 << 0, OPT_VERBOSE = 1 << 1, @@ -363,8 +365,9 @@ enum { OPT_I = 1 << 8, /*OPT_n = 1 << 9, - ignored */ OPT_p = 1 << 10, - OPT_IPV4 = 1 << 11, - OPT_IPV6 = (1 << 12) * ENABLE_PING6, + OPT_i = 1 << 11, + OPT_IPV4 = 1 << 12, + OPT_IPV6 = (1 << 13) * ENABLE_PING6, }; @@ -382,6 +385,7 @@ struct globals { unsigned long long tsum; /* in us, sum of all times */ unsigned cur_us; /* low word only, we don't need more */ unsigned deadline_us; + unsigned interval_us; unsigned timeout; unsigned sizeof_rcv_packet; char *rcv_packet; /* [datalen + MAXIPLEN + MAXICMPLEN] */ @@ -483,7 +487,7 @@ static void sendping_tail(void (*sp)(int), int size_pkt) if (pingcount == 0 || G.ntransmitted < pingcount) { /* Didn't send all pings yet - schedule next in 1s */ signal(SIGALRM, sp); - alarm(PINGINTERVAL); + ualarm(G.interval_us, 0); } else { /* -c NN, and all NN are sent */ /* Wait for the last ping to come back. * -W timeout: wait for a response in seconds. @@ -885,6 +889,8 @@ static int common_ping_main(int opt, char **argv) { len_and_sockaddr *lsa; char *str_s, *str_p; + char *str_i = (char*)"1"; + duration_t interval; INIT_G(); @@ -892,7 +898,7 @@ static int common_ping_main(int opt, char **argv) OPT_STRING /* exactly one arg; -v and -q don't mix */ "\0" "=1:q--v:v--q", - &pingcount, &str_s, &opt_ttl, &G.deadline_us, &timeout, &str_I, &str_p + &pingcount, &str_s, &opt_ttl, &G.deadline_us, &timeout, &str_I, &str_p, &str_i ); if (opt & OPT_s) datalen = xatou16(str_s); // -s @@ -910,6 +916,10 @@ static int common_ping_main(int opt, char **argv) unsigned d = G.deadline_us < INT_MAX/1000000 ? G.deadline_us : INT_MAX/1000000; G.deadline_us = 1 | ((d * 1000000) + monotonic_us()); } + interval = parse_duration_str(str_i); + if (interval > INT_MAX/1000000) + interval = INT_MAX/1000000; + G.interval_us = interval * 1000000; myid = (uint16_t) getpid(); hostname = argv[optind]; From 277e00ed12264f7ae1d17f2cbc8be40d3dc0655d Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Fri, 3 Aug 2018 18:51:46 +0200 Subject: [PATCH 31/62] Complie libbb/duration.c if ping[6] is selected Signed-off-by: Denys Vlasenko --- libbb/duration.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libbb/duration.c b/libbb/duration.c index 765a1e9fe9..2f272771f3 100644 --- a/libbb/duration.c +++ b/libbb/duration.c @@ -15,6 +15,8 @@ //kbuild:lib-$(CONFIG_SLEEP) += duration.o //kbuild:lib-$(CONFIG_TOP) += duration.o //kbuild:lib-$(CONFIG_TIMEOUT) += duration.o +//kbuild:lib-$(CONFIG_PING) += duration.o +//kbuild:lib-$(CONFIG_PING6) += duration.o #include "libbb.h" From 3f2e963768cd9ceb52988db2848ecf0b930f3f44 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Fri, 3 Aug 2018 19:53:49 +0200 Subject: [PATCH 32/62] uptime: add -s support Signed-off-by: Denys Vlasenko --- procps/uptime.c | 30 ++++++++++++++++++++++++------ 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/procps/uptime.c b/procps/uptime.c index b0ee8391b7..3262f41b4a 100644 --- a/procps/uptime.c +++ b/procps/uptime.c @@ -45,7 +45,6 @@ # include #endif - #ifndef FSHIFT # define FSHIFT 16 /* nr of bits of precision */ #endif @@ -53,29 +52,48 @@ #define LOAD_INT(x) (unsigned)((x) >> FSHIFT) #define LOAD_FRAC(x) LOAD_INT(((x) & (FIXED_1 - 1)) * 100) - int uptime_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int uptime_main(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) { unsigned updays, uphours, upminutes; + unsigned opts; struct sysinfo info; struct tm *current_time; time_t current_secs; + opts = getopt32(argv, "s"); + time(¤t_secs); + sysinfo(&info); + + if (opts) // -s + current_secs -= info.uptime; + current_time = localtime(¤t_secs); - sysinfo(&info); + if (opts) { // -s + printf("%04u-%02u-%02u %02u:%02u:%02u\n", + current_time->tm_year + 1900, current_time->tm_mon + 1, current_time->tm_mday, + current_time->tm_hour, current_time->tm_min, current_time->tm_sec + ); + /* The above way of calculating boot time is wobbly, + * info.uptime has only 1 second precision, which makes + * "uptime -s" wander +- one second. + * /proc/uptime may be better, it has 0.01s precision. + */ + return EXIT_SUCCESS; + } printf(" %02u:%02u:%02u up ", - current_time->tm_hour, current_time->tm_min, current_time->tm_sec); + current_time->tm_hour, current_time->tm_min, current_time->tm_sec + ); updays = (unsigned) info.uptime / (unsigned)(60*60*24); - if (updays) + if (updays != 0) printf("%u day%s, ", updays, (updays != 1) ? "s" : ""); upminutes = (unsigned) info.uptime / (unsigned)60; uphours = (upminutes / (unsigned)60) % (unsigned)24; upminutes %= 60; - if (uphours) + if (uphours != 0) printf("%2u:%02u", uphours, upminutes); else printf("%u min", upminutes); From 45d68c3749d29339494eb38bb4d5fe3ea189ac77 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Sat, 4 Aug 2018 17:48:59 +0200 Subject: [PATCH 33/62] tar: make source code a bit more terse, no logic changes Signed-off-by: Denys Vlasenko --- archival/tar.c | 57 ++++++++++++++++++++++---------------------------- 1 file changed, 25 insertions(+), 32 deletions(-) diff --git a/archival/tar.c b/archival/tar.c index ae1ccc756c..9239d8ee40 100644 --- a/archival/tar.c +++ b/archival/tar.c @@ -596,34 +596,30 @@ static int FAST_FUNC writeFileToTarball(const char *fileName, struct stat *statb /* Don't inline: vfork scares gcc and pessimizes code */ static void NOINLINE vfork_compressor(int tar_fd, const char *gzip) { - pid_t gzipPid; - // On Linux, vfork never unpauses parent early, although standard // allows for that. Do we want to waste bytes checking for it? # define WAIT_FOR_CHILD 0 volatile int vfork_exec_errno = 0; - struct fd_pair gzipDataPipe; + struct fd_pair data; # if WAIT_FOR_CHILD - struct fd_pair gzipStatusPipe; - xpiped_pair(gzipStatusPipe); + struct fd_pair status; + xpiped_pair(status); # endif - xpiped_pair(gzipDataPipe); + xpiped_pair(data); signal(SIGPIPE, SIG_IGN); /* we only want EPIPE on errors */ - gzipPid = xvfork(); - - if (gzipPid == 0) { + if (xvfork() == 0) { /* child */ /* NB: close _first_, then move fds! */ - close(gzipDataPipe.wr); + close(data.wr); # if WAIT_FOR_CHILD - close(gzipStatusPipe.rd); - /* gzipStatusPipe.wr will close only on exec - + close(status.rd); + /* status.wr will close only on exec - * parent waits for this close to happen */ - fcntl(gzipStatusPipe.wr, F_SETFD, FD_CLOEXEC); + fcntl(status.wr, F_SETFD, FD_CLOEXEC); # endif - xmove_fd(gzipDataPipe.rd, 0); + xmove_fd(data.rd, 0); xmove_fd(tar_fd, 1); /* exec gzip/bzip2 program/applet */ BB_EXECLP(gzip, gzip, "-f", (char *)0); @@ -632,20 +628,18 @@ static void NOINLINE vfork_compressor(int tar_fd, const char *gzip) } /* parent */ - xmove_fd(gzipDataPipe.wr, tar_fd); - close(gzipDataPipe.rd); + xmove_fd(data.wr, tar_fd); + close(data.rd); # if WAIT_FOR_CHILD - close(gzipStatusPipe.wr); + close(status.wr); while (1) { - char buf; - int n; - /* Wait until child execs (or fails to) */ - n = full_read(gzipStatusPipe.rd, &buf, 1); + char buf; + int n = full_read(status.rd, &buf, 1); if (n < 0 /* && errno == EAGAIN */) continue; /* try it again */ } - close(gzipStatusPipe.rd); + close(status.rd); # endif if (vfork_exec_errno) { errno = vfork_exec_errno; @@ -1155,17 +1149,16 @@ int tar_main(int argc UNUSED_PARAM, char **argv) if (LONE_DASH(tar_filename)) { tar_handle->src_fd = tar_fd; tar_handle->seek = seek_by_read; + } else + if (ENABLE_FEATURE_TAR_AUTODETECT + && flags == O_RDONLY + && !(opt & OPT_ANY_COMPRESS) + ) { + tar_handle->src_fd = open_zipped(tar_filename, /*fail_if_not_compressed:*/ 0); + if (tar_handle->src_fd < 0) + bb_perror_msg_and_die("can't open '%s'", tar_filename); } else { - if (ENABLE_FEATURE_TAR_AUTODETECT - && flags == O_RDONLY - && !(opt & OPT_ANY_COMPRESS) - ) { - tar_handle->src_fd = open_zipped(tar_filename, /*fail_if_not_compressed:*/ 0); - if (tar_handle->src_fd < 0) - bb_perror_msg_and_die("can't open '%s'", tar_filename); - } else { - tar_handle->src_fd = xopen(tar_filename, flags); - } + tar_handle->src_fd = xopen(tar_filename, flags); } } From 037759bb4f57b0cb9b0daf6ffed43f90b62b8cb4 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Sat, 4 Aug 2018 18:15:19 +0200 Subject: [PATCH 34/62] tar: handle the case when opened created tarball happens to have fd#0 Reproducer: exec 0>&- exec 1>&- tar czf z.tar.gz FILE function old new delta vfork_compressor 229 257 +28 Signed-off-by: Denys Vlasenko --- archival/tar.c | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/archival/tar.c b/archival/tar.c index 9239d8ee40..120c77f3be 100644 --- a/archival/tar.c +++ b/archival/tar.c @@ -611,6 +611,7 @@ static void NOINLINE vfork_compressor(int tar_fd, const char *gzip) if (xvfork() == 0) { /* child */ + int tfd; /* NB: close _first_, then move fds! */ close(data.wr); # if WAIT_FOR_CHILD @@ -619,8 +620,23 @@ static void NOINLINE vfork_compressor(int tar_fd, const char *gzip) * parent waits for this close to happen */ fcntl(status.wr, F_SETFD, FD_CLOEXEC); # endif + /* copy it: parent's tar_fd variable must not change */ + tfd = tar_fd; + if (tfd == 0) { + /* Output tar fd may be zero. + * xmove_fd(data.rd, 0) would destroy it. + * Reproducer: + * exec 0>&- + * exec 1>&- + * tar czf Z.tar.gz FILE + * Swapping move_fd's order wouldn't work: + * data.rd is 1 and _it_ would be destroyed. + */ + xmove_fd(tfd, 3); + tfd = 3; + } xmove_fd(data.rd, 0); - xmove_fd(tar_fd, 1); + xmove_fd(tfd, 1); /* exec gzip/bzip2 program/applet */ BB_EXECLP(gzip, gzip, "-f", (char *)0); vfork_exec_errno = errno; @@ -715,11 +731,7 @@ static NOINLINE int writeTarFile( return errorFlag; } -#else /* !FEATURE_TAR_CREATE */ - -# define writeTarFile(...) 0 - -#endif +#endif /* FEATURE_TAR_CREATE */ #if ENABLE_FEATURE_TAR_FROM static llist_t *append_file_list_to_list(llist_t *list) From 2005d3ff3661220f11e8ff1911b24051b3669566 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Sat, 4 Aug 2018 21:06:02 +0200 Subject: [PATCH 35/62] tar: fix a thinko in prev commit - we need to copy to _unused_ fd function old new delta vfork_compressor 257 246 -11 Signed-off-by: Denys Vlasenko --- archival/tar.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/archival/tar.c b/archival/tar.c index 120c77f3be..1c71f7f661 100644 --- a/archival/tar.c +++ b/archival/tar.c @@ -632,8 +632,7 @@ static void NOINLINE vfork_compressor(int tar_fd, const char *gzip) * Swapping move_fd's order wouldn't work: * data.rd is 1 and _it_ would be destroyed. */ - xmove_fd(tfd, 3); - tfd = 3; + tfd = dup(tfd); } xmove_fd(data.rd, 0); xmove_fd(tfd, 1); From c2aa218f23a4e952746ebef7bb86668c6255471c Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Sat, 4 Aug 2018 22:25:28 +0200 Subject: [PATCH 36/62] ash,hush: properly handle ${v//pattern/repl} if pattern starts with / Closes 2695 function old new delta parse_dollar 762 790 +28 subevalvar 1258 1267 +9 ------------------------------------------------------------------------------ (add/remove: 0/0 grow/shrink: 2/0 up/down: 37/0) Total: 37 bytes Signed-off-by: Denys Vlasenko --- shell/ash.c | 10 +++++++++- .../var_bash_pattern_starting_with_slash.right | 2 ++ .../var_bash_pattern_starting_with_slash.tests | 3 +++ shell/hush.c | 9 +++++++++ .../var_bash_pattern_starting_with_slash.right | 2 ++ .../var_bash_pattern_starting_with_slash.tests | 3 +++ 6 files changed, 28 insertions(+), 1 deletion(-) create mode 100644 shell/ash_test/ash-vars/var_bash_pattern_starting_with_slash.right create mode 100755 shell/ash_test/ash-vars/var_bash_pattern_starting_with_slash.tests create mode 100644 shell/hush_test/hush-vars/var_bash_pattern_starting_with_slash.right create mode 100755 shell/hush_test/hush-vars/var_bash_pattern_starting_with_slash.tests diff --git a/shell/ash.c b/shell/ash.c index 03fbbee53e..5c431c9ffb 100644 --- a/shell/ash.c +++ b/shell/ash.c @@ -6854,8 +6854,15 @@ subevalvar(char *p, char *varname, int strloc, int subtype, if (subtype == VSREPLACE || subtype == VSREPLACEALL) { /* Find '/' and replace with NUL */ repl = p; + /* The pattern can't be empty. + * IOW: if the first char after "${v//" is a slash, + * it does not terminate the pattern - it's the first char of the pattern: + * v=/dev/ram; echo ${v////-} prints -dev-ram (pattern is "/") + * v=/dev/ram; echo ${v///r/-} prints /dev-am (pattern is "/r") + */ + if (*repl == '/') + repl++; for (;;) { - /* Handle escaped slashes, e.g. "${v/\//_}" (they are CTLESC'ed by this point) */ if (*repl == '\0') { repl = NULL; break; @@ -6864,6 +6871,7 @@ subevalvar(char *p, char *varname, int strloc, int subtype, *repl = '\0'; break; } + /* Handle escaped slashes, e.g. "${v/\//_}" (they are CTLESC'ed by this point) */ if ((unsigned char)*repl == CTLESC && repl[1]) repl++; repl++; diff --git a/shell/ash_test/ash-vars/var_bash_pattern_starting_with_slash.right b/shell/ash_test/ash-vars/var_bash_pattern_starting_with_slash.right new file mode 100644 index 0000000000..439dca5782 --- /dev/null +++ b/shell/ash_test/ash-vars/var_bash_pattern_starting_with_slash.right @@ -0,0 +1,2 @@ +-dev-ram +/dev-am diff --git a/shell/ash_test/ash-vars/var_bash_pattern_starting_with_slash.tests b/shell/ash_test/ash-vars/var_bash_pattern_starting_with_slash.tests new file mode 100755 index 0000000000..b83fb8eeb4 --- /dev/null +++ b/shell/ash_test/ash-vars/var_bash_pattern_starting_with_slash.tests @@ -0,0 +1,3 @@ +v=/dev/ram +echo ${v////-} +echo ${v///r/-} diff --git a/shell/hush.c b/shell/hush.c index 6852b5f79c..3407711cdd 100644 --- a/shell/hush.c +++ b/shell/hush.c @@ -4930,6 +4930,15 @@ static int parse_dollar(o_string *as_string, end_ch = '}' * 0x100 + '/'; } o_addchr(dest, ch); + /* The pattern can't be empty. + * IOW: if the first char after "${v//" is a slash, + * it does not terminate the pattern - it's the first char of the pattern: + * v=/dev/ram; echo ${v////-} prints -dev-ram (pattern is "/") + * v=/dev/ram; echo ${v///r/-} prints /dev-am (pattern is "/r") + */ + if (i_peek(input) == '/') { + o_addchr(dest, i_getch(input)); + } again: if (!BB_MMU) pos = dest->length; diff --git a/shell/hush_test/hush-vars/var_bash_pattern_starting_with_slash.right b/shell/hush_test/hush-vars/var_bash_pattern_starting_with_slash.right new file mode 100644 index 0000000000..439dca5782 --- /dev/null +++ b/shell/hush_test/hush-vars/var_bash_pattern_starting_with_slash.right @@ -0,0 +1,2 @@ +-dev-ram +/dev-am diff --git a/shell/hush_test/hush-vars/var_bash_pattern_starting_with_slash.tests b/shell/hush_test/hush-vars/var_bash_pattern_starting_with_slash.tests new file mode 100755 index 0000000000..b83fb8eeb4 --- /dev/null +++ b/shell/hush_test/hush-vars/var_bash_pattern_starting_with_slash.tests @@ -0,0 +1,3 @@ +v=/dev/ram +echo ${v////-} +echo ${v///r/-} From 9abf53beb48b40e98d50fd35f445d6474af3a494 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Sun, 5 Aug 2018 10:39:18 +0200 Subject: [PATCH 37/62] ash: eval: Variable assignments on functions are no longer persistent MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Upstream commit: Date: Wed, 4 Apr 2018 17:54:01 +0800 eval: Variable assignments on functions are no longer persistent Dirk Fieldhouse wrote: > In POSIX.1-2017 ("simultaneously IEEE Std 1003.1™-2017 and The Open > Group Technical Standard Base Specifications, Issue 7") > , > we read under '2.9.1 Simple Commands' > > "Variable assignments shall be performed as follows: > ... > - If the command name is a standard utility implemented as a function > (see XBD Utility), the effect of variable assignments shall be as if the > utility was not implemented as a function. > ... > - If the command name is a function that is not a standard utility > implemented as a function, variable assignments shall affect the current > execution environment during the execution of the function. It is > unspecified: > > * Whether or not the variable assignments persist after the > completion of the function > > * Whether or not the variables gain the export attribute during > the execution of the function > > * Whether or not export attributes gained as a result of the > variable assignments persist after the completion of the function (if > variable assignments persist after the completion of the function)" POSIX used to require the current dash behaviour. However, you're right that this is no longer the case. This patch will remove the persistence of the variable assignment. I have considered the exporting the variables during the function execution but have decided against it because: 1) It makes the code bigger. 2) dash has never done this in the past. 3) You cannot use this portably anyway. Reported-by: Dirk Fieldhouse Signed-off-by: Herbert Xu function old new delta evalcommand 1606 1635 +29 evalcase 313 317 +4 evalfun 280 268 -12 pushlocalvars 48 - -48 ------------------------------------------------------------------------------ (add/remove: 0/1 grow/shrink: 2/1 up/down: 33/-60) Total: -27 bytes Signed-off-by: Denys Vlasenko --- shell/ash.c | 3 --- shell/ash_test/ash-misc/env_and_func.right | 2 +- shell/ash_test/ash-misc/env_and_func.tests | 2 +- shell/ash_test/ash-vars/var_leak.right | 2 +- shell/ash_test/ash-vars/var_leak.tests | 4 +--- shell/hush_test/hush-misc/env_and_func.tests | 2 +- 6 files changed, 5 insertions(+), 10 deletions(-) diff --git a/shell/ash.c b/shell/ash.c index 5c431c9ffb..6cda7251e7 100644 --- a/shell/ash.c +++ b/shell/ash.c @@ -9600,9 +9600,7 @@ evalfun(struct funcnode *func, int argc, char **argv, int flags) shellparam.optind = 1; shellparam.optoff = -1; #endif - pushlocalvars(); evaltree(func->n.ndefun.body, flags & EV_TESTED); - poplocalvars(0); funcdone: INT_OFF; funcline = savefuncline; @@ -10235,7 +10233,6 @@ evalcommand(union node *cmd, int flags) goto readstatus; case CMDFUNCTION: - poplocalvars(1); /* See above for the rationale */ dowait(DOWAIT_NONBLOCK, NULL); if (evalfun(cmdentry.u.func, argc, argv, flags)) diff --git a/shell/ash_test/ash-misc/env_and_func.right b/shell/ash_test/ash-misc/env_and_func.right index 5fc3488aef..4a15450586 100644 --- a/shell/ash_test/ash-misc/env_and_func.right +++ b/shell/ash_test/ash-misc/env_and_func.right @@ -1,2 +1,2 @@ var=val -var=val +var=old diff --git a/shell/ash_test/ash-misc/env_and_func.tests b/shell/ash_test/ash-misc/env_and_func.tests index 3efef1a419..1c63eafd80 100755 --- a/shell/ash_test/ash-misc/env_and_func.tests +++ b/shell/ash_test/ash-misc/env_and_func.tests @@ -3,6 +3,6 @@ f() { echo "var=$var"; } # bash: POSIXLY_CORRECT behavior is to "leak" new variable values # out of function invocations (similar to "special builtins" behavior); # but in "bash mode", they don't leak. -# hush does not "leak" values. ash does. +# hush does not "leak" values. ash used to, but now does not. var=val f echo "var=$var" diff --git a/shell/ash_test/ash-vars/var_leak.right b/shell/ash_test/ash-vars/var_leak.right index 01a5e3263e..7646800862 100644 --- a/shell/ash_test/ash-vars/var_leak.right +++ b/shell/ash_test/ash-vars/var_leak.right @@ -1,4 +1,4 @@ should be empty: '' should be empty: '' should be not empty: 'val2' -should be not empty: 'val3' +should be empty: '' diff --git a/shell/ash_test/ash-vars/var_leak.tests b/shell/ash_test/ash-vars/var_leak.tests index 5242e24eb1..adf66692ec 100755 --- a/shell/ash_test/ash-vars/var_leak.tests +++ b/shell/ash_test/ash-vars/var_leak.tests @@ -15,9 +15,7 @@ VAR='' VAR=val2 exec 2>&1 echo "should be not empty: '$VAR'" -# ash follows the "function call is a special builtin" rule here -# (bash does not do it) f() { true; } VAR='' VAR=val3 f -echo "should be not empty: '$VAR'" +echo "should be empty: '$VAR'" diff --git a/shell/hush_test/hush-misc/env_and_func.tests b/shell/hush_test/hush-misc/env_and_func.tests index 3efef1a419..1c63eafd80 100755 --- a/shell/hush_test/hush-misc/env_and_func.tests +++ b/shell/hush_test/hush-misc/env_and_func.tests @@ -3,6 +3,6 @@ f() { echo "var=$var"; } # bash: POSIXLY_CORRECT behavior is to "leak" new variable values # out of function invocations (similar to "special builtins" behavior); # but in "bash mode", they don't leak. -# hush does not "leak" values. ash does. +# hush does not "leak" values. ash used to, but now does not. var=val f echo "var=$var" From 67dae152f4bf5456e4ea7950f4b55356aa37ec6c Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Sun, 5 Aug 2018 13:59:35 +0200 Subject: [PATCH 38/62] ash: var: Set IFS to fixed value at start time Upstream commit: Date: Sat, 19 May 2018 02:39:43 +0800 var: Set IFS to fixed value at start time This patch forces the IFS variable to always be set to its default value, regardless of the environment. It also removes the long unused IFS_BROKEN code. Signed-off-by: Herbert Xu Signed-off-by: Denys Vlasenko --- shell/ash.c | 1 + 1 file changed, 1 insertion(+) diff --git a/shell/ash.c b/shell/ash.c index 6cda7251e7..6ef0a7ac27 100644 --- a/shell/ash.c +++ b/shell/ash.c @@ -13960,6 +13960,7 @@ init(void) } } + setvareq((char*)defifsvar, VTEXTFIXED); setvareq((char*)defoptindvar, VTEXTFIXED); setvar0("PPID", utoa(getppid())); From 440da97ed79841b55f0b61e4108a336b61642bff Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Sun, 5 Aug 2018 14:29:58 +0200 Subject: [PATCH 39/62] ash: expand: Fix ghost fields with unquoted $@/$* Upstream commit: Date: Fri, 23 Mar 2018 18:58:47 +0800 expand: Fix ghost fields with unquoted $@/$* You're right. The proper fix to this is to ensure that nulonly is not set in varvalue for $*. It should only be set for $@ when it's inside double quotes. In fact there is another bug while we're playing with $@/$*. When IFS is set to a non-whitespace character such as :, $* outside quotes won't remove empty fields as it should. This patch fixes both problems. Reported-by: Martijn Dekker Suggested-by: Harald van Dijk Signed-off-by: Herbert Xu function old new delta argstr 1111 1113 +2 evalvar 571 569 -2 varvalue 579 576 -3 ------------------------------------------------------------------------------ (add/remove: 0/0 grow/shrink: 1/2 up/down: 2/-5) Total: -3 bytes Signed-off-by: Denys Vlasenko --- shell/ash.c | 38 +++++++++++++------ .../ash-vars/var_wordsplit_ifs5.right | 1 + .../ash-vars/var_wordsplit_ifs5.tests | 4 ++ .../hush-vars/var_wordsplit_ifs5.right | 1 + .../hush-vars/var_wordsplit_ifs5.tests | 4 ++ 5 files changed, 36 insertions(+), 12 deletions(-) create mode 100644 shell/ash_test/ash-vars/var_wordsplit_ifs5.right create mode 100755 shell/ash_test/ash-vars/var_wordsplit_ifs5.tests create mode 100644 shell/hush_test/hush-vars/var_wordsplit_ifs5.right create mode 100755 shell/hush_test/hush-vars/var_wordsplit_ifs5.tests diff --git a/shell/ash.c b/shell/ash.c index 6ef0a7ac27..4641dfd194 100644 --- a/shell/ash.c +++ b/shell/ash.c @@ -5902,7 +5902,7 @@ static int substr_atoi(const char *s) #define EXP_CASE 0x10 /* keeps quotes around for CASE pattern */ #define EXP_VARTILDE2 0x20 /* expand tildes after colons only */ #define EXP_WORD 0x40 /* expand word in parameter expansion */ -#define EXP_QUOTED 0x80 /* expand word in double quotes */ +#define EXP_QUOTED 0x100 /* expand word in double quotes */ /* * rmescape() flags */ @@ -7175,14 +7175,13 @@ subevalvar(char *p, char *varname, int strloc, int subtype, * ash -c 'echo ${#1#}' name:'1=#' */ static NOINLINE ssize_t -varvalue(char *name, int varflags, int flags, int *quotedp) +varvalue(char *name, int varflags, int flags, int quoted) { const char *p; int num; int i; ssize_t len = 0; int sep; - int quoted = *quotedp; int subtype = varflags & VSTYPE; int discard = subtype == VSPLUS || subtype == VSLENGTH; int quotes = (discard ? 0 : (flags & QUOTES_ESC)) | QUOTES_KEEPNUL; @@ -7230,13 +7229,27 @@ varvalue(char *name, int varflags, int flags, int *quotedp) case '*': { char **ap; char sepc; + char c; - if (quoted) - sep = 0; - sep |= ifsset() ? ifsval()[0] : ' '; + /* We will set c to 0 or ~0 depending on whether + * we're doing field splitting. We won't do field + * splitting if either we're quoted or sep is zero. + * + * Instead of testing (quoted || !sep) the following + * trick optimises away any branches by using the + * fact that EXP_QUOTED (which is the only bit that + * can be set in quoted) is the same as EXP_FULL << + * CHAR_BIT (which is the only bit that can be set + * in sep). + */ +#if EXP_QUOTED >> CHAR_BIT != EXP_FULL +#error The following two lines expect EXP_QUOTED == EXP_FULL << CHAR_BIT +#endif + c = !((quoted | ~sep) & EXP_QUOTED) - 1; + sep &= ~quoted; + sep |= ifsset() ? (unsigned char)(c & ifsval()[0]) : ' '; param: sepc = sep; - *quotedp = !sepc; ap = shellparam.p; if (!ap) return -1; @@ -7301,7 +7314,6 @@ evalvar(char *p, int flag) char varflags; char subtype; int quoted; - char easy; char *var; int patloc; int startloc; @@ -7315,12 +7327,11 @@ evalvar(char *p, int flag) quoted = flag & EXP_QUOTED; var = p; - easy = (!quoted || (*var == '@' && shellparam.nparam)); startloc = expdest - (char *)stackblock(); p = strchr(p, '=') + 1; //TODO: use var_end(p)? again: - varlen = varvalue(var, varflags, flag, "ed); + varlen = varvalue(var, varflags, flag, quoted); if (varflags & VSNUL) varlen--; @@ -7366,8 +7377,11 @@ evalvar(char *p, int flag) if (subtype == VSNORMAL) { record: - if (!easy) - goto end; + if (quoted) { + quoted = *var == '@' && shellparam.nparam; + if (!quoted) + goto end; + } recordregion(startloc, expdest - (char *)stackblock(), quoted); goto end; } diff --git a/shell/ash_test/ash-vars/var_wordsplit_ifs5.right b/shell/ash_test/ash-vars/var_wordsplit_ifs5.right new file mode 100644 index 0000000000..46ffcece7d --- /dev/null +++ b/shell/ash_test/ash-vars/var_wordsplit_ifs5.right @@ -0,0 +1 @@ +Zero:0 diff --git a/shell/ash_test/ash-vars/var_wordsplit_ifs5.tests b/shell/ash_test/ash-vars/var_wordsplit_ifs5.tests new file mode 100755 index 0000000000..d382116dff --- /dev/null +++ b/shell/ash_test/ash-vars/var_wordsplit_ifs5.tests @@ -0,0 +1,4 @@ +IFS= +set -- +set -- $@ $* +echo Zero:$# diff --git a/shell/hush_test/hush-vars/var_wordsplit_ifs5.right b/shell/hush_test/hush-vars/var_wordsplit_ifs5.right new file mode 100644 index 0000000000..46ffcece7d --- /dev/null +++ b/shell/hush_test/hush-vars/var_wordsplit_ifs5.right @@ -0,0 +1 @@ +Zero:0 diff --git a/shell/hush_test/hush-vars/var_wordsplit_ifs5.tests b/shell/hush_test/hush-vars/var_wordsplit_ifs5.tests new file mode 100755 index 0000000000..d382116dff --- /dev/null +++ b/shell/hush_test/hush-vars/var_wordsplit_ifs5.tests @@ -0,0 +1,4 @@ +IFS= +set -- +set -- $@ $* +echo Zero:$# From e9dccab9f4bf3311ae50f19e39e7e499b25edca2 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Sun, 5 Aug 2018 14:55:01 +0200 Subject: [PATCH 40/62] hush: fix fallout from FILE->HFILE conversion Signed-off-by: Denys Vlasenko --- shell/hush.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/shell/hush.c b/shell/hush.c index 3407711cdd..c4b1248259 100644 --- a/shell/hush.c +++ b/shell/hush.c @@ -1683,9 +1683,15 @@ static void close_all_HFILE_list(void) * It is disastrous if we share memory with a vforked parent. * I'm not sure we never come here after vfork. * Therefore just close fd, nothing more. + * + * ">" instead of ">=": we don't close fd#0, + * interactive shell uses hfopen(NULL) as stdin input + * which has fl->fd == 0, but fd#0 gets redirected in pipes. + * If we'd close it here, then e.g. interactive "set | sort" + * with NOFORKed sort, would have sort's input fd closed. */ - /*hfclose(fl); - unsafe */ - if (fl->fd >= 0) + if (fl->fd > 0) + /*hfclose(fl); - unsafe */ close(fl->fd); fl = fl->next_hfile; } From fd6f295a98956b7f495ba09a56528ef9e0d51398 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Sun, 5 Aug 2018 15:13:08 +0200 Subject: [PATCH 41/62] hush: set IFS to default on startup function old new delta hush_main 1095 1110 +15 Signed-off-by: Denys Vlasenko --- shell/hush.c | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/shell/hush.c b/shell/hush.c index c4b1248259..4c8814a01c 100644 --- a/shell/hush.c +++ b/shell/hush.c @@ -2397,6 +2397,12 @@ static int set_local_var(char *str, unsigned flags) return 0; } +static void FAST_FUNC set_local_var_from_halves(const char *name, const char *val) +{ + char *var = xasprintf("%s=%s", name, val); + set_local_var(var, /*flag:*/ 0); +} + /* Used at startup and after each cd */ static void set_pwd_var(unsigned flag) { @@ -2443,15 +2449,6 @@ static int unset_local_var(const char *name) } #endif -#if BASH_HOSTNAME_VAR || ENABLE_FEATURE_SH_MATH || ENABLE_HUSH_READ || ENABLE_HUSH_GETOPTS \ - || (ENABLE_HUSH_INTERACTIVE && ENABLE_FEATURE_EDITING_FANCY_PROMPT) -static void FAST_FUNC set_local_var_from_halves(const char *name, const char *val) -{ - char *var = xasprintf("%s=%s", name, val); - set_local_var(var, /*flag:*/ 0); -} -#endif - /* * Helpers for "var1=val1 var2=val2 cmd" feature @@ -9854,6 +9851,10 @@ int hush_main(int argc, char **argv) uname(&uts); set_local_var_from_halves("HOSTNAME", uts.nodename); } +#endif + /* IFS is not inherited from the parent environment */ + set_local_var_from_halves("IFS", defifs); + /* bash also exports SHLVL and _, * and sets (but doesn't export) the following variables: * BASH=/bin/bash @@ -9884,10 +9885,8 @@ int hush_main(int argc, char **argv) * TERM=dumb * OPTERR=1 * OPTIND=1 - * IFS=$' \t\n' * PS4='+ ' */ -#endif #if ENABLE_HUSH_LINENO_VAR if (ENABLE_HUSH_LINENO_VAR) { From 19358cc31317dca4642417066c1445ce00438e18 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Sun, 5 Aug 2018 15:42:29 +0200 Subject: [PATCH 42/62] ash,hush: fold shell_builtin_read() way-too-many params into a struct param function old new delta getoptscmd 587 584 -3 readcmd 240 224 -16 shell_builtin_read 1426 1399 -27 builtin_read 210 182 -28 ------------------------------------------------------------------------------ (add/remove: 0/0 grow/shrink: 0/4 up/down: 0/-74) Total: -74 bytes Signed-off-by: Denys Vlasenko --- shell/ash.c | 38 ++++++++++++------------------ shell/hush.c | 33 +++++++++----------------- shell/shell_common.c | 55 +++++++++++++++++++++----------------------- shell/shell_common.h | 22 ++++++++++-------- 4 files changed, 64 insertions(+), 84 deletions(-) diff --git a/shell/ash.c b/shell/ash.c index 4641dfd194..f74bef6b1c 100644 --- a/shell/ash.c +++ b/shell/ash.c @@ -13762,38 +13762,35 @@ letcmd(int argc UNUSED_PARAM, char **argv) static int FAST_FUNC readcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) { - char *opt_n = NULL; - char *opt_p = NULL; - char *opt_t = NULL; - char *opt_u = NULL; - char *opt_d = NULL; /* optimized out if !BASH */ - int read_flags = 0; + struct builtin_read_params params; const char *r; int i; + memset(¶ms, 0, sizeof(params)); + while ((i = nextopt("p:u:rt:n:sd:")) != '\0') { switch (i) { case 'p': - opt_p = optionarg; + params.opt_p = optionarg; break; case 'n': - opt_n = optionarg; + params.opt_n = optionarg; break; case 's': - read_flags |= BUILTIN_READ_SILENT; + params.read_flags |= BUILTIN_READ_SILENT; break; case 't': - opt_t = optionarg; + params.opt_t = optionarg; break; case 'r': - read_flags |= BUILTIN_READ_RAW; + params.read_flags |= BUILTIN_READ_RAW; break; case 'u': - opt_u = optionarg; + params.opt_u = optionarg; break; #if BASH_READ_D case 'd': - opt_d = optionarg; + params.opt_d = optionarg; break; #endif default: @@ -13801,21 +13798,16 @@ readcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) } } + params.argv = argptr; + params.setvar = setvar0; + params.ifs = bltinlookup("IFS"); /* can be NULL */ + /* "read -s" needs to save/restore termios, can't allow ^C * to jump out of it. */ again: INT_OFF; - r = shell_builtin_read(setvar0, - argptr, - bltinlookup("IFS"), /* can be NULL */ - read_flags, - opt_n, - opt_p, - opt_t, - opt_u, - opt_d - ); + r = shell_builtin_read(¶ms); INT_ON; if ((uintptr_t)r == 1 && errno == EINTR) { diff --git a/shell/hush.c b/shell/hush.c index 4c8814a01c..3c19bceaa0 100644 --- a/shell/hush.c +++ b/shell/hush.c @@ -10500,40 +10500,29 @@ static int FAST_FUNC builtin_type(char **argv) static int FAST_FUNC builtin_read(char **argv) { const char *r; - char *opt_n = NULL; - char *opt_p = NULL; - char *opt_t = NULL; - char *opt_u = NULL; - char *opt_d = NULL; /* optimized out if !BASH */ - const char *ifs; - int read_flags; + struct builtin_read_params params; + + memset(¶ms, 0, sizeof(params)); /* "!": do not abort on errors. * Option string must start with "sr" to match BUILTIN_READ_xxx */ - read_flags = getopt32(argv, + params.read_flags = getopt32(argv, #if BASH_READ_D - "!srn:p:t:u:d:", &opt_n, &opt_p, &opt_t, &opt_u, &opt_d + "!srn:p:t:u:d:", ¶ms.opt_n, ¶ms.opt_p, ¶ms.opt_t, ¶ms.opt_u, ¶ms.opt_d #else - "!srn:p:t:u:", &opt_n, &opt_p, &opt_t, &opt_u + "!srn:p:t:u:", ¶ms.opt_n, ¶ms.opt_p, ¶ms.opt_t, ¶ms.opt_u #endif ); - if (read_flags == (uint32_t)-1) + if ((uint32_t)params.read_flags == (uint32_t)-1) return EXIT_FAILURE; argv += optind; - ifs = get_local_var_value("IFS"); /* can be NULL */ + params.argv = argv; + params.setvar = set_local_var_from_halves; + params.ifs = get_local_var_value("IFS"); /* can be NULL */ again: - r = shell_builtin_read(set_local_var_from_halves, - argv, - ifs, - read_flags, - opt_n, - opt_p, - opt_t, - opt_u, - opt_d - ); + r = shell_builtin_read(¶ms); if ((uintptr_t)r == 1 && errno == EINTR) { unsigned sig = check_and_run_traps(); diff --git a/shell/shell_common.c b/shell/shell_common.c index 0a07296f30..f2bf5ab654 100644 --- a/shell/shell_common.c +++ b/shell/shell_common.c @@ -46,16 +46,7 @@ int FAST_FUNC is_well_formed_var_name(const char *s, char terminator) //Here we can simply store "VAR=" at buffer start and store read data directly //after "=", then pass buffer to setvar() to consume. const char* FAST_FUNC -shell_builtin_read(void FAST_FUNC (*setvar)(const char *name, const char *val), - char **argv, - const char *ifs, - int read_flags, - const char *opt_n, - const char *opt_p, - const char *opt_t, - const char *opt_u, - const char *opt_d -) +shell_builtin_read(struct builtin_read_params *params) { struct pollfd pfd[1]; #define fd (pfd[0].fd) /* -u FD */ @@ -70,9 +61,13 @@ shell_builtin_read(void FAST_FUNC (*setvar)(const char *name, const char *val), int bufpos; /* need to be able to hold -1 */ int startword; smallint backslash; + char **argv; + const char *ifs; + int read_flags; errno = err = 0; + argv = params->argv; pp = argv; while (*pp) { if (!is_well_formed_var_name(*pp, '\0')) { @@ -84,29 +79,29 @@ shell_builtin_read(void FAST_FUNC (*setvar)(const char *name, const char *val), } nchars = 0; /* if != 0, -n is in effect */ - if (opt_n) { - nchars = bb_strtou(opt_n, NULL, 10); + if (params->opt_n) { + nchars = bb_strtou(params->opt_n, NULL, 10); if (nchars < 0 || errno) return "invalid count"; /* note: "-n 0": off (bash 3.2 does this too) */ } end_ms = 0; - if (opt_t && !ENABLE_FEATURE_SH_READ_FRAC) { - end_ms = bb_strtou(opt_t, NULL, 10); + if (params->opt_t && !ENABLE_FEATURE_SH_READ_FRAC) { + end_ms = bb_strtou(params->opt_t, NULL, 10); if (errno) return "invalid timeout"; if (end_ms > UINT_MAX / 2048) /* be safely away from overflow */ end_ms = UINT_MAX / 2048; end_ms *= 1000; } - if (opt_t && ENABLE_FEATURE_SH_READ_FRAC) { + if (params->opt_t && ENABLE_FEATURE_SH_READ_FRAC) { /* bash 4.3 (maybe earlier) supports -t N.NNNNNN */ char *p; /* Eat up to three fractional digits */ int frac_digits = 3 + 1; - end_ms = bb_strtou(opt_t, &p, 10); + end_ms = bb_strtou(params->opt_t, &p, 10); if (end_ms > UINT_MAX / 2048) /* be safely away from overflow */ end_ms = UINT_MAX / 2048; @@ -128,13 +123,13 @@ shell_builtin_read(void FAST_FUNC (*setvar)(const char *name, const char *val), } fd = STDIN_FILENO; - if (opt_u) { - fd = bb_strtou(opt_u, NULL, 10); + if (params->opt_u) { + fd = bb_strtou(params->opt_u, NULL, 10); if (fd < 0 || errno) return "invalid file descriptor"; } - if (opt_t && end_ms == 0) { + if (params->opt_t && end_ms == 0) { /* "If timeout is 0, read returns immediately, without trying * to read any data. The exit status is 0 if input is available * on the specified file descriptor, non-zero otherwise." @@ -147,14 +142,16 @@ shell_builtin_read(void FAST_FUNC (*setvar)(const char *name, const char *val), return (const char *)(uintptr_t)(r <= 0); } - if (opt_p && isatty(fd)) { - fputs(opt_p, stderr); + if (params->opt_p && isatty(fd)) { + fputs(params->opt_p, stderr); fflush_all(); } + ifs = params->ifs; if (ifs == NULL) ifs = defifs; + read_flags = params->read_flags; if (nchars || (read_flags & BUILTIN_READ_SILENT)) { tcgetattr(fd, &tty); old_tty = tty; @@ -181,11 +178,11 @@ shell_builtin_read(void FAST_FUNC (*setvar)(const char *name, const char *val), retval = (const char *)(uintptr_t)0; startword = 1; backslash = 0; - if (opt_t) + if (params->opt_t) end_ms += (unsigned)monotonic_ms(); buffer = NULL; bufpos = 0; - delim = opt_d ? *opt_d : '\n'; + delim = params->opt_d ? params->opt_d[0] : '\n'; do { char c; int timeout; @@ -194,7 +191,7 @@ shell_builtin_read(void FAST_FUNC (*setvar)(const char *name, const char *val), buffer = xrealloc(buffer, bufpos + 0x101); timeout = -1; - if (opt_t) { + if (params->opt_t) { timeout = end_ms - (unsigned)monotonic_ms(); /* ^^^^^^^^^^^^^ all values are unsigned, * wrapping math is used here, good even if @@ -246,7 +243,7 @@ shell_builtin_read(void FAST_FUNC (*setvar)(const char *name, const char *val), * without variable names (bash compat). * Thus, "read" and "read REPLY" are not the same. */ - if (!opt_d && argv[0]) { + if (!params->opt_d && argv[0]) { /* http://www.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_06_05 */ const char *is_ifs = strchr(ifs, c); if (startword && is_ifs) { @@ -261,7 +258,7 @@ shell_builtin_read(void FAST_FUNC (*setvar)(const char *name, const char *val), if (argv[1] != NULL && is_ifs) { buffer[bufpos] = '\0'; bufpos = 0; - setvar(*argv, buffer); + params->setvar(*argv, buffer); argv++; /* can we skip one non-space ifs char? (2: yes) */ startword = isspace(c) ? 2 : 1; @@ -313,14 +310,14 @@ shell_builtin_read(void FAST_FUNC (*setvar)(const char *name, const char *val), } /* Use the remainder as a value for the next variable */ - setvar(*argv, buffer); + params->setvar(*argv, buffer); /* Set the rest to "" */ while (*++argv) - setvar(*argv, ""); + params->setvar(*argv, ""); } else { /* Note: no $IFS removal */ buffer[bufpos] = '\0'; - setvar("REPLY", buffer); + params->setvar("REPLY", buffer); } ret: diff --git a/shell/shell_common.h b/shell/shell_common.h index 875fd9ea7b..a1323021d9 100644 --- a/shell/shell_common.h +++ b/shell/shell_common.h @@ -30,6 +30,17 @@ int FAST_FUNC is_well_formed_var_name(const char *s, char terminator); /* Builtins */ +struct builtin_read_params { + int read_flags; + void FAST_FUNC (*setvar)(const char *name, const char *val); + char **argv; + const char *ifs; + const char *opt_n; + const char *opt_p; + const char *opt_t; + const char *opt_u; + const char *opt_d; +}; enum { BUILTIN_READ_SILENT = 1 << 0, BUILTIN_READ_RAW = 1 << 1, @@ -40,16 +51,7 @@ enum { // shell_builtin_read(setvar,argv,ifs,read_flags) //#endif const char* FAST_FUNC -shell_builtin_read(void FAST_FUNC (*setvar)(const char *name, const char *val), - char **argv, - const char *ifs, - int read_flags, - const char *opt_n, - const char *opt_p, - const char *opt_t, - const char *opt_u, - const char *opt_d -); +shell_builtin_read(struct builtin_read_params *params); int FAST_FUNC shell_builtin_ulimit(char **argv); From 58eb805c2c453c6764acbd65f5604465438d9272 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Sun, 5 Aug 2018 15:58:13 +0200 Subject: [PATCH 43/62] ash: parser: Fix parsing of ${} Upstream commit: Date: Tue, 3 Apr 2018 00:40:25 +0800 parser: Fix parsing of ${} dash -c 'echo ${}' should print "Bad subtitution" but instead fails with "Syntax error: Missing '}'". This is caused by us reading an extra character beyond the right brace. This patch fixes it so that this construct only fails during expansion rather than during parsing. Fixes: 3df3edd13389 ("[PARSER] Report substition errors at...") Signed-off-by: Herbert Xu function old new delta readtoken1 2907 2916 +9 Signed-off-by: Denys Vlasenko --- shell/ash.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/shell/ash.c b/shell/ash.c index f74bef6b1c..b596833e7c 100644 --- a/shell/ash.c +++ b/shell/ash.c @@ -12431,7 +12431,7 @@ parsesub: { STPUTC(c, out); c = pgetc_eatbnl(); } while (isdigit(c)); - } else { + } else if (c != '}') { /* $[{[#]][}] */ int cc = c; @@ -12457,7 +12457,8 @@ parsesub: { } USTPUTC(cc, out); - } + } else + goto badsub; if (c != '}' && subtype == VSLENGTH) { /* ${#VAR didn't end with } */ From 1c5eb88cd84c90e4c3d014f4abc8f8310c008842 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Sun, 5 Aug 2018 17:07:26 +0200 Subject: [PATCH 44/62] ash: eval: Restore input files in evalcommand Upstream commit: Date: Tue, 27 Mar 2018 00:39:35 +0800 eval: Restore input files in evalcommand When evalcommand invokes a command that modifies parsefile and then bails out without popping the file, we need to ensure the input file is restored so that the shell can continue to execute. Reported-by: Martijn Dekker Signed-off-by: Herbert Xu function old new delta unwindfiles - 20 +20 evalcommand 1635 1653 +18 getoptscmd 584 595 +11 popallfiles 20 10 -10 ------------------------------------------------------------------------------ (add/remove: 1/0 grow/shrink: 2/1 up/down: 49/-10) Total: 39 bytes Signed-off-by: Denys Vlasenko --- shell/ash.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/shell/ash.c b/shell/ash.c index b596833e7c..79ade5df49 100644 --- a/shell/ash.c +++ b/shell/ash.c @@ -9942,6 +9942,7 @@ find_builtin(const char *name) /* * Execute a simple command. */ +static void unwindfiles(struct parsefile *stop); static int isassignment(const char *p) { @@ -9964,6 +9965,7 @@ evalcommand(union node *cmd, int flags) "\0\0", bltincmd /* why three NULs? */ }; struct localvar_list *localvar_stop; + struct parsefile *file_stop; struct redirtab *redir_stop; struct stackmark smark; union node *argp; @@ -9989,6 +9991,7 @@ evalcommand(union node *cmd, int flags) TRACE(("evalcommand(0x%lx, %d) called\n", (long)cmd, flags)); setstackmark(&smark); localvar_stop = pushlocalvars(); + file_stop = g_parsefile; back_exitstatus = 0; cmdentry.cmdtype = CMDBUILTIN; @@ -10260,6 +10263,7 @@ evalcommand(union node *cmd, int flags) if (cmd->ncmd.redirect) popredir(/*drop:*/ cmd_is_exec); unwindredir(redir_stop); + unwindfiles(file_stop); unwindlocalvars(localvar_stop); if (lastarg) { /* dsl: I think this is intended to be used to support @@ -10782,14 +10786,20 @@ popfile(void) INT_ON; } +static void +unwindfiles(struct parsefile *stop) +{ + while (g_parsefile != stop) + popfile(); +} + /* * Return to top level. */ static void popallfiles(void) { - while (g_parsefile != &basepf) - popfile(); + unwindfiles(&basepf); } /* From 2596f412cd02d4b040262e5f40de2e7a7e6b32cf Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Sun, 5 Aug 2018 18:04:09 +0200 Subject: [PATCH 45/62] ash: exec: Return 126 on most errors in shellexec Upstream commit: Date: Sat, 19 May 2018 02:39:37 +0800 exec: Return 126 on most errors in shellexec Currently when shellexec fails on most errors the shell will exit with exit status 2. This patch changes it to 126 in order to avoid ambiguities with the exit status from a successful exec. The errors that result in 127 has also been expanded to include ENOTDIR, ENAMETOOLONG and ELOOP. Signed-off-by: Herbert Xu function old new delta shellexec 245 254 +9 Signed-off-by: Denys Vlasenko --- shell/ash.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/shell/ash.c b/shell/ash.c index 79ade5df49..ad50537a1a 100644 --- a/shell/ash.c +++ b/shell/ash.c @@ -8098,15 +8098,15 @@ static void shellexec(char *prog, char **argv, const char *path, int idx) /* Map to POSIX errors */ switch (e) { - case EACCES: + default: exerrno = 126; break; + case ELOOP: + case ENAMETOOLONG: case ENOENT: + case ENOTDIR: exerrno = 127; break; - default: - exerrno = 2; - break; } exitstatus = exerrno; TRACE(("shellexec failed for %s, errno %d, suppress_int %d\n", From 77c18491b8647832422be3f95187e5aa5270e044 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Sun, 5 Aug 2018 20:03:04 +0200 Subject: [PATCH 46/62] hush: adopt ash's quote_in_varexp1.tests Signed-off-by: Denys Vlasenko --- shell/hush_test/hush-quoting/quote_in_varexp1.right | 2 ++ shell/hush_test/hush-quoting/quote_in_varexp1.tests | 2 ++ 2 files changed, 4 insertions(+) create mode 100644 shell/hush_test/hush-quoting/quote_in_varexp1.right create mode 100755 shell/hush_test/hush-quoting/quote_in_varexp1.tests diff --git a/shell/hush_test/hush-quoting/quote_in_varexp1.right b/shell/hush_test/hush-quoting/quote_in_varexp1.right new file mode 100644 index 0000000000..99a0aea7cb --- /dev/null +++ b/shell/hush_test/hush-quoting/quote_in_varexp1.right @@ -0,0 +1,2 @@ +'' +Ok:0 diff --git a/shell/hush_test/hush-quoting/quote_in_varexp1.tests b/shell/hush_test/hush-quoting/quote_in_varexp1.tests new file mode 100755 index 0000000000..1b97b05566 --- /dev/null +++ b/shell/hush_test/hush-quoting/quote_in_varexp1.tests @@ -0,0 +1,2 @@ +x="''''"; echo "${x#"${x+''}"''}" +echo Ok:$? From eb54ca8be0b45a101f9bdcf6efa26645c6b94a08 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Tue, 7 Aug 2018 18:54:52 +0200 Subject: [PATCH 47/62] ash: expand: Do not quote backslashes in unquoted parameter expansion Upstream commit: Date: Wed, 28 Mar 2018 18:37:51 +0800 expand: Do not quote backslashes in unquoted parameter expansion Here is a better example: a="/*/\nullx" b="/*/\null"; printf "%s\n" $a $b dash currently prints /*/\nullx /*/\null bash prints /*/\nullx /dev/null You may argue the bash behaviour is inconsistent but it actually makes sense. What happens is that quote removal only applies to the original token as seen by the shell. It is never applied to the result of parameter expansion. Now you may ask why on earth does the second line say "/dev/null" instead of "/dev/\null". Well that's because it is not the quote removal step that removed the backslash, but the pathname expansion. The fact that the /de\v does not become /dev even though it exists is just the result of the optimisation to avoid unnecessarily calling stat(2). I have checked POSIX and I don't see anything that forbids this behaviour. So going back to dash yes I think we should adopt the bash behaviour for pathname expansion and keep the existing case semantics. This patch does exactly that. Note that this patch does not work unless you have already applied https://patchwork.kernel.org/patch/10306507/ because otherwise the optimisation mentioned above does not get detected correctly and we will end up doing quote removal twice. This patch also updates expmeta to handle naked backslashes at the end of the pattern which is now possible. Signed-off-by: Herbert Xu function old new delta expmeta 618 653 +35 memtodest 146 147 +1 Tested to work with both ASH_INTERNAL_GLOB on and off. hush does not handle this correctly. Signed-off-by: Denys Vlasenko --- shell/ash.c | 10 ++++------ shell/ash_test/ash-glob/glob_bkslash_in_var.right | 4 ++++ shell/ash_test/ash-glob/glob_bkslash_in_var.tests | 10 ++++++++++ shell/hush_test/hush-glob/glob_bkslash_in_var.right | 4 ++++ shell/hush_test/hush-glob/glob_bkslash_in_var.tests | 10 ++++++++++ 5 files changed, 32 insertions(+), 6 deletions(-) create mode 100644 shell/ash_test/ash-glob/glob_bkslash_in_var.right create mode 100755 shell/ash_test/ash-glob/glob_bkslash_in_var.tests create mode 100644 shell/hush_test/hush-glob/glob_bkslash_in_var.right create mode 100755 shell/hush_test/hush-glob/glob_bkslash_in_var.tests diff --git a/shell/ash.c b/shell/ash.c index ad50537a1a..dc1a55a6bf 100644 --- a/shell/ash.c +++ b/shell/ash.c @@ -6236,9 +6236,7 @@ memtodest(const char *p, size_t len, int syntax, int quotes) if (quotes & QUOTES_ESC) { int n = SIT(c, syntax); if (n == CCTL - || (((quotes & EXP_FULL) || syntax != BASESYNTAX) - && n == CBACK - ) + || (syntax != BASESYNTAX && n == CBACK) ) { USTPUTC(CTLESC, q); } @@ -7639,7 +7637,7 @@ expmeta(exp_t *exp, char *name, unsigned name_len, unsigned expdir_len) } } } else { - if (*p == '\\') + if (*p == '\\' && p[1]) esc++; if (p[esc] == '/') { if (metaflag) @@ -7653,7 +7651,7 @@ expmeta(exp_t *exp, char *name, unsigned name_len, unsigned expdir_len) return; p = name; do { - if (*p == '\\') + if (*p == '\\' && p[1]) p++; *enddir++ = *p; } while (*p++); @@ -7665,7 +7663,7 @@ expmeta(exp_t *exp, char *name, unsigned name_len, unsigned expdir_len) if (name < start) { p = name; do { - if (*p == '\\') + if (*p == '\\' && p[1]) p++; *enddir++ = *p++; } while (p < start); diff --git a/shell/ash_test/ash-glob/glob_bkslash_in_var.right b/shell/ash_test/ash-glob/glob_bkslash_in_var.right new file mode 100644 index 0000000000..f1484b9e41 --- /dev/null +++ b/shell/ash_test/ash-glob/glob_bkslash_in_var.right @@ -0,0 +1,4 @@ +Unquoted non-matching glob in var:'test*.TMP/\name_doesnt_exist' +Unquoted matching glob in var: 'testdir.TMP/name' +Quoted non-matching glob in var: 'test*.TMP/\name_doesnt_exist' +Quoted matching glob in var: 'test*.TMP/\name' diff --git a/shell/ash_test/ash-glob/glob_bkslash_in_var.tests b/shell/ash_test/ash-glob/glob_bkslash_in_var.tests new file mode 100755 index 0000000000..e3dedc4ac7 --- /dev/null +++ b/shell/ash_test/ash-glob/glob_bkslash_in_var.tests @@ -0,0 +1,10 @@ +mkdir testdir.TMP +>testdir.TMP/name +a="test*.TMP/\name_doesnt_exist" +b="test*.TMP/\name" +printf "Unquoted non-matching glob in var:'%s'\n" $a +printf "Unquoted matching glob in var: '%s'\n" $b +printf "Quoted non-matching glob in var: '%s'\n" "$a" +printf "Quoted matching glob in var: '%s'\n" "$b" +rm -f testdir.TMP/name +rmdir testdir.TMP diff --git a/shell/hush_test/hush-glob/glob_bkslash_in_var.right b/shell/hush_test/hush-glob/glob_bkslash_in_var.right new file mode 100644 index 0000000000..f1484b9e41 --- /dev/null +++ b/shell/hush_test/hush-glob/glob_bkslash_in_var.right @@ -0,0 +1,4 @@ +Unquoted non-matching glob in var:'test*.TMP/\name_doesnt_exist' +Unquoted matching glob in var: 'testdir.TMP/name' +Quoted non-matching glob in var: 'test*.TMP/\name_doesnt_exist' +Quoted matching glob in var: 'test*.TMP/\name' diff --git a/shell/hush_test/hush-glob/glob_bkslash_in_var.tests b/shell/hush_test/hush-glob/glob_bkslash_in_var.tests new file mode 100755 index 0000000000..e3dedc4ac7 --- /dev/null +++ b/shell/hush_test/hush-glob/glob_bkslash_in_var.tests @@ -0,0 +1,10 @@ +mkdir testdir.TMP +>testdir.TMP/name +a="test*.TMP/\name_doesnt_exist" +b="test*.TMP/\name" +printf "Unquoted non-matching glob in var:'%s'\n" $a +printf "Unquoted matching glob in var: '%s'\n" $b +printf "Quoted non-matching glob in var: '%s'\n" "$a" +printf "Quoted matching glob in var: '%s'\n" "$b" +rm -f testdir.TMP/name +rmdir testdir.TMP From 4bf08542480fe6271692cc9359f9747e9d727a79 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Sat, 11 Aug 2018 18:44:11 +0200 Subject: [PATCH 48/62] hush: add a comment on how globbing (should) work Signed-off-by: Denys Vlasenko --- shell/hush.c | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/shell/hush.c b/shell/hush.c index 3c19bceaa0..4b46752a33 100644 --- a/shell/hush.c +++ b/shell/hush.c @@ -3151,6 +3151,41 @@ static int o_get_last_ptr(o_string *o, int n) return ((int)(uintptr_t)list[n-1]) + string_start; } +/* + * Globbing routines. + * + * Most words in commands need to be globbed, even ones which are + * (single or double) quoted. This stems from the possiblity of + * constructs like "abc"* and 'abc'* - these should be globbed. + * Having a different code path for fully-quoted strings ("abc", + * 'abc') would only help performance-wise, but we still need + * code for partially-quoted strings. + * + * Unfortunately, if we want to match bash and ash behavior in all cases, + * the logic can't be see as "shell-syntax argument is first transformed + * to a string, then globbed, and if globbing does not match anything, + * it is used verbatim". Here are two examples where it fails: + * + * echo 'b\*'? + * + * The globbing can't be avoided (because of '?' at the end). + * The glob pattern is: b\\\*? - IOW, both \ and * are literals + * and are glob-escaped. If this does not match, bash/ash print b\*? + * - IOW: they "unbackslash" the pattern. + * Now, look at this: + * + * v='\\\*'; echo b$v? + * + * The glob pattern is the same here: b\\\*? - an unquoted $var expansion + * should be used as glob pattern with no changes. However, if glob + * does not match, bash/ash print b\\\*? - NOT THE SAME as 1st example! + * + * ash implements this by having an encoded representation of the word + * to glob, which IS NOT THE SAME as the glob pattern - it has more data. + * Glob pattern is derived from it. If glob fails, the decision what result + * should be is made using that encoded representation. Not glob pattern. + */ + #if ENABLE_HUSH_BRACE_EXPANSION /* There in a GNU extension, GLOB_BRACE, but it is not usable: * first, it processes even {a} (no commas), second, From c97df2939ec82bdc36586897e02416f935e89519 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Tue, 14 Aug 2018 11:04:58 +0200 Subject: [PATCH 49/62] hush: tweak comment, no code changes Signed-off-by: Denys Vlasenko --- shell/hush.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/shell/hush.c b/shell/hush.c index 4b46752a33..881331c5be 100644 --- a/shell/hush.c +++ b/shell/hush.c @@ -3162,7 +3162,7 @@ static int o_get_last_ptr(o_string *o, int n) * code for partially-quoted strings. * * Unfortunately, if we want to match bash and ash behavior in all cases, - * the logic can't be see as "shell-syntax argument is first transformed + * the logic can't be "shell-syntax argument is first transformed * to a string, then globbed, and if globbing does not match anything, * it is used verbatim". Here are two examples where it fails: * @@ -3171,14 +3171,14 @@ static int o_get_last_ptr(o_string *o, int n) * The globbing can't be avoided (because of '?' at the end). * The glob pattern is: b\\\*? - IOW, both \ and * are literals * and are glob-escaped. If this does not match, bash/ash print b\*? - * - IOW: they "unbackslash" the pattern. + * - IOW: they "unbackslash" the glob pattern. * Now, look at this: * * v='\\\*'; echo b$v? * - * The glob pattern is the same here: b\\\*? - an unquoted $var expansion + * The glob pattern is the same here: b\\\*? - the unquoted $v expansion * should be used as glob pattern with no changes. However, if glob - * does not match, bash/ash print b\\\*? - NOT THE SAME as 1st example! + * does not match, bash/ash print b\\\*? - NOT THE SAME as first example! * * ash implements this by having an encoded representation of the word * to glob, which IS NOT THE SAME as the glob pattern - it has more data. From 375951667287b1c6007dcab8809c13e1b4fec67a Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Tue, 14 Aug 2018 11:19:09 +0200 Subject: [PATCH 50/62] ip: tweak --help text Signed-off-by: Denys Vlasenko --- networking/ip.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/networking/ip.c b/networking/ip.c index 9ecb99abb3..97d618cd94 100644 --- a/networking/ip.c +++ b/networking/ip.c @@ -267,8 +267,7 @@ //--------------123456789.123456789.123456789.123456789.123456789.123456789.123456789.123....79 //usage:#define iptunnel_trivial_usage //usage: "add|change|del|show [NAME]\n" -//usage: " [mode ipip|gre|sit]\n" -//usage: " [remote ADDR] [local ADDR] [ttl TTL]" +//usage: " [mode ipip|gre|sit] [remote ADDR] [local ADDR] [ttl TTL]" //usage:#define iptunnel_full_usage "\n\n" //usage: "iptunnel add|change|del|show [NAME]\n" //usage: " [mode ipip|gre|sit] [remote ADDR] [local ADDR]\n" @@ -308,10 +307,9 @@ //usage: IF_FEATURE_IP_TUNNEL( IP_BAR_TUNNEL"tunnel") //usage: IF_FEATURE_IP_NEIGH( IP_BAR_NEIGH "neigh") //usage: IF_FEATURE_IP_RULE( IP_BAR_RULE "rule") -//usage: " [COMMAND]" +//usage: " [ARGS]" //usage:#define ip_full_usage "\n\n" //usage: "OPTIONS := -f[amily] inet|inet6|link | -o[neline]\n" -//usage: "COMMAND :=" //usage: IF_FEATURE_IP_ADDRESS("\n" //usage: "ip addr "ipaddr_trivial_usage) //usage: IF_FEATURE_IP_ROUTE("\n" From 78a5ef9d2c034b5a6314fb66279160331d25cc73 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Sat, 25 Aug 2018 19:36:06 +0200 Subject: [PATCH 51/62] ping: use setitimer() instead of ualarm() function old new delta sendping_tail 218 265 +47 ualarm 79 - -79 ------------------------------------------------------------------------------ (add/remove: 0/2 grow/shrink: 1/0 up/down: 47/-79) Total: -32 bytes Signed-off-by: Denys Vlasenko --- networking/ping.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/networking/ping.c b/networking/ping.c index a579ea4aeb..570184fee8 100644 --- a/networking/ping.c +++ b/networking/ping.c @@ -485,9 +485,15 @@ static void sendping_tail(void (*sp)(int), int size_pkt) bb_error_msg_and_die(bb_msg_write_error); if (pingcount == 0 || G.ntransmitted < pingcount) { - /* Didn't send all pings yet - schedule next in 1s */ + /* Didn't send all pings yet - schedule next in -i SEC interval */ + struct itimerval i; signal(SIGALRM, sp); - ualarm(G.interval_us, 0); + /*ualarm(G.interval_us, 0); - does not work for >=1sec on some libc */ + i.it_interval.tv_sec = 0; + i.it_interval.tv_usec = 0; + i.it_value.tv_sec = G.interval_us / 1000000; + i.it_value.tv_usec = G.interval_us % 1000000; + setitimer(ITIMER_REAL, &i, NULL); } else { /* -c NN, and all NN are sent */ /* Wait for the last ping to come back. * -W timeout: wait for a response in seconds. From 6791140123ebe7535f525f1a893a8536219122c4 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Sun, 26 Aug 2018 16:32:16 +0200 Subject: [PATCH 52/62] fix !CONFIG_FLOAT_DURATION build Signed-off-by: Denys Vlasenko --- libbb/duration.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libbb/duration.c b/libbb/duration.c index 2f272771f3..5acd0dba30 100644 --- a/libbb/duration.c +++ b/libbb/duration.c @@ -73,6 +73,6 @@ void FAST_FUNC sleep_for_duration(duration_t duration) #else duration_t FAST_FUNC parse_duration_str(char *str) { - return xatou_range_sfx(*argv, 0, UINT_MAX, duration_suffixes); + return xatou_range_sfx(str, 0, UINT_MAX, duration_suffixes); } #endif From 9d539f9fbd0dc4ea70ed8ba66e3c78150fa8a8b2 Mon Sep 17 00:00:00 2001 From: Peter Korsgaard Date: Thu, 9 Aug 2018 11:25:22 +0200 Subject: [PATCH 53/62] sysklogd: add timestamp option to ignore message timestamps Some syslog producers provide inconsistent timestamps, so provide an option to ignore the message timestamps and always locally timestamp. In order to implement this, invert the valid-timestamp check, but only use the timestamp if this option is not enabled. This is in line with what what other syslogd implementations do: From sysklogd syslogd.c: * Sun Nov 7 12:28:47 CET 2004: Martin Schulze * Discard any timestamp information found in received syslog * messages. This will affect local messages sent from a * different timezone. rsyslog's imuxsock module similary has an (enabled by default) IgnoreTimestamp option: https://www.rsyslog.com/doc/v8-stable/configuration/modules/imuxsock.html function old new delta packed_usage 32877 32912 +35 timestamp_and_log 363 376 +13 syslogd_main 1638 1641 +3 ------------------------------------------------------------------------------ (add/remove: 0/0 grow/shrink: 3/0 up/down: 51/0) Total: 51 bytes Signed-off-by: Peter Korsgaard Signed-off-by: Denys Vlasenko --- sysklogd/syslogd.c | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/sysklogd/syslogd.c b/sysklogd/syslogd.c index 4265f4f904..5630d97fc5 100644 --- a/sysklogd/syslogd.c +++ b/sysklogd/syslogd.c @@ -140,6 +140,7 @@ //usage: ) //usage: "\n -l N Log only messages more urgent than prio N (1-8)" //usage: "\n -S Smaller output" +//usage: "\n -t Strip client-generated timestamps" //usage: IF_FEATURE_SYSLOGD_DUP( //usage: "\n -D Drop duplicates" //usage: ) @@ -316,6 +317,7 @@ enum { OPTBIT_outfile, // -O OPTBIT_loglevel, // -l OPTBIT_small, // -S + OPTBIT_timestamp, // -t IF_FEATURE_ROTATE_LOGFILE(OPTBIT_filesize ,) // -s IF_FEATURE_ROTATE_LOGFILE(OPTBIT_rotatecnt ,) // -b IF_FEATURE_REMOTE_LOG( OPTBIT_remotelog ,) // -R @@ -330,6 +332,7 @@ enum { OPT_outfile = 1 << OPTBIT_outfile , OPT_loglevel = 1 << OPTBIT_loglevel, OPT_small = 1 << OPTBIT_small , + OPT_timestamp = 1 << OPTBIT_timestamp, OPT_filesize = IF_FEATURE_ROTATE_LOGFILE((1 << OPTBIT_filesize )) + 0, OPT_rotatecnt = IF_FEATURE_ROTATE_LOGFILE((1 << OPTBIT_rotatecnt )) + 0, OPT_remotelog = IF_FEATURE_REMOTE_LOG( (1 << OPTBIT_remotelog )) + 0, @@ -339,7 +342,7 @@ enum { OPT_cfg = IF_FEATURE_SYSLOGD_CFG( (1 << OPTBIT_cfg )) + 0, OPT_kmsg = IF_FEATURE_KMSG_SYSLOG( (1 << OPTBIT_kmsg )) + 0, }; -#define OPTION_STR "m:nO:l:S" \ +#define OPTION_STR "m:nO:l:St" \ IF_FEATURE_ROTATE_LOGFILE("s:" ) \ IF_FEATURE_ROTATE_LOGFILE("b:" ) \ IF_FEATURE_REMOTE_LOG( "R:*") \ @@ -813,21 +816,27 @@ static void parse_fac_prio_20(int pri, char *res20) * that there is no timestamp, short-circuiting the test. */ static void timestamp_and_log(int pri, char *msg, int len) { - char *timestamp; + char *timestamp = NULL; time_t now; /* Jan 18 00:11:22 msg... */ /* 01234567890123456 */ - if (len < 16 || msg[3] != ' ' || msg[6] != ' ' - || msg[9] != ':' || msg[12] != ':' || msg[15] != ' ' + if (len >= 16 && msg[3] == ' ' && msg[6] == ' ' + && msg[9] == ':' && msg[12] == ':' && msg[15] == ' ' ) { + if (!(option_mask32 & OPT_timestamp)) { + /* use message timestamp */ + timestamp = msg; + now = 0; + } + msg += 16; + } + + if (!timestamp) { time(&now); timestamp = ctime(&now) + 4; /* skip day of week */ - } else { - now = 0; - timestamp = msg; - msg += 16; } + timestamp[15] = '\0'; if (option_mask32 & OPT_kmsg) { From 9a2621a3b9243919921935f0e3eff6d942379e77 Mon Sep 17 00:00:00 2001 From: Andre Goddard Rosa Date: Sun, 26 Aug 2018 17:37:30 +0200 Subject: [PATCH 54/62] smemcap: keep outputting by ignoring transient processes function old new delta archivefile 167 176 +9 Signed-off-by: Andre Goddard Rosa Signed-off-by: Denys Vlasenko --- procps/smemcap.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/procps/smemcap.c b/procps/smemcap.c index 81f4cafad0..96b565f591 100644 --- a/procps/smemcap.c +++ b/procps/smemcap.c @@ -66,6 +66,10 @@ static void archivefile(const char *path) /* buffer the file */ fd = xopen(path, O_RDONLY); + if (fd == -1) { + /* skip vanished processes between dir listing and traversal */ + return; + } do { cur = xzalloc(sizeof(*cur)); *prev = cur; From 270becc2409e7b4c4e83b3f3272ff28d30805c55 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Thu, 30 Aug 2018 11:49:51 +0200 Subject: [PATCH 55/62] smemcap: keep outputting by ignoring transient processes, take two Signed-off-by: Denys Vlasenko --- procps/smemcap.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/procps/smemcap.c b/procps/smemcap.c index 96b565f591..2f8ab192e2 100644 --- a/procps/smemcap.c +++ b/procps/smemcap.c @@ -65,7 +65,7 @@ static void archivefile(const char *path) struct stat s; /* buffer the file */ - fd = xopen(path, O_RDONLY); + fd = open(path, O_RDONLY); if (fd == -1) { /* skip vanished processes between dir listing and traversal */ return; From 0d598ab9f03dbf320f7b81c05e4a94cb303dfbc7 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Sun, 2 Sep 2018 18:35:29 +0200 Subject: [PATCH 56/62] Revert "libbb: remove unnecessary variable in xmalloc_fgets" The variable is in fact necessary. commit 2da9724b56169f00bd7fb6b9a11c9409a7620981 Author: Quentin Rameau Date: Sun Apr 1 17:05:35 2018 +0200 libbb: remove unnecessary variable in xmalloc_fgets Signed-off-by: Denys Vlasenko --- libbb/get_line_from_file.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/libbb/get_line_from_file.c b/libbb/get_line_from_file.c index f3d6c62039..49ef093c25 100644 --- a/libbb/get_line_from_file.c +++ b/libbb/get_line_from_file.c @@ -47,7 +47,9 @@ char* FAST_FUNC bb_get_chunk_from_file(FILE *file, size_t *end) /* Get line, including trailing \n if any */ char* FAST_FUNC xmalloc_fgets(FILE *file) { - return bb_get_chunk_from_file(file, NULL); + int i; + + return bb_get_chunk_from_file(file, &i); } /* Get line. Remove trailing \n */ char* FAST_FUNC xmalloc_fgetline(FILE *file) From 22a99516206b33b7ae124d426319bab03d5c8309 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Sun, 2 Sep 2018 18:48:09 +0200 Subject: [PATCH 57/62] libbb: in xmalloc_fgets(), use size_t for bb_get_chunk_from_file() Signed-off-by: Denys Vlasenko --- libbb/get_line_from_file.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libbb/get_line_from_file.c b/libbb/get_line_from_file.c index 49ef093c25..903ff1fb68 100644 --- a/libbb/get_line_from_file.c +++ b/libbb/get_line_from_file.c @@ -47,7 +47,7 @@ char* FAST_FUNC bb_get_chunk_from_file(FILE *file, size_t *end) /* Get line, including trailing \n if any */ char* FAST_FUNC xmalloc_fgets(FILE *file) { - int i; + size_t i; return bb_get_chunk_from_file(file, &i); } From 3060992ec94722b4f8f3711a1884270c81a6e5f5 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Mon, 3 Sep 2018 10:25:29 +0200 Subject: [PATCH 58/62] libbb: fix use-after-free in copy_file Signed-off-by: Denys Vlasenko --- libbb/copy_file.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libbb/copy_file.c b/libbb/copy_file.c index 98bd4fe72c..2d6557cd44 100644 --- a/libbb/copy_file.c +++ b/libbb/copy_file.c @@ -388,14 +388,15 @@ int FAST_FUNC copy_file(const char *source, const char *dest, int flags) char *lpath = xmalloc_readlink_or_warn(source); if (lpath) { int r = symlink(lpath, dest); - free(lpath); if (r < 0) { /* shared message */ bb_perror_msg("can't create %slink '%s' to '%s'", "sym", dest, lpath ); + free(lpath); return -1; } + free(lpath); if (flags & FILEUTILS_PRESERVE_STATUS) if (lchown(dest, source_stat.st_uid, source_stat.st_gid) < 0) bb_perror_msg("can't preserve %s of '%s'", "ownership", dest); From 28d91d754e423fd0df584bbfa9b903eacac21224 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Mon, 3 Sep 2018 10:36:51 +0200 Subject: [PATCH 59/62] libbb: fix potential NULL pointer use function old new delta unicode_conv_to_printable2 193 216 +23 Signed-off-by: Denys Vlasenko --- libbb/unicode.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libbb/unicode.c b/libbb/unicode.c index 9c4da50d3b..d378175a4f 100644 --- a/libbb/unicode.c +++ b/libbb/unicode.c @@ -1121,6 +1121,8 @@ static char* FAST_FUNC unicode_conv_to_printable2(uni_stat_t *stats, const char dst[dst_len++] = ' '; } } + if (!dst) /* for example, if input was "" */ + dst = xzalloc(1); dst[dst_len] = '\0'; if (stats) { stats->byte_count = dst_len; From 8f1ae256347b32057d32846f915f53f9106f00bc Mon Sep 17 00:00:00 2001 From: sagivd Date: Sat, 25 Aug 2018 13:49:24 +0300 Subject: [PATCH 60/62] chcon: fix issues with recurse and retval for retained files - recurse is not acting as expected (looks like broken old code) - when not using verbose output, return value for retained files is 1, which is not expected. Signed-off-by: sagivd Signed-off-by: Denys Vlasenko --- selinux/chcon.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/selinux/chcon.c b/selinux/chcon.c index 3ddb2dd46d..92eb767375 100644 --- a/selinux/chcon.c +++ b/selinux/chcon.c @@ -131,8 +131,10 @@ static int FAST_FUNC change_filedir_context( bb_error_msg("can't change context of %s to %s", fname, context_string); } - } else if (option_mask32 & OPT_VERBOSE) { - printf("context of %s retained as %s\n", fname, context_string); + } else { + if (option_mask32 & OPT_VERBOSE) { + printf("context of %s retained as %s\n", fname, context_string); + } rc = TRUE; } skip: @@ -202,7 +204,7 @@ int chcon_main(int argc UNUSED_PARAM, char **argv) fname[fname_len] = '\0'; if (recursive_action(fname, - 1< Date: Tue, 4 Sep 2018 14:48:00 +0200 Subject: [PATCH 61/62] whois: fix a possible out-of-bounds stack access If fgets() returns incomplete string, we replace NUL with '\n', and then trim() runs on a non-NUL-terminated buffer. Prevent that. While at it, bump buffer from 1k to 2k. function old new delta query 519 524 +5 Signed-off-by: Denys Vlasenko --- networking/whois.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/networking/whois.c b/networking/whois.c index f0ec86301a..f3da32b4e1 100644 --- a/networking/whois.c +++ b/networking/whois.c @@ -39,20 +39,26 @@ static char *query(const char *host, int port, const char *domain) bool success; char *redir = NULL; const char *pfx = ""; - char linebuf[1024]; + /* some .io domains reported to have very long strings in whois + * responses, 1k was not enough: + */ + char linebuf[2 * 1024]; char *buf = NULL; unsigned bufpos = 0; again: printf("[Querying %s:%d '%s%s']\n", host, port, pfx, domain); fd = create_and_connect_stream_or_die(host, port); - success = 0; fdprintf(fd, "%s%s\r\n", pfx, domain); fp = xfdopen_for_read(fd); - while (fgets(linebuf, sizeof(linebuf), fp)) { - unsigned len = strcspn(linebuf, "\r\n"); + success = 0; + while (fgets(linebuf, sizeof(linebuf)-1, fp)) { + unsigned len; + + len = strcspn(linebuf, "\r\n"); linebuf[len++] = '\n'; + linebuf[len] = '\0'; buf = xrealloc(buf, bufpos + len + 1); memcpy(buf + bufpos, linebuf, len); From 05b18065ab9c375f6185b65a3631d4c6cc1a4be9 Mon Sep 17 00:00:00 2001 From: Chen Yu Date: Tue, 4 Sep 2018 15:26:22 +0800 Subject: [PATCH 62/62] remove_file: don't call rmdir if remove_file return failure When deleting a directory, the directory should not be removed if the file in the subdirectory fails to be deleted. Background information: When I tested the kernel using LTP (linux-test-project).I found the mv command have some issue. The LTP test case use the mv command to move the directory t1 in the cgroup file system to the /tmp directory. becase files in the cgroup file system are not allowed to be removed. so the mv reported "Permission denied", but I used the ls command to view the results and found that the directory t1 had been removed from the cgroup file system. For the same test case, I used the mv tool in the GNU coreutils, and the directory t1 will not be removed. the following testcase use busybox mv: / # mount -t cgroup -o cpu cgroup /cpu / # cd /cpu /cpu # mkdir -p t1 /cpu # ls cgroup.clone_children cpu.cfs_period_us cpu.stat t1 cgroup.procs cpu.cfs_quota_us notify_on_release tasks cgroup.sane_behavior cpu.shares release_agent /cpu # mv t1 /tmp mv: can't remove 't1/cgroup.procs': Operation not permitted mv: can't remove 't1/cpu.cfs_period_us': Operation not permitted mv: can't remove 't1/cpu.stat': Operation not permitted mv: can't remove 't1/cpu.shares': Operation not permitted mv: can't remove 't1/cpu.cfs_quota_us': Operation not permitted mv: can't remove 't1/tasks': Operation not permitted mv: can't remove 't1/notify_on_release': Operation not permitted mv: can't remove 't1/cgroup.clone_children': Operation not permitted /cpu # ls cgroup.clone_children cpu.cfs_period_us cpu.stat cgroup.procs cpu.cfs_quota_us notify_on_release tasks cgroup.sane_behavior cpu.shares release_agent /cpu # This patch fixed it, don't call rmdir if remove_file return failure, and under certain file systems, the mv could work normally. Signed-off-by: Chen Yu Signed-off-by: Denys Vlasenko --- libbb/remove_file.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libbb/remove_file.c b/libbb/remove_file.c index 86c9a5c56b..cea5d47e6f 100644 --- a/libbb/remove_file.c +++ b/libbb/remove_file.c @@ -73,7 +73,7 @@ int FAST_FUNC remove_file(const char *path, int flags) return status; } - if (rmdir(path) < 0) { + if (status == 0 && rmdir(path) < 0) { bb_perror_msg("can't remove '%s'", path); return -1; }