From 687f41f10bd5f493e82395e127f7d4d52b11d308 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Tue, 1 Jun 2021 00:19:03 +0200 Subject: udhcpc[6]: fix "untangle timeout and remaining lease" fallout As reported in bug 13776, before this fix the renew never times out. function old new delta udhcpc_main 2541 2585 +44 udhcpc6_main 2567 2558 -9 ------------------------------------------------------------------------------ (add/remove: 0/0 grow/shrink: 1/1 up/down: 44/-9) Total: 35 bytes Signed-off-by: Denys Vlasenko --- networking/udhcp/d6_dhcpc.c | 34 +++++++++++++++++++++------------- networking/udhcp/dhcpc.c | 43 ++++++++++++++++++++++++++----------------- 2 files changed, 47 insertions(+), 30 deletions(-) diff --git a/networking/udhcp/d6_dhcpc.c b/networking/udhcp/d6_dhcpc.c index 0a5cae310..5bca4a824 100644 --- a/networking/udhcp/d6_dhcpc.c +++ b/networking/udhcp/d6_dhcpc.c @@ -1356,14 +1356,17 @@ int udhcpc6_main(int argc UNUSED_PARAM, char **argv) log1("waiting %u seconds", timeout); diff = (unsigned)monotonic_sec(); retval = poll(pfds, 2, timeout * 1000); + diff = (unsigned)monotonic_sec() - diff; + lease_remaining -= diff; + if (lease_remaining < 0) + lease_remaining = 0; + timeout -= diff; + if (timeout < 0) + timeout = 0; + if (retval < 0) { /* EINTR? A signal was caught, don't panic */ if (errno == EINTR) { - diff = (unsigned)monotonic_sec() - diff; - lease_remaining -= diff; - if (lease_remaining < 0) - lease_remaining = 0; - timeout -= diff; continue; } /* Else: an error occured, panic! */ @@ -1455,7 +1458,6 @@ int udhcpc6_main(int argc UNUSED_PARAM, char **argv) case_RENEW_REQUESTED: case RENEWING: if (packet_num < 3) { - packet_num++; /* send an unicast renew request */ /* Sometimes observed to fail (EADDRNOTAVAIL) to bind * a new UDP socket for sending inside send_renew. @@ -1471,23 +1473,26 @@ int udhcpc6_main(int argc UNUSED_PARAM, char **argv) send_d6_renew(xid, &srv6_buf, requested_ipv6); timeout = discover_timeout; /* ^^^ used to be = lease_remaining / 2 - WAY too long */ + packet_num++; continue; } /* Timed out, enter rebinding state */ log1s("entering rebinding state"); client_data.state = REBINDING; + packet_num = 0; /* fall right through */ case REBINDING: /* Switch to bcast receive */ change_listen_mode(LISTEN_RAW); /* Lease is *really* about to run out, * try to find DHCP server using broadcast */ - if (lease_remaining > 0) { + if (lease_remaining > 0 && packet_num < 3) { if (opt & OPT_l) send_d6_info_request(xid); else /* send a broadcast renew request */ send_d6_renew(xid, /*server_ipv6:*/ NULL, requested_ipv6); timeout = discover_timeout; + packet_num++; continue; } /* Timed out, enter init state */ @@ -1809,12 +1814,9 @@ int udhcpc6_main(int argc UNUSED_PARAM, char **argv) start = monotonic_sec(); d6_run_script(packet.d6_options, packet_end, (client_data.state == REQUESTING ? "bound" : "renew")); - timeout = (unsigned)lease_remaining / 2; - timeout -= (unsigned)monotonic_sec() - start; - packet_num = 0; - - client_data.state = BOUND; - change_listen_mode(LISTEN_NONE); + lease_remaining -= (unsigned)monotonic_sec() - start; + if (lease_remaining < 0) + lease_remaining = 0; if (opt & OPT_q) { /* quit after lease */ goto ret0; } @@ -1827,6 +1829,12 @@ int udhcpc6_main(int argc UNUSED_PARAM, char **argv) opt = ((opt & ~OPT_b) | OPT_f); } #endif + +// BOUND_for_half_lease: + timeout = (unsigned)lease_remaining / 2; + client_data.state = BOUND; + change_listen_mode(LISTEN_NONE); + packet_num = 0; continue; /* back to main loop */ } continue; diff --git a/networking/udhcp/dhcpc.c b/networking/udhcp/dhcpc.c index 6666cbce6..ea06405ba 100644 --- a/networking/udhcp/dhcpc.c +++ b/networking/udhcp/dhcpc.c @@ -1417,14 +1417,17 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv) log1("waiting %u seconds", timeout); diff = (unsigned)monotonic_sec(); retval = poll(pfds, 2, timeout * 1000); + diff = (unsigned)monotonic_sec() - diff; + lease_remaining -= diff; + if (lease_remaining < 0) + lease_remaining = 0; + timeout -= diff; + if (timeout < 0) + timeout = 0; + if (retval < 0) { /* EINTR? A signal was caught, don't panic */ if (errno == EINTR) { - diff = (unsigned)monotonic_sec() - diff; - lease_remaining -= diff; - if (lease_remaining < 0) - lease_remaining = 0; - timeout -= diff; continue; } /* Else: an error occurred, panic! */ @@ -1513,7 +1516,6 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv) case_RENEW_REQUESTED: case RENEWING: if (packet_num < 3) { - packet_num++; /* send an unicast renew request */ /* Sometimes observed to fail (EADDRNOTAVAIL) to bind * a new UDP socket for sending inside send_renew. @@ -1526,6 +1528,7 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv) if (send_renew(xid, server_addr, requested_ip) >= 0) { timeout = discover_timeout; /* ^^^ used to be = lease_remaining / 2 - WAY too long */ + packet_num++; continue; } /* else: error sending. @@ -1534,21 +1537,26 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv) * which wasn't reachable (and probably did not exist). */ } -//TODO: if 3 renew's failed (no reply) but remaining lease is large, -//it might make sense to make a large pause (~1 hour?) and try later? +//TODO: if 3 renew's failed (no reply) but remaining lease is large enough, +//it might make sense to go back to BOUND and try later? a-la +// if (lease_remaining > 30) goto BOUND_for_half_lease; +//If we do that, "packet_num < 3" test below might be superfluous +//(lease_remaining will run out anyway) /* Timed out or error, enter rebinding state */ log1s("entering rebinding state"); client_data.state = REBINDING; + packet_num = 0; /* fall right through */ case REBINDING: /* Switch to bcast receive */ change_listen_mode(LISTEN_RAW); /* Lease is *really* about to run out, * try to find DHCP server using broadcast */ - if (lease_remaining > 0) { + if (lease_remaining > 0 && packet_num < 3) { /* send a broadcast renew request */ send_renew(xid, 0 /*INADDR_ANY*/, requested_ip); timeout = discover_timeout; + packet_num++; continue; } /* Timed out, enter init state */ @@ -1773,13 +1781,9 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv) /* enter bound state */ start = monotonic_sec(); udhcp_run_script(&packet, client_data.state == REQUESTING ? "bound" : "renew"); - timeout = (unsigned)lease_remaining / 2; -//TODO: why / 2? - timeout -= (unsigned)monotonic_sec() - start; - packet_num = 0; - - client_data.state = BOUND; - change_listen_mode(LISTEN_NONE); + lease_remaining -= (unsigned)monotonic_sec() - start; + if (lease_remaining < 0) + lease_remaining = 0; if (opt & OPT_q) { /* quit after lease */ goto ret0; } @@ -1792,9 +1796,14 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv) opt = ((opt & ~OPT_b) | OPT_f); } #endif + +// BOUND_for_half_lease: + timeout = (unsigned)lease_remaining / 2; + client_data.state = BOUND; + change_listen_mode(LISTEN_NONE); /* make future renew packets use different xid */ /* xid = random_xid(); ...but why bother? */ - + packet_num = 0; continue; /* back to main loop */ } if (*message == DHCPNAK) { -- cgit v1.2.3-55-g6feb From b9258b86a7985a45921696ede192c51cb9eb52be Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Wed, 2 Jun 2021 04:01:10 +0200 Subject: head,tail: trim --help text function old new delta packed_usage 33598 33560 -38 Signed-off-by: Denys Vlasenko --- coreutils/head.c | 13 +++++++------ coreutils/tail.c | 17 +++++++++-------- 2 files changed, 16 insertions(+), 14 deletions(-) diff --git a/coreutils/head.c b/coreutils/head.c index efb023c6f..9586f869f 100644 --- a/coreutils/head.c +++ b/coreutils/head.c @@ -29,17 +29,18 @@ //usage:#define head_trivial_usage //usage: "[OPTIONS] [FILE]..." //usage:#define head_full_usage "\n\n" -//usage: "Print first 10 lines of FILEs (or stdin) to stdout.\n" +//usage: "Print first 10 lines of FILEs (or stdin).\n" //usage: "With more than one FILE, precede each with a filename header.\n" -//usage: "\n -n N[kbm] Print first N lines" +//usage: "\n -n N[bkm] Print first N lines" +//usage: IF_FEATURE_FANCY_HEAD( +//usage: "\n -n -N[bkm] Print all except N last lines" +//usage: "\n -c [-]N[bkm] Print first N bytes" +//usage: ) +//usage: "\n (b:*512 k:*1024 m:*1024^2)" //usage: IF_FEATURE_FANCY_HEAD( -//usage: "\n -n -N[kbm] Print all except N last lines" -//usage: "\n -c [-]N[kbm] Print first N bytes" //usage: "\n -q Never print headers" //usage: "\n -v Always print headers" //usage: ) -//usage: "\n" -//usage: "\nN may be suffixed by k (x1024), b (x512), or m (x1024^2)." //usage: //usage:#define head_example_usage //usage: "$ head -n 2 /etc/passwd\n" diff --git a/coreutils/tail.c b/coreutils/tail.c index 08fde6cdd..4602f4d42 100644 --- a/coreutils/tail.c +++ b/coreutils/tail.c @@ -48,19 +48,20 @@ //usage:#define tail_trivial_usage //usage: "[OPTIONS] [FILE]..." //usage:#define tail_full_usage "\n\n" -//usage: "Print last 10 lines of FILEs (or stdin) to stdout.\n" +//usage: "Print last 10 lines of FILEs (or stdin) to.\n" //usage: "With more than one FILE, precede each with a filename header.\n" -//usage: "\n -f Print data as file grows" -//usage: "\n -c [+]N[kbm] Print last N bytes" -//usage: "\n -n N[kbm] Print last N lines" -//usage: "\n -n +N[kbm] Start on Nth line and print the rest" +//usage: "\n -c [+]N[bkm] Print last N bytes" +//usage: "\n -n N[bkm] Print last N lines" +//usage: "\n -n +N[bkm] Start on Nth line and print the rest" +//usage: "\n (b:*512 k:*1024 m:*1024^2)" //usage: IF_FEATURE_FANCY_TAIL( //usage: "\n -q Never print headers" -//usage: "\n -s SECONDS Wait SECONDS between reads with -f" //usage: "\n -v Always print headers" +//usage: ) +//usage: "\n -f Print data as file grows" +//usage: IF_FEATURE_FANCY_TAIL( //usage: "\n -F Same as -f, but keep retrying" -//usage: "\n" -//usage: "\nN may be suffixed by k (x1024), b (x512), or m (x1024^2)." +//usage: "\n -s SECONDS Wait SECONDS between reads with -f" //usage: ) //usage: //usage:#define tail_example_usage -- cgit v1.2.3-55-g6feb From e0ea125ce24ebdd1febf930fca436f1a8fb7d48d Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Wed, 2 Jun 2021 04:11:40 +0200 Subject: tail: fix typo in variable name Signed-off-by: Denys Vlasenko --- coreutils/tail.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/coreutils/tail.c b/coreutils/tail.c index 4602f4d42..93f1514d0 100644 --- a/coreutils/tail.c +++ b/coreutils/tail.c @@ -119,7 +119,7 @@ int tail_main(int argc, char **argv) char *tailbuf; size_t tailbufsize; - unsigned header_threshhold = 1; + unsigned header_threshold = 1; unsigned nfiles; int i, opt; @@ -152,10 +152,10 @@ int tail_main(int argc, char **argv) if (opt & 0x2) count = eat_num(str_c); // -c if (opt & 0x4) count = eat_num(str_n); // -n #if ENABLE_FEATURE_FANCY_TAIL - /* q: make it impossible for nfiles to be > header_threshhold */ - if (opt & 0x8) header_threshhold = UINT_MAX; // -q + /* q: make it impossible for nfiles to be > header_threshold */ + if (opt & 0x8) header_threshold = UINT_MAX; // -q //if (opt & 0x10) // -s - if (opt & 0x20) header_threshhold = 0; // -v + if (opt & 0x20) header_threshold = 0; // -v # define FOLLOW_RETRY (opt & 0x40) #else # define FOLLOW_RETRY 0 @@ -216,7 +216,7 @@ int tail_main(int argc, char **argv) if (ENABLE_FEATURE_FANCY_TAIL && fd < 0) continue; /* may happen with -F */ - if (nfiles > header_threshhold) { + if (nfiles > header_threshold) { tail_xprint_header(fmt, argv[i]); fmt = header_fmt_str; } @@ -373,7 +373,7 @@ int tail_main(int argc, char **argv) } if (ENABLE_FEATURE_FANCY_TAIL && fd < 0) continue; - if (nfiles > header_threshhold) { + if (nfiles > header_threshold) { fmt = header_fmt_str; } for (;;) { -- cgit v1.2.3-55-g6feb From f193aeac1fe27c94d0f0b27314777df41480feb6 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Wed, 2 Jun 2021 04:55:10 +0200 Subject: tail: do not lose the tail of old file if new file (-F) is detected function old new delta tail_main 1619 1645 +26 .rodata 103246 103250 +4 ------------------------------------------------------------------------------ (add/remove: 0/0 grow/shrink: 2/0 up/down: 30/0) Total: 30 bytes Signed-off-by: Denys Vlasenko --- coreutils/tail.c | 38 ++++++++++++++++++++++++++------------ 1 file changed, 26 insertions(+), 12 deletions(-) diff --git a/coreutils/tail.c b/coreutils/tail.c index 93f1514d0..6201eb023 100644 --- a/coreutils/tail.c +++ b/coreutils/tail.c @@ -346,9 +346,11 @@ int tail_main(int argc, char **argv) int nread; const char *filename = argv[i]; int fd = fds[i]; + int new_fd = -1; + struct stat sbuf; if (FOLLOW_RETRY) { - struct stat sbuf, fsbuf; + struct stat fsbuf; if (fd < 0 || fstat(fd, &fsbuf) < 0 @@ -356,19 +358,21 @@ int tail_main(int argc, char **argv) || fsbuf.st_dev != sbuf.st_dev || fsbuf.st_ino != sbuf.st_ino ) { - int new_fd; - - if (fd >= 0) - close(fd); + /* Looks like file has been created/renamed/deleted */ new_fd = open(filename, O_RDONLY); if (new_fd >= 0) { bb_error_msg("%s has %s; following end of new file", filename, (fd < 0) ? "appeared" : "been replaced" ); + if (fd < 0) { + /* No previously open fd for this file, + * start using new_fd immediately. */ + fds[i] = fd = new_fd; + new_fd = -1; + } } else if (fd >= 0) { - bb_perror_msg("%s has become inaccessible", filename); + bb_perror_msg("%s has been renamed or deleted", filename); } - fds[i] = fd = new_fd; } } if (ENABLE_FEATURE_FANCY_TAIL && fd < 0) @@ -378,17 +382,27 @@ int tail_main(int argc, char **argv) } for (;;) { /* tail -f keeps following files even if they are truncated */ - struct stat sbuf; /* /proc files report zero st_size, don't lseek them */ - if (fstat(fd, &sbuf) == 0 && sbuf.st_size > 0) { + if (fstat(fd, &sbuf) == 0 + /* && S_ISREG(sbuf.st_mode) TODO? */ + && sbuf.st_size > 0 + ) { off_t current = lseek(fd, 0, SEEK_CUR); - if (sbuf.st_size < current) + if (sbuf.st_size < current) { + //bb_perror_msg("%s: file truncated", filename); - says coreutils 8.32 xlseek(fd, 0, SEEK_SET); + } } nread = tail_read(fd, tailbuf, BUFSIZ); - if (nread <= 0) - break; + if (nread <= 0) { + if (new_fd < 0) + break; + /* Switch to "tail -F"ing the new file */ + xmove_fd(new_fd, fd); + new_fd = -1; + continue; + } if (fmt && (fd != prev_fd)) { tail_xprint_header(fmt, filename); fmt = NULL; -- cgit v1.2.3-55-g6feb From d95f89ec576c5a0ecba24ead7f012f1fd8ea7b9b Mon Sep 17 00:00:00 2001 From: Ron Yorston Date: Thu, 20 May 2021 08:26:47 +0100 Subject: vi: make cursor positioning more vi compatible Commit 24effc7a3 (vi: cursor positioning after whole-line 'y') tried to save a few bytes by treating whole-line deletion the same as whole-line yank. If the deletion removed the last lines of the file the cursor was left beyond the end of the file. Revert the part of the commit related to whole-line deletion. Position the cursor on the first non-whitespace character of the line when whole lines are 'put'. function old new delta do_cmd 4759 4781 +22 ------------------------------------------------------------------------------ (add/remove: 0/0 grow/shrink: 1/0 up/down: 22/0) Total: 22 bytes Signed-off-by: Ron Yorston Signed-off-by: Denys Vlasenko --- editors/vi.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/editors/vi.c b/editors/vi.c index 4a7f8c3c2..c4f3b9660 100644 --- a/editors/vi.c +++ b/editors/vi.c @@ -3717,6 +3717,7 @@ static void do_cmd(int c) # endif } while (--cmdcnt > 0); dot += cnt; + dot_skip_over_ws(); # if ENABLE_FEATURE_VI_YANKMARK && ENABLE_FEATURE_VI_VERBOSE_STATUS yank_status("Put", p, i); # endif @@ -4172,6 +4173,9 @@ static void do_cmd(int c) if (dot != (end-1)) { dot_prev(); } + } else if (c == 'd') { + dot_begin(); + dot_skip_over_ws(); } else { dot = save_dot; } -- cgit v1.2.3-55-g6feb From 16e2fa9049d5ddd7d4c8aea875dad91e07868685 Mon Sep 17 00:00:00 2001 From: Ron Yorston Date: Thu, 20 May 2021 08:27:19 +0100 Subject: vi: make autoindent respect expandtab setting Autoindent took a copy of the indent from a neighbouring line, which may not have respected the expandtab setting. Determine the target column and construct a suitable indent. This will consist entirely of spaces if expandtab is enabled or an efficient combination of tabs and spaces otherwise. function old new delta char_insert 719 741 +22 ------------------------------------------------------------------------------ (add/remove: 0/0 grow/shrink: 1/0 up/down: 22/0) Total: 22 bytes Signed-off-by: Ron Yorston Signed-off-by: Denys Vlasenko --- editors/vi.c | 32 +++++++++++++++++++------------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/editors/vi.c b/editors/vi.c index c4f3b9660..36116e677 100644 --- a/editors/vi.c +++ b/editors/vi.c @@ -2111,6 +2111,7 @@ static char *char_insert(char *p, char c, int undo) // insert the char c at 'p' #if ENABLE_FEATURE_VI_SETOPTS char *q; size_t len; + int col, ntab, nspc; #endif if (c == 22) { // Is this an ctrl-V? @@ -2151,7 +2152,7 @@ static char *char_insert(char *p, char c, int undo) // insert the char c at 'p' } #if ENABLE_FEATURE_VI_SETOPTS } else if (c == '\t' && expandtab) { // expand tab - int col = get_column(p); + col = get_column(p); col = next_tabstop(col) - col + 1; while (col--) { # if ENABLE_FEATURE_VI_UNDO @@ -2186,23 +2187,28 @@ static char *char_insert(char *p, char c, int undo) // insert the char c at 'p' showmatching(p - 1); } if (autoindent && c == '\n') { // auto indent the new line - // use current/previous line as template + // use indent of current/previous line q = openabove ? p : prev_line(p); len = strspn(q, " \t"); // space or tab - if (openabove) { - p--; // this replaces dot_prev() in do_cmd() - q += len; // template will be shifted by text_hole_make() - } + if (openabove) + p--; // indent goes before newly inserted NL if (len) { - uintptr_t bias; - bias = text_hole_make(p, len); - p += bias; - q += bias; + col = get_column(q + len); + if (expandtab) { + ntab = 0; + nspc = col; + } else { + ntab = col / tabstop; + nspc = col % tabstop; + } + p += text_hole_make(p, ntab + nspc); # if ENABLE_FEATURE_VI_UNDO - undo_push_insert(p, len, undo); + undo_push_insert(p, ntab + nspc, undo); # endif - memcpy(p, q, len); - p += len; + memset(p, '\t', ntab); + p += ntab; + memset(p, ' ', nspc); + p += nspc; } } #endif -- cgit v1.2.3-55-g6feb From 9659a8db1dd28bdf8659fdae5d097b6f48bd2736 Mon Sep 17 00:00:00 2001 From: Ron Yorston Date: Thu, 20 May 2021 08:27:48 +0100 Subject: vi: remove autoindent from otherwise empty lines Lines that have no content apart from automatic indentation should be treated as empty when the user hits return or ESC. The implementation uses the global variable 'indentcol'. Usually this is zero. It can also be -1 to indicate an 'O' (open above) command, replacing the overloading of the tabstop option bit. A value greater than zero indicates that the current line has been autoindented to the given column (or that the autoindent has been adjusted with ctrl-D). Any other change to the line resets 'indentcol' to zero. Replace strspn() with ident_len(). The latter handles the unlikely case that it's called on the last line of a file which doesn't have a terminating newline. function old new delta char_insert 741 912 +171 indent_len - 42 +42 do_cmd 4781 4785 +4 ------------------------------------------------------------------------------ (add/remove: 1/0 grow/shrink: 2/0 up/down: 217/0) Total: 217 bytes Signed-off-by: Ron Yorston Signed-off-by: Denys Vlasenko --- editors/vi.c | 85 +++++++++++++++++++++++++++++++++++++++++------------------- 1 file changed, 59 insertions(+), 26 deletions(-) diff --git a/editors/vi.c b/editors/vi.c index 36116e677..d85cdd98d 100644 --- a/editors/vi.c +++ b/editors/vi.c @@ -307,7 +307,6 @@ struct globals { #define err_method (vi_setops & VI_ERR_METHOD) // indicate error with beep or flash #define ignorecase (vi_setops & VI_IGNORECASE) #define showmatch (vi_setops & VI_SHOWMATCH ) -#define openabove (vi_setops & VI_TABSTOP ) // order of constants and strings must match #define OPTS_STR \ "ai\0""autoindent\0" \ @@ -316,15 +315,10 @@ struct globals { "ic\0""ignorecase\0" \ "sm\0""showmatch\0" \ "ts\0""tabstop\0" -#define set_openabove() (vi_setops |= VI_TABSTOP) -#define clear_openabove() (vi_setops &= ~VI_TABSTOP) #else #define autoindent (0) #define expandtab (0) #define err_method (0) -#define openabove (0) -#define set_openabove() ((void)0) -#define clear_openabove() ((void)0) #endif #if ENABLE_FEATURE_VI_READONLY @@ -380,6 +374,9 @@ struct globals { #if ENABLE_FEATURE_VI_SEARCH char *last_search_pattern; // last pattern from a '/' or '?' search #endif +#if ENABLE_FEATURE_VI_SETOPTS + int indentcol; // column of recently autoindent, 0 or -1 +#endif // former statics #if ENABLE_FEATURE_VI_YANKMARK @@ -503,6 +500,7 @@ struct globals { #define ioq_start (G.ioq_start ) #define dotcnt (G.dotcnt ) #define last_search_pattern (G.last_search_pattern) +#define indentcol (G.indentcol ) #define edit_file__cur_line (G.edit_file__cur_line) #define refresh__old_offset (G.refresh__old_offset) @@ -2103,16 +2101,26 @@ static uintptr_t stupid_insert(char *p, char c) // stupidly insert the char c at return bias; } +// find number of characters in indent, p must be at beginning of line +static size_t indent_len(char *p) +{ + char *r = p; + + while (r < (end - 1) && isblank(*r)) + r++; + return r - p; +} + #if !ENABLE_FEATURE_VI_UNDO #define char_insert(a,b,c) char_insert(a,b) #endif static char *char_insert(char *p, char c, int undo) // insert the char c at 'p' { #if ENABLE_FEATURE_VI_SETOPTS - char *q; size_t len; int col, ntab, nspc; #endif + char *bol = begin_line(p); if (c == 22) { // Is this an ctrl-V? p += stupid_insert(p, '^'); // use ^ to indicate literal next @@ -2134,22 +2142,33 @@ static char *char_insert(char *p, char c, int undo) // insert the char c at 'p' if ((p[-1] != '\n') && (dot > text)) { p--; } - } else if (c == 4) { // ctrl-D reduces indentation - int prev; - char *r, *bol; - bol = begin_line(p); - for (r = bol; r < end_line(p); ++r) { - if (!isblank(*r)) - break; +#if ENABLE_FEATURE_VI_SETOPTS + if (autoindent) { + len = indent_len(bol); + if (len && get_column(bol + len) == indentcol) { + // remove autoindent from otherwise empty line + text_hole_delete(bol, bol + len - 1, undo); + p = bol; + } } - - prev = prev_tabstop(get_column(r)); +#endif + } else if (c == 4) { // ctrl-D reduces indentation + char *r = bol + indent_len(bol); + int prev = prev_tabstop(get_column(r)); while (r > bol && get_column(r) > prev) { if (p > bol) p--; r--; r = text_hole_delete(r, r, ALLOW_UNDO_QUEUED); } + +#if ENABLE_FEATURE_VI_SETOPTS + if (autoindent && indentcol && r == end_line(p)) { + // record changed size of autoindent + indentcol = get_column(p); + return p; + } +#endif #if ENABLE_FEATURE_VI_SETOPTS } else if (c == '\t' && expandtab) { // expand tab col = get_column(p); @@ -2188,12 +2207,23 @@ static char *char_insert(char *p, char c, int undo) // insert the char c at 'p' } if (autoindent && c == '\n') { // auto indent the new line // use indent of current/previous line - q = openabove ? p : prev_line(p); - len = strspn(q, " \t"); // space or tab - if (openabove) - p--; // indent goes before newly inserted NL + bol = indentcol < 0 ? p : prev_line(p); + len = indent_len(bol); + col = get_column(bol + len); + + if (len && col == indentcol) { + // previous line was empty except for autoindent + // move the indent to the current line + memmove(bol + 1, bol, len); + *bol = '\n'; + return p; + } + + if (indentcol < 0) + p--; // open above, indent before newly inserted NL + if (len) { - col = get_column(q + len); + indentcol = col; if (expandtab) { ntab = 0; nspc = col; @@ -2208,11 +2238,14 @@ static char *char_insert(char *p, char c, int undo) // insert the char c at 'p' memset(p, '\t', ntab); p += ntab; memset(p, ' ', nspc); - p += nspc; + return p + nspc; } } #endif } +#if ENABLE_FEATURE_VI_SETOPTS + indentcol = 0; +#endif return p; } @@ -2587,7 +2620,6 @@ static void setops(char *args, int flg_no) index = 1 << (index >> 1); // convert to VI_bit if (index & VI_TABSTOP) { - // don't set this bit in vi_setops, it's reused as 'openabove' int t; if (!eq || flg_no) // no "=NNN" or it is "notabstop"? goto bad; @@ -4050,17 +4082,18 @@ static void do_cmd(int c) break; case 'O': // O- open an empty line above dot_begin(); - set_openabove(); +#if ENABLE_FEATURE_VI_SETOPTS + indentcol = -1; +#endif goto dc3; case 'o': // o- open an empty line below dot_end(); dc3: dot = char_insert(dot, '\n', ALLOW_UNDO); if (c == 'O' && !autoindent) { - // done in char_insert() for openabove+autoindent + // done in char_insert() for 'O'+autoindent dot_prev(); } - clear_openabove(); goto dc_i; break; case 'R': // R- continuous Replace char -- cgit v1.2.3-55-g6feb From 265fcddd08f22c99a2a419a1537c18f4d6d43e9f Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Wed, 2 Jun 2021 13:50:26 +0200 Subject: udhcpc: include client-id option in DECLINEs, even if it's a custom -x 61:HEX option client_data.vendorclass, .hostname and .fqdn probably need the same treatment: just insert them into the list of -x opts, get rid of if (client_data.vendorclass) udhcp_add_binary_option(packet, client_data.vendorclass); if (client_data.hostname) udhcp_add_binary_option(packet, client_data.hostname); if (client_data.fqdn) udhcp_add_binary_option(packet, client_data.fqdn); function old new delta udhcp_insert_new_option - 166 +166 perform_release 171 207 +36 perform_d6_release 227 259 +32 udhcpc6_main 2558 2580 +22 init_d6_packet 103 84 -19 udhcpc_main 2585 2564 -21 attach_option 397 253 -144 ------------------------------------------------------------------------------ (add/remove: 1/0 grow/shrink: 3/3 up/down: 256/-184) Total: 72 bytes Signed-off-by: Denys Vlasenko --- networking/udhcp/common.c | 63 +++++++++++++++++++++++++++------------------ networking/udhcp/common.h | 11 ++++++++ networking/udhcp/d6_dhcpc.c | 58 +++++++++++++++++++++-------------------- networking/udhcp/dhcpc.c | 56 ++++++++++++++++++++++++---------------- networking/udhcp/dhcpc.h | 4 +-- 5 files changed, 115 insertions(+), 77 deletions(-) diff --git a/networking/udhcp/common.c b/networking/udhcp/common.c index f2d6907ad..684d76b2b 100644 --- a/networking/udhcp/common.c +++ b/networking/udhcp/common.c @@ -420,6 +420,43 @@ int FAST_FUNC udhcp_str2nip(const char *str, void *arg) return 1; } +void* FAST_FUNC udhcp_insert_new_option( + struct option_set **opt_list, + unsigned code, + const void *buffer, + unsigned length, + bool dhcpv6) +{ + IF_NOT_UDHCPC6(bool dhcpv6 = 0;) + struct option_set *new, **curr; + + log2("attaching option %02x to list", code); + new = xmalloc(sizeof(*new)); + if (!dhcpv6) { + new->data = xmalloc(length + OPT_DATA); + new->data[OPT_CODE] = code; + new->data[OPT_LEN] = length; + memcpy(new->data + OPT_DATA, buffer, length); + } else { + new->data = xmalloc(length + D6_OPT_DATA); + new->data[D6_OPT_CODE] = code >> 8; + new->data[D6_OPT_CODE + 1] = code & 0xff; + new->data[D6_OPT_LEN] = length >> 8; + new->data[D6_OPT_LEN + 1] = length & 0xff; + memcpy(new->data + D6_OPT_DATA, buffer, length); + } + + curr = opt_list; +//FIXME: DHCP6 codes > 255!! + while (*curr && (*curr)->data[OPT_CODE] < code) + curr = &(*curr)->next; + + new->next = *curr; + *curr = new; + + return new->data; +} + /* udhcp_str2optset: * Parse string option representation to binary form and add it to opt_list. * Called to parse "udhcpc -x OPTNAME:OPTVAL" @@ -459,32 +496,8 @@ static NOINLINE void attach_option( existing = udhcp_find_option(*opt_list, optflag->code); if (!existing) { - struct option_set *new, **curr; - /* make a new option */ - log2("attaching option %02x to list", optflag->code); - new = xmalloc(sizeof(*new)); - if (!dhcpv6) { - new->data = xmalloc(length + OPT_DATA); - new->data[OPT_CODE] = optflag->code; - new->data[OPT_LEN] = length; - memcpy(new->data + OPT_DATA, buffer, length); - } else { - new->data = xmalloc(length + D6_OPT_DATA); - new->data[D6_OPT_CODE] = optflag->code >> 8; - new->data[D6_OPT_CODE + 1] = optflag->code & 0xff; - new->data[D6_OPT_LEN] = length >> 8; - new->data[D6_OPT_LEN + 1] = length & 0xff; - memcpy(new->data + D6_OPT_DATA, buffer, - length); - } - - curr = opt_list; - while (*curr && (*curr)->data[OPT_CODE] < optflag->code) - curr = &(*curr)->next; - - new->next = *curr; - *curr = new; + udhcp_insert_new_option(opt_list, optflag->code, buffer, length, dhcpv6); goto ret; } diff --git a/networking/udhcp/common.h b/networking/udhcp/common.h index cc0abd269..e5af62874 100644 --- a/networking/udhcp/common.h +++ b/networking/udhcp/common.h @@ -319,6 +319,17 @@ void udhcp_dump_packet(struct dhcp_packet *packet) FAST_FUNC; /* 2nd param is "uint32_t*" */ int FAST_FUNC udhcp_str2nip(const char *str, void *arg); + +#if !ENABLE_UDHCPC6 +#define udhcp_insert_new_option(opt_list, code, buffer, length, dhcpv6) \ + udhcp_insert_new_option(opt_list, code, buffer, length) +#endif +void* FAST_FUNC udhcp_insert_new_option(struct option_set **opt_list, + unsigned code, + const void *buffer, + unsigned length, + bool dhcpv6); + /* 2nd param is "struct option_set**" */ #if !ENABLE_UDHCPC6 #define udhcp_str2optset(str, arg, optflags, option_strings, dhcpv6) \ diff --git a/networking/udhcp/d6_dhcpc.c b/networking/udhcp/d6_dhcpc.c index 5bca4a824..c4bedb259 100644 --- a/networking/udhcp/d6_dhcpc.c +++ b/networking/udhcp/d6_dhcpc.c @@ -482,7 +482,6 @@ static ALWAYS_INLINE uint32_t random_xid(void) static uint8_t *init_d6_packet(struct d6_packet *packet, char type, uint32_t xid) { uint8_t *ptr; - struct d6_option *clientid; unsigned secs; memset(packet, 0, sizeof(*packet)); @@ -503,9 +502,7 @@ static uint8_t *init_d6_packet(struct d6_packet *packet, char type, uint32_t xid *((uint16_t*)ptr) = (secs < 0xffff) ? htons(secs) : 0xffff; ptr += 2; - /* add CLIENTID option */ - clientid = (void*)client_data.clientid; - return mempcpy(ptr, clientid, clientid->len + 2+2); + return ptr; } static uint8_t *add_d6_client_options(uint8_t *ptr) @@ -593,10 +590,10 @@ static NOINLINE int send_d6_info_request(uint32_t xid) struct d6_packet packet; uint8_t *opt_ptr; - /* Fill in: msg type, client id */ + /* Fill in: msg type */ opt_ptr = init_d6_packet(&packet, D6_MSG_INFORMATION_REQUEST, xid); - /* Add options: + /* Add options: client-id, * "param req" option according to -O, options specified with -x */ opt_ptr = add_d6_client_options(opt_ptr); @@ -693,7 +690,7 @@ static NOINLINE int send_d6_discover(uint32_t xid, struct in6_addr *requested_ip uint8_t *opt_ptr; unsigned len; - /* Fill in: msg type, client id */ + /* Fill in: msg type */ opt_ptr = init_d6_packet(&packet, D6_MSG_SOLICIT, xid); /* Create new IA_NA, optionally with included IAADDR with requested IP */ @@ -726,7 +723,7 @@ static NOINLINE int send_d6_discover(uint32_t xid, struct in6_addr *requested_ip opt_ptr = mempcpy(opt_ptr, client6_data.ia_pd, len); } - /* Add options: + /* Add options: client-id, * "param req" option according to -O, options specified with -x */ opt_ptr = add_d6_client_options(opt_ptr); @@ -771,7 +768,7 @@ static NOINLINE int send_d6_select(uint32_t xid) struct d6_packet packet; uint8_t *opt_ptr; - /* Fill in: msg type, client id */ + /* Fill in: msg type */ opt_ptr = init_d6_packet(&packet, D6_MSG_REQUEST, xid); /* server id */ @@ -783,7 +780,7 @@ static NOINLINE int send_d6_select(uint32_t xid) if (client6_data.ia_pd) opt_ptr = mempcpy(opt_ptr, client6_data.ia_pd, client6_data.ia_pd->len + 2+2); - /* Add options: + /* Add options: client-id, * "param req" option according to -O, options specified with -x */ opt_ptr = add_d6_client_options(opt_ptr); @@ -844,7 +841,7 @@ static NOINLINE int send_d6_renew(uint32_t xid, struct in6_addr *server_ipv6, st struct d6_packet packet; uint8_t *opt_ptr; - /* Fill in: msg type, client id */ + /* Fill in: msg type */ opt_ptr = init_d6_packet(&packet, DHCPREQUEST, xid); /* server id */ @@ -856,7 +853,7 @@ static NOINLINE int send_d6_renew(uint32_t xid, struct in6_addr *server_ipv6, st if (client6_data.ia_pd) opt_ptr = mempcpy(opt_ptr, client6_data.ia_pd, client6_data.ia_pd->len + 2+2); - /* Add options: + /* Add options: client-id, * "param req" option according to -O, options specified with -x */ opt_ptr = add_d6_client_options(opt_ptr); @@ -878,6 +875,7 @@ int send_d6_release(struct in6_addr *server_ipv6, struct in6_addr *our_cur_ipv6) { struct d6_packet packet; uint8_t *opt_ptr; + struct option_set *ci; /* Fill in: msg type, client id */ opt_ptr = init_d6_packet(&packet, D6_MSG_RELEASE, random_xid()); @@ -889,6 +887,10 @@ int send_d6_release(struct in6_addr *server_ipv6, struct in6_addr *our_cur_ipv6) /* IA PD */ if (client6_data.ia_pd) opt_ptr = mempcpy(opt_ptr, client6_data.ia_pd, client6_data.ia_pd->len + 2+2); + /* Client-id */ + ci = udhcp_find_option(client_data.options, D6_OPT_CLIENTID); + if (ci) + opt_ptr = mempcpy(opt_ptr, ci->data, D6_OPT_DATA + 2+2 + 6); bb_info_msg("sending %s", "release"); return d6_send_kernel_packet_from_client_data_ifindex( @@ -1184,7 +1186,7 @@ int udhcpc6_main(int argc UNUSED_PARAM, char **argv) { const char *str_r; IF_FEATURE_UDHCP_PORT(char *str_P;) - void *clientid_mac_ptr; + uint8_t *clientid_mac_ptr; llist_t *list_O = NULL; llist_t *list_x = NULL; int tryagain_timeout = 20; @@ -1284,22 +1286,19 @@ int udhcpc6_main(int argc UNUSED_PARAM, char **argv) if (d6_read_interface(client_data.interface, &client_data.ifindex, &client6_data.ll_ip6, - client_data.client_mac) + client_data_client_mac) ) { return 1; } - /* Create client ID based on mac, set clientid_mac_ptr */ - { - struct d6_option *clientid; - clientid = xzalloc(2+2+2+2+6); - clientid->code = D6_OPT_CLIENTID; - clientid->len = 2+2+6; - clientid->data[1] = 3; /* DUID-LL */ - clientid->data[3] = 1; /* ethernet */ - clientid_mac_ptr = clientid->data + 2+2; - memcpy(clientid_mac_ptr, client_data.client_mac, 6); - client_data.clientid = (void*)clientid; + clientid_mac_ptr = NULL; + if (!udhcp_find_option(client_data.options, D6_OPT_CLIENTID)) { + /* not set, set the default client ID */ + client_data.clientid[1] = 3; /* DUID-LL */ + client_data.clientid[3] = 1; /* ethernet */ + clientid_mac_ptr = udhcp_insert_new_option(&client_data.options, D6_OPT_CLIENTID, + client_data.clientid, 2+2 + 6, /*dhcp6:*/ 1); + clientid_mac_ptr += 2+2 + 2+2; /* skip option code, len, DUID-LL, ethernet */ } #if !BB_MMU @@ -1386,12 +1385,13 @@ int udhcpc6_main(int argc UNUSED_PARAM, char **argv) if (d6_read_interface(client_data.interface, &client_data.ifindex, &client6_data.ll_ip6, - client_data.client_mac) + client_data_client_mac) ) { goto ret0; /* iface is gone? */ } - memcpy(clientid_mac_ptr, client_data.client_mac, 6); + if (clientid_mac_ptr) + memcpy(clientid_mac_ptr, client_data_client_mac, 6); switch (client_data.state) { case INIT_SELECTING: @@ -1505,7 +1505,9 @@ int udhcpc6_main(int argc UNUSED_PARAM, char **argv) continue; /* case RELEASED: */ } - /* yah, I know, *you* say it would never happen */ + /* RELEASED state (when we got SIGUSR2) ends up here. + * (wait for SIGUSR1 to re-init, or for TERM, etc) + */ timeout = INT_MAX; continue; /* back to main loop */ } /* if poll timed out */ diff --git a/networking/udhcp/dhcpc.c b/networking/udhcp/dhcpc.c index ea06405ba..a06eeaa16 100644 --- a/networking/udhcp/dhcpc.c +++ b/networking/udhcp/dhcpc.c @@ -612,9 +612,7 @@ static void init_packet(struct dhcp_packet *packet, char type) secs = client_data.last_secs - client_data.first_secs; packet->secs = (secs < 0xffff) ? htons(secs) : 0xffff; - memcpy(packet->chaddr, client_data.client_mac, 6); - if (client_data.clientid) - udhcp_add_binary_option(packet, client_data.clientid); + memcpy(packet->chaddr, client_data_client_mac, 6); } static void add_client_options(struct dhcp_packet *packet) @@ -715,7 +713,7 @@ static NOINLINE int send_discover(uint32_t xid, uint32_t requested) /* Fill in: op, htype, hlen, cookie, chaddr fields, * random xid field (we override it below), - * client-id option (unless -C), message type option: + * message type option: */ init_packet(&packet, DHCPDISCOVER); @@ -724,7 +722,7 @@ static NOINLINE int send_discover(uint32_t xid, uint32_t requested) udhcp_add_simple_option(&packet, DHCP_REQUESTED_IP, requested); /* Add options: maxsize, - * optionally: hostname, fqdn, vendorclass, + * optionally: hostname, fqdn, vendorclass, client-id, * "param req" option according to -O, options specified with -x */ add_client_options(&packet); @@ -758,7 +756,7 @@ static NOINLINE int send_select(uint32_t xid, uint32_t server, uint32_t requeste */ /* Fill in: op, htype, hlen, cookie, chaddr fields, * random xid field (we override it below), - * client-id option (unless -C), message type option: + * message type option: */ init_packet(&packet, DHCPREQUEST); @@ -768,7 +766,7 @@ static NOINLINE int send_select(uint32_t xid, uint32_t server, uint32_t requeste udhcp_add_simple_option(&packet, DHCP_SERVER_ID, server); /* Add options: maxsize, - * optionally: hostname, fqdn, vendorclass, + * optionally: hostname, fqdn, vendorclass, client-id, * "param req" option according to -O, and options specified with -x */ add_client_options(&packet); @@ -805,7 +803,7 @@ static NOINLINE int send_renew(uint32_t xid, uint32_t server, uint32_t ciaddr) */ /* Fill in: op, htype, hlen, cookie, chaddr fields, * random xid field (we override it below), - * client-id option (unless -C), message type option: + * message type option: */ init_packet(&packet, DHCPREQUEST); @@ -813,7 +811,7 @@ static NOINLINE int send_renew(uint32_t xid, uint32_t server, uint32_t ciaddr) packet.ciaddr = ciaddr; /* Add options: maxsize, - * optionally: hostname, fqdn, vendorclass, + * optionally: hostname, fqdn, vendorclass, client-id, * "param req" option according to -O, and options specified with -x */ add_client_options(&packet); @@ -837,7 +835,7 @@ static NOINLINE int send_decline(/*uint32_t xid,*/ uint32_t server, uint32_t req struct dhcp_packet packet; /* Fill in: op, htype, hlen, cookie, chaddr, random xid fields, - * client-id option (unless -C), message type option: + * message type option: */ init_packet(&packet, DHCPDECLINE); @@ -854,6 +852,8 @@ static NOINLINE int send_decline(/*uint32_t xid,*/ uint32_t server, uint32_t req udhcp_add_simple_option(&packet, DHCP_SERVER_ID, server); +//TODO: add client-id opt? + bb_simple_info_msg("broadcasting decline"); return raw_bcast_from_client_data_ifindex(&packet, INADDR_ANY); } @@ -865,9 +865,10 @@ ALWAYS_INLINE /* one caller, help compiler to use this fact */ int send_release(uint32_t server, uint32_t ciaddr) { struct dhcp_packet packet; + struct option_set *ci; /* Fill in: op, htype, hlen, cookie, chaddr, random xid fields, - * client-id option (unless -C), message type option: + * message type option: */ init_packet(&packet, DHCPRELEASE); @@ -876,6 +877,14 @@ int send_release(uint32_t server, uint32_t ciaddr) udhcp_add_simple_option(&packet, DHCP_SERVER_ID, server); + /* RFC 2131 section 3.1.6: + * If the client used a 'client identifier' when it obtained the lease, + * it MUST use the same 'client identifier' in the DHCPRELEASE message. + */ + ci = udhcp_find_option(client_data.options, DHCP_CLIENT_ID); + if (ci) + udhcp_add_binary_option(&packet, ci->data); + bb_info_msg("sending %s", "release"); /* Note: normally we unicast here since "server" is not zero. * However, there _are_ people who run "address-less" DHCP servers, @@ -1230,7 +1239,7 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv) const char *str_V, *str_h, *str_F, *str_r; IF_FEATURE_UDHCPC_ARPING(const char *str_a = "2000";) IF_FEATURE_UDHCP_PORT(char *str_P;) - void *clientid_mac_ptr; + uint8_t *clientid_mac_ptr; llist_t *list_O = NULL; llist_t *list_x = NULL; int tryagain_timeout = 20; @@ -1339,7 +1348,7 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv) if (udhcp_read_interface(client_data.interface, &client_data.ifindex, NULL, - client_data.client_mac) + client_data_client_mac) ) { return 1; } @@ -1347,10 +1356,11 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv) clientid_mac_ptr = NULL; if (!(opt & OPT_C) && !udhcp_find_option(client_data.options, DHCP_CLIENT_ID)) { /* not suppressed and not set, set the default client ID */ - client_data.clientid = alloc_dhcp_option(DHCP_CLIENT_ID, "", 7); - client_data.clientid[OPT_DATA] = 1; /* type: ethernet */ - clientid_mac_ptr = client_data.clientid + OPT_DATA+1; - memcpy(clientid_mac_ptr, client_data.client_mac, 6); + client_data_client_mac[-1] = 1; /* type: ethernet */ + clientid_mac_ptr = udhcp_insert_new_option( + &client_data.options, DHCP_CLIENT_ID, + client_data_client_mac - 1, 1 + 6, /*dhcp6:*/ 0); + clientid_mac_ptr += 3; /* skip option code, len, ethernet */ } if (str_V[0] != '\0') { // can drop -V, str_V, client_data.vendorclass, @@ -1447,12 +1457,12 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv) if (udhcp_read_interface(client_data.interface, &client_data.ifindex, NULL, - client_data.client_mac) + client_data_client_mac) ) { goto ret0; /* iface is gone? */ } if (clientid_mac_ptr) - memcpy(clientid_mac_ptr, client_data.client_mac, 6); + memcpy(clientid_mac_ptr, client_data_client_mac, 6); switch (client_data.state) { case INIT_SELECTING: @@ -1569,7 +1579,9 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv) continue; /* case RELEASED: */ } - /* yah, I know, *you* say it would never happen */ + /* RELEASED state (when we got SIGUSR2) ends up here. + * (wait for SIGUSR1 to re-init, or for TERM, etc) + */ timeout = INT_MAX; continue; /* back to main loop */ } /* if poll timed out */ @@ -1645,7 +1657,7 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv) /* Ignore packets that aren't for us */ if (packet.hlen != 6 - || memcmp(packet.chaddr, client_data.client_mac, 6) != 0 + || memcmp(packet.chaddr, client_data_client_mac, 6) != 0 ) { //FIXME: need to also check that last 10 bytes are zero log1("chaddr does not match%s", ", ignoring packet"); // log2? @@ -1757,7 +1769,7 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv) if (!arpping(requested_ip, NULL, (uint32_t) 0, - client_data.client_mac, + client_data_client_mac, client_data.interface, arpping_ms) ) { diff --git a/networking/udhcp/dhcpc.h b/networking/udhcp/dhcpc.h index 7ad01ea8f..a4cc188b6 100644 --- a/networking/udhcp/dhcpc.h +++ b/networking/udhcp/dhcpc.h @@ -8,7 +8,8 @@ PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN struct client_data_t { - uint8_t client_mac[6]; /* Our mac address */ + uint8_t clientid[2+2 + 6]; /* Our mac address (prefixed by padding used for client-id) */ +#define client_data_client_mac (client_data.clientid + 2+2) IF_FEATURE_UDHCP_PORT(uint16_t port;) int ifindex; /* Index number of the interface to use */ uint8_t opt_mask[256 / 8]; /* Bitmask of options to send (-O option) */ @@ -17,7 +18,6 @@ struct client_data_t { char *pidfile; /* Optionally store the process ID */ const char *script; /* User script to run at dhcp events */ struct option_set *options; /* list of DHCP options to send to server */ - uint8_t *clientid; /* Optional client id to use */ uint8_t *vendorclass; /* Optional vendor class-id to use */ uint8_t *hostname; /* Optional hostname to use */ uint8_t *fqdn; /* Optional fully qualified domain name to use */ -- cgit v1.2.3-55-g6feb From 698cdef538f51bb85b68d591b1e42eb6b04d891c Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Wed, 2 Jun 2021 15:07:46 +0200 Subject: udhcpc: remove deprecated -H/-h HOSTNAME options (9 years), deprecate -V VENDOR function old new delta udhcpc_main 2563 2582 +19 dhcp_option_strings 294 301 +7 dhcp_optflags 80 82 +2 .rodata 103250 103248 -2 udhcpc_longopts 252 241 -11 add_client_options 209 175 -34 alloc_dhcp_option 59 - -59 ------------------------------------------------------------------------------ (add/remove: 0/1 grow/shrink: 3/3 up/down: 28/-106) Total: -78 bytes Signed-off-by: Denys Vlasenko --- networking/udhcp/common.c | 3 +- networking/udhcp/d6_dhcpc.c | 3 +- networking/udhcp/dhcpc.c | 78 +++++++++++++++++++-------------------------- networking/udhcp/dhcpc.h | 2 -- 4 files changed, 36 insertions(+), 50 deletions(-) diff --git a/networking/udhcp/common.c b/networking/udhcp/common.c index 684d76b2b..7929950f5 100644 --- a/networking/udhcp/common.c +++ b/networking/udhcp/common.c @@ -49,6 +49,7 @@ const struct dhcp_optflag dhcp_optflags[] = { { OPTION_U32 , 0x33 }, /* DHCP_LEASE_TIME */ { OPTION_IP , 0x36 }, /* DHCP_SERVER_ID */ { OPTION_STRING , 0x38 }, /* DHCP_ERR_MESSAGE */ + { OPTION_STRING , 0x3c }, /* DHCP_VENDOR */ //TODO: must be combined with 'sname' and 'file' handling: { OPTION_STRING_HOST , 0x42 }, /* DHCP_TFTP_SERVER_NAME */ { OPTION_STRING , 0x43 }, /* DHCP_BOOT_FILE */ @@ -83,7 +84,6 @@ const struct dhcp_optflag dhcp_optflags[] = { { OPTION_U8 , 0x35 }, /* DHCP_MESSAGE_TYPE */ { OPTION_U16 , 0x39 }, /* DHCP_MAX_SIZE */ //looks like these opts will work just fine even without these defs: -// { OPTION_STRING , 0x3c }, /* DHCP_VENDOR */ // /* not really a string: */ // { OPTION_STRING , 0x3d }, /* DHCP_CLIENT_ID */ { 0, 0 } /* zeroed terminating entry */ @@ -120,6 +120,7 @@ const char dhcp_option_strings[] ALIGN1 = "lease" "\0" /* DHCP_LEASE_TIME */ "serverid" "\0" /* DHCP_SERVER_ID */ "message" "\0" /* DHCP_ERR_MESSAGE */ + "vendor" "\0" /* DHCP_VENDOR */ "tftp" "\0" /* DHCP_TFTP_SERVER_NAME*/ "bootfile" "\0" /* DHCP_BOOT_FILE */ // "userclass" "\0" /* DHCP_USER_CLASS */ diff --git a/networking/udhcp/d6_dhcpc.c b/networking/udhcp/d6_dhcpc.c index c4bedb259..3fd1fa7ce 100644 --- a/networking/udhcp/d6_dhcpc.c +++ b/networking/udhcp/d6_dhcpc.c @@ -1296,7 +1296,8 @@ int udhcpc6_main(int argc UNUSED_PARAM, char **argv) /* not set, set the default client ID */ client_data.clientid[1] = 3; /* DUID-LL */ client_data.clientid[3] = 1; /* ethernet */ - clientid_mac_ptr = udhcp_insert_new_option(&client_data.options, D6_OPT_CLIENTID, + clientid_mac_ptr = udhcp_insert_new_option( + &client_data.options, D6_OPT_CLIENTID, client_data.clientid, 2+2 + 6, /*dhcp6:*/ 1); clientid_mac_ptr += 2+2 + 2+2; /* skip option code, len, DUID-LL, ethernet */ } diff --git a/networking/udhcp/dhcpc.c b/networking/udhcp/dhcpc.c index a06eeaa16..16228f048 100644 --- a/networking/udhcp/dhcpc.c +++ b/networking/udhcp/dhcpc.c @@ -55,8 +55,7 @@ struct tpacket_auxdata { #if ENABLE_LONG_OPTS static const char udhcpc_longopts[] ALIGN1 = "clientid-none\0" No_argument "C" - "vendorclass\0" Required_argument "V" - "hostname\0" Required_argument "H" + "vendorclass\0" Required_argument "V" //deprecated "fqdn\0" Required_argument "F" "interface\0" Required_argument "i" "now\0" No_argument "n" @@ -84,27 +83,25 @@ static const char udhcpc_longopts[] ALIGN1 = enum { OPT_C = 1 << 0, OPT_V = 1 << 1, - OPT_H = 1 << 2, - OPT_h = 1 << 3, - OPT_F = 1 << 4, - OPT_i = 1 << 5, - OPT_n = 1 << 6, - OPT_p = 1 << 7, - OPT_q = 1 << 8, - OPT_R = 1 << 9, - OPT_r = 1 << 10, - OPT_s = 1 << 11, - OPT_T = 1 << 12, - OPT_t = 1 << 13, - OPT_S = 1 << 14, - OPT_A = 1 << 15, - OPT_O = 1 << 16, - OPT_o = 1 << 17, - OPT_x = 1 << 18, - OPT_f = 1 << 19, - OPT_B = 1 << 20, + OPT_F = 1 << 2, + OPT_i = 1 << 3, + OPT_n = 1 << 4, + OPT_p = 1 << 5, + OPT_q = 1 << 6, + OPT_R = 1 << 7, + OPT_r = 1 << 8, + OPT_s = 1 << 9, + OPT_T = 1 << 10, + OPT_t = 1 << 11, + OPT_S = 1 << 12, + OPT_A = 1 << 13, + OPT_O = 1 << 14, + OPT_o = 1 << 15, + OPT_x = 1 << 16, + OPT_f = 1 << 17, + OPT_B = 1 << 18, /* The rest has variable bit positions, need to be clever */ - OPTBIT_B = 20, + OPTBIT_B = 18, USE_FOR_MMU( OPTBIT_b,) IF_FEATURE_UDHCPC_ARPING(OPTBIT_a,) IF_FEATURE_UDHCP_PORT( OPTBIT_P,) @@ -638,10 +635,6 @@ static void add_client_options(struct dhcp_packet *packet) packet->options[end + OPT_DATA + len] = DHCP_END; } - if (client_data.vendorclass) - udhcp_add_binary_option(packet, client_data.vendorclass); - if (client_data.hostname) - udhcp_add_binary_option(packet, client_data.hostname); if (client_data.fqdn) udhcp_add_binary_option(packet, client_data.fqdn); @@ -722,7 +715,7 @@ static NOINLINE int send_discover(uint32_t xid, uint32_t requested) udhcp_add_simple_option(&packet, DHCP_REQUESTED_IP, requested); /* Add options: maxsize, - * optionally: hostname, fqdn, vendorclass, client-id, + * optionally: fqdn, client-id, * "param req" option according to -O, options specified with -x */ add_client_options(&packet); @@ -766,7 +759,7 @@ static NOINLINE int send_select(uint32_t xid, uint32_t server, uint32_t requeste udhcp_add_simple_option(&packet, DHCP_SERVER_ID, server); /* Add options: maxsize, - * optionally: hostname, fqdn, vendorclass, client-id, + * optionally: fqdn, client-id, * "param req" option according to -O, and options specified with -x */ add_client_options(&packet); @@ -811,7 +804,7 @@ static NOINLINE int send_renew(uint32_t xid, uint32_t server, uint32_t ciaddr) packet.ciaddr = ciaddr; /* Add options: maxsize, - * optionally: hostname, fqdn, vendorclass, client-id, + * optionally: fqdn, client-id, * "param req" option according to -O, and options specified with -x */ add_client_options(&packet); @@ -1236,7 +1229,7 @@ int udhcpc_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int udhcpc_main(int argc UNUSED_PARAM, char **argv) { uint8_t *message; - const char *str_V, *str_h, *str_F, *str_r; + const char *str_V, *str_F, *str_r; IF_FEATURE_UDHCPC_ARPING(const char *str_a = "2000";) IF_FEATURE_UDHCP_PORT(char *str_P;) uint8_t *clientid_mac_ptr; @@ -1272,14 +1265,14 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv) /* Parse command line */ opt = getopt32long(argv, "^" /* O,x: list; -T,-t,-A take numeric param */ - "CV:H:h:F:i:np:qRr:s:T:+t:+SA:+O:*ox:*fB" + "CV:F:i:np:qRr:s:T:+t:+SA:+O:*ox:*fB" USE_FOR_MMU("b") IF_FEATURE_UDHCPC_ARPING("a::") IF_FEATURE_UDHCP_PORT("P:") "v" "\0" IF_UDHCP_VERBOSE("vv") /* -v is a counter */ , udhcpc_longopts - , &str_V, &str_h, &str_h, &str_F + , &str_V, &str_F , &client_data.interface, &client_data.pidfile /* i,p */ , &str_r /* r */ , &client_data.script /* s */ @@ -1290,11 +1283,6 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv) IF_FEATURE_UDHCP_PORT(, &str_P) IF_UDHCP_VERBOSE(, &dhcp_verbose) ); - if (opt & (OPT_h|OPT_H)) { - //msg added 2011-11 - bb_simple_error_msg("option -h NAME is deprecated, use -x hostname:NAME"); - client_data.hostname = alloc_dhcp_option(DHCP_HOST_NAME, str_h, 0); - } if (opt & OPT_F) { /* FQDN option format: [0x51][len][flags][0][0] */ client_data.fqdn = alloc_dhcp_option(DHCP_FQDN, str_F, 3); @@ -1344,6 +1332,13 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv) ); free(optstr); } + if (str_V[0] != '\0') { + //msg added 2021-06 + bb_error_msg("option -V VENDOR is deprecated, use -x vendor:VENDOR"); + udhcp_insert_new_option( + &client_data.options, DHCP_VENDOR, + str_V, strlen(str_V), /*dhcp6:*/ 0); + } if (udhcp_read_interface(client_data.interface, &client_data.ifindex, @@ -1362,15 +1357,6 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv) client_data_client_mac - 1, 1 + 6, /*dhcp6:*/ 0); clientid_mac_ptr += 3; /* skip option code, len, ethernet */ } - if (str_V[0] != '\0') { - // can drop -V, str_V, client_data.vendorclass, - // but need to add "vendor" to the list of recognized - // string opts for this to work; - // and need to tweak add_client_options() too... - // ...so the question is, should we? - //bb_error_msg("option -V VENDOR is deprecated, use -x vendor:VENDOR"); - client_data.vendorclass = alloc_dhcp_option(DHCP_VENDOR, str_V, 0); - } #if !BB_MMU /* on NOMMU reexec (i.e., background) early */ diff --git a/networking/udhcp/dhcpc.h b/networking/udhcp/dhcpc.h index a4cc188b6..5c710963f 100644 --- a/networking/udhcp/dhcpc.h +++ b/networking/udhcp/dhcpc.h @@ -18,8 +18,6 @@ struct client_data_t { char *pidfile; /* Optionally store the process ID */ const char *script; /* User script to run at dhcp events */ struct option_set *options; /* list of DHCP options to send to server */ - uint8_t *vendorclass; /* Optional vendor class-id to use */ - uint8_t *hostname; /* Optional hostname to use */ uint8_t *fqdn; /* Optional fully qualified domain name to use */ llist_t *envp; /* list of DHCP options used for env vars */ -- cgit v1.2.3-55-g6feb From 949e9621d10faada3bb55a4aa206df44e7d39ae8 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Wed, 2 Jun 2021 15:51:50 +0200 Subject: udhcpc: get rid of client_data.fqdn field function old new delta attach_option 253 276 +23 udhcpc_main 2582 2588 +6 udhcpc6_main 2579 2571 -8 add_client_options 175 158 -17 udhcp_insert_new_option 169 138 -31 ------------------------------------------------------------------------------ (add/remove: 0/0 grow/shrink: 2/3 up/down: 29/-56) Total: -27 bytes Signed-off-by: Denys Vlasenko --- networking/udhcp/common.c | 13 +++++---- networking/udhcp/common.h | 5 ++-- networking/udhcp/d6_dhcpc.c | 40 ++++++++++--------------- networking/udhcp/dhcpc.c | 71 ++++++++++++++++++++------------------------- networking/udhcp/dhcpc.h | 4 +-- 5 files changed, 57 insertions(+), 76 deletions(-) diff --git a/networking/udhcp/common.c b/networking/udhcp/common.c index 7929950f5..b325c4112 100644 --- a/networking/udhcp/common.c +++ b/networking/udhcp/common.c @@ -424,7 +424,6 @@ int FAST_FUNC udhcp_str2nip(const char *str, void *arg) void* FAST_FUNC udhcp_insert_new_option( struct option_set **opt_list, unsigned code, - const void *buffer, unsigned length, bool dhcpv6) { @@ -434,17 +433,15 @@ void* FAST_FUNC udhcp_insert_new_option( log2("attaching option %02x to list", code); new = xmalloc(sizeof(*new)); if (!dhcpv6) { - new->data = xmalloc(length + OPT_DATA); + new->data = xzalloc(length + OPT_DATA); new->data[OPT_CODE] = code; new->data[OPT_LEN] = length; - memcpy(new->data + OPT_DATA, buffer, length); } else { - new->data = xmalloc(length + D6_OPT_DATA); + new->data = xzalloc(length + D6_OPT_DATA); new->data[D6_OPT_CODE] = code >> 8; new->data[D6_OPT_CODE + 1] = code & 0xff; new->data[D6_OPT_LEN] = length >> 8; new->data[D6_OPT_LEN + 1] = length & 0xff; - memcpy(new->data + D6_OPT_DATA, buffer, length); } curr = opt_list; @@ -498,7 +495,11 @@ static NOINLINE void attach_option( existing = udhcp_find_option(*opt_list, optflag->code); if (!existing) { /* make a new option */ - udhcp_insert_new_option(opt_list, optflag->code, buffer, length, dhcpv6); + uint8_t *p = udhcp_insert_new_option(opt_list, optflag->code, length, dhcpv6); + if (!dhcpv6) + memcpy(p + OPT_DATA, buffer, length); + else + memcpy(p + D6_OPT_DATA, buffer, length); goto ret; } diff --git a/networking/udhcp/common.h b/networking/udhcp/common.h index e5af62874..48a23792a 100644 --- a/networking/udhcp/common.h +++ b/networking/udhcp/common.h @@ -321,12 +321,11 @@ void udhcp_dump_packet(struct dhcp_packet *packet) FAST_FUNC; int FAST_FUNC udhcp_str2nip(const char *str, void *arg); #if !ENABLE_UDHCPC6 -#define udhcp_insert_new_option(opt_list, code, buffer, length, dhcpv6) \ - udhcp_insert_new_option(opt_list, code, buffer, length) +#define udhcp_insert_new_option(opt_list, code, length, dhcpv6) \ + udhcp_insert_new_option(opt_list, code, length) #endif void* FAST_FUNC udhcp_insert_new_option(struct option_set **opt_list, unsigned code, - const void *buffer, unsigned length, bool dhcpv6); diff --git a/networking/udhcp/d6_dhcpc.c b/networking/udhcp/d6_dhcpc.c index 3fd1fa7ce..c68dc8c4f 100644 --- a/networking/udhcp/d6_dhcpc.c +++ b/networking/udhcp/d6_dhcpc.c @@ -1111,17 +1111,6 @@ static void perform_d6_release(struct in6_addr *server_ipv6, struct in6_addr *ou client_data.state = RELEASED; } -///static uint8_t* alloc_dhcp_option(int code, const char *str, int extra) -///{ -/// uint8_t *storage; -/// int len = strnlen(str, 255); -/// storage = xzalloc(len + extra + OPT_DATA); -/// storage[OPT_CODE] = code; -/// storage[OPT_LEN] = len + extra; -/// memcpy(storage + extra + OPT_DATA, str, len); -/// return storage; -///} - #if BB_MMU static void client_background(void) { @@ -1283,23 +1272,24 @@ int udhcpc6_main(int argc UNUSED_PARAM, char **argv) free(optstr); } - if (d6_read_interface(client_data.interface, - &client_data.ifindex, - &client6_data.ll_ip6, - client_data_client_mac) - ) { - return 1; - } - clientid_mac_ptr = NULL; if (!udhcp_find_option(client_data.options, D6_OPT_CLIENTID)) { /* not set, set the default client ID */ - client_data.clientid[1] = 3; /* DUID-LL */ - client_data.clientid[3] = 1; /* ethernet */ clientid_mac_ptr = udhcp_insert_new_option( &client_data.options, D6_OPT_CLIENTID, - client_data.clientid, 2+2 + 6, /*dhcp6:*/ 1); - clientid_mac_ptr += 2+2 + 2+2; /* skip option code, len, DUID-LL, ethernet */ + 2+2 + 6, /*dhcp6:*/ 1); + clientid_mac_ptr += 2+2; /* skip option code, len */ + clientid_mac_ptr[1] = 3; /* DUID-LL */ + clientid_mac_ptr[3] = 1; /* type: ethernet */ + clientid_mac_ptr += 2+2; /* skip DUID-LL, ethernet */ + } + + if (d6_read_interface(client_data.interface, + &client_data.ifindex, + &client6_data.ll_ip6, + client_data.client_mac) + ) { + return 1; } #if !BB_MMU @@ -1386,13 +1376,13 @@ int udhcpc6_main(int argc UNUSED_PARAM, char **argv) if (d6_read_interface(client_data.interface, &client_data.ifindex, &client6_data.ll_ip6, - client_data_client_mac) + client_data.client_mac) ) { goto ret0; /* iface is gone? */ } if (clientid_mac_ptr) - memcpy(clientid_mac_ptr, client_data_client_mac, 6); + memcpy(clientid_mac_ptr, client_data.client_mac, 6); switch (client_data.state) { case INIT_SELECTING: diff --git a/networking/udhcp/dhcpc.c b/networking/udhcp/dhcpc.c index 16228f048..ab669d2b5 100644 --- a/networking/udhcp/dhcpc.c +++ b/networking/udhcp/dhcpc.c @@ -609,7 +609,7 @@ static void init_packet(struct dhcp_packet *packet, char type) secs = client_data.last_secs - client_data.first_secs; packet->secs = (secs < 0xffff) ? htons(secs) : 0xffff; - memcpy(packet->chaddr, client_data_client_mac, 6); + memcpy(packet->chaddr, client_data.client_mac, 6); } static void add_client_options(struct dhcp_packet *packet) @@ -635,9 +635,6 @@ static void add_client_options(struct dhcp_packet *packet) packet->options[end + OPT_DATA + len] = DHCP_END; } - if (client_data.fqdn) - udhcp_add_binary_option(packet, client_data.fqdn); - /* Request broadcast replies if we have no IP addr */ if ((option_mask32 & OPT_B) && packet->ciaddr == 0) packet->flags |= htons(BROADCAST_FLAG); @@ -715,7 +712,6 @@ static NOINLINE int send_discover(uint32_t xid, uint32_t requested) udhcp_add_simple_option(&packet, DHCP_REQUESTED_IP, requested); /* Add options: maxsize, - * optionally: fqdn, client-id, * "param req" option according to -O, options specified with -x */ add_client_options(&packet); @@ -759,7 +755,6 @@ static NOINLINE int send_select(uint32_t xid, uint32_t server, uint32_t requeste udhcp_add_simple_option(&packet, DHCP_SERVER_ID, server); /* Add options: maxsize, - * optionally: fqdn, client-id, * "param req" option according to -O, and options specified with -x */ add_client_options(&packet); @@ -804,7 +799,6 @@ static NOINLINE int send_renew(uint32_t xid, uint32_t server, uint32_t ciaddr) packet.ciaddr = ciaddr; /* Add options: maxsize, - * optionally: fqdn, client-id, * "param req" option according to -O, and options specified with -x */ add_client_options(&packet); @@ -1154,17 +1148,6 @@ static void perform_release(uint32_t server_addr, uint32_t requested_ip) client_data.state = RELEASED; } -static uint8_t* alloc_dhcp_option(int code, const char *str, int extra) -{ - uint8_t *storage; - int len = strnlen(str, 255); - storage = xzalloc(len + extra + OPT_DATA); - storage[OPT_CODE] = code; - storage[OPT_LEN] = len + extra; - memcpy(storage + extra + OPT_DATA, str, len); - return storage; -} - #if BB_MMU static void client_background(void) { @@ -1284,8 +1267,13 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv) IF_UDHCP_VERBOSE(, &dhcp_verbose) ); if (opt & OPT_F) { + char *p; + unsigned len; /* FQDN option format: [0x51][len][flags][0][0] */ - client_data.fqdn = alloc_dhcp_option(DHCP_FQDN, str_F, 3); + len = strlen(str_F); + p = udhcp_insert_new_option( + &client_data.options, DHCP_FQDN, + len + 3, /*dhcp6:*/ 0); /* Flag bits: 0000NEOS * S: 1 = Client requests server to update A RR in DNS as well as PTR * O: 1 = Server indicates to client that DNS has been updated regardless @@ -1294,9 +1282,10 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv) * N: 1 = Client requests server to not update DNS (S must be 0 then) * Two [0] bytes which follow are deprecated and must be 0. */ - client_data.fqdn[OPT_DATA + 0] = 0x1; - /*client_data.fqdn[OPT_DATA + 1] = 0; - xzalloc did it */ - /*client_data.fqdn[OPT_DATA + 2] = 0; */ + p[OPT_DATA + 0] = 0x1; + /*p[OPT_DATA + 1] = 0; - xzalloc did it */ + /*p[OPT_DATA + 2] = 0; */ + memcpy(p + OPT_DATA + 3, str_F, len); /* do not store NUL byte */ } if (opt & OPT_r) requested_ip = inet_addr(str_r); @@ -1333,31 +1322,35 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv) free(optstr); } if (str_V[0] != '\0') { + char *p; + unsigned len; //msg added 2021-06 bb_error_msg("option -V VENDOR is deprecated, use -x vendor:VENDOR"); - udhcp_insert_new_option( + len = strlen(str_V); + p = udhcp_insert_new_option( &client_data.options, DHCP_VENDOR, - str_V, strlen(str_V), /*dhcp6:*/ 0); + len, /*dhcp6:*/ 0); + memcpy(p + OPT_DATA, str_V, len); /* do not store NUL byte */ + } + + clientid_mac_ptr = NULL; + if (!(opt & OPT_C) && !udhcp_find_option(client_data.options, DHCP_CLIENT_ID)) { + /* not suppressed and not set, create default client ID */ + clientid_mac_ptr = udhcp_insert_new_option( + &client_data.options, DHCP_CLIENT_ID, + 1 + 6, /*dhcp6:*/ 0); + clientid_mac_ptr[OPT_DATA] = 1; /* type: ethernet */ + clientid_mac_ptr += OPT_DATA + 1; /* skip option code, len, ethernet */ } if (udhcp_read_interface(client_data.interface, &client_data.ifindex, NULL, - client_data_client_mac) + client_data.client_mac) ) { return 1; } - clientid_mac_ptr = NULL; - if (!(opt & OPT_C) && !udhcp_find_option(client_data.options, DHCP_CLIENT_ID)) { - /* not suppressed and not set, set the default client ID */ - client_data_client_mac[-1] = 1; /* type: ethernet */ - clientid_mac_ptr = udhcp_insert_new_option( - &client_data.options, DHCP_CLIENT_ID, - client_data_client_mac - 1, 1 + 6, /*dhcp6:*/ 0); - clientid_mac_ptr += 3; /* skip option code, len, ethernet */ - } - #if !BB_MMU /* on NOMMU reexec (i.e., background) early */ if (!(opt & OPT_f)) { @@ -1443,12 +1436,12 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv) if (udhcp_read_interface(client_data.interface, &client_data.ifindex, NULL, - client_data_client_mac) + client_data.client_mac) ) { goto ret0; /* iface is gone? */ } if (clientid_mac_ptr) - memcpy(clientid_mac_ptr, client_data_client_mac, 6); + memcpy(clientid_mac_ptr, client_data.client_mac, 6); switch (client_data.state) { case INIT_SELECTING: @@ -1643,7 +1636,7 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv) /* Ignore packets that aren't for us */ if (packet.hlen != 6 - || memcmp(packet.chaddr, client_data_client_mac, 6) != 0 + || memcmp(packet.chaddr, client_data.client_mac, 6) != 0 ) { //FIXME: need to also check that last 10 bytes are zero log1("chaddr does not match%s", ", ignoring packet"); // log2? @@ -1755,7 +1748,7 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv) if (!arpping(requested_ip, NULL, (uint32_t) 0, - client_data_client_mac, + client_data.client_mac, client_data.interface, arpping_ms) ) { diff --git a/networking/udhcp/dhcpc.h b/networking/udhcp/dhcpc.h index 5c710963f..cd9ead6bd 100644 --- a/networking/udhcp/dhcpc.h +++ b/networking/udhcp/dhcpc.h @@ -8,8 +8,7 @@ PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN struct client_data_t { - uint8_t clientid[2+2 + 6]; /* Our mac address (prefixed by padding used for client-id) */ -#define client_data_client_mac (client_data.clientid + 2+2) + uint8_t client_mac[6]; /* Our mac address */ IF_FEATURE_UDHCP_PORT(uint16_t port;) int ifindex; /* Index number of the interface to use */ uint8_t opt_mask[256 / 8]; /* Bitmask of options to send (-O option) */ @@ -18,7 +17,6 @@ struct client_data_t { char *pidfile; /* Optionally store the process ID */ const char *script; /* User script to run at dhcp events */ struct option_set *options; /* list of DHCP options to send to server */ - uint8_t *fqdn; /* Optional fully qualified domain name to use */ llist_t *envp; /* list of DHCP options used for env vars */ unsigned first_secs; -- cgit v1.2.3-55-g6feb From 0ae53451cf6fd61bcfa4bc78ef575fe6606373b4 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Wed, 2 Jun 2021 16:49:20 +0200 Subject: udhcpc[6]: close listening socket more eagerly (e.g. across script runs) function old new delta udhcpc6_main 2571 2600 +29 udhcpc_main 2588 2566 -22 ------------------------------------------------------------------------------ (add/remove: 0/0 grow/shrink: 1/1 up/down: 29/-22) Total: 7 bytes Signed-off-by: Denys Vlasenko --- networking/udhcp/d6_dhcpc.c | 32 ++++++++++++++++++++------------ networking/udhcp/dhcpc.c | 41 +++++++++++++++++++++++++---------------- 2 files changed, 45 insertions(+), 28 deletions(-) diff --git a/networking/udhcp/d6_dhcpc.c b/networking/udhcp/d6_dhcpc.c index c68dc8c4f..b2df9f091 100644 --- a/networking/udhcp/d6_dhcpc.c +++ b/networking/udhcp/d6_dhcpc.c @@ -1090,6 +1090,8 @@ static void change_listen_mode(int new_mode) static void perform_d6_release(struct in6_addr *server_ipv6, struct in6_addr *our_cur_ipv6) { + change_listen_mode(LISTEN_NONE); + /* send release packet */ if (client_data.state == BOUND || client_data.state == RENEWING @@ -1107,7 +1109,6 @@ static void perform_d6_release(struct in6_addr *server_ipv6, struct in6_addr *ou * of the states above. */ d6_run_script_no_option("deconfig"); - change_listen_mode(LISTEN_NONE); client_data.state = RELEASED; } @@ -1311,7 +1312,6 @@ int udhcpc6_main(int argc UNUSED_PARAM, char **argv) client_data.state = INIT_SELECTING; d6_run_script_no_option("deconfig"); - change_listen_mode(LISTEN_RAW); packet_num = 0; timeout = 0; lease_remaining = 0; @@ -1387,8 +1387,10 @@ int udhcpc6_main(int argc UNUSED_PARAM, char **argv) switch (client_data.state) { case INIT_SELECTING: if (!discover_retries || packet_num < discover_retries) { - if (packet_num == 0) + if (packet_num == 0) { + change_listen_mode(LISTEN_RAW); xid = random_xid(); + } /* multicast */ if (opt & OPT_l) send_d6_info_request(xid); @@ -1399,6 +1401,7 @@ int udhcpc6_main(int argc UNUSED_PARAM, char **argv) continue; } leasefail: + change_listen_mode(LISTEN_NONE); d6_run_script_no_option("leasefail"); #if BB_MMU /* -b is not supported on NOMMU */ if (opt & OPT_b) { /* background if no lease */ @@ -1435,7 +1438,6 @@ int udhcpc6_main(int argc UNUSED_PARAM, char **argv) * "discover...select...discover..." loops * were seen in the wild. Treat them similarly * to "no response to discover" case */ - change_listen_mode(LISTEN_RAW); client_data.state = INIT_SELECTING; goto leasefail; case BOUND: @@ -1470,11 +1472,11 @@ int udhcpc6_main(int argc UNUSED_PARAM, char **argv) /* Timed out, enter rebinding state */ log1s("entering rebinding state"); client_data.state = REBINDING; + /* Switch to bcast receive */ + change_listen_mode(LISTEN_RAW); packet_num = 0; /* fall right through */ case REBINDING: - /* Switch to bcast receive */ - change_listen_mode(LISTEN_RAW); /* Lease is *really* about to run out, * try to find DHCP server using broadcast */ if (lease_remaining > 0 && packet_num < 3) { @@ -1487,6 +1489,7 @@ int udhcpc6_main(int argc UNUSED_PARAM, char **argv) continue; } /* Timed out, enter init state */ + change_listen_mode(LISTEN_NONE); bb_simple_info_msg("lease lost, entering init state"); d6_run_script_no_option("deconfig"); client_data.state = INIT_SELECTING; @@ -1522,12 +1525,15 @@ int udhcpc6_main(int argc UNUSED_PARAM, char **argv) /* Start things over */ case RENEW_REQUESTED: /* two or more SIGUSR1 received */ + change_listen_mode(LISTEN_NONE); d6_run_script_no_option("deconfig"); - /* case REQUESTING: break; */ - /* case RELEASED: break; */ - /* case INIT_SELECTING: break; */ + + default: + /* case REQUESTING: */ + /* case RELEASED: */ + /* case INIT_SELECTING: */ + change_listen_mode(LISTEN_NONE); } - change_listen_mode(LISTEN_RAW); client_data.state = INIT_SELECTING; packet_num = 0; /* Kill any timeouts, user wants this to hurry along */ @@ -1535,6 +1541,7 @@ int udhcpc6_main(int argc UNUSED_PARAM, char **argv) continue; case SIGUSR2: perform_d6_release(&srv6_buf, requested_ipv6); + /* ^^^ switches to LISTEN_NONE */ timeout = INT_MAX; continue; case SIGTERM: @@ -1592,6 +1599,8 @@ int udhcpc6_main(int argc UNUSED_PARAM, char **argv) unsigned address_timeout; unsigned prefix_timeout; type_is_ok: + change_listen_mode(LISTEN_NONE); + address_timeout = 0; prefix_timeout = 0; option = d6_find_option(packet.d6_options, packet_end, D6_OPT_STATUS_CODE); @@ -1602,7 +1611,6 @@ int udhcpc6_main(int argc UNUSED_PARAM, char **argv) packet_end, "nak"); if (client_data.state != REQUESTING) d6_run_script_no_option("deconfig"); - change_listen_mode(LISTEN_RAW); sleep(3); /* avoid excessive network traffic */ client_data.state = INIT_SELECTING; client_data.first_secs = 0; /* make secs field count from 0 */ @@ -1624,6 +1632,7 @@ int udhcpc6_main(int argc UNUSED_PARAM, char **argv) client6_data.server_id = option; if (packet.d6_msg_type == D6_MSG_ADVERTISE) { /* enter requesting state */ + change_listen_mode(LISTEN_RAW); client_data.state = REQUESTING; timeout = 0; packet_num = 0; @@ -1826,7 +1835,6 @@ int udhcpc6_main(int argc UNUSED_PARAM, char **argv) // BOUND_for_half_lease: timeout = (unsigned)lease_remaining / 2; client_data.state = BOUND; - change_listen_mode(LISTEN_NONE); packet_num = 0; continue; /* back to main loop */ } diff --git a/networking/udhcp/dhcpc.c b/networking/udhcp/dhcpc.c index ab669d2b5..5fb96c2d8 100644 --- a/networking/udhcp/dhcpc.c +++ b/networking/udhcp/dhcpc.c @@ -1122,6 +1122,8 @@ static void perform_release(uint32_t server_addr, uint32_t requested_ip) char buffer[sizeof("255.255.255.255")]; struct in_addr temp_addr; + change_listen_mode(LISTEN_NONE); + /* send release packet */ if (client_data.state == BOUND || client_data.state == RENEWING @@ -1143,8 +1145,6 @@ static void perform_release(uint32_t server_addr, uint32_t requested_ip) * of the states above. */ udhcp_run_script(NULL, "deconfig"); - - change_listen_mode(LISTEN_NONE); client_data.state = RELEASED; } @@ -1343,6 +1343,10 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv) clientid_mac_ptr += OPT_DATA + 1; /* skip option code, len, ethernet */ } + /* Not really necessary (we redo it on every iteration) + * but allows early (before daemonization) detection + * of bad interface name. + */ if (udhcp_read_interface(client_data.interface, &client_data.ifindex, NULL, @@ -1372,7 +1376,6 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv) client_data.state = INIT_SELECTING; udhcp_run_script(NULL, "deconfig"); - change_listen_mode(LISTEN_RAW); packet_num = 0; timeout = 0; lease_remaining = 0; @@ -1446,8 +1449,10 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv) switch (client_data.state) { case INIT_SELECTING: if (!discover_retries || packet_num < discover_retries) { - if (packet_num == 0) + if (packet_num == 0) { + change_listen_mode(LISTEN_RAW); xid = random_xid(); + } /* broadcast */ send_discover(xid, requested_ip); timeout = discover_timeout; @@ -1455,6 +1460,7 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv) continue; } leasefail: + change_listen_mode(LISTEN_NONE); udhcp_run_script(NULL, "leasefail"); #if BB_MMU /* -b is not supported on NOMMU */ if (opt & OPT_b) { /* background if no lease */ @@ -1491,7 +1497,6 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv) * "discover...select...discover..." loops * were seen in the wild. Treat them similarly * to "no response to discover" case */ - change_listen_mode(LISTEN_RAW); client_data.state = INIT_SELECTING; goto leasefail; case BOUND: @@ -1528,17 +1533,17 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv) } //TODO: if 3 renew's failed (no reply) but remaining lease is large enough, //it might make sense to go back to BOUND and try later? a-la -// if (lease_remaining > 30) goto BOUND_for_half_lease; +// if (lease_remaining > 30) change_listen_mode(LISTEN_NONE) + goto BOUND_for_half_lease; //If we do that, "packet_num < 3" test below might be superfluous //(lease_remaining will run out anyway) /* Timed out or error, enter rebinding state */ log1s("entering rebinding state"); client_data.state = REBINDING; + /* Switch to bcast receive */ + change_listen_mode(LISTEN_RAW); packet_num = 0; /* fall right through */ case REBINDING: - /* Switch to bcast receive */ - change_listen_mode(LISTEN_RAW); /* Lease is *really* about to run out, * try to find DHCP server using broadcast */ if (lease_remaining > 0 && packet_num < 3) { @@ -1549,6 +1554,7 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv) continue; } /* Timed out, enter init state */ + change_listen_mode(LISTEN_NONE); bb_simple_info_msg("lease lost, entering init state"); udhcp_run_script(NULL, "deconfig"); client_data.state = INIT_SELECTING; @@ -1584,12 +1590,15 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv) /* Start things over */ case RENEW_REQUESTED: /* two or more SIGUSR1 received */ + change_listen_mode(LISTEN_NONE); udhcp_run_script(NULL, "deconfig"); - /* case REQUESTING: break; */ - /* case RELEASED: break; */ - /* case INIT_SELECTING: break; */ + + default: + /* case REQUESTING: */ + /* case RELEASED: */ + /* case INIT_SELECTING: */ + change_listen_mode(LISTEN_NONE); } - change_listen_mode(LISTEN_RAW); client_data.state = INIT_SELECTING; packet_num = 0; /* Kill any timeouts, user wants this to hurry along */ @@ -1597,6 +1606,7 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv) continue; case SIGUSR2: perform_release(server_addr, requested_ip); + /* ^^^ switches to LISTEN_NONE */ timeout = INT_MAX; continue; case SIGTERM: @@ -1706,6 +1716,8 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv) char server_str[sizeof("255.255.255.255")]; uint8_t *temp; + change_listen_mode(LISTEN_NONE); + temp_addr.s_addr = server_addr; strcpy(server_str, inet_ntoa(temp_addr)); temp_addr.s_addr = packet.yiaddr; @@ -1758,7 +1770,6 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv) if (client_data.state != REQUESTING) udhcp_run_script(NULL, "deconfig"); - change_listen_mode(LISTEN_RAW); client_data.state = INIT_SELECTING; client_data.first_secs = 0; /* make secs field count from 0 */ requested_ip = 0; @@ -1768,7 +1779,6 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv) } } #endif - /* enter bound state */ start = monotonic_sec(); udhcp_run_script(&packet, client_data.state == REQUESTING ? "bound" : "renew"); @@ -1791,7 +1801,6 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv) // BOUND_for_half_lease: timeout = (unsigned)lease_remaining / 2; client_data.state = BOUND; - change_listen_mode(LISTEN_NONE); /* make future renew packets use different xid */ /* xid = random_xid(); ...but why bother? */ packet_num = 0; @@ -1818,11 +1827,11 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv) goto non_matching_svid; } /* return to init state */ + change_listen_mode(LISTEN_NONE); bb_info_msg("received %s", "DHCP NAK"); udhcp_run_script(&packet, "nak"); if (client_data.state != REQUESTING) udhcp_run_script(NULL, "deconfig"); - change_listen_mode(LISTEN_RAW); sleep(3); /* avoid excessive network traffic */ client_data.state = INIT_SELECTING; client_data.first_secs = 0; /* make secs field count from 0 */ -- cgit v1.2.3-55-g6feb From 4bbc391c7f925d9c7d878c851887aa1545bd9bcd Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Wed, 2 Jun 2021 19:51:52 +0200 Subject: udhcpc: improve logs - show offer as it is received function old new delta udhcpc_main 2566 2608 +42 .rodata 103248 103272 +24 udhcp_recv_raw_packet 559 562 +3 d6_recv_raw_packet 254 255 +1 ------------------------------------------------------------------------------ (add/remove: 0/0 grow/shrink: 4/0 up/down: 70/0) Total: 70 bytes Signed-off-by: Denys Vlasenko --- networking/udhcp/d6_dhcpc.c | 3 ++- networking/udhcp/d6_packet.c | 3 ++- networking/udhcp/dhcpc.c | 13 ++++++++----- networking/udhcp/packet.c | 3 ++- 4 files changed, 14 insertions(+), 8 deletions(-) diff --git a/networking/udhcp/d6_dhcpc.c b/networking/udhcp/d6_dhcpc.c index b2df9f091..ef555bc8a 100644 --- a/networking/udhcp/d6_dhcpc.c +++ b/networking/udhcp/d6_dhcpc.c @@ -954,7 +954,8 @@ static NOINLINE int d6_recv_raw_packet(struct in6_addr *peer_ipv6, struct d6_pac if (peer_ipv6) *peer_ipv6 = packet.ip6.ip6_src; /* struct copy */ - log1("received %s", "a packet"); + log2("received %s", "a packet"); + /* log2 because more informative msg for valid packets is printed later at log1 level */ d6_dump_packet(&packet.data); bytes -= sizeof(packet.ip6) + sizeof(packet.udp); diff --git a/networking/udhcp/d6_packet.c b/networking/udhcp/d6_packet.c index 172f8e1ab..c1949f6e3 100644 --- a/networking/udhcp/d6_packet.c +++ b/networking/udhcp/d6_packet.c @@ -44,7 +44,8 @@ int FAST_FUNC d6_recv_kernel_packet(struct in6_addr *peer_ipv6 bb_simple_info_msg("packet with bad magic, ignoring"); return -2; } - log1("received %s", "a packet"); + log2("received %s", "a packet"); + /* log2 because more informative msg for valid packets is printed later at log1 level */ d6_dump_packet(packet); return bytes; diff --git a/networking/udhcp/dhcpc.c b/networking/udhcp/dhcpc.c index 5fb96c2d8..a30632d86 100644 --- a/networking/udhcp/dhcpc.c +++ b/networking/udhcp/dhcpc.c @@ -975,11 +975,12 @@ static NOINLINE int udhcp_recv_raw_packet(struct dhcp_packet *dhcp_pkt, int fd) skip_udp_sum_check: if (packet.data.cookie != htonl(DHCP_MAGIC)) { - bb_simple_info_msg("packet with bad magic, ignoring"); + log1s("packet with bad magic, ignoring"); return -2; } - log1("received %s", "a packet"); + log2("received %s", "a packet"); + /* log2 because more informative msg for valid packets is printed later at log1 level */ udhcp_dump_packet(&packet.data); bytes -= sizeof(packet.ip) + sizeof(packet.udp); @@ -1649,13 +1650,13 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv) || memcmp(packet.chaddr, client_data.client_mac, 6) != 0 ) { //FIXME: need to also check that last 10 bytes are zero - log1("chaddr does not match%s", ", ignoring packet"); // log2? + log1("chaddr does not match%s", ", ignoring packet"); continue; } message = udhcp_get_option(&packet, DHCP_MESSAGE_TYPE); if (message == NULL) { - bb_info_msg("no message type option%s", ", ignoring packet"); + log1("no message type option%s", ", ignoring packet"); continue; } @@ -1663,6 +1664,7 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv) case INIT_SELECTING: /* Must be a DHCPOFFER */ if (*message == DHCPOFFER) { + struct in_addr temp_addr; uint8_t *temp; /* What exactly is server's IP? There are several values. @@ -1698,7 +1700,8 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv) move_from_unaligned32(server_addr, temp); } /*xid = packet.xid; - already is */ - requested_ip = packet.yiaddr; + temp_addr.s_addr = requested_ip = packet.yiaddr; + log1("received an offer of %s", inet_ntoa(temp_addr)); /* enter requesting state */ client_data.state = REQUESTING; diff --git a/networking/udhcp/packet.c b/networking/udhcp/packet.c index 4d8e005d4..78f580ce9 100644 --- a/networking/udhcp/packet.c +++ b/networking/udhcp/packet.c @@ -95,7 +95,8 @@ int FAST_FUNC udhcp_recv_kernel_packet(struct dhcp_packet *packet, int fd) bb_simple_info_msg("packet with bad magic, ignoring"); return -2; } - log1("received %s", "a packet"); + log2("received %s", "a packet"); + /* log2 because more informative msg for valid packets is printed later at log1 level */ udhcp_dump_packet(packet); return bytes; -- cgit v1.2.3-55-g6feb From 01daecca1d908c7f08421cff815d81bd8ec3009a Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Wed, 2 Jun 2021 20:23:43 +0200 Subject: udhcpc[6]: remove superfluous "created raw socket" log message function old new delta change_listen_mode 299 280 -19 .rodata 103272 103250 -22 ------------------------------------------------------------------------------ (add/remove: 0/0 grow/shrink: 0/2 up/down: 0/-41) Total: -41 bytes Signed-off-by: Denys Vlasenko --- networking/udhcp/d6_dhcpc.c | 3 --- networking/udhcp/dhcpc.c | 4 +--- networking/udhcp/socket.c | 2 +- 3 files changed, 2 insertions(+), 7 deletions(-) diff --git a/networking/udhcp/d6_dhcpc.c b/networking/udhcp/d6_dhcpc.c index ef555bc8a..555446602 100644 --- a/networking/udhcp/d6_dhcpc.c +++ b/networking/udhcp/d6_dhcpc.c @@ -1063,9 +1063,6 @@ static int d6_raw_socket(int ifindex) log1("attached filter to raw socket fd %d", fd); // log? } #endif - - log1s("created raw socket"); - return fd; } diff --git a/networking/udhcp/dhcpc.c b/networking/udhcp/dhcpc.c index a30632d86..f441976dd 100644 --- a/networking/udhcp/dhcpc.c +++ b/networking/udhcp/dhcpc.c @@ -1093,8 +1093,6 @@ static int udhcp_raw_socket(int ifindex) log1s("can't set PACKET_AUXDATA on raw socket"); } - log1s("created raw socket"); - return fd; } @@ -1701,7 +1699,7 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv) } /*xid = packet.xid; - already is */ temp_addr.s_addr = requested_ip = packet.yiaddr; - log1("received an offer of %s", inet_ntoa(temp_addr)); + log1("received offer of %s", inet_ntoa(temp_addr)); /* enter requesting state */ client_data.state = REQUESTING; diff --git a/networking/udhcp/socket.c b/networking/udhcp/socket.c index 65a1a8ead..35e10688b 100644 --- a/networking/udhcp/socket.c +++ b/networking/udhcp/socket.c @@ -82,7 +82,7 @@ int FAST_FUNC udhcp_listen_socket(/*uint32_t ip,*/ int port, const char *inf) struct sockaddr_in addr; char *colon; - log1("opening listen socket on *:%d %s", port, inf); + log2("opening listen socket on *:%d %s", port, inf); fd = xsocket(PF_INET, SOCK_DGRAM, IPPROTO_UDP); setsockopt_reuseaddr(fd); -- cgit v1.2.3-55-g6feb From 1c7253726fcbab09917f143f0b703efbd2df55c3 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Thu, 3 Jun 2021 09:20:45 +0200 Subject: udhcpc[6]: when renewing, send 1 packet (not 3), on failure go back to BOUND This restores old behavior where we slept for 1/2 of lease, then tried renewing, thel slept for 1/4 and tried again, etc. But now we will NOT be listening to all packets for 1/2 of lease time, processing (rejecting) everyone else's DHCP traffic. We'll go back to bound state, where we have no listening socket at all. function old new delta udhcpc6_main 2600 2655 +55 udhcpc_main 2608 2625 +17 .rodata 103250 103249 -1 ------------------------------------------------------------------------------ (add/remove: 0/0 grow/shrink: 2/1 up/down: 72/-1) Total: 71 bytes Signed-off-by: Denys Vlasenko --- networking/udhcp/d6_dhcpc.c | 43 +++++++++++++++++++++-------------- networking/udhcp/dhcpc.c | 55 +++++++++++++++++++++++---------------------- 2 files changed, 54 insertions(+), 44 deletions(-) diff --git a/networking/udhcp/d6_dhcpc.c b/networking/udhcp/d6_dhcpc.c index 555446602..276ceca3c 100644 --- a/networking/udhcp/d6_dhcpc.c +++ b/networking/udhcp/d6_dhcpc.c @@ -1420,7 +1420,7 @@ int udhcpc6_main(int argc UNUSED_PARAM, char **argv) retval = 1; goto ret; } - /* wait before trying again */ + /* Wait before trying again */ timeout = tryagain_timeout; packet_num = 0; continue; @@ -1442,14 +1442,14 @@ int udhcpc6_main(int argc UNUSED_PARAM, char **argv) /* 1/2 lease passed, enter renewing state */ client_data.state = RENEWING; client_data.first_secs = 0; /* make secs field count from 0 */ - change_listen_mode(LISTEN_KERNEL); + got_SIGUSR1: log1s("entering renew state"); + change_listen_mode(LISTEN_KERNEL); /* fall right through */ - case RENEW_REQUESTED: /* manual (SIGUSR1) renew */ - case_RENEW_REQUESTED: + case RENEW_REQUESTED: /* in manual (SIGUSR1) renew */ case RENEWING: - if (packet_num < 3) { - /* send an unicast renew request */ + if (packet_num == 0) { + /* Send an unicast renew request */ /* Sometimes observed to fail (EADDRNOTAVAIL) to bind * a new UDP socket for sending inside send_renew. * I hazard to guess existing listening socket @@ -1463,13 +1463,18 @@ int udhcpc6_main(int argc UNUSED_PARAM, char **argv) else send_d6_renew(xid, &srv6_buf, requested_ipv6); timeout = discover_timeout; - /* ^^^ used to be = lease_remaining / 2 - WAY too long */ packet_num++; continue; + } /* else: we had sent one packet, but got no reply */ + log1s("no response to renew"); + if (lease_remaining > 30) { + /* Some lease time remains, try to renew later */ + change_listen_mode(LISTEN_NONE); + goto BOUND_for_half_lease; } - /* Timed out, enter rebinding state */ - log1s("entering rebinding state"); + /* Enter rebinding state */ client_data.state = REBINDING; + log1s("entering rebinding state"); /* Switch to bcast receive */ change_listen_mode(LISTEN_RAW); packet_num = 0; @@ -1509,8 +1514,14 @@ int udhcpc6_main(int argc UNUSED_PARAM, char **argv) /* Is it a signal? */ switch (udhcp_sp_read()) { case SIGUSR1: + if (client_data.state <= REQUESTING) + /* Initial negotiations in progress, do not disturb */ + break; + + if (lease_remaining > 30) /* if renew fails, do not go back to BOUND */ + lease_remaining = 30; client_data.first_secs = 0; /* make secs field count from 0 */ - bb_simple_info_msg("performing DHCP renew"); + packet_num = 0; switch (client_data.state) { /* Try to renew/rebind */ @@ -1519,21 +1530,19 @@ int udhcpc6_main(int argc UNUSED_PARAM, char **argv) case REBINDING: change_listen_mode(LISTEN_KERNEL); client_data.state = RENEW_REQUESTED; - goto case_RENEW_REQUESTED; + goto got_SIGUSR1; - /* Start things over */ - case RENEW_REQUESTED: /* two or more SIGUSR1 received */ + /* Two SIGUSR1 received, start things over */ + case RENEW_REQUESTED: change_listen_mode(LISTEN_NONE); d6_run_script_no_option("deconfig"); + /* Wake from SIGUSR2-induced deconfigured state */ default: - /* case REQUESTING: */ /* case RELEASED: */ - /* case INIT_SELECTING: */ change_listen_mode(LISTEN_NONE); } client_data.state = INIT_SELECTING; - packet_num = 0; /* Kill any timeouts, user wants this to hurry along */ timeout = 0; continue; @@ -1830,7 +1839,7 @@ int udhcpc6_main(int argc UNUSED_PARAM, char **argv) } #endif -// BOUND_for_half_lease: + BOUND_for_half_lease: timeout = (unsigned)lease_remaining / 2; client_data.state = BOUND; packet_num = 0; diff --git a/networking/udhcp/dhcpc.c b/networking/udhcp/dhcpc.c index f441976dd..e0bddcdc9 100644 --- a/networking/udhcp/dhcpc.c +++ b/networking/udhcp/dhcpc.c @@ -1480,7 +1480,7 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv) retval = 1; goto ret; } - /* wait before trying again */ + /* Wait before trying again */ timeout = tryagain_timeout; packet_num = 0; continue; @@ -1502,14 +1502,14 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv) /* 1/2 lease passed, enter renewing state */ client_data.state = RENEWING; client_data.first_secs = 0; /* make secs field count from 0 */ - change_listen_mode(LISTEN_KERNEL); + got_SIGUSR1: log1s("entering renew state"); + change_listen_mode(LISTEN_KERNEL); /* fall right through */ - case RENEW_REQUESTED: /* manual (SIGUSR1) renew */ - case_RENEW_REQUESTED: + case RENEW_REQUESTED: /* in manual (SIGUSR1) renew */ case RENEWING: - if (packet_num < 3) { - /* send an unicast renew request */ + if (packet_num == 0) { + /* Send an unicast renew request */ /* Sometimes observed to fail (EADDRNOTAVAIL) to bind * a new UDP socket for sending inside send_renew. * I hazard to guess existing listening socket @@ -1520,7 +1520,6 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv) */ if (send_renew(xid, server_addr, requested_ip) >= 0) { timeout = discover_timeout; - /* ^^^ used to be = lease_remaining / 2 - WAY too long */ packet_num++; continue; } @@ -1529,15 +1528,16 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv) * which gave us bogus server ID 1.1.1.1 * which wasn't reachable (and probably did not exist). */ + } /* else: we had sent one packet, but got no reply */ + log1s("no response to renew"); + if (lease_remaining > 30) { + /* Some lease time remains, try to renew later */ + change_listen_mode(LISTEN_NONE); + goto BOUND_for_half_lease; } -//TODO: if 3 renew's failed (no reply) but remaining lease is large enough, -//it might make sense to go back to BOUND and try later? a-la -// if (lease_remaining > 30) change_listen_mode(LISTEN_NONE) + goto BOUND_for_half_lease; -//If we do that, "packet_num < 3" test below might be superfluous -//(lease_remaining will run out anyway) - /* Timed out or error, enter rebinding state */ - log1s("entering rebinding state"); + /* Enter rebinding state */ client_data.state = REBINDING; + log1s("entering rebinding state"); /* Switch to bcast receive */ change_listen_mode(LISTEN_RAW); packet_num = 0; @@ -1575,31 +1575,34 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv) /* Is it a signal? */ switch (udhcp_sp_read()) { case SIGUSR1: + if (client_data.state <= REQUESTING) + /* Initial negotiations in progress, do not disturb */ + break; + + if (lease_remaining > 30) /* if renew fails, do not go back to BOUND */ + lease_remaining = 30; client_data.first_secs = 0; /* make secs field count from 0 */ - bb_simple_info_msg("performing DHCP renew"); + packet_num = 0; switch (client_data.state) { /* Try to renew/rebind */ case BOUND: case RENEWING: case REBINDING: - change_listen_mode(LISTEN_KERNEL); client_data.state = RENEW_REQUESTED; - goto case_RENEW_REQUESTED; + goto got_SIGUSR1; - /* Start things over */ - case RENEW_REQUESTED: /* two or more SIGUSR1 received */ + /* Two SIGUSR1 received, start things over */ + case RENEW_REQUESTED: change_listen_mode(LISTEN_NONE); udhcp_run_script(NULL, "deconfig"); + /* Wake from SIGUSR2-induced deconfigured state */ default: - /* case REQUESTING: */ /* case RELEASED: */ - /* case INIT_SELECTING: */ change_listen_mode(LISTEN_NONE); } client_data.state = INIT_SELECTING; - packet_num = 0; /* Kill any timeouts, user wants this to hurry along */ timeout = 0; continue; @@ -1723,10 +1726,9 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv) strcpy(server_str, inet_ntoa(temp_addr)); temp_addr.s_addr = packet.yiaddr; + lease_remaining = 60 * 60; temp = udhcp_get_option32(&packet, DHCP_LEASE_TIME); - if (!temp) { - lease_remaining = 60 * 60; - } else { + if (temp) { uint32_t lease; /* it IS unaligned sometimes, don't "optimize" */ move_from_unaligned32(lease, temp); @@ -1798,8 +1800,7 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv) opt = ((opt & ~OPT_b) | OPT_f); } #endif - -// BOUND_for_half_lease: + BOUND_for_half_lease: timeout = (unsigned)lease_remaining / 2; client_data.state = BOUND; /* make future renew packets use different xid */ -- cgit v1.2.3-55-g6feb From 774020c224653590110b30ea461d0ead34a6d875 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Thu, 3 Jun 2021 11:12:20 +0200 Subject: udhcp: convert log1s() macro to function function old new delta log1s - 15 +15 udhcp_recv_kernel_packet 134 125 -9 d6_recv_kernel_packet 118 109 -9 change_listen_mode 280 271 -9 send_packet 162 141 -21 udhcpc_main 2625 2598 -27 udhcpc6_main 2655 2628 -27 d6_recv_raw_packet 255 216 -39 udhcp_recv_raw_packet 562 484 -78 ------------------------------------------------------------------------------ (add/remove: 1/0 grow/shrink: 0/8 up/down: 15/-219) Total: -204 bytes Signed-off-by: Denys Vlasenko --- networking/udhcp/common.c | 7 +++++++ networking/udhcp/common.h | 3 ++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/networking/udhcp/common.c b/networking/udhcp/common.c index b325c4112..31e525cb0 100644 --- a/networking/udhcp/common.c +++ b/networking/udhcp/common.c @@ -185,6 +185,13 @@ const uint8_t dhcp_option_lengths[] ALIGN1 = { */ }; +#if defined CONFIG_UDHCP_DEBUG && CONFIG_UDHCP_DEBUG >= 1 +void FAST_FUNC log1s(const char *msg) +{ + if (dhcp_verbose >= 1) + bb_simple_info_msg(msg); +} +#endif #if defined CONFIG_UDHCP_DEBUG && CONFIG_UDHCP_DEBUG >= 2 static void log_option(const char *pfx, const uint8_t *opt) diff --git a/networking/udhcp/common.h b/networking/udhcp/common.h index 48a23792a..8c678dd32 100644 --- a/networking/udhcp/common.h +++ b/networking/udhcp/common.h @@ -275,7 +275,8 @@ struct option_set *udhcp_find_option(struct option_set *opt_list, uint8_t code) # define IF_UDHCP_VERBOSE(...) __VA_ARGS__ extern unsigned dhcp_verbose; # define log1(...) do { if (dhcp_verbose >= 1) bb_info_msg(__VA_ARGS__); } while (0) -# define log1s(msg) do { if (dhcp_verbose >= 1) bb_simple_info_msg(msg); } while (0) +//# define log1s(msg) do { if (dhcp_verbose >= 1) bb_simple_info_msg(msg); } while (0) +void log1s(const char *msg) FAST_FUNC; # if CONFIG_UDHCP_DEBUG >= 2 void udhcp_dump_packet(struct dhcp_packet *packet) FAST_FUNC; # define log2(...) do { if (dhcp_verbose >= 2) bb_info_msg(__VA_ARGS__); } while (0) -- cgit v1.2.3-55-g6feb From 0d15d5bccabff2a46b5da360b07c1ae1909b37b8 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Thu, 3 Jun 2021 11:32:40 +0200 Subject: udhcp: shrink arpping() function old new delta .rodata 103249 103246 -3 arpping 437 420 -17 ------------------------------------------------------------------------------ (add/remove: 0/0 grow/shrink: 0/2 up/down: 0/-20) Total: -20 bytes Signed-off-by: Denys Vlasenko --- networking/udhcp/arpping.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/networking/udhcp/arpping.c b/networking/udhcp/arpping.c index a395e838d..a11c4e841 100644 --- a/networking/udhcp/arpping.c +++ b/networking/udhcp/arpping.c @@ -47,6 +47,7 @@ int FAST_FUNC arpping(uint32_t test_nip, int rv = 1; /* "no reply received" yet */ struct sockaddr addr; /* for interface name */ struct arpMsg arp; + const char *msg; if (!timeo) return 1; @@ -58,7 +59,7 @@ int FAST_FUNC arpping(uint32_t test_nip, } if (setsockopt_broadcast(s) == -1) { - bb_simple_perror_msg("can't enable bcast on raw socket"); + bb_simple_perror_msg("can't enable bcast on ARP socket"); goto ret; } @@ -131,6 +132,9 @@ int FAST_FUNC arpping(uint32_t test_nip, ret: close(s); - log1("%srp reply received for this address", rv ? "no a" : "A"); + msg = "no ARP reply received for this address"; + if (rv == 0) + msg += 3; + log1s(msg); return rv; } -- cgit v1.2.3-55-g6feb From 2f1d13d56aecc622366f717932929db5f0b8b35b Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Thu, 3 Jun 2021 12:07:56 +0200 Subject: examples/udhcp/udhcpd.conf: update Signed-off-by: Denys Vlasenko --- examples/udhcp/udhcpd.conf | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/examples/udhcp/udhcpd.conf b/examples/udhcp/udhcpd.conf index df1258aaf..6eb10852e 100644 --- a/examples/udhcp/udhcpd.conf +++ b/examples/udhcp/udhcpd.conf @@ -79,7 +79,7 @@ option staticroutes 10.0.0.0/8 10.127.0.1, 10.11.12.0/24 10.11.12.1 option 0x08 01020304 # option 8: "cookie server IP addr: 1.2.3.4" option 14 "dumpfile" -# Currently supported options [hex option value] (for more info, see options.c): +# Currently supported options [hex option value] (for more info, see common.c): #opt lease NUM # [0x33] #opt subnet IP # [0x01] #opt broadcast IP # [0x1c] @@ -91,17 +91,20 @@ option 14 "dumpfile" #opt search STRING_LIST # [0x77] search domains #opt nisdomain STRING # [0x28] #opt timezone NUM # [0x02] (localtime - UTC_time) in seconds. signed +#opt tzstr STRING # [0x64] RFC 4833. IEEE 1003.1 TZ string +#opt tzdbstr STRING # [0x65] RFC 4833. Reference to the TZ database string #opt tftp STRING # [0x42] tftp server name #opt bootfile STRING # [0x43] tftp file to download (e.g. kernel image) #opt bootsize NUM # [0x0d] size of that file #opt rootpath STRING # [0x11] (NFS) path to mount as root fs #opt wpad STRING # [0xfc] Web Proxy Auto Discovery Protocol #opt serverid IP # [0x36] default: server's IP -#opt message STRING # [0x38] error message (udhcpd sends it on success too) +#opt message STRING # [0x38] error message (if set, udhcpd would send it on success too) #opt vlanid NUM # [0x84] 802.1P VLAN ID #opt vlanpriority NUM # [0x85] 802.1Q VLAN priority +#opt vendor STRING # [0x3c] client's vendor string, not intended to be sent by DHCP servers # RFC 5071: PXELINUX Options -#opt 0xd0 F100747E # [0xd0] magic +#opt 0xd0 F100747E # [0xd0] magic needed for other options to be recognized by clients #opt pxeconffile STRING # [0xd1] #opt pxepathprefix STRING # [0xd2] #opt reboottime NUM # [0xd3] bootstrap timeout -- cgit v1.2.3-55-g6feb From f6def87a2e73e65fb6743cb5b6d0592b390628e5 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Thu, 3 Jun 2021 16:14:04 +0200 Subject: udhcpc: code shrink, rename functions, no logic changes function old new delta d4_run_script - 739 +739 d4_recv_raw_packet - 484 +484 d4_run_script_deconfig - 12 +12 perform_release 207 200 -7 udhcpc_main 2598 2556 -42 udhcp_recv_raw_packet 484 - -484 udhcp_run_script 739 - -739 ------------------------------------------------------------------------------ (add/remove: 3/2 grow/shrink: 0/2 up/down: 1235/-1272) Total: -37 bytes Signed-off-by: Denys Vlasenko --- networking/udhcp/d6_dhcpc.c | 4 ++-- networking/udhcp/dhcpc.c | 30 +++++++++++++++++------------- 2 files changed, 19 insertions(+), 15 deletions(-) diff --git a/networking/udhcp/d6_dhcpc.c b/networking/udhcp/d6_dhcpc.c index 276ceca3c..1a58f5f44 100644 --- a/networking/udhcp/d6_dhcpc.c +++ b/networking/udhcp/d6_dhcpc.c @@ -441,7 +441,7 @@ static char **fill_envp(const uint8_t *option, const uint8_t *option_end) return envp; } -/* Call a script with a par file and env vars */ +/* Call a script with env vars */ static void d6_run_script(const uint8_t *option, const uint8_t *option_end, const char *name) { @@ -464,7 +464,7 @@ static void d6_run_script(const uint8_t *option, const uint8_t *option_end, free(envp); } -/* Call a script with a par file and no env var */ +/* Call a script with no env var */ static void d6_run_script_no_option(const char *name) { d6_run_script(NULL, NULL, name); diff --git a/networking/udhcp/dhcpc.c b/networking/udhcp/dhcpc.c index e0bddcdc9..e55b606cd 100644 --- a/networking/udhcp/dhcpc.c +++ b/networking/udhcp/dhcpc.c @@ -566,8 +566,8 @@ static void fill_envp(struct dhcp_packet *packet) } } -/* Call a script with a par file and env vars */ -static void udhcp_run_script(struct dhcp_packet *packet, const char *name) +/* Call a script with env vars */ +static void d4_run_script(struct dhcp_packet *packet, const char *name) { char *argv[3]; @@ -585,6 +585,10 @@ static void udhcp_run_script(struct dhcp_packet *packet, const char *name) client_data.envp = NULL; } +static void d4_run_script_deconfig(void) +{ + d4_run_script(NULL, "deconfig"); +} /*** Sending/receiving packets ***/ @@ -882,7 +886,7 @@ int send_release(uint32_t server, uint32_t ciaddr) /* Returns -1 on errors that are fatal for the socket, -2 for those that aren't */ /* NOINLINE: limit stack usage in caller */ -static NOINLINE int udhcp_recv_raw_packet(struct dhcp_packet *dhcp_pkt, int fd) +static NOINLINE int d4_recv_raw_packet(struct dhcp_packet *dhcp_pkt, int fd) { int bytes; struct ip_udp_dhcp_packet packet; @@ -1143,7 +1147,7 @@ static void perform_release(uint32_t server_addr, uint32_t requested_ip) * Users requested to be notified in all cases, even if not in one * of the states above. */ - udhcp_run_script(NULL, "deconfig"); + d4_run_script_deconfig(); client_data.state = RELEASED; } @@ -1374,7 +1378,7 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv) srand(monotonic_us()); client_data.state = INIT_SELECTING; - udhcp_run_script(NULL, "deconfig"); + d4_run_script_deconfig(); packet_num = 0; timeout = 0; lease_remaining = 0; @@ -1460,7 +1464,7 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv) } leasefail: change_listen_mode(LISTEN_NONE); - udhcp_run_script(NULL, "leasefail"); + d4_run_script(NULL, "leasefail"); #if BB_MMU /* -b is not supported on NOMMU */ if (opt & OPT_b) { /* background if no lease */ bb_simple_info_msg("no lease, forking to background"); @@ -1555,7 +1559,7 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv) /* Timed out, enter init state */ change_listen_mode(LISTEN_NONE); bb_simple_info_msg("lease lost, entering init state"); - udhcp_run_script(NULL, "deconfig"); + d4_run_script_deconfig(); client_data.state = INIT_SELECTING; client_data.first_secs = 0; /* make secs field count from 0 */ timeout = 0; @@ -1595,7 +1599,7 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv) /* Two SIGUSR1 received, start things over */ case RENEW_REQUESTED: change_listen_mode(LISTEN_NONE); - udhcp_run_script(NULL, "deconfig"); + d4_run_script_deconfig(); /* Wake from SIGUSR2-induced deconfigured state */ default: @@ -1627,7 +1631,7 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv) if (client_data.listen_mode == LISTEN_KERNEL) len = udhcp_recv_kernel_packet(&packet, client_data.sockfd); else - len = udhcp_recv_raw_packet(&packet, client_data.sockfd); + len = d4_recv_raw_packet(&packet, client_data.sockfd); if (len == -1) { /* Error is severe, reopen socket */ bb_error_msg("read error: "STRERROR_FMT", reopening socket" STRERROR_ERRNO); @@ -1772,7 +1776,7 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv) send_decline(/*xid,*/ server_addr, packet.yiaddr); if (client_data.state != REQUESTING) - udhcp_run_script(NULL, "deconfig"); + d4_run_script_deconfig(); client_data.state = INIT_SELECTING; client_data.first_secs = 0; /* make secs field count from 0 */ requested_ip = 0; @@ -1784,7 +1788,7 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv) #endif /* enter bound state */ start = monotonic_sec(); - udhcp_run_script(&packet, client_data.state == REQUESTING ? "bound" : "renew"); + d4_run_script(&packet, client_data.state == REQUESTING ? "bound" : "renew"); lease_remaining -= (unsigned)monotonic_sec() - start; if (lease_remaining < 0) lease_remaining = 0; @@ -1831,9 +1835,9 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv) /* return to init state */ change_listen_mode(LISTEN_NONE); bb_info_msg("received %s", "DHCP NAK"); - udhcp_run_script(&packet, "nak"); + d4_run_script(&packet, "nak"); if (client_data.state != REQUESTING) - udhcp_run_script(NULL, "deconfig"); + d4_run_script_deconfig(); sleep(3); /* avoid excessive network traffic */ client_data.state = INIT_SELECTING; client_data.first_secs = 0; /* make secs field count from 0 */ -- cgit v1.2.3-55-g6feb From ecaf8e8d082b48f186be0a096764f0442b546626 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Thu, 3 Jun 2021 16:22:35 +0200 Subject: udhcpc[6]: on SIGUSR1, do not go from rebind to renew state function old new delta udhcpc6_main 2628 2636 +8 udhcpc_main 2556 2563 +7 ------------------------------------------------------------------------------ (add/remove: 0/0 grow/shrink: 2/0 up/down: 15/0) Total: 15 bytes Signed-off-by: Denys Vlasenko --- networking/udhcp/d6_dhcpc.c | 10 ++++++---- networking/udhcp/dhcpc.c | 10 ++++++---- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/networking/udhcp/d6_dhcpc.c b/networking/udhcp/d6_dhcpc.c index 1a58f5f44..7f288f891 100644 --- a/networking/udhcp/d6_dhcpc.c +++ b/networking/udhcp/d6_dhcpc.c @@ -1517,6 +1517,9 @@ int udhcpc6_main(int argc UNUSED_PARAM, char **argv) if (client_data.state <= REQUESTING) /* Initial negotiations in progress, do not disturb */ break; + if (client_data.state == REBINDING) + /* Do not go back from rebind to renew state */ + break; if (lease_remaining > 30) /* if renew fails, do not go back to BOUND */ lease_remaining = 30; @@ -1524,22 +1527,21 @@ int udhcpc6_main(int argc UNUSED_PARAM, char **argv) packet_num = 0; switch (client_data.state) { - /* Try to renew/rebind */ case BOUND: case RENEWING: - case REBINDING: + /* Try to renew/rebind */ change_listen_mode(LISTEN_KERNEL); client_data.state = RENEW_REQUESTED; goto got_SIGUSR1; - /* Two SIGUSR1 received, start things over */ case RENEW_REQUESTED: + /* Two SIGUSR1 received, start things over */ change_listen_mode(LISTEN_NONE); d6_run_script_no_option("deconfig"); - /* Wake from SIGUSR2-induced deconfigured state */ default: /* case RELEASED: */ + /* Wake from SIGUSR2-induced deconfigured state */ change_listen_mode(LISTEN_NONE); } client_data.state = INIT_SELECTING; diff --git a/networking/udhcp/dhcpc.c b/networking/udhcp/dhcpc.c index e55b606cd..f388003a3 100644 --- a/networking/udhcp/dhcpc.c +++ b/networking/udhcp/dhcpc.c @@ -1582,6 +1582,9 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv) if (client_data.state <= REQUESTING) /* Initial negotiations in progress, do not disturb */ break; + if (client_data.state == REBINDING) + /* Do not go back from rebind to renew state */ + break; if (lease_remaining > 30) /* if renew fails, do not go back to BOUND */ lease_remaining = 30; @@ -1589,21 +1592,20 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv) packet_num = 0; switch (client_data.state) { - /* Try to renew/rebind */ case BOUND: case RENEWING: - case REBINDING: + /* Try to renew/rebind */ client_data.state = RENEW_REQUESTED; goto got_SIGUSR1; - /* Two SIGUSR1 received, start things over */ case RENEW_REQUESTED: + /* Two SIGUSR1 received, start things over */ change_listen_mode(LISTEN_NONE); d4_run_script_deconfig(); - /* Wake from SIGUSR2-induced deconfigured state */ default: /* case RELEASED: */ + /* Wake from SIGUSR2-induced deconfigured state */ change_listen_mode(LISTEN_NONE); } client_data.state = INIT_SELECTING; -- cgit v1.2.3-55-g6feb From 327b9f88486e67ade875780ea06dc8031fd70a49 Mon Sep 17 00:00:00 2001 From: Paul Spooren Date: Wed, 19 May 2021 00:58:32 +0200 Subject: nslookup: mention QUERY_TYPE SRV SRV lookups are supported since "6b4960155 nslookup: implement support for SRV records" and should therefore be mentioned as a possible QUERY_TYPE in the help message. Signed-off-by: Paul Spooren Signed-off-by: Denys Vlasenko --- networking/nslookup.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/networking/nslookup.c b/networking/nslookup.c index c43ad46f3..de7b5c0e7 100644 --- a/networking/nslookup.c +++ b/networking/nslookup.c @@ -25,7 +25,7 @@ //usage:#define nslookup_full_usage "\n\n" //usage: "Query DNS about HOST" //usage: IF_FEATURE_NSLOOKUP_BIG("\n") -//usage: IF_FEATURE_NSLOOKUP_BIG("\nQUERY_TYPE: soa,ns,a,"IF_FEATURE_IPV6("aaaa,")"cname,mx,txt,ptr,any") +//usage: IF_FEATURE_NSLOOKUP_BIG("\nQUERY_TYPE: soa,ns,a,"IF_FEATURE_IPV6("aaaa,")"cname,mx,txt,ptr,srv,any") //usage:#define nslookup_example_usage //usage: "$ nslookup localhost\n" //usage: "Server: default\n" -- cgit v1.2.3-55-g6feb From b1a2762ecfe3d0f7c953abe4c48eb0582303c197 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Thu, 3 Jun 2021 20:26:30 +0200 Subject: cpio: fix "cpio -d -p A/B/C" Signed-off-by: Denys Vlasenko --- archival/cpio.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/archival/cpio.c b/archival/cpio.c index 94303389e..d84f6937d 100644 --- a/archival/cpio.c +++ b/archival/cpio.c @@ -418,7 +418,8 @@ int cpio_main(int argc UNUSED_PARAM, char **argv) if (argv[0] == NULL) bb_show_usage(); if (opt & OPT_CREATE_LEADING_DIR) - mkdir(argv[0], 0777); + /* GNU cpio 2.13: "cpio -d -p a/b/c" works */ + bb_make_directory(argv[0], -1, FILEUTILS_RECUR); /* Crude existence check: * close(xopen(argv[0], O_RDONLY | O_DIRECTORY)); * We can also xopen, fstat, IS_DIR, later fchdir. @@ -428,6 +429,11 @@ int cpio_main(int argc UNUSED_PARAM, char **argv) * a diffrerent problem earlier. * This is good enough for now. */ +//FIXME: GNU cpio -d -p DIR does not immediately create DIR - +//it just prepends "DIR/" to the names of files to be created. +//The first file (fails to) be copied, and then the -d logic +//triggers and creates all necessary directories. +//IOW: bare "cpio -d -p DIR" + ^C shouldn't create anything. #if !BB_MMU pp.rd = 3; pp.wr = 4; -- cgit v1.2.3-55-g6feb From 5a3d3b8055f684539f05e00c38fdc5cefb94c883 Mon Sep 17 00:00:00 2001 From: Seth David Schoen Date: Wed, 3 Feb 2021 16:19:18 -0800 Subject: udhcpd: don't hardcode treating .0 and .255 specially Even following current Internet standards, it can be perfectly legitimate to issue IPv4 addresses that end in .0 or .255 via DHCP -- this can happen whenever the network is larger than /8. For example, 10.3.4.0 and 10.3.4.255 are legitimate host addresses in 10/8 or 10.3/16. (We also want to be able to issue .0 addresses in smaller networks following our proposed kernel patch and standards changes.) This behavior is already fully controllable by the user, simply by setting start_ip and end_ip correctly. Users who don't want to issue .0 or .255 should set start_ip greater than .0 or end_ip less than .255 and udhcpd will already respect these bounds. (This is also the case for other DHCP servers -- the recommended example configurations will default to a lower bound starting with .1 or some other value, which is typically appropriate, but the user is still allowed to change this to .0 -- or to a range that overlaps a .0 or .255 address -- if so desired.) Signed-off-by: Seth David Schoen Signed-off-by: Denys Vlasenko --- networking/udhcp/dhcpd.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/networking/udhcp/dhcpd.c b/networking/udhcp/dhcpd.c index 260130507..91f70970a 100644 --- a/networking/udhcp/dhcpd.c +++ b/networking/udhcp/dhcpd.c @@ -295,12 +295,11 @@ static uint32_t find_free_or_expired_nip(const uint8_t *safe_mac, unsigned arppi uint32_t nip; struct dyn_lease *lease; - /* ie, 192.168.55.0 */ - if ((addr & 0xff) == 0) - goto next_addr; - /* ie, 192.168.55.255 */ - if ((addr & 0xff) == 0xff) - goto next_addr; + /* (Addresses ending in .0 or .255 can legitimately be allocated + * in various situations, so _don't_ skip these. The user needs + * to choose start_ip and end_ip correctly for a particular + * network environment.) */ + nip = htonl(addr); /* skip our own address */ if (nip == server_data.server_nip) -- cgit v1.2.3-55-g6feb From 3d9c64915810cf684d75c8697b42e30c14011324 Mon Sep 17 00:00:00 2001 From: Sören Tempel Date: Sun, 23 May 2021 14:14:10 +0200 Subject: ls: don't output any colors with TERM=dumb MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The TERM variable is usually set to "dumb" to indicate that the terminal does not support any ANSI escape sequences. Presently, ls does not honor this variable and outputs colors anyhow which results in unreadable output, unless the user explicitly disables colors using `ls --color=never`. The rational behind this change is that ls should "just work" by default, even on dumb terminals. For this reason, this patch adds a check which additionally consults the TERM variable before printing any colors. This is analogous to the existing check for ensuring that standard output is a tty. As such, colors can still be forced with `--color=force`, even if TERM is set to dumb. function old new delta is_TERM_dumb - 40 +40 ls_main 579 598 +19 .rodata 103246 103251 +5 ------------------------------------------------------------------------------ (add/remove: 1/0 grow/shrink: 2/0 up/down: 64/0) Total: 64 bytes Signed-off-by: Sören Tempel Signed-off-by: Denys Vlasenko --- coreutils/ls.c | 12 ++++++++---- include/libbb.h | 1 + libbb/xfuncs.c | 6 ++++++ 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/coreutils/ls.c b/coreutils/ls.c index 80ef92079..1f7d7f7bf 100644 --- a/coreutils/ls.c +++ b/coreutils/ls.c @@ -1145,11 +1145,15 @@ int ls_main(int argc UNUSED_PARAM, char **argv) #if ENABLE_FEATURE_LS_COLOR /* set G_show_color = 1/0 */ - if (ENABLE_FEATURE_LS_COLOR_IS_DEFAULT && isatty(STDOUT_FILENO)) { + if (ENABLE_FEATURE_LS_COLOR_IS_DEFAULT && !is_TERM_dumb()) { char *p = getenv("LS_COLORS"); /* LS_COLORS is unset, or (not empty && not "none") ? */ - if (!p || (p[0] && strcmp(p, "none") != 0)) - G_show_color = 1; + if (!p || (p[0] && strcmp(p, "none") != 0)) { + if (isatty(STDOUT_FILENO)) { + /* check isatty() last because it's expensive (syscall) */ + G_show_color = 1; + } + } } if (opt & OPT_color) { if (color_opt[0] == 'n') @@ -1158,7 +1162,7 @@ int ls_main(int argc UNUSED_PARAM, char **argv) case 3: case 4: case 5: - if (isatty(STDOUT_FILENO)) { + if (!is_TERM_dumb() && isatty(STDOUT_FILENO)) { case 0: case 1: case 2: diff --git a/include/libbb.h b/include/libbb.h index 03f9c35f3..4c9c83bd1 100644 --- a/include/libbb.h +++ b/include/libbb.h @@ -1773,6 +1773,7 @@ extern void print_login_issue(const char *issue_file, const char *tty) FAST_FUNC extern void print_login_prompt(void) FAST_FUNC; char *xmalloc_ttyname(int fd) FAST_FUNC RETURNS_MALLOC; +int is_TERM_dumb(void) FAST_FUNC; /* NB: typically you want to pass fd 0, not 1. Think 'applet | grep something' */ int get_terminal_width_height(int fd, unsigned *width, unsigned *height) FAST_FUNC; int get_terminal_width(int fd) FAST_FUNC; diff --git a/libbb/xfuncs.c b/libbb/xfuncs.c index d93d8aaf5..c81ce4546 100644 --- a/libbb/xfuncs.c +++ b/libbb/xfuncs.c @@ -303,6 +303,12 @@ int FAST_FUNC get_terminal_width(int fd) return width; } +int FAST_FUNC is_dumb_term(void) +{ + char *term = getenv("TERM"); + return term && strcmp(term, "dumb") == 0; +} + int FAST_FUNC tcsetattr_stdin_TCSANOW(const struct termios *tp) { return tcsetattr(STDIN_FILENO, TCSANOW, tp); -- cgit v1.2.3-55-g6feb From df0383c624fa86d89cc438517340b4277366f133 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Sat, 5 Jun 2021 08:33:03 +0200 Subject: libbb: correct the name of is_TERM_dumb() Signed-off-by: Denys Vlasenko --- libbb/xfuncs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libbb/xfuncs.c b/libbb/xfuncs.c index c81ce4546..c40dcb706 100644 --- a/libbb/xfuncs.c +++ b/libbb/xfuncs.c @@ -303,7 +303,7 @@ int FAST_FUNC get_terminal_width(int fd) return width; } -int FAST_FUNC is_dumb_term(void) +int FAST_FUNC is_TERM_dumb(void) { char *term = getenv("TERM"); return term && strcmp(term, "dumb") == 0; -- cgit v1.2.3-55-g6feb From 08ea7be73bca11aa2de65cb24a4102233d2ff32c Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Sat, 5 Jun 2021 10:36:17 +0200 Subject: ls: trim --help text Signed-off-by: Denys Vlasenko --- coreutils/ls.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/coreutils/ls.c b/coreutils/ls.c index 1f7d7f7bf..9e8561606 100644 --- a/coreutils/ls.c +++ b/coreutils/ls.c @@ -109,11 +109,11 @@ //usage:#define ls_full_usage "\n\n" //usage: "List directory contents\n" //usage: "\n -1 One column output" -//usage: "\n -a Include entries which start with ." +//usage: "\n -a Include names starting with ." //usage: "\n -A Like -a, but exclude . and .." ////usage: "\n -C List by columns" - don't show, this is a default anyway //usage: "\n -x List by lines" -//usage: "\n -d List directory entries instead of contents" +//usage: "\n -d List directory names, not contents" //usage: IF_FEATURE_LS_FOLLOWLINKS( //usage: "\n -L Follow symlinks" //usage: "\n -H Follow symlinks on command line" @@ -122,10 +122,10 @@ //usage: "\n -R Recurse" //usage: ) //usage: IF_FEATURE_LS_FILETYPES( -//usage: "\n -p Append / to dir entries" -//usage: "\n -F Append indicator (one of */=@|) to entries" +//usage: "\n -p Append / to directory names" +//usage: "\n -F Append indicator (one of */=@|) to names" //usage: ) -//usage: "\n -l Long listing format" +//usage: "\n -l Long format" //usage: "\n -i List inode numbers" //usage: "\n -n List numeric UIDs and GIDs instead of names" //usage: "\n -s List allocated blocks" @@ -134,7 +134,7 @@ //usage: "\n -lu List atime" //usage: ) //usage: IF_FEATURE_LS_TIMESTAMPS(IF_LONG_OPTS( -//usage: "\n --full-time List full date and time" +//usage: "\n --full-time List full date/time" //usage: )) //usage: IF_FEATURE_HUMAN_READABLE( //usage: "\n -h Human readable sizes (1K 243M 2G)" @@ -160,7 +160,7 @@ //usage: "\n -w N Format N columns wide" //usage: ) //usage: IF_FEATURE_LS_COLOR( -//usage: "\n --color[={always,never,auto}] Control coloring" +//usage: "\n --color[={always,never,auto}]" //usage: ) #include "libbb.h" @@ -187,7 +187,7 @@ enum { -TERMINAL_WIDTH = 80, /* use 79 if terminal has linefold bug */ +TERMINAL_WIDTH = 80, /* use 79 if terminal has linefold bug */ SPLIT_FILE = 0, SPLIT_DIR = 1, @@ -298,7 +298,7 @@ struct dnode { // but there are invisible fields as well // (such as nanosecond-resolution timespamps) // and padding, which we also don't want to store. -// We also can pre-parse dev_t dn_rdev (in glibc, it's huge). +// We also pre-parse dev_t dn_rdev (in glibc, it's huge). // On 32-bit uclibc: dnode size went from 112 to 84 bytes. // /* Same names as in struct stat, but with dn_ instead of st_ pfx: */ -- cgit v1.2.3-55-g6feb From ac444861b0b0212803ef775c366c41c4c020b1f1 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Sat, 5 Jun 2021 14:51:53 +0200 Subject: svlogd: if processor's stdin can not be opened, do not try ad infinitum function old new delta processorstart 426 423 -3 Signed-off-by: Denys Vlasenko --- runit/svlogd.c | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/runit/svlogd.c b/runit/svlogd.c index 294e31aca..4490492e3 100644 --- a/runit/svlogd.c +++ b/runit/svlogd.c @@ -412,19 +412,32 @@ static void processorstart(struct logdir *ld) int fd; /* child */ - /* Non-ignored signals revert to SIG_DFL on exec anyway */ + /* Non-ignored signals revert to SIG_DFL on exec anyway. + * But we can get signals BEFORE execl(), this is unlikely + * but wouldn't be good... + */ /*bb_signals(0 + (1 << SIGTERM) + //+ (1 << SIGCHLD) + (1 << SIGALRM) + (1 << SIGHUP) , SIG_DFL);*/ - sig_unblock(SIGTERM); - sig_unblock(SIGALRM); - sig_unblock(SIGHUP); + /* runit 2.1.2 does not unblock SIGCHLD, a bug? we do: */ + sigprocmask(SIG_UNBLOCK, &blocked_sigset, NULL); if (verbose) bb_error_msg(INFO"processing: %s/%s", ld->name, ld->fnsave); - fd = xopen(ld->fnsave, O_RDONLY|O_NDELAY); + + fd = open_or_warn(ld->fnsave, O_RDONLY|O_NDELAY); + /* Used to have xopen() above, but it causes infinite restarts of processor + * if file is gone - which can happen even because of _us_! + * Users report that if on reboot, time is reset to before existing + * logfiles creation time, rmoldest() deletes the newest logfile (!) + * and we end up here trying to open this now-deleted file. + */ + if (fd < 0) + _exit(0); /* fake "success": do not run processor again */ + xmove_fd(fd, 0); ld->fnsave[26] = 't'; /* <- that's why we need sv_ch! */ fd = xopen(ld->fnsave, O_WRONLY|O_NDELAY|O_TRUNC|O_CREAT); -- cgit v1.2.3-55-g6feb From d3e1090308b6d3c55e01a2000a743b73605ddd7f Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Sat, 5 Jun 2021 15:24:04 +0200 Subject: tcp/udpsvd: robustify SIGCHLD handling function old new delta if_verbose_print_connection_status - 40 +40 tcpudpsvd_main 1798 1794 -4 connection_status 31 - -31 ------------------------------------------------------------------------------ (add/remove: 1/1 grow/shrink: 0/1 up/down: 40/-35) Total: 5 bytes Signed-off-by: Denys Vlasenko --- networking/tcpudp.c | 33 +++++++++++++++++++++++---------- runit/runsv.c | 5 ++++- 2 files changed, 27 insertions(+), 11 deletions(-) diff --git a/networking/tcpudp.c b/networking/tcpudp.c index 8c4afabf6..708e05c2e 100644 --- a/networking/tcpudp.c +++ b/networking/tcpudp.c @@ -216,17 +216,25 @@ enum { OPT_K = (1 << 16), }; -static void connection_status(void) +static void if_verbose_print_connection_status(void) { - /* "only 1 client max" desn't need this */ - if (cmax > 1) - bb_error_msg("status %u/%u", cnum, cmax); + if (verbose) { + /* "only 1 client max" desn't need this */ + if (cmax > 1) + bb_error_msg("status %u/%u", cnum, cmax); + } } +/* SIGCHLD handler is reentrancy-safe because SIGCHLD is unmasked + * only over accept() or recvfrom() calls, not over memory allocations + * or printouts. Do need to save/restore errno in order not to mangle + * these syscalls' error code, if any. + */ static void sig_child_handler(int sig UNUSED_PARAM) { int wstat; pid_t pid; + int sv_errno = errno; while ((pid = wait_any_nohang(&wstat)) > 0) { if (max_per_host) @@ -236,8 +244,8 @@ static void sig_child_handler(int sig UNUSED_PARAM) if (verbose) print_waitstat(pid, wstat); } - if (verbose) - connection_status(); + if_verbose_print_connection_status(); + errno = sv_errno; } int tcpudpsvd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; @@ -458,7 +466,7 @@ int tcpudpsvd_main(int argc UNUSED_PARAM, char **argv) xconnect(0, &remote.u.sa, sa_len); /* hole? at this point we have no wildcard udp socket... * can this cause clients to get "port unreachable" icmp? - * Yup, time window is very small, but it exists (is it?) */ + * Yup, time window is very small, but it exists (does it?) */ /* ..."open new socket", continued */ xbind(sock, &lsa->u.sa, sa_len); socket_want_pktinfo(sock); @@ -491,8 +499,7 @@ int tcpudpsvd_main(int argc UNUSED_PARAM, char **argv) if (pid != 0) { /* Parent */ cnum++; - if (verbose) - connection_status(); + if_verbose_print_connection_status(); if (hccp) hccp->pid = pid; /* clean up changes done by vforked child */ @@ -586,8 +593,14 @@ int tcpudpsvd_main(int argc UNUSED_PARAM, char **argv) xdup2(0, 1); + /* Restore signal handling for the to-be-execed process */ signal(SIGPIPE, SIG_DFL); /* this one was SIG_IGNed */ - /* Non-ignored signals revert to SIG_DFL on exec anyway */ + /* Non-ignored signals revert to SIG_DFL on exec anyway + * But we can get signals BEFORE execvp(), this is unlikely + * but it would invoke sig_child_handler(), which would + * check waitpid(WNOHANG), then print "status N/M" if verbose. + * I guess we can live with that possibility. + */ /*signal(SIGCHLD, SIG_DFL);*/ sig_unblock(SIGCHLD); diff --git a/runit/runsv.c b/runit/runsv.c index ecab8cdf5..61ea240ff 100644 --- a/runit/runsv.c +++ b/runit/runsv.c @@ -380,7 +380,10 @@ static void startservice(struct svdir *s) xdup2(logpipe.wr, 1); } } - /* Non-ignored signals revert to SIG_DFL on exec anyway */ + /* Non-ignored signals revert to SIG_DFL on exec anyway. + * But we can get signals BEFORE execl(), this is unlikely + * but wouldn't be good... + */ /*bb_signals(0 + (1 << SIGCHLD) + (1 << SIGTERM) -- cgit v1.2.3-55-g6feb From 5dadd497ffd9835a2860cf89ad781d1b513803dc Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Sat, 5 Jun 2021 16:20:05 +0200 Subject: runsv: robustify signal handling - SIGTERM to child between vfork and exec could mess things up While at it, rename bb_signals_recursive_norestart() to bb_signals_norestart(): "recursive" was implying we are setting SA_NODEFER allowing signal handler to be entered recursively, but we do not do that. function old new delta bb_signals_norestart - 70 +70 startservice 380 394 +14 bb_signals_recursive_norestart 70 - -70 ------------------------------------------------------------------------------ (add/remove: 1/1 grow/shrink: 1/0 up/down: 84/-70) Total: 14 bytes Signed-off-by: Denys Vlasenko --- console-tools/showkey.c | 2 +- include/libbb.h | 2 +- libbb/signals.c | 2 +- runit/runsv.c | 29 +++++++++++++++++++---------- runit/svlogd.c | 8 ++++---- sysklogd/klogd.c | 2 +- 6 files changed, 27 insertions(+), 18 deletions(-) diff --git a/console-tools/showkey.c b/console-tools/showkey.c index 4d7a9b9e5..84eb38a0a 100644 --- a/console-tools/showkey.c +++ b/console-tools/showkey.c @@ -106,7 +106,7 @@ int showkey_main(int argc UNUSED_PARAM, char **argv) xioctl(STDIN_FILENO, KDSKBMODE, (void *)(ptrdiff_t)((option_mask32 & OPT_k) ? K_MEDIUMRAW : K_RAW)); // we should exit on any signal; signals should interrupt read - bb_signals_recursive_norestart(BB_FATAL_SIGS, record_signo); + bb_signals_norestart(BB_FATAL_SIGS, record_signo); // inform user that program ends after time of inactivity printf(press_keys, "10s after last keypress"); diff --git a/include/libbb.h b/include/libbb.h index 4c9c83bd1..a3f76a206 100644 --- a/include/libbb.h +++ b/include/libbb.h @@ -593,7 +593,7 @@ void bb_signals(int sigs, void (*f)(int)) FAST_FUNC; /* Unlike signal() and bb_signals, sets handler with sigaction() * and in a way that while signal handler is run, no other signals * will be blocked; syscalls will not be restarted: */ -void bb_signals_recursive_norestart(int sigs, void (*f)(int)) FAST_FUNC; +void bb_signals_norestart(int sigs, void (*f)(int)) FAST_FUNC; /* syscalls like read() will be interrupted with EINTR: */ void signal_no_SA_RESTART_empty_mask(int sig, void (*handler)(int)) FAST_FUNC; /* syscalls like read() won't be interrupted (though select/poll will be): */ diff --git a/libbb/signals.c b/libbb/signals.c index d3d84ef6a..0bebc847d 100644 --- a/libbb/signals.c +++ b/libbb/signals.c @@ -56,7 +56,7 @@ void FAST_FUNC bb_signals(int sigs, void (*f)(int)) } } -void FAST_FUNC bb_signals_recursive_norestart(int sigs, void (*f)(int)) +void FAST_FUNC bb_signals_norestart(int sigs, void (*f)(int)) { int sig_no = 0; int bit = 1; diff --git a/runit/runsv.c b/runit/runsv.c index 61ea240ff..7fad563f5 100644 --- a/runit/runsv.c +++ b/runit/runsv.c @@ -149,11 +149,15 @@ static void warn_cannot(const char *m) warn2_cannot(m, ""); } +/* SIGCHLD/TERM handlers are reentrancy-safe because they are unmasked + * only over poll() call, not over memory allocations + * or printouts. Do not need to save/restore errno either, + * as poll() error is not checked there. + */ static void s_child(int sig_no UNUSED_PARAM) { write(selfpipe.wr, "", 1); } - static void s_term(int sig_no UNUSED_PARAM) { sigterm = 1; @@ -380,14 +384,14 @@ static void startservice(struct svdir *s) xdup2(logpipe.wr, 1); } } - /* Non-ignored signals revert to SIG_DFL on exec anyway. - * But we can get signals BEFORE execl(), this is unlikely - * but wouldn't be good... + /* Non-ignored signals revert to SIG_DFL on exec. + * But we can get signals BEFORE execl(), unlikely as that may be. + * SIGCHLD is safe (would merely write to selfpipe), + * but SIGTERM would set sigterm = 1 (with vfork, we affect parent). + * Avoid that. */ - /*bb_signals(0 - + (1 << SIGCHLD) - + (1 << SIGTERM) - , SIG_DFL);*/ + /*signal(SIGCHLD, SIG_DFL);*/ + signal(SIGTERM, SIG_DFL); sig_unblock(SIGCHLD); sig_unblock(SIGTERM); execv(arg[0], (char**) arg); @@ -514,9 +518,13 @@ int runsv_main(int argc UNUSED_PARAM, char **argv) ndelay_on(selfpipe.wr); sig_block(SIGCHLD); - bb_signals_recursive_norestart(1 << SIGCHLD, s_child); sig_block(SIGTERM); - bb_signals_recursive_norestart(1 << SIGTERM, s_term); + /* No particular reason why we don't set SA_RESTART + * (poll() wouldn't restart regardless of that flag), + * we just follow what runit-2.1.2 does: + */ + bb_signals_norestart(1 << SIGCHLD, s_child); + bb_signals_norestart(1 << SIGTERM, s_term); xchdir(dir); /* bss: svd[0].pid = 0; */ @@ -628,6 +636,7 @@ int runsv_main(int argc UNUSED_PARAM, char **argv) sig_unblock(SIGTERM); sig_unblock(SIGCHLD); poll(x, 2 + haslog, 3600*1000); + /* NB: signal handlers can trash errno of poll() */ sig_block(SIGTERM); sig_block(SIGCHLD); diff --git a/runit/svlogd.c b/runit/svlogd.c index 4490492e3..02c305696 100644 --- a/runit/svlogd.c +++ b/runit/svlogd.c @@ -1111,10 +1111,10 @@ int svlogd_main(int argc, char **argv) sigaddset(&blocked_sigset, SIGALRM); sigaddset(&blocked_sigset, SIGHUP); sigprocmask(SIG_BLOCK, &blocked_sigset, NULL); - bb_signals_recursive_norestart(1 << SIGTERM, sig_term_handler); - bb_signals_recursive_norestart(1 << SIGCHLD, sig_child_handler); - bb_signals_recursive_norestart(1 << SIGALRM, sig_alarm_handler); - bb_signals_recursive_norestart(1 << SIGHUP, sig_hangup_handler); + bb_signals_norestart(1 << SIGTERM, sig_term_handler); + bb_signals_norestart(1 << SIGCHLD, sig_child_handler); + bb_signals_norestart(1 << SIGALRM, sig_alarm_handler); + bb_signals_norestart(1 << SIGHUP, sig_hangup_handler); /* Without timestamps, we don't have to print each line * separately, so we can look for _last_ newline, not first, diff --git a/sysklogd/klogd.c b/sysklogd/klogd.c index 82596bc0b..df0edee0a 100644 --- a/sysklogd/klogd.c +++ b/sysklogd/klogd.c @@ -226,7 +226,7 @@ int klogd_main(int argc UNUSED_PARAM, char **argv) signal(SIGHUP, SIG_IGN); /* We want klogd_read to not be restarted, thus _norestart: */ - bb_signals_recursive_norestart(BB_FATAL_SIGS, record_signo); + bb_signals_norestart(BB_FATAL_SIGS, record_signo); syslog(LOG_NOTICE, "klogd started: %s", bb_banner); -- cgit v1.2.3-55-g6feb From 5a72b0cd74d6de8ed39a9704798ce1118b0995f1 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Sat, 5 Jun 2021 16:36:21 +0200 Subject: runsv: code shrink: use single handler function for SIGTERM and SIGCHLD function old new delta s_chld_term - 36 +36 runsv_main 1677 1662 -15 s_child 22 - -22 s_term 29 - -29 ------------------------------------------------------------------------------ (add/remove: 1/2 grow/shrink: 0/1 up/down: 36/-66) Total: -30 bytes Signed-off-by: Denys Vlasenko --- runit/runsv.c | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/runit/runsv.c b/runit/runsv.c index 7fad563f5..a4b8af494 100644 --- a/runit/runsv.c +++ b/runit/runsv.c @@ -149,20 +149,17 @@ static void warn_cannot(const char *m) warn2_cannot(m, ""); } -/* SIGCHLD/TERM handlers are reentrancy-safe because they are unmasked +/* SIGCHLD/TERM handler is reentrancy-safe because they are unmasked * only over poll() call, not over memory allocations * or printouts. Do not need to save/restore errno either, * as poll() error is not checked there. */ -static void s_child(int sig_no UNUSED_PARAM) +static void s_chld_term(int sig_no) { + if (sig_no == SIGTERM) + sigterm = 1; write(selfpipe.wr, "", 1); } -static void s_term(int sig_no UNUSED_PARAM) -{ - sigterm = 1; - write(selfpipe.wr, "", 1); /* XXX */ -} static int open_trunc_or_warn(const char *name) { @@ -523,8 +520,10 @@ int runsv_main(int argc UNUSED_PARAM, char **argv) * (poll() wouldn't restart regardless of that flag), * we just follow what runit-2.1.2 does: */ - bb_signals_norestart(1 << SIGCHLD, s_child); - bb_signals_norestart(1 << SIGTERM, s_term); + bb_signals_norestart(0 + + (1 << SIGCHLD) + + (1 << SIGTERM) + , s_chld_term); xchdir(dir); /* bss: svd[0].pid = 0; */ -- cgit v1.2.3-55-g6feb From 274ce6cce1eaa9033dee0050700f0ca9c2b5afdd Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Sat, 5 Jun 2021 16:48:06 +0200 Subject: udhcpc: revert deprecation message for -V "-x vendor:VENDOR" will not be a trivial replacement of it: (1) by default, we do send a vendor string ("udhcp BB_VER"), will need code to preserve the default. (2) -V '' currently disables vendor string. -x vendor:'' would not easily achieve that: it adds no option at all (string options can't be empty), and default (1) would trigger. To avoid that, we will need yet another hack to detect -x vendor:'' and interpret that as "no vendor string at all". IOW: removing -V is likely to increase code size, not decrease. function old new delta udhcpc_main 2563 2555 -8 .rodata 103251 103198 -53 ------------------------------------------------------------------------------ (add/remove: 0/0 grow/shrink: 0/2 up/down: 0/-61) Total: -61 bytes Signed-off-by: Denys Vlasenko --- networking/udhcp/dhcpc.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/networking/udhcp/dhcpc.c b/networking/udhcp/dhcpc.c index f388003a3..4e3d8ca5e 100644 --- a/networking/udhcp/dhcpc.c +++ b/networking/udhcp/dhcpc.c @@ -1326,10 +1326,7 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv) } if (str_V[0] != '\0') { char *p; - unsigned len; - //msg added 2021-06 - bb_error_msg("option -V VENDOR is deprecated, use -x vendor:VENDOR"); - len = strlen(str_V); + unsigned len = strnlen(str_V, 254); p = udhcp_insert_new_option( &client_data.options, DHCP_VENDOR, len, /*dhcp6:*/ 0); -- cgit v1.2.3-55-g6feb From c0f8113f86871f40daee690d7dd944bd5a31d95b Mon Sep 17 00:00:00 2001 From: Khem Raj Date: Wed, 12 May 2021 13:01:35 -0700 Subject: gen_build_files: Use C locale when calling sed on globbed files When include/applets.h is re-generated it generates code macros in include/applets.h e.g. IF_XZCAT(APPLET_ODDNAME(xzcat, unxz, BB_DIR_USR_BIN, BB_SUID_DROP, xzcat)) ... IF_CHVT(APPLET_NOEXEC(chvt, chvt, BB_DIR_USR_BIN, BB_SUID_DROP, chvt)) ... sed is used to process source files like below to feed into this header generation sed -n 's@^//applet:@@p' "$srctree"/*/*.c "$srctree"/*/*/*.c this means we let shell decide the order of .c files being fed into sed tool, applets.h has code snippets thats generated out of code fragments from these .c files and the order of the generated code depends on the order of .c files being fed to sed and then piped to generate tool, even though the generated code is logically same, it does result in re-odered code in applets.h based on which shell was used during build on exact busybox sources since sort order is different based on chosen locale and also default shell being bash or dash This sets the environment variable LC_ALL to the value C, which will enforce bytewise sorting, irrespective of the shell Signed-off-by: Khem Raj Signed-off-by: Denys Vlasenko --- scripts/gen_build_files.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/scripts/gen_build_files.sh b/scripts/gen_build_files.sh index 92de681ac..8b5b15a1b 100755 --- a/scripts/gen_build_files.sh +++ b/scripts/gen_build_files.sh @@ -4,6 +4,8 @@ # but users complain that many sed implementations # are misinterpreting --. +export LC_ALL=C + test $# -ge 2 || { echo "Syntax: $0 SRCTREE OBJTREE"; exit 1; } # cd to objtree -- cgit v1.2.3-55-g6feb From 1b30c63dfdd4f31faa6deda69345bf07f23aa334 Mon Sep 17 00:00:00 2001 From: Eicke Herbertz Date: Sat, 5 Jun 2021 11:42:06 +0000 Subject: shell: also do word splitting when -d DELIM is used The original commit 3bef5d89b0 introduced an additional check for an unset `opt_d` before doing word splitting. I'm unsure why it's there in the first place, but the commit message also describes a different behaviour than what -d actually does in bash, while the code mostly does the right thing. `opt_d` sets the line delimiter for read to stop reading and should not affect word splitting. Testcase: $ echo qwe rty | { read -d Z a b; echo a:$a b:$b; } a:qwe b:rty function old new delta shell_builtin_read 1314 1304 -10 Signed-off-by: Eicke Herbertz Signed-off-by: Denys Vlasenko --- shell/shell_common.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shell/shell_common.c b/shell/shell_common.c index f95a35e8b..e3d6783b5 100644 --- a/shell/shell_common.c +++ b/shell/shell_common.c @@ -230,7 +230,7 @@ shell_builtin_read(struct builtin_read_params *params) * without variable names (bash compat). * Thus, "read" and "read REPLY" are not the same. */ - if (!params->opt_d && argv[0]) { + if (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) { -- cgit v1.2.3-55-g6feb From 947a22b33262c93e5c50286b723b9086a33a4c1f Mon Sep 17 00:00:00 2001 From: Paul Spooren Date: Thu, 13 May 2021 23:39:05 +0200 Subject: build system: use SOURCE_DATE_EPOCH for timestamp if available The SOURCE_DATE_EPOCH is an effort of the Reproducible Builds organization to make timestamps/build dates in compiled tools deterministic over several repetitive builds. Busybox shows by default the build date timestamp which changes whenever compiled. To have a reasonable accurate build date while staying reproducible, it's possible to use the *date of last source modification* rather than the current time and date. Further information on SOURCE_DATE_EPOCH are available online [1]. This patch modifies `confdata.c` so that the content of the SOURCE_DATE_EPOCH env variable is used as timestamp. To be independent of different timezones between builds, whenever SOURCE_DATE_EPOCH is defined the GMT time is used. [1]: https://reproducible-builds.org/docs/source-date-epoch/ Signed-off-by: Paul Spooren Signed-off-by: Denys Vlasenko --- scripts/kconfig/confdata.c | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/scripts/kconfig/confdata.c b/scripts/kconfig/confdata.c index 9976011a9..249a3195e 100644 --- a/scripts/kconfig/confdata.c +++ b/scripts/kconfig/confdata.c @@ -342,6 +342,8 @@ int conf_write(const char *name) time_t now; int use_timestamp = 1; char *env; + char *source_date_epoch; + struct tm *build_time; dirname[0] = 0; if (name && name[0]) { @@ -378,7 +380,16 @@ int conf_write(const char *name) } sym = sym_lookup("KERNELVERSION", 0); sym_calc_value(sym); - time(&now); + + source_date_epoch = getenv("SOURCE_DATE_EPOCH"); + if (source_date_epoch && *source_date_epoch) { + now = strtoull(source_date_epoch, NULL, 10); + build_time = gmtime(&now); + } else { + time(&now); + build_time = localtime(&now); + } + env = getenv("KCONFIG_NOTIMESTAMP"); if (env && *env) use_timestamp = 0; @@ -398,14 +409,14 @@ int conf_write(const char *name) if (use_timestamp) { size_t ret = \ strftime(buf, sizeof(buf), "#define AUTOCONF_TIMESTAMP " - "\"%Y-%m-%d %H:%M:%S %Z\"\n", localtime(&now)); + "\"%Y-%m-%d %H:%M:%S %Z\"\n", build_time); /* if user has Factory timezone or some other odd install, the * %Z above will overflow the string leaving us with undefined * results ... so let's try again without the timezone. */ if (ret == 0) strftime(buf, sizeof(buf), "#define AUTOCONF_TIMESTAMP " - "\"%Y-%m-%d %H:%M:%S\"\n", localtime(&now)); + "\"%Y-%m-%d %H:%M:%S\"\n", build_time); } else { /* bbox */ strcpy(buf, "#define AUTOCONF_TIMESTAMP \"\"\n"); } -- cgit v1.2.3-55-g6feb From 8c1f8aa016faee3fa151d134c3544b2dd5bab832 Mon Sep 17 00:00:00 2001 From: Ján Sáreník Date: Sat, 5 Jun 2021 18:24:57 +0200 Subject: run-parts: permit dot later in file name MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit See https://gist.github.com/andyshinn/3ae01fa13cb64c9d36e7#gistcomment-2044506 To test: mkdir /tmp/testrp printf "#!/bin/sh\necho test\n" > /tmp/testrp/test.sh chmod a+x /tmp/testrp/* busybox run-parts /tmp/testrp test mv /tmp/testrp/test.sh /tmp/testrp/.test.sh busybox run-parts /tmp/testrp # no output function old new delta act 190 200 +10 Signed-off-by: Ján Sáreník Signed-off-by: Denys Vlasenko --- debianutils/run_parts.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/debianutils/run_parts.c b/debianutils/run_parts.c index 585a4b58f..f528c88ff 100644 --- a/debianutils/run_parts.c +++ b/debianutils/run_parts.c @@ -113,13 +113,24 @@ enum { }; /* Is this a valid filename (upper/lower alpha, digits, - * underscores, and hyphens only?) + * underscores, hyphens, and non-leading dots only?) */ static bool invalid_name(const char *c) { c = bb_basename(c); - while (*c && (isalnum(*c) || *c == '_' || *c == '-')) + if (*c == '.') + return *c; + + /* Debian run-parts 4.8.3, manpage: + * "...the names must consist entirely of ASCII letters, + * ASCII digits, ASCII underscores, and ASCII minus-hyphens. + * However, the name must not begin with a period." + * The last sentence is a giveaway that something is fishy + * (why mention leading dot if dots are not allowed anyway?). + * Yes, you guessed it right: in fact non-leading dots ARE allowed. + */ + while (isalnum(*c) || *c == '_' || *c == '-' || *c == '.') c++; return *c; /* TRUE (!0) if terminating NUL is not reached */ -- cgit v1.2.3-55-g6feb From a1b0d3856d9a0419cb74bf4c87525265871b5868 Mon Sep 17 00:00:00 2001 From: Ron Yorston Date: Thu, 23 Jul 2020 08:32:27 +0100 Subject: ash: add process substitution in bash-compatibility mode Process substitution is a Korn shell feature that's also available in bash and some other shells. This patch implements process substitution in ash when ASH_BASH_COMPAT is enabled. function old new delta argstr 1386 1522 +136 strtodest - 52 +52 readtoken1 3346 3392 +46 .rodata 183206 183250 +44 unwindredir - 28 +28 cmdloop 365 372 +7 static.spclchars 10 12 +2 cmdputs 380 367 -13 exitreset 86 69 -17 evalcommand 1754 1737 -17 varvalue 675 634 -41 ------------------------------------------------------------------------------ (add/remove: 2/0 grow/shrink: 5/4 up/down: 315/-88) Total: 227 bytes text data bss dec hex filename 953967 4219 1904 960090 ea65a busybox_old 954192 4219 1904 960315 ea73b busybox_unstripped v2: Replace array of file descriptors with a linked list. Include tests that were unaccountably omitted from v1. v3: Update linked list code to the intended version. v4: Change order of conditional code in cmdputs(). v5: Use existing popredir() mechanism to manage file descriptors. v6: Rebase to latest version of BusyBox ash. Reduce code churn. Signed-off-by: Ron Yorston Signed-off-by: Denys Vlasenko --- include/platform.h | 2 + shell/ash.c | 160 ++++++++++++++++++++++++--- shell/ash_test/ash-psubst/bash_procsub.right | 9 ++ shell/ash_test/ash-psubst/bash_procsub.tests | 33 ++++++ 4 files changed, 187 insertions(+), 17 deletions(-) create mode 100644 shell/ash_test/ash-psubst/bash_procsub.right create mode 100755 shell/ash_test/ash-psubst/bash_procsub.tests diff --git a/include/platform.h b/include/platform.h index 4633b2507..9e1fb047d 100644 --- a/include/platform.h +++ b/include/platform.h @@ -426,6 +426,8 @@ typedef unsigned smalluint; #define HAVE_SYS_STATFS_H 1 #define HAVE_PRINTF_PERCENTM 1 #define HAVE_WAIT3 1 +#define HAVE_DEV_FD 1 +#define DEV_FD_PREFIX "/dev/fd/" #if defined(__UCLIBC__) # if UCLIBC_VERSION < KERNEL_VERSION(0, 9, 32) diff --git a/shell/ash.c b/shell/ash.c index 6a16833b1..05c47950f 100644 --- a/shell/ash.c +++ b/shell/ash.c @@ -229,6 +229,14 @@ #define BASH_READ_D ENABLE_ASH_BASH_COMPAT #define IF_BASH_READ_D IF_ASH_BASH_COMPAT #define BASH_WAIT_N ENABLE_ASH_BASH_COMPAT +/* <(...) and >(...) */ +#if HAVE_DEV_FD +# define BASH_PROCESS_SUBST ENABLE_ASH_BASH_COMPAT +# define IF_BASH_PROCESS_SUBST IF_ASH_BASH_COMPAT +#else +# define BASH_PROCESS_SUBST 0 +# define IF_BASH_PROCESS_SUBST(...) +#endif #if defined(__ANDROID_API__) && __ANDROID_API__ <= 24 /* Bionic at least up to version 24 has no glob() */ @@ -804,6 +812,12 @@ out2str(const char *p) #define CTLENDARI ((unsigned char)'\207') #define CTLQUOTEMARK ((unsigned char)'\210') #define CTL_LAST CTLQUOTEMARK +#if BASH_PROCESS_SUBST +# define CTLTOPROC ((unsigned char)'\211') +# define CTLFROMPROC ((unsigned char)'\212') +# undef CTL_LAST +# define CTL_LAST CTLFROMPROC +#endif /* variable substitution byte (follows CTLVAR) */ #define VSTYPE 0x0f /* type of variable substitution */ @@ -1075,6 +1089,10 @@ trace_puts_quoted(char *s) case CTLESC: c = 'e'; goto backslash; case CTLVAR: c = 'v'; goto backslash; case CTLBACKQ: c = 'q'; goto backslash; +#if BASH_PROCESS_SUBST + case CTLTOPROC: c = 'p'; goto backslash; + case CTLFROMPROC: c = 'P'; goto backslash; +#endif backslash: putc('\\', tracefile); putc(c, tracefile); @@ -1236,8 +1254,17 @@ sharg(union node *arg, FILE *fp) case CTLENDVAR: putc('}', fp); break; +#if BASH_PROCESS_SUBST + case CTLTOPROC: + putc('>', fp); + goto backq; + case CTLFROMPROC: + putc('<', fp); + goto backq; +#endif case CTLBACKQ: putc('$', fp); + IF_BASH_PROCESS_SUBST(backq:) putc('(', fp); shtree(bqlist->n, -1, NULL, fp); putc(')', fp); @@ -3234,8 +3261,13 @@ static const uint8_t syntax_index_table[] ALIGN1 = { /* 134 CTLARI */ CCTL_CCTL_CCTL_CCTL, /* 135 CTLENDARI */ CCTL_CCTL_CCTL_CCTL, /* 136 CTLQUOTEMARK */ CCTL_CCTL_CCTL_CCTL, +#if BASH_PROCESS_SUBST + /* 137 CTLTOPROC */ CCTL_CCTL_CCTL_CCTL, + /* 138 CTLFROMPROC */ CCTL_CCTL_CCTL_CCTL, +#else /* 137 */ CWORD_CWORD_CWORD_CWORD, /* 138 */ CWORD_CWORD_CWORD_CWORD, +#endif /* 139 */ CWORD_CWORD_CWORD_CWORD, /* 140 */ CWORD_CWORD_CWORD_CWORD, /* 141 */ CWORD_CWORD_CWORD_CWORD, @@ -4849,9 +4881,24 @@ cmdputs(const char *s) quoted >>= 1; subtype = 0; goto dostr; +#if BASH_PROCESS_SUBST + case CTLBACKQ: + c = '$'; + str = "(...)"; + break; + case CTLTOPROC: + c = '>'; + str = "(...)"; + break; + case CTLFROMPROC: + c = '<'; + str = "(...)"; + break; +#else case CTLBACKQ: str = "$(...)"; goto dostr; +#endif #if ENABLE_FEATURE_SH_MATH case CTLARI: str = "$(("; @@ -5891,6 +5938,21 @@ redirectsafe(union node *redir, int flags) return err; } +#if BASH_PROCESS_SUBST +static void +pushfd(int fd) +{ + struct redirtab *sv; + + sv = ckzalloc(sizeof(*sv) + sizeof(sv->two_fd[0])); + sv->pair_count = 1; + sv->two_fd[0].orig_fd = fd; + sv->two_fd[0].moved_to = CLOSED; + sv->next = redirlist; + redirlist = sv; +} +#endif + static struct redirtab* pushredir(union node *redir) { @@ -6529,10 +6591,20 @@ evaltreenr(union node *n, int flags) } static void FAST_FUNC -evalbackcmd(union node *n, struct backcmd *result) +evalbackcmd(union node *n, struct backcmd *result + IF_BASH_PROCESS_SUBST(, int ctl)) { int pip[2]; struct job *jp; +#if BASH_PROCESS_SUBST + /* determine end of pipe used by parent (ip) and child (ic) */ + const int ip = (ctl == CTLTOPROC); + const int ic = !(ctl == CTLTOPROC); +#else + const int ctl = CTLBACKQ; + const int ip = 0; + const int ic = 1; +#endif result->fd = -1; result->buf = NULL; @@ -6544,15 +6616,17 @@ evalbackcmd(union node *n, struct backcmd *result) if (pipe(pip) < 0) ash_msg_and_raise_perror("can't create pipe"); - jp = makejob(/*n,*/ 1); - if (forkshell(jp, n, FORK_NOJOB) == 0) { + /* process substitution uses NULL job/node, like openhere() */ + jp = (ctl == CTLBACKQ) ? makejob(/*n,*/ 1) : NULL; + if (forkshell(jp, (ctl == CTLBACKQ) ? n : NULL, FORK_NOJOB) == 0) { /* child */ FORCE_INT_ON; - close(pip[0]); - if (pip[1] != 1) { - /*close(1);*/ - dup2_or_raise(pip[1], 1); - close(pip[1]); + close(pip[ip]); + /* ic is index of child end of pipe *and* fd to connect it to */ + if (pip[ic] != ic) { + /*close(ic);*/ + dup2_or_raise(pip[ic], ic); + close(pip[ic]); } /* TODO: eflag clearing makes the following not abort: * ash -c 'set -e; z=$(false;echo foo); echo $z' @@ -6568,8 +6642,18 @@ evalbackcmd(union node *n, struct backcmd *result) /* NOTREACHED */ } /* parent */ - close(pip[1]); - result->fd = pip[0]; +#if BASH_PROCESS_SUBST + if (ctl != CTLBACKQ) { + int fd = fcntl(pip[ip], F_DUPFD, 64); + if (fd > 0) { + close(pip[ip]); + pip[ip] = fd; + } + pushfd(pip[ip]); + } +#endif + close(pip[ic]); + result->fd = pip[ip]; result->jp = jp; out: @@ -6581,8 +6665,11 @@ evalbackcmd(union node *n, struct backcmd *result) * Expand stuff in backwards quotes. */ static void -expbackq(union node *cmd, int flag) +expbackq(union node *cmd, int flag IF_BASH_PROCESS_SUBST(, int ctl)) { +#if !BASH_PROCESS_SUBST + const int ctl = CTLBACKQ; +#endif struct backcmd in; int i; char buf[128]; @@ -6597,9 +6684,15 @@ expbackq(union node *cmd, int flag) INT_OFF; startloc = expdest - (char *)stackblock(); pushstackmark(&smark, startloc); - evalbackcmd(cmd, &in); + evalbackcmd(cmd, &in IF_BASH_PROCESS_SUBST(, ctl)); popstackmark(&smark); + if (ctl != CTLBACKQ) { + sprintf(buf, DEV_FD_PREFIX"%d", in.fd); + strtodest(buf, BASESYNTAX); + goto done; + } + p = in.buf; i = in.nleft; if (i == 0) @@ -6621,6 +6714,7 @@ expbackq(union node *cmd, int flag) close(in.fd); back_exitstatus = waitforjob(in.jp); } + done: INT_ON; /* Eat all trailing newlines */ @@ -6708,6 +6802,10 @@ argstr(char *p, int flag) CTLESC, CTLVAR, CTLBACKQ, +#if BASH_PROCESS_SUBST + CTLTOPROC, + CTLFROMPROC, +#endif #if ENABLE_FEATURE_SH_MATH CTLARI, CTLENDARI, @@ -6807,8 +6905,12 @@ argstr(char *p, int flag) p = evalvar(p, flag | inquotes); TRACE(("argstr: evalvar:'%s'\n", (char *)stackblock())); goto start; +#if BASH_PROCESS_SUBST + case CTLTOPROC: + case CTLFROMPROC: +#endif case CTLBACKQ: - expbackq(argbackq->n, flag | inquotes); + expbackq(argbackq->n, flag | inquotes IF_BASH_PROCESS_SUBST(, c)); goto start; #if ENABLE_FEATURE_SH_MATH case CTLARI: @@ -12198,8 +12300,9 @@ realeofmark(const char *eofmark) #define CHECKEND() {goto checkend; checkend_return:;} #define PARSEREDIR() {goto parseredir; parseredir_return:;} #define PARSESUB() {goto parsesub; parsesub_return:;} -#define PARSEBACKQOLD() {oldstyle = 1; goto parsebackq; parsebackq_oldreturn:;} -#define PARSEBACKQNEW() {oldstyle = 0; goto parsebackq; parsebackq_newreturn:;} +#define PARSEBACKQOLD() {style = OLD; goto parsebackq; parsebackq_oldreturn:;} +#define PARSEBACKQNEW() {style = NEW; goto parsebackq; parsebackq_newreturn:;} +#define PARSEPROCSUB() {style = PSUB; goto parsebackq; parsebackq_psreturn:;} #define PARSEARITH() {goto parsearith; parsearith_return:;} static int readtoken1(int c, int syntax, char *eofmark, int striptabs) @@ -12210,7 +12313,9 @@ readtoken1(int c, int syntax, char *eofmark, int striptabs) size_t len; struct nodelist *bqlist; smallint quotef; - smallint oldstyle; + smallint style; + enum { OLD, NEW, PSUB }; +#define oldstyle (style == OLD) smallint pssyntax; /* we are expanding a prompt string */ IF_BASH_DOLLAR_SQUOTE(smallint bash_dollar_squote = 0;) /* syntax stack */ @@ -12391,6 +12496,15 @@ readtoken1(int c, int syntax, char *eofmark, int striptabs) c = 0x100 + '>'; /* flag &> */ pungetc(); } +#endif +#if BASH_PROCESS_SUBST + if (c == '<' || c == '>') { + if (pgetc() == '(') { + PARSEPROCSUB(); + break; + } + pungetc(); + } #endif goto endword; /* exit outer loop */ } @@ -12876,9 +12990,18 @@ parsebackq: { memcpy(out, str, savelen); STADJUST(savelen, out); } - USTPUTC(CTLBACKQ, out); +#if BASH_PROCESS_SUBST + if (style == PSUB) + USTPUTC(c == '<' ? CTLFROMPROC : CTLTOPROC, out); + else +#endif + USTPUTC(CTLBACKQ, out); if (oldstyle) goto parsebackq_oldreturn; +#if BASH_PROCESS_SUBST + else if (style == PSUB) + goto parsebackq_psreturn; +#endif goto parsebackq_newreturn; } @@ -13329,6 +13452,9 @@ cmdloop(int top) #if JOBS if (doing_jobctl) showjobs(SHOW_CHANGED|SHOW_STDERR); +#endif +#if BASH_PROCESS_SUBST + unwindredir(NULL); #endif inter = 0; if (iflag && top) { diff --git a/shell/ash_test/ash-psubst/bash_procsub.right b/shell/ash_test/ash-psubst/bash_procsub.right new file mode 100644 index 000000000..aa16a96be --- /dev/null +++ b/shell/ash_test/ash-psubst/bash_procsub.right @@ -0,0 +1,9 @@ +hello 1 +hello 2 +hello 3 +<(echo "hello 0") +hello 4 +HI THERE +hello error +hello error +hello stderr diff --git a/shell/ash_test/ash-psubst/bash_procsub.tests b/shell/ash_test/ash-psubst/bash_procsub.tests new file mode 100755 index 000000000..63b836782 --- /dev/null +++ b/shell/ash_test/ash-psubst/bash_procsub.tests @@ -0,0 +1,33 @@ +# simplest case +cat <(echo "hello 1") + +# can have more than one +cat <(echo "hello 2") <(echo "hello 3") + +# doesn't work in quotes +echo "<(echo \"hello 0\")" + +# process substitution can be nested inside command substitution +echo $(cat <(echo "hello 4")) + +# example from http://wiki.bash-hackers.org/syntax/expansion/proc_subst +# process substitutions can be passed to a function as parameters or +# variables +f() { + cat "$1" >"$x" +} +x=>(tr '[:lower:]' '[:upper:]') f <(echo 'hi there') + +# process substitution can be combined with redirection on exec +rm -f err +# save stderr +exec 4>&2 +# copy stderr to a file +exec 2> >(tee err) +echo "hello error" >&2 +sync +# restore stderr +exec 2>&4 +cat err +rm -f err +echo "hello stderr" >&2 -- cgit v1.2.3-55-g6feb From 457825f77a7c7286647ee888a1000a6bb12ca8fc Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Sun, 6 Jun 2021 12:07:11 +0200 Subject: shells: do not allow bare "read" in non-bash compat configs On Sat, Feb 9, 2019 Cristian Ionescu-Idbohrn wrote: > In my case (at work), I have to watch and prevent people from doing > unportable things. For me, that's a burden. Signed-off-by: Denys Vlasenko --- libbb/getopt32.c | 11 +++++++---- shell/ash.c | 4 ++++ shell/hush.c | 13 ++++++++++--- shell/shell_common.c | 2 +- 4 files changed, 22 insertions(+), 8 deletions(-) diff --git a/libbb/getopt32.c b/libbb/getopt32.c index 378510063..5ab4d66f1 100644 --- a/libbb/getopt32.c +++ b/libbb/getopt32.c @@ -89,6 +89,12 @@ getopt32(char **argv, const char *applet_opts, ...) root:x:0:0:root:/root:/bin/bash user:x:500:500::/home/user:/bin/bash + "^" options string is "^optchars""\0""opt_complementary". + + "!" If the first character in the applet_opts string is a '!', + report bad options, missing required options, + inconsistent options with all-ones return value (instead of abort. + "+" If the first character in the applet_opts string is a plus, then option processing will stop as soon as a non-option is encountered in the argv array. Useful for applets like env @@ -96,10 +102,7 @@ getopt32(char **argv, const char *applet_opts, ...) env -i ls -d / Here we want env to process just the '-i', not the '-d'. - "!" Report bad options, missing required options, - inconsistent options with all-ones return value (instead of abort). - - "^" options string is "^optchars""\0""opt_complementary". + (The order of multiple prefixes must be "^!+...") uint32_t getopt32long(char **argv, const char *applet_opts, const char *logopts...) diff --git a/shell/ash.c b/shell/ash.c index 05c47950f..bcf7a3470 100644 --- a/shell/ash.c +++ b/shell/ash.c @@ -14161,6 +14161,10 @@ readcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) } } + if (!ENABLE_ASH_BASH_COMPAT && !argptr) { + bb_simple_error_msg("read: need variable name"); + return 1; + } params.argv = argptr; params.setvar = setvar0; params.ifs = bltinlookup("IFS"); /* can be NULL */ diff --git a/shell/hush.c b/shell/hush.c index 144ad3edd..77921e11c 100644 --- a/shell/hush.c +++ b/shell/hush.c @@ -4251,7 +4251,7 @@ static int done_word(struct parse_context *ctx) || endofname(command->argv[0])[0] != '\0' ) { /* bash says just "not a valid identifier" */ - syntax_error("not a valid identifier in for"); + syntax_error("bad variable name in for"); return 1; } /* Force FOR to have just one word (variable name) */ @@ -10799,10 +10799,17 @@ static int FAST_FUNC builtin_read(char **argv) */ params.read_flags = getopt32(argv, # if BASH_READ_D - "!srn:p:t:u:d:", ¶ms.opt_n, ¶ms.opt_p, ¶ms.opt_t, ¶ms.opt_u, ¶ms.opt_d + IF_NOT_HUSH_BASH_COMPAT("^") + "!srn:p:t:u:d:" IF_NOT_HUSH_BASH_COMPAT("\0" "-1"/*min 1 arg*/), + ¶ms.opt_n, ¶ms.opt_p, ¶ms.opt_t, ¶ms.opt_u, ¶ms.opt_d # else - "!srn:p:t:u:", ¶ms.opt_n, ¶ms.opt_p, ¶ms.opt_t, ¶ms.opt_u + IF_NOT_HUSH_BASH_COMPAT("^") + "!srn:p:t:u:" IF_NOT_HUSH_BASH_COMPAT("\0" "-1"/*min 1 arg*/), + ¶ms.opt_n, ¶ms.opt_p, ¶ms.opt_t, ¶ms.opt_u # endif +//TODO: print "read: need variable name" +//for the case of !BASH "read" with no args (now it fails silently) +//(or maybe extend getopt32() to emit a message if "-1" fails) ); if ((uint32_t)params.read_flags == (uint32_t)-1) return EXIT_FAILURE; diff --git a/shell/shell_common.c b/shell/shell_common.c index e3d6783b5..2e36d9208 100644 --- a/shell/shell_common.c +++ b/shell/shell_common.c @@ -59,7 +59,7 @@ shell_builtin_read(struct builtin_read_params *params) while (*pp) { if (endofname(*pp)[0] != '\0') { /* Mimic bash message */ - bb_error_msg("read: '%s': not a valid identifier", *pp); + bb_error_msg("read: '%s': bad variable name", *pp); return (const char *)(uintptr_t)1; } pp++; -- cgit v1.2.3-55-g6feb From 4c4b02c290ce8d24c467964eb25f5bfc8fc98c8b Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Sun, 6 Jun 2021 13:00:20 +0200 Subject: ash: save Ron's patch from oblivion Signed-off-by: Denys Vlasenko --- ...e_unnecessary_code_in_backquote_expansion.patch | 135 +++++++++++++++++++++ 1 file changed, 135 insertions(+) create mode 100644 shell/ash_remove_unnecessary_code_in_backquote_expansion.patch diff --git a/shell/ash_remove_unnecessary_code_in_backquote_expansion.patch b/shell/ash_remove_unnecessary_code_in_backquote_expansion.patch new file mode 100644 index 000000000..06067dde0 --- /dev/null +++ b/shell/ash_remove_unnecessary_code_in_backquote_expansion.patch @@ -0,0 +1,135 @@ +From: Herbert Xu +Date: Thu, 19 Apr 2018 18:16:12 +0800 + +> ash originally had support for omitting the fork when expanding a +> builtin in backquotes. dash has gradually been removing this support, +> most recently in commit 66b614e29038e31745c4a5d296f64f8d64f5c377 +> ("[EVAL] Remove unused EV_BACKCMD flag"). +> +> Some traces still remain, however. Remove: +> +> - the buf and nleft elements of the backcmd structure; +> - a misleading comment regarding handling of builtins. +> +> Signed-off-by: Ron Yorston + +Unfortunately we may need this at some point in the future due +to changes in POSIX. So let's keep it around for now until we +get things such as `jobs -p` to work. + +************************************* + +From: Ron Yorston +Date: Thu, 19 Apr 2018 17:18:47 +0100 + +>Unfortunately we may need this at some point in the future due +>to changes in POSIX. So let's keep it around for now until we +>get things such as `jobs -p` to work. + +As you wish. + +Something even more trivial I noticed later: the TRACE at the end of +expbackq incorrectly refers to the function as evalbackq. + +************************************* + +Date: Tue, 10 Apr 2018 13:23:35 +0100 +From: Ron Yorston +To: busybox@busybox.net +Subject: [PATCH] ash: remove unnecessary code in backquote expansion + +Some traces remain of ash's ancient support for omitting the fork when +expanding a builtin command in backquotes. + +Remove: + +- the buf and nleft elements of the backcmd structure; +- a misleading comment regarding handling of builtins. + +I've submitted a similar patch to dash. + +Signed-off-by: Ron Yorston +--- + shell/ash.c | 37 +++++++++---------------------------- + 1 file changed, 9 insertions(+), 28 deletions(-) + +diff --git a/shell/ash.c b/shell/ash.c +index 45c747dbc..6f1458722 100644 +--- a/shell/ash.c ++++ b/shell/ash.c +@@ -6356,15 +6356,12 @@ exptilde(char *startp, char *p, int flags) + } + + /* +- * Execute a command inside back quotes. If it's a builtin command, we +- * want to save its output in a block obtained from malloc. Otherwise +- * we fork off a subprocess and get the output of the command via a pipe. +- * Should be called with interrupts off. ++ * Execute a command inside back quotes. We fork off a subprocess and ++ * get the output of the command via a pipe. Should be called with ++ * interrupts off. + */ + struct backcmd { /* result of evalbackcmd */ + int fd; /* file descriptor to read from */ +- int nleft; /* number of chars in buffer */ +- char *buf; /* buffer */ + struct job *jp; /* job structure for command */ + }; + +@@ -6394,8 +6391,6 @@ evalbackcmd(union node *n, struct backcmd *result) + struct job *jp; + + result->fd = -1; +- result->buf = NULL; +- result->nleft = 0; + result->jp = NULL; + if (n == NULL) { + goto out; +@@ -6432,8 +6427,7 @@ evalbackcmd(union node *n, struct backcmd *result) + result->jp = jp; + + out: +- TRACE(("evalbackcmd done: fd=%d buf=0x%x nleft=%d jp=0x%x\n", +- result->fd, result->buf, result->nleft, result->jp)); ++ TRACE(("evalbackcmd done: fd=%d jp=0x%x\n", result->fd, result->jp)); + } + + /* +@@ -6445,7 +6439,6 @@ expbackq(union node *cmd, int flag) + struct backcmd in; + int i; + char buf[128]; +- char *p; + char *dest; + int startloc; + int syntax = flag & EXP_QUOTED ? DQSYNTAX : BASESYNTAX; +@@ -6457,24 +6450,12 @@ expbackq(union node *cmd, int flag) + evalbackcmd(cmd, &in); + popstackmark(&smark); + +- p = in.buf; +- i = in.nleft; +- if (i == 0) +- goto read; +- for (;;) { +- memtodest(p, i, syntax, flag & QUOTES_ESC); +- read: +- if (in.fd < 0) +- break; +- i = nonblock_immune_read(in.fd, buf, sizeof(buf)); +- TRACE(("expbackq: read returns %d\n", i)); +- if (i <= 0) +- break; +- p = buf; +- } +- +- free(in.buf); + if (in.fd >= 0) { ++ while ((i = nonblock_immune_read(in.fd, buf, sizeof(buf))) > 0) { ++ TRACE(("expbackq: read returns %d\n", i)); ++ memtodest(buf, i, syntax, flag & QUOTES_ESC); ++ } ++ + close(in.fd); + back_exitstatus = waitforjob(in.jp); + } -- cgit v1.2.3-55-g6feb From 4d983dcddeee94892d3072e84c7c9a01d4696055 Mon Sep 17 00:00:00 2001 From: Sergey Ponomarev Date: Sat, 9 Jan 2021 23:09:51 +0200 Subject: httpd_post_upload.cgi: use mktemp to avoid $RANDOM The $RANDOM variable may be disabled on ash compilation but we can safelly use mktemp instead. Signed-off-by: Sergey Ponomarev Signed-off-by: Denys Vlasenko --- networking/httpd_post_upload.cgi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/networking/httpd_post_upload.cgi b/networking/httpd_post_upload.cgi index e4ffd2bb5..538f7181b 100755 --- a/networking/httpd_post_upload.cgi +++ b/networking/httpd_post_upload.cgi @@ -18,7 +18,7 @@ # ^M <--------- extra empty line # -----------------------------29995809218093749221856446032--^M -file=/tmp/$$-$RANDOM +file=$(mktemp) CR=`printf '\r'` -- cgit v1.2.3-55-g6feb