aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRon Yorston <rmy@pobox.com>2021-06-07 11:34:06 +0100
committerRon Yorston <rmy@pobox.com>2021-06-07 11:34:06 +0100
commitabe872e2a0342357a5608342cb2892e94027b3e7 (patch)
tree297cdccf332fbb5e4eb31b1eac643180059f9b5f
parent1f33f42d7bcb019b268d938df643a7a785dc19ab (diff)
parent4d983dcddeee94892d3072e84c7c9a01d4696055 (diff)
downloadbusybox-w32-abe872e2a0342357a5608342cb2892e94027b3e7.tar.gz
busybox-w32-abe872e2a0342357a5608342cb2892e94027b3e7.tar.bz2
busybox-w32-abe872e2a0342357a5608342cb2892e94027b3e7.zip
Merge branch 'busybox' into merge
-rw-r--r--archival/cpio.c8
-rw-r--r--console-tools/showkey.c2
-rw-r--r--coreutils/head.c13
-rw-r--r--coreutils/ls.c30
-rw-r--r--coreutils/tail.c67
-rw-r--r--debianutils/run_parts.c15
-rw-r--r--editors/vi.c111
-rw-r--r--examples/udhcp/udhcpd.conf9
-rw-r--r--include/libbb.h3
-rw-r--r--libbb/getopt32.c11
-rw-r--r--libbb/signals.c2
-rw-r--r--libbb/xfuncs.c6
-rwxr-xr-xnetworking/httpd_post_upload.cgi2
-rw-r--r--networking/nslookup.c2
-rw-r--r--networking/tcpudp.c33
-rw-r--r--networking/udhcp/arpping.c8
-rw-r--r--networking/udhcp/common.c74
-rw-r--r--networking/udhcp/common.h13
-rw-r--r--networking/udhcp/d6_dhcpc.c188
-rw-r--r--networking/udhcp/d6_packet.c3
-rw-r--r--networking/udhcp/dhcpc.c322
-rw-r--r--networking/udhcp/dhcpc.h4
-rw-r--r--networking/udhcp/dhcpd.c11
-rw-r--r--networking/udhcp/packet.c3
-rw-r--r--networking/udhcp/socket.c2
-rw-r--r--runit/runsv.c39
-rw-r--r--runit/svlogd.c31
-rwxr-xr-xscripts/gen_build_files.sh2
-rw-r--r--scripts/kconfig/confdata.c17
-rw-r--r--shell/ash.c191
-rw-r--r--shell/ash_remove_unnecessary_code_in_backquote_expansion.patch135
-rw-r--r--shell/ash_test/ash-psubst/bash_procsub.right9
-rwxr-xr-xshell/ash_test/ash-psubst/bash_procsub.tests33
-rw-r--r--shell/hush.c13
-rw-r--r--shell/shell_common.c4
-rw-r--r--sysklogd/klogd.c2
36 files changed, 977 insertions, 441 deletions
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)
418 if (argv[0] == NULL) 418 if (argv[0] == NULL)
419 bb_show_usage(); 419 bb_show_usage();
420 if (opt & OPT_CREATE_LEADING_DIR) 420 if (opt & OPT_CREATE_LEADING_DIR)
421 mkdir(argv[0], 0777); 421 /* GNU cpio 2.13: "cpio -d -p a/b/c" works */
422 bb_make_directory(argv[0], -1, FILEUTILS_RECUR);
422 /* Crude existence check: 423 /* Crude existence check:
423 * close(xopen(argv[0], O_RDONLY | O_DIRECTORY)); 424 * close(xopen(argv[0], O_RDONLY | O_DIRECTORY));
424 * We can also xopen, fstat, IS_DIR, later fchdir. 425 * We can also xopen, fstat, IS_DIR, later fchdir.
@@ -428,6 +429,11 @@ int cpio_main(int argc UNUSED_PARAM, char **argv)
428 * a diffrerent problem earlier. 429 * a diffrerent problem earlier.
429 * This is good enough for now. 430 * This is good enough for now.
430 */ 431 */
432//FIXME: GNU cpio -d -p DIR does not immediately create DIR -
433//it just prepends "DIR/" to the names of files to be created.
434//The first file (fails to) be copied, and then the -d logic
435//triggers and creates all necessary directories.
436//IOW: bare "cpio -d -p DIR" + ^C shouldn't create anything.
431#if !BB_MMU 437#if !BB_MMU
432 pp.rd = 3; 438 pp.rd = 3;
433 pp.wr = 4; 439 pp.wr = 4;
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)
106 xioctl(STDIN_FILENO, KDSKBMODE, (void *)(ptrdiff_t)((option_mask32 & OPT_k) ? K_MEDIUMRAW : K_RAW)); 106 xioctl(STDIN_FILENO, KDSKBMODE, (void *)(ptrdiff_t)((option_mask32 & OPT_k) ? K_MEDIUMRAW : K_RAW));
107 107
108 // we should exit on any signal; signals should interrupt read 108 // we should exit on any signal; signals should interrupt read
109 bb_signals_recursive_norestart(BB_FATAL_SIGS, record_signo); 109 bb_signals_norestart(BB_FATAL_SIGS, record_signo);
110 110
111 // inform user that program ends after time of inactivity 111 // inform user that program ends after time of inactivity
112 printf(press_keys, "10s after last keypress"); 112 printf(press_keys, "10s after last keypress");
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 @@
29//usage:#define head_trivial_usage 29//usage:#define head_trivial_usage
30//usage: "[OPTIONS] [FILE]..." 30//usage: "[OPTIONS] [FILE]..."
31//usage:#define head_full_usage "\n\n" 31//usage:#define head_full_usage "\n\n"
32//usage: "Print first 10 lines of FILEs (or stdin) to stdout.\n" 32//usage: "Print first 10 lines of FILEs (or stdin).\n"
33//usage: "With more than one FILE, precede each with a filename header.\n" 33//usage: "With more than one FILE, precede each with a filename header.\n"
34//usage: "\n -n N[kbm] Print first N lines" 34//usage: "\n -n N[bkm] Print first N lines"
35//usage: IF_FEATURE_FANCY_HEAD(
36//usage: "\n -n -N[bkm] Print all except N last lines"
37//usage: "\n -c [-]N[bkm] Print first N bytes"
38//usage: )
39//usage: "\n (b:*512 k:*1024 m:*1024^2)"
35//usage: IF_FEATURE_FANCY_HEAD( 40//usage: IF_FEATURE_FANCY_HEAD(
36//usage: "\n -n -N[kbm] Print all except N last lines"
37//usage: "\n -c [-]N[kbm] Print first N bytes"
38//usage: "\n -q Never print headers" 41//usage: "\n -q Never print headers"
39//usage: "\n -v Always print headers" 42//usage: "\n -v Always print headers"
40//usage: ) 43//usage: )
41//usage: "\n"
42//usage: "\nN may be suffixed by k (x1024), b (x512), or m (x1024^2)."
43//usage: 44//usage:
44//usage:#define head_example_usage 45//usage:#define head_example_usage
45//usage: "$ head -n 2 /etc/passwd\n" 46//usage: "$ head -n 2 /etc/passwd\n"
diff --git a/coreutils/ls.c b/coreutils/ls.c
index 67f6c8c56..0330f62df 100644
--- a/coreutils/ls.c
+++ b/coreutils/ls.c
@@ -109,11 +109,11 @@
109//usage:#define ls_full_usage "\n\n" 109//usage:#define ls_full_usage "\n\n"
110//usage: "List directory contents\n" 110//usage: "List directory contents\n"
111//usage: "\n -1 One column output" 111//usage: "\n -1 One column output"
112//usage: "\n -a Include entries which start with ." 112//usage: "\n -a Include names starting with ."
113//usage: "\n -A Like -a, but exclude . and .." 113//usage: "\n -A Like -a, but exclude . and .."
114////usage: "\n -C List by columns" - don't show, this is a default anyway 114////usage: "\n -C List by columns" - don't show, this is a default anyway
115//usage: "\n -x List by lines" 115//usage: "\n -x List by lines"
116//usage: "\n -d List directory entries instead of contents" 116//usage: "\n -d List directory names, not contents"
117//usage: IF_FEATURE_LS_FOLLOWLINKS( 117//usage: IF_FEATURE_LS_FOLLOWLINKS(
118//usage: "\n -L Follow symlinks" 118//usage: "\n -L Follow symlinks"
119//usage: "\n -H Follow symlinks on command line" 119//usage: "\n -H Follow symlinks on command line"
@@ -122,10 +122,10 @@
122//usage: "\n -R Recurse" 122//usage: "\n -R Recurse"
123//usage: ) 123//usage: )
124//usage: IF_FEATURE_LS_FILETYPES( 124//usage: IF_FEATURE_LS_FILETYPES(
125//usage: "\n -p Append / to dir entries" 125//usage: "\n -p Append / to directory names"
126//usage: "\n -F Append indicator (one of */=@|) to entries" 126//usage: "\n -F Append indicator (one of */=@|) to names"
127//usage: ) 127//usage: )
128//usage: "\n -l Long listing format" 128//usage: "\n -l Long format"
129//usage: "\n -i List inode numbers" 129//usage: "\n -i List inode numbers"
130//usage: "\n -n List numeric UIDs and GIDs instead of names" 130//usage: "\n -n List numeric UIDs and GIDs instead of names"
131//usage: "\n -s List allocated blocks" 131//usage: "\n -s List allocated blocks"
@@ -134,7 +134,7 @@
134//usage: "\n -lu List atime" 134//usage: "\n -lu List atime"
135//usage: ) 135//usage: )
136//usage: IF_FEATURE_LS_TIMESTAMPS(IF_LONG_OPTS( 136//usage: IF_FEATURE_LS_TIMESTAMPS(IF_LONG_OPTS(
137//usage: "\n --full-time List full date and time" 137//usage: "\n --full-time List full date/time"
138//usage: )) 138//usage: ))
139//usage: IF_FEATURE_HUMAN_READABLE( 139//usage: IF_FEATURE_HUMAN_READABLE(
140//usage: "\n -h Human readable sizes (1K 243M 2G)" 140//usage: "\n -h Human readable sizes (1K 243M 2G)"
@@ -160,7 +160,7 @@
160//usage: "\n -w N Format N columns wide" 160//usage: "\n -w N Format N columns wide"
161//usage: ) 161//usage: )
162//usage: IF_FEATURE_LS_COLOR( 162//usage: IF_FEATURE_LS_COLOR(
163//usage: "\n --color[={always,never,auto}] Control coloring" 163//usage: "\n --color[={always,never,auto}]"
164//usage: ) 164//usage: )
165 165
166#include "libbb.h" 166#include "libbb.h"
@@ -187,7 +187,7 @@
187 187
188 188
189enum { 189enum {
190TERMINAL_WIDTH = 80, /* use 79 if terminal has linefold bug */ 190TERMINAL_WIDTH = 80, /* use 79 if terminal has linefold bug */
191 191
192SPLIT_FILE = 0, 192SPLIT_FILE = 0,
193SPLIT_DIR = 1, 193SPLIT_DIR = 1,
@@ -298,7 +298,7 @@ struct dnode {
298// but there are invisible fields as well 298// but there are invisible fields as well
299// (such as nanosecond-resolution timespamps) 299// (such as nanosecond-resolution timespamps)
300// and padding, which we also don't want to store. 300// and padding, which we also don't want to store.
301// We also can pre-parse dev_t dn_rdev (in glibc, it's huge). 301// We also pre-parse dev_t dn_rdev (in glibc, it's huge).
302// On 32-bit uclibc: dnode size went from 112 to 84 bytes. 302// On 32-bit uclibc: dnode size went from 112 to 84 bytes.
303// 303//
304 /* Same names as in struct stat, but with dn_ instead of st_ pfx: */ 304 /* Same names as in struct stat, but with dn_ instead of st_ pfx: */
@@ -1182,11 +1182,15 @@ int ls_main(int argc UNUSED_PARAM, char **argv)
1182 1182
1183#if ENABLE_FEATURE_LS_COLOR 1183#if ENABLE_FEATURE_LS_COLOR
1184 /* set G_show_color = 1/0 */ 1184 /* set G_show_color = 1/0 */
1185 if (ENABLE_FEATURE_LS_COLOR_IS_DEFAULT && isatty(STDOUT_FILENO)) { 1185 if (ENABLE_FEATURE_LS_COLOR_IS_DEFAULT && !is_TERM_dumb()) {
1186 char *p = getenv("LS_COLORS"); 1186 char *p = getenv("LS_COLORS");
1187 /* LS_COLORS is unset, or (not empty && not "none") ? */ 1187 /* LS_COLORS is unset, or (not empty && not "none") ? */
1188 if (!p || (p[0] && strcmp(p, "none") != 0)) 1188 if (!p || (p[0] && strcmp(p, "none") != 0)) {
1189 G_show_color = 1; 1189 if (isatty(STDOUT_FILENO)) {
1190 /* check isatty() last because it's expensive (syscall) */
1191 G_show_color = 1;
1192 }
1193 }
1190 } 1194 }
1191 if (opt & OPT_color) { 1195 if (opt & OPT_color) {
1192 if (color_opt[0] == 'n') 1196 if (color_opt[0] == 'n')
@@ -1195,7 +1199,7 @@ int ls_main(int argc UNUSED_PARAM, char **argv)
1195 case 3: 1199 case 3:
1196 case 4: 1200 case 4:
1197 case 5: 1201 case 5:
1198 if (isatty(STDOUT_FILENO)) { 1202 if (!is_TERM_dumb() && isatty(STDOUT_FILENO)) {
1199 case 0: 1203 case 0:
1200 case 1: 1204 case 1:
1201 case 2: 1205 case 2:
diff --git a/coreutils/tail.c b/coreutils/tail.c
index 08fde6cdd..6201eb023 100644
--- a/coreutils/tail.c
+++ b/coreutils/tail.c
@@ -48,19 +48,20 @@
48//usage:#define tail_trivial_usage 48//usage:#define tail_trivial_usage
49//usage: "[OPTIONS] [FILE]..." 49//usage: "[OPTIONS] [FILE]..."
50//usage:#define tail_full_usage "\n\n" 50//usage:#define tail_full_usage "\n\n"
51//usage: "Print last 10 lines of FILEs (or stdin) to stdout.\n" 51//usage: "Print last 10 lines of FILEs (or stdin) to.\n"
52//usage: "With more than one FILE, precede each with a filename header.\n" 52//usage: "With more than one FILE, precede each with a filename header.\n"
53//usage: "\n -f Print data as file grows" 53//usage: "\n -c [+]N[bkm] Print last N bytes"
54//usage: "\n -c [+]N[kbm] Print last N bytes" 54//usage: "\n -n N[bkm] Print last N lines"
55//usage: "\n -n N[kbm] Print last N lines" 55//usage: "\n -n +N[bkm] Start on Nth line and print the rest"
56//usage: "\n -n +N[kbm] Start on Nth line and print the rest" 56//usage: "\n (b:*512 k:*1024 m:*1024^2)"
57//usage: IF_FEATURE_FANCY_TAIL( 57//usage: IF_FEATURE_FANCY_TAIL(
58//usage: "\n -q Never print headers" 58//usage: "\n -q Never print headers"
59//usage: "\n -s SECONDS Wait SECONDS between reads with -f"
60//usage: "\n -v Always print headers" 59//usage: "\n -v Always print headers"
60//usage: )
61//usage: "\n -f Print data as file grows"
62//usage: IF_FEATURE_FANCY_TAIL(
61//usage: "\n -F Same as -f, but keep retrying" 63//usage: "\n -F Same as -f, but keep retrying"
62//usage: "\n" 64//usage: "\n -s SECONDS Wait SECONDS between reads with -f"
63//usage: "\nN may be suffixed by k (x1024), b (x512), or m (x1024^2)."
64//usage: ) 65//usage: )
65//usage: 66//usage:
66//usage:#define tail_example_usage 67//usage:#define tail_example_usage
@@ -118,7 +119,7 @@ int tail_main(int argc, char **argv)
118 119
119 char *tailbuf; 120 char *tailbuf;
120 size_t tailbufsize; 121 size_t tailbufsize;
121 unsigned header_threshhold = 1; 122 unsigned header_threshold = 1;
122 unsigned nfiles; 123 unsigned nfiles;
123 int i, opt; 124 int i, opt;
124 125
@@ -151,10 +152,10 @@ int tail_main(int argc, char **argv)
151 if (opt & 0x2) count = eat_num(str_c); // -c 152 if (opt & 0x2) count = eat_num(str_c); // -c
152 if (opt & 0x4) count = eat_num(str_n); // -n 153 if (opt & 0x4) count = eat_num(str_n); // -n
153#if ENABLE_FEATURE_FANCY_TAIL 154#if ENABLE_FEATURE_FANCY_TAIL
154 /* q: make it impossible for nfiles to be > header_threshhold */ 155 /* q: make it impossible for nfiles to be > header_threshold */
155 if (opt & 0x8) header_threshhold = UINT_MAX; // -q 156 if (opt & 0x8) header_threshold = UINT_MAX; // -q
156 //if (opt & 0x10) // -s 157 //if (opt & 0x10) // -s
157 if (opt & 0x20) header_threshhold = 0; // -v 158 if (opt & 0x20) header_threshold = 0; // -v
158# define FOLLOW_RETRY (opt & 0x40) 159# define FOLLOW_RETRY (opt & 0x40)
159#else 160#else
160# define FOLLOW_RETRY 0 161# define FOLLOW_RETRY 0
@@ -215,7 +216,7 @@ int tail_main(int argc, char **argv)
215 if (ENABLE_FEATURE_FANCY_TAIL && fd < 0) 216 if (ENABLE_FEATURE_FANCY_TAIL && fd < 0)
216 continue; /* may happen with -F */ 217 continue; /* may happen with -F */
217 218
218 if (nfiles > header_threshhold) { 219 if (nfiles > header_threshold) {
219 tail_xprint_header(fmt, argv[i]); 220 tail_xprint_header(fmt, argv[i]);
220 fmt = header_fmt_str; 221 fmt = header_fmt_str;
221 } 222 }
@@ -345,9 +346,11 @@ int tail_main(int argc, char **argv)
345 int nread; 346 int nread;
346 const char *filename = argv[i]; 347 const char *filename = argv[i];
347 int fd = fds[i]; 348 int fd = fds[i];
349 int new_fd = -1;
350 struct stat sbuf;
348 351
349 if (FOLLOW_RETRY) { 352 if (FOLLOW_RETRY) {
350 struct stat sbuf, fsbuf; 353 struct stat fsbuf;
351 354
352 if (fd < 0 355 if (fd < 0
353 || fstat(fd, &fsbuf) < 0 356 || fstat(fd, &fsbuf) < 0
@@ -355,39 +358,51 @@ int tail_main(int argc, char **argv)
355 || fsbuf.st_dev != sbuf.st_dev 358 || fsbuf.st_dev != sbuf.st_dev
356 || fsbuf.st_ino != sbuf.st_ino 359 || fsbuf.st_ino != sbuf.st_ino
357 ) { 360 ) {
358 int new_fd; 361 /* Looks like file has been created/renamed/deleted */
359
360 if (fd >= 0)
361 close(fd);
362 new_fd = open(filename, O_RDONLY); 362 new_fd = open(filename, O_RDONLY);
363 if (new_fd >= 0) { 363 if (new_fd >= 0) {
364 bb_error_msg("%s has %s; following end of new file", 364 bb_error_msg("%s has %s; following end of new file",
365 filename, (fd < 0) ? "appeared" : "been replaced" 365 filename, (fd < 0) ? "appeared" : "been replaced"
366 ); 366 );
367 if (fd < 0) {
368 /* No previously open fd for this file,
369 * start using new_fd immediately. */
370 fds[i] = fd = new_fd;
371 new_fd = -1;
372 }
367 } else if (fd >= 0) { 373 } else if (fd >= 0) {
368 bb_perror_msg("%s has become inaccessible", filename); 374 bb_perror_msg("%s has been renamed or deleted", filename);
369 } 375 }
370 fds[i] = fd = new_fd;
371 } 376 }
372 } 377 }
373 if (ENABLE_FEATURE_FANCY_TAIL && fd < 0) 378 if (ENABLE_FEATURE_FANCY_TAIL && fd < 0)
374 continue; 379 continue;
375 if (nfiles > header_threshhold) { 380 if (nfiles > header_threshold) {
376 fmt = header_fmt_str; 381 fmt = header_fmt_str;
377 } 382 }
378 for (;;) { 383 for (;;) {
379 /* tail -f keeps following files even if they are truncated */ 384 /* tail -f keeps following files even if they are truncated */
380 struct stat sbuf;
381 /* /proc files report zero st_size, don't lseek them */ 385 /* /proc files report zero st_size, don't lseek them */
382 if (fstat(fd, &sbuf) == 0 && sbuf.st_size > 0) { 386 if (fstat(fd, &sbuf) == 0
387 /* && S_ISREG(sbuf.st_mode) TODO? */
388 && sbuf.st_size > 0
389 ) {
383 off_t current = lseek(fd, 0, SEEK_CUR); 390 off_t current = lseek(fd, 0, SEEK_CUR);
384 if (sbuf.st_size < current) 391 if (sbuf.st_size < current) {
392 //bb_perror_msg("%s: file truncated", filename); - says coreutils 8.32
385 xlseek(fd, 0, SEEK_SET); 393 xlseek(fd, 0, SEEK_SET);
394 }
386 } 395 }
387 396
388 nread = tail_read(fd, tailbuf, BUFSIZ); 397 nread = tail_read(fd, tailbuf, BUFSIZ);
389 if (nread <= 0) 398 if (nread <= 0) {
390 break; 399 if (new_fd < 0)
400 break;
401 /* Switch to "tail -F"ing the new file */
402 xmove_fd(new_fd, fd);
403 new_fd = -1;
404 continue;
405 }
391 if (fmt && (fd != prev_fd)) { 406 if (fmt && (fd != prev_fd)) {
392 tail_xprint_header(fmt, filename); 407 tail_xprint_header(fmt, filename);
393 fmt = NULL; 408 fmt = NULL;
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 {
113}; 113};
114 114
115/* Is this a valid filename (upper/lower alpha, digits, 115/* Is this a valid filename (upper/lower alpha, digits,
116 * underscores, and hyphens only?) 116 * underscores, hyphens, and non-leading dots only?)
117 */ 117 */
118static bool invalid_name(const char *c) 118static bool invalid_name(const char *c)
119{ 119{
120 c = bb_basename(c); 120 c = bb_basename(c);
121 121
122 while (*c && (isalnum(*c) || *c == '_' || *c == '-')) 122 if (*c == '.')
123 return *c;
124
125 /* Debian run-parts 4.8.3, manpage:
126 * "...the names must consist entirely of ASCII letters,
127 * ASCII digits, ASCII underscores, and ASCII minus-hyphens.
128 * However, the name must not begin with a period."
129 * The last sentence is a giveaway that something is fishy
130 * (why mention leading dot if dots are not allowed anyway?).
131 * Yes, you guessed it right: in fact non-leading dots ARE allowed.
132 */
133 while (isalnum(*c) || *c == '_' || *c == '-' || *c == '.')
123 c++; 134 c++;
124 135
125 return *c; /* TRUE (!0) if terminating NUL is not reached */ 136 return *c; /* TRUE (!0) if terminating NUL is not reached */
diff --git a/editors/vi.c b/editors/vi.c
index 3dcde9b24..a4d6b21b4 100644
--- a/editors/vi.c
+++ b/editors/vi.c
@@ -307,7 +307,6 @@ struct globals {
307#define err_method (vi_setops & VI_ERR_METHOD) // indicate error with beep or flash 307#define err_method (vi_setops & VI_ERR_METHOD) // indicate error with beep or flash
308#define ignorecase (vi_setops & VI_IGNORECASE) 308#define ignorecase (vi_setops & VI_IGNORECASE)
309#define showmatch (vi_setops & VI_SHOWMATCH ) 309#define showmatch (vi_setops & VI_SHOWMATCH )
310#define openabove (vi_setops & VI_TABSTOP )
311// order of constants and strings must match 310// order of constants and strings must match
312#define OPTS_STR \ 311#define OPTS_STR \
313 "ai\0""autoindent\0" \ 312 "ai\0""autoindent\0" \
@@ -316,15 +315,10 @@ struct globals {
316 "ic\0""ignorecase\0" \ 315 "ic\0""ignorecase\0" \
317 "sm\0""showmatch\0" \ 316 "sm\0""showmatch\0" \
318 "ts\0""tabstop\0" 317 "ts\0""tabstop\0"
319#define set_openabove() (vi_setops |= VI_TABSTOP)
320#define clear_openabove() (vi_setops &= ~VI_TABSTOP)
321#else 318#else
322#define autoindent (0) 319#define autoindent (0)
323#define expandtab (0) 320#define expandtab (0)
324#define err_method (0) 321#define err_method (0)
325#define openabove (0)
326#define set_openabove() ((void)0)
327#define clear_openabove() ((void)0)
328#endif 322#endif
329 323
330#if ENABLE_FEATURE_VI_READONLY 324#if ENABLE_FEATURE_VI_READONLY
@@ -380,6 +374,9 @@ struct globals {
380#if ENABLE_FEATURE_VI_SEARCH 374#if ENABLE_FEATURE_VI_SEARCH
381 char *last_search_pattern; // last pattern from a '/' or '?' search 375 char *last_search_pattern; // last pattern from a '/' or '?' search
382#endif 376#endif
377#if ENABLE_FEATURE_VI_SETOPTS
378 int indentcol; // column of recently autoindent, 0 or -1
379#endif
383 380
384 // former statics 381 // former statics
385#if ENABLE_FEATURE_VI_YANKMARK 382#if ENABLE_FEATURE_VI_YANKMARK
@@ -505,6 +502,7 @@ struct globals {
505#define ioq_start (G.ioq_start ) 502#define ioq_start (G.ioq_start )
506#define dotcnt (G.dotcnt ) 503#define dotcnt (G.dotcnt )
507#define last_search_pattern (G.last_search_pattern) 504#define last_search_pattern (G.last_search_pattern)
505#define indentcol (G.indentcol )
508 506
509#define edit_file__cur_line (G.edit_file__cur_line) 507#define edit_file__cur_line (G.edit_file__cur_line)
510#define refresh__old_offset (G.refresh__old_offset) 508#define refresh__old_offset (G.refresh__old_offset)
@@ -2155,15 +2153,26 @@ static uintptr_t stupid_insert(char *p, char c) // stupidly insert the char c at
2155 return bias; 2153 return bias;
2156} 2154}
2157 2155
2156// find number of characters in indent, p must be at beginning of line
2157static size_t indent_len(char *p)
2158{
2159 char *r = p;
2160
2161 while (r < (end - 1) && isblank(*r))
2162 r++;
2163 return r - p;
2164}
2165
2158#if !ENABLE_FEATURE_VI_UNDO 2166#if !ENABLE_FEATURE_VI_UNDO
2159#define char_insert(a,b,c) char_insert(a,b) 2167#define char_insert(a,b,c) char_insert(a,b)
2160#endif 2168#endif
2161static char *char_insert(char *p, char c, int undo) // insert the char c at 'p' 2169static char *char_insert(char *p, char c, int undo) // insert the char c at 'p'
2162{ 2170{
2163#if ENABLE_FEATURE_VI_SETOPTS 2171#if ENABLE_FEATURE_VI_SETOPTS
2164 char *q;
2165 size_t len; 2172 size_t len;
2173 int col, ntab, nspc;
2166#endif 2174#endif
2175 char *bol = begin_line(p);
2167 2176
2168 if (c == 22) { // Is this an ctrl-V? 2177 if (c == 22) { // Is this an ctrl-V?
2169 p += stupid_insert(p, '^'); // use ^ to indicate literal next 2178 p += stupid_insert(p, '^'); // use ^ to indicate literal next
@@ -2185,25 +2194,36 @@ static char *char_insert(char *p, char c, int undo) // insert the char c at 'p'
2185 if ((p[-1] != '\n') && (dot > text)) { 2194 if ((p[-1] != '\n') && (dot > text)) {
2186 p--; 2195 p--;
2187 } 2196 }
2188 } else if (c == 4) { // ctrl-D reduces indentation 2197#if ENABLE_FEATURE_VI_SETOPTS
2189 int prev; 2198 if (autoindent) {
2190 char *r, *bol; 2199 len = indent_len(bol);
2191 bol = begin_line(p); 2200 if (len && get_column(bol + len) == indentcol) {
2192 for (r = bol; r < end_line(p); ++r) { 2201 // remove autoindent from otherwise empty line
2193 if (!isblank(*r)) 2202 text_hole_delete(bol, bol + len - 1, undo);
2194 break; 2203 p = bol;
2204 }
2195 } 2205 }
2196 2206#endif
2197 prev = prev_tabstop(get_column(r)); 2207 } else if (c == 4) { // ctrl-D reduces indentation
2208 char *r = bol + indent_len(bol);
2209 int prev = prev_tabstop(get_column(r));
2198 while (r > bol && get_column(r) > prev) { 2210 while (r > bol && get_column(r) > prev) {
2199 if (p > bol) 2211 if (p > bol)
2200 p--; 2212 p--;
2201 r--; 2213 r--;
2202 r = text_hole_delete(r, r, ALLOW_UNDO_QUEUED); 2214 r = text_hole_delete(r, r, ALLOW_UNDO_QUEUED);
2203 } 2215 }
2216
2217#if ENABLE_FEATURE_VI_SETOPTS
2218 if (autoindent && indentcol && r == end_line(p)) {
2219 // record changed size of autoindent
2220 indentcol = get_column(p);
2221 return p;
2222 }
2223#endif
2204#if ENABLE_FEATURE_VI_SETOPTS 2224#if ENABLE_FEATURE_VI_SETOPTS
2205 } else if (c == '\t' && expandtab) { // expand tab 2225 } else if (c == '\t' && expandtab) { // expand tab
2206 int col = get_column(p); 2226 col = get_column(p);
2207 col = next_tabstop(col) - col + 1; 2227 col = next_tabstop(col) - col + 1;
2208 while (col--) { 2228 while (col--) {
2209# if ENABLE_FEATURE_VI_UNDO 2229# if ENABLE_FEATURE_VI_UNDO
@@ -2242,27 +2262,46 @@ static char *char_insert(char *p, char c, int undo) // insert the char c at 'p'
2242 showmatching(p - 1); 2262 showmatching(p - 1);
2243 } 2263 }
2244 if (autoindent && c == '\n') { // auto indent the new line 2264 if (autoindent && c == '\n') { // auto indent the new line
2245 // use current/previous line as template 2265 // use indent of current/previous line
2246 q = openabove ? p : prev_line(p); 2266 bol = indentcol < 0 ? p : prev_line(p);
2247 len = strspn(q, " \t"); // space or tab 2267 len = indent_len(bol);
2248 if (openabove) { 2268 col = get_column(bol + len);
2249 p--; // this replaces dot_prev() in do_cmd() 2269
2250 q += len; // template will be shifted by text_hole_make() 2270 if (len && col == indentcol) {
2271 // previous line was empty except for autoindent
2272 // move the indent to the current line
2273 memmove(bol + 1, bol, len);
2274 *bol = '\n';
2275 return p;
2251 } 2276 }
2277
2278 if (indentcol < 0)
2279 p--; // open above, indent before newly inserted NL
2280
2252 if (len) { 2281 if (len) {
2253 uintptr_t bias; 2282 indentcol = col;
2254 bias = text_hole_make(p, len); 2283 if (expandtab) {
2255 p += bias; 2284 ntab = 0;
2256 q += bias; 2285 nspc = col;
2286 } else {
2287 ntab = col / tabstop;
2288 nspc = col % tabstop;
2289 }
2290 p += text_hole_make(p, ntab + nspc);
2257# if ENABLE_FEATURE_VI_UNDO 2291# if ENABLE_FEATURE_VI_UNDO
2258 undo_push_insert(p, len, undo); 2292 undo_push_insert(p, ntab + nspc, undo);
2259# endif 2293# endif
2260 memcpy(p, q, len); 2294 memset(p, '\t', ntab);
2261 p += len; 2295 p += ntab;
2296 memset(p, ' ', nspc);
2297 return p + nspc;
2262 } 2298 }
2263 } 2299 }
2264#endif 2300#endif
2265 } 2301 }
2302#if ENABLE_FEATURE_VI_SETOPTS
2303 indentcol = 0;
2304#endif
2266 return p; 2305 return p;
2267} 2306}
2268 2307
@@ -2647,7 +2686,6 @@ static void setops(char *args, int flg_no)
2647 index = 1 << (index >> 1); // convert to VI_bit 2686 index = 1 << (index >> 1); // convert to VI_bit
2648 2687
2649 if (index & VI_TABSTOP) { 2688 if (index & VI_TABSTOP) {
2650 // don't set this bit in vi_setops, it's reused as 'openabove'
2651 int t; 2689 int t;
2652 if (!eq || flg_no) // no "=NNN" or it is "notabstop"? 2690 if (!eq || flg_no) // no "=NNN" or it is "notabstop"?
2653 goto bad; 2691 goto bad;
@@ -3783,6 +3821,7 @@ static void do_cmd(int c)
3783# endif 3821# endif
3784 } while (--cmdcnt > 0); 3822 } while (--cmdcnt > 0);
3785 dot += cnt; 3823 dot += cnt;
3824 dot_skip_over_ws();
3786# if ENABLE_FEATURE_VI_YANKMARK && ENABLE_FEATURE_VI_VERBOSE_STATUS 3825# if ENABLE_FEATURE_VI_YANKMARK && ENABLE_FEATURE_VI_VERBOSE_STATUS
3787 yank_status("Put", p, i); 3826 yank_status("Put", p, i);
3788# endif 3827# endif
@@ -4109,17 +4148,18 @@ static void do_cmd(int c)
4109 break; 4148 break;
4110 case 'O': // O- open an empty line above 4149 case 'O': // O- open an empty line above
4111 dot_begin(); 4150 dot_begin();
4112 set_openabove(); 4151#if ENABLE_FEATURE_VI_SETOPTS
4152 indentcol = -1;
4153#endif
4113 goto dc3; 4154 goto dc3;
4114 case 'o': // o- open an empty line below 4155 case 'o': // o- open an empty line below
4115 dot_end(); 4156 dot_end();
4116 dc3: 4157 dc3:
4117 dot = char_insert(dot, '\n', ALLOW_UNDO); 4158 dot = char_insert(dot, '\n', ALLOW_UNDO);
4118 if (c == 'O' && !autoindent) { 4159 if (c == 'O' && !autoindent) {
4119 // done in char_insert() for openabove+autoindent 4160 // done in char_insert() for 'O'+autoindent
4120 dot_prev(); 4161 dot_prev();
4121 } 4162 }
4122 clear_openabove();
4123 goto dc_i; 4163 goto dc_i;
4124 break; 4164 break;
4125 case 'R': // R- continuous Replace char 4165 case 'R': // R- continuous Replace char
@@ -4238,6 +4278,9 @@ static void do_cmd(int c)
4238 if (dot != (end-1)) { 4278 if (dot != (end-1)) {
4239 dot_prev(); 4279 dot_prev();
4240 } 4280 }
4281 } else if (c == 'd') {
4282 dot_begin();
4283 dot_skip_over_ws();
4241 } else { 4284 } else {
4242 dot = save_dot; 4285 dot = save_dot;
4243 } 4286 }
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
79option 0x08 01020304 # option 8: "cookie server IP addr: 1.2.3.4" 79option 0x08 01020304 # option 8: "cookie server IP addr: 1.2.3.4"
80option 14 "dumpfile" 80option 14 "dumpfile"
81 81
82# Currently supported options [hex option value] (for more info, see options.c): 82# Currently supported options [hex option value] (for more info, see common.c):
83#opt lease NUM # [0x33] 83#opt lease NUM # [0x33]
84#opt subnet IP # [0x01] 84#opt subnet IP # [0x01]
85#opt broadcast IP # [0x1c] 85#opt broadcast IP # [0x1c]
@@ -91,17 +91,20 @@ option 14 "dumpfile"
91#opt search STRING_LIST # [0x77] search domains 91#opt search STRING_LIST # [0x77] search domains
92#opt nisdomain STRING # [0x28] 92#opt nisdomain STRING # [0x28]
93#opt timezone NUM # [0x02] (localtime - UTC_time) in seconds. signed 93#opt timezone NUM # [0x02] (localtime - UTC_time) in seconds. signed
94#opt tzstr STRING # [0x64] RFC 4833. IEEE 1003.1 TZ string
95#opt tzdbstr STRING # [0x65] RFC 4833. Reference to the TZ database string
94#opt tftp STRING # [0x42] tftp server name 96#opt tftp STRING # [0x42] tftp server name
95#opt bootfile STRING # [0x43] tftp file to download (e.g. kernel image) 97#opt bootfile STRING # [0x43] tftp file to download (e.g. kernel image)
96#opt bootsize NUM # [0x0d] size of that file 98#opt bootsize NUM # [0x0d] size of that file
97#opt rootpath STRING # [0x11] (NFS) path to mount as root fs 99#opt rootpath STRING # [0x11] (NFS) path to mount as root fs
98#opt wpad STRING # [0xfc] Web Proxy Auto Discovery Protocol 100#opt wpad STRING # [0xfc] Web Proxy Auto Discovery Protocol
99#opt serverid IP # [0x36] default: server's IP 101#opt serverid IP # [0x36] default: server's IP
100#opt message STRING # [0x38] error message (udhcpd sends it on success too) 102#opt message STRING # [0x38] error message (if set, udhcpd would send it on success too)
101#opt vlanid NUM # [0x84] 802.1P VLAN ID 103#opt vlanid NUM # [0x84] 802.1P VLAN ID
102#opt vlanpriority NUM # [0x85] 802.1Q VLAN priority 104#opt vlanpriority NUM # [0x85] 802.1Q VLAN priority
105#opt vendor STRING # [0x3c] client's vendor string, not intended to be sent by DHCP servers
103# RFC 5071: PXELINUX Options 106# RFC 5071: PXELINUX Options
104#opt 0xd0 F100747E # [0xd0] magic 107#opt 0xd0 F100747E # [0xd0] magic needed for other options to be recognized by clients
105#opt pxeconffile STRING # [0xd1] 108#opt pxeconffile STRING # [0xd1]
106#opt pxepathprefix STRING # [0xd2] 109#opt pxepathprefix STRING # [0xd2]
107#opt reboottime NUM # [0xd3] bootstrap timeout 110#opt reboottime NUM # [0xd3] bootstrap timeout
diff --git a/include/libbb.h b/include/libbb.h
index 3b2d23df8..e80ed1e32 100644
--- a/include/libbb.h
+++ b/include/libbb.h
@@ -629,7 +629,7 @@ void bb_signals(int sigs, void (*f)(int)) FAST_FUNC;
629/* Unlike signal() and bb_signals, sets handler with sigaction() 629/* Unlike signal() and bb_signals, sets handler with sigaction()
630 * and in a way that while signal handler is run, no other signals 630 * and in a way that while signal handler is run, no other signals
631 * will be blocked; syscalls will not be restarted: */ 631 * will be blocked; syscalls will not be restarted: */
632void bb_signals_recursive_norestart(int sigs, void (*f)(int)) FAST_FUNC; 632void bb_signals_norestart(int sigs, void (*f)(int)) FAST_FUNC;
633/* syscalls like read() will be interrupted with EINTR: */ 633/* syscalls like read() will be interrupted with EINTR: */
634void signal_no_SA_RESTART_empty_mask(int sig, void (*handler)(int)) FAST_FUNC; 634void signal_no_SA_RESTART_empty_mask(int sig, void (*handler)(int)) FAST_FUNC;
635/* syscalls like read() won't be interrupted (though select/poll will be): */ 635/* syscalls like read() won't be interrupted (though select/poll will be): */
@@ -1817,6 +1817,7 @@ extern void print_login_issue(const char *issue_file, const char *tty) FAST_FUNC
1817extern void print_login_prompt(void) FAST_FUNC; 1817extern void print_login_prompt(void) FAST_FUNC;
1818 1818
1819char *xmalloc_ttyname(int fd) FAST_FUNC RETURNS_MALLOC; 1819char *xmalloc_ttyname(int fd) FAST_FUNC RETURNS_MALLOC;
1820int is_TERM_dumb(void) FAST_FUNC;
1820/* NB: typically you want to pass fd 0, not 1. Think 'applet | grep something' */ 1821/* NB: typically you want to pass fd 0, not 1. Think 'applet | grep something' */
1821int get_terminal_width_height(int fd, unsigned *width, unsigned *height) FAST_FUNC; 1822int get_terminal_width_height(int fd, unsigned *width, unsigned *height) FAST_FUNC;
1822int get_terminal_width(int fd) FAST_FUNC; 1823int get_terminal_width(int fd) FAST_FUNC;
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, ...)
89 root:x:0:0:root:/root:/bin/bash 89 root:x:0:0:root:/root:/bin/bash
90 user:x:500:500::/home/user:/bin/bash 90 user:x:500:500::/home/user:/bin/bash
91 91
92 "^" options string is "^optchars""\0""opt_complementary".
93
94 "!" If the first character in the applet_opts string is a '!',
95 report bad options, missing required options,
96 inconsistent options with all-ones return value (instead of abort.
97
92 "+" If the first character in the applet_opts string is a plus, 98 "+" If the first character in the applet_opts string is a plus,
93 then option processing will stop as soon as a non-option is 99 then option processing will stop as soon as a non-option is
94 encountered in the argv array. Useful for applets like env 100 encountered in the argv array. Useful for applets like env
@@ -96,10 +102,7 @@ getopt32(char **argv, const char *applet_opts, ...)
96 env -i ls -d / 102 env -i ls -d /
97 Here we want env to process just the '-i', not the '-d'. 103 Here we want env to process just the '-i', not the '-d'.
98 104
99 "!" Report bad options, missing required options, 105 (The order of multiple prefixes must be "^!+...")
100 inconsistent options with all-ones return value (instead of abort).
101
102 "^" options string is "^optchars""\0""opt_complementary".
103 106
104uint32_t 107uint32_t
105getopt32long(char **argv, const char *applet_opts, const char *logopts...) 108getopt32long(char **argv, const char *applet_opts, const char *logopts...)
diff --git a/libbb/signals.c b/libbb/signals.c
index e64ba5023..c09a562ed 100644
--- a/libbb/signals.c
+++ b/libbb/signals.c
@@ -58,7 +58,7 @@ void FAST_FUNC bb_signals(int sigs, void (*f)(int))
58 } 58 }
59} 59}
60 60
61void FAST_FUNC bb_signals_recursive_norestart(int sigs, void (*f)(int)) 61void FAST_FUNC bb_signals_norestart(int sigs, void (*f)(int))
62{ 62{
63 int sig_no = 0; 63 int sig_no = 0;
64 int bit = 1; 64 int bit = 1;
diff --git a/libbb/xfuncs.c b/libbb/xfuncs.c
index ef990118e..102b5a227 100644
--- a/libbb/xfuncs.c
+++ b/libbb/xfuncs.c
@@ -305,6 +305,12 @@ int FAST_FUNC get_terminal_width(int fd)
305 return width; 305 return width;
306} 306}
307 307
308int FAST_FUNC is_TERM_dumb(void)
309{
310 char *term = getenv("TERM");
311 return term && strcmp(term, "dumb") == 0;
312}
313
308#if !ENABLE_PLATFORM_MINGW32 314#if !ENABLE_PLATFORM_MINGW32
309int FAST_FUNC tcsetattr_stdin_TCSANOW(const struct termios *tp) 315int FAST_FUNC tcsetattr_stdin_TCSANOW(const struct termios *tp)
310{ 316{
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 @@
18# ^M <--------- extra empty line 18# ^M <--------- extra empty line
19# -----------------------------29995809218093749221856446032--^M 19# -----------------------------29995809218093749221856446032--^M
20 20
21file=/tmp/$$-$RANDOM 21file=$(mktemp)
22 22
23CR=`printf '\r'` 23CR=`printf '\r'`
24 24
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 @@
25//usage:#define nslookup_full_usage "\n\n" 25//usage:#define nslookup_full_usage "\n\n"
26//usage: "Query DNS about HOST" 26//usage: "Query DNS about HOST"
27//usage: IF_FEATURE_NSLOOKUP_BIG("\n") 27//usage: IF_FEATURE_NSLOOKUP_BIG("\n")
28//usage: IF_FEATURE_NSLOOKUP_BIG("\nQUERY_TYPE: soa,ns,a,"IF_FEATURE_IPV6("aaaa,")"cname,mx,txt,ptr,any") 28//usage: IF_FEATURE_NSLOOKUP_BIG("\nQUERY_TYPE: soa,ns,a,"IF_FEATURE_IPV6("aaaa,")"cname,mx,txt,ptr,srv,any")
29//usage:#define nslookup_example_usage 29//usage:#define nslookup_example_usage
30//usage: "$ nslookup localhost\n" 30//usage: "$ nslookup localhost\n"
31//usage: "Server: default\n" 31//usage: "Server: default\n"
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 {
216 OPT_K = (1 << 16), 216 OPT_K = (1 << 16),
217}; 217};
218 218
219static void connection_status(void) 219static void if_verbose_print_connection_status(void)
220{ 220{
221 /* "only 1 client max" desn't need this */ 221 if (verbose) {
222 if (cmax > 1) 222 /* "only 1 client max" desn't need this */
223 bb_error_msg("status %u/%u", cnum, cmax); 223 if (cmax > 1)
224 bb_error_msg("status %u/%u", cnum, cmax);
225 }
224} 226}
225 227
228/* SIGCHLD handler is reentrancy-safe because SIGCHLD is unmasked
229 * only over accept() or recvfrom() calls, not over memory allocations
230 * or printouts. Do need to save/restore errno in order not to mangle
231 * these syscalls' error code, if any.
232 */
226static void sig_child_handler(int sig UNUSED_PARAM) 233static void sig_child_handler(int sig UNUSED_PARAM)
227{ 234{
228 int wstat; 235 int wstat;
229 pid_t pid; 236 pid_t pid;
237 int sv_errno = errno;
230 238
231 while ((pid = wait_any_nohang(&wstat)) > 0) { 239 while ((pid = wait_any_nohang(&wstat)) > 0) {
232 if (max_per_host) 240 if (max_per_host)
@@ -236,8 +244,8 @@ static void sig_child_handler(int sig UNUSED_PARAM)
236 if (verbose) 244 if (verbose)
237 print_waitstat(pid, wstat); 245 print_waitstat(pid, wstat);
238 } 246 }
239 if (verbose) 247 if_verbose_print_connection_status();
240 connection_status(); 248 errno = sv_errno;
241} 249}
242 250
243int tcpudpsvd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 251int tcpudpsvd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
@@ -458,7 +466,7 @@ int tcpudpsvd_main(int argc UNUSED_PARAM, char **argv)
458 xconnect(0, &remote.u.sa, sa_len); 466 xconnect(0, &remote.u.sa, sa_len);
459 /* hole? at this point we have no wildcard udp socket... 467 /* hole? at this point we have no wildcard udp socket...
460 * can this cause clients to get "port unreachable" icmp? 468 * can this cause clients to get "port unreachable" icmp?
461 * Yup, time window is very small, but it exists (is it?) */ 469 * Yup, time window is very small, but it exists (does it?) */
462 /* ..."open new socket", continued */ 470 /* ..."open new socket", continued */
463 xbind(sock, &lsa->u.sa, sa_len); 471 xbind(sock, &lsa->u.sa, sa_len);
464 socket_want_pktinfo(sock); 472 socket_want_pktinfo(sock);
@@ -491,8 +499,7 @@ int tcpudpsvd_main(int argc UNUSED_PARAM, char **argv)
491 if (pid != 0) { 499 if (pid != 0) {
492 /* Parent */ 500 /* Parent */
493 cnum++; 501 cnum++;
494 if (verbose) 502 if_verbose_print_connection_status();
495 connection_status();
496 if (hccp) 503 if (hccp)
497 hccp->pid = pid; 504 hccp->pid = pid;
498 /* clean up changes done by vforked child */ 505 /* clean up changes done by vforked child */
@@ -586,8 +593,14 @@ int tcpudpsvd_main(int argc UNUSED_PARAM, char **argv)
586 593
587 xdup2(0, 1); 594 xdup2(0, 1);
588 595
596 /* Restore signal handling for the to-be-execed process */
589 signal(SIGPIPE, SIG_DFL); /* this one was SIG_IGNed */ 597 signal(SIGPIPE, SIG_DFL); /* this one was SIG_IGNed */
590 /* Non-ignored signals revert to SIG_DFL on exec anyway */ 598 /* Non-ignored signals revert to SIG_DFL on exec anyway
599 * But we can get signals BEFORE execvp(), this is unlikely
600 * but it would invoke sig_child_handler(), which would
601 * check waitpid(WNOHANG), then print "status N/M" if verbose.
602 * I guess we can live with that possibility.
603 */
591 /*signal(SIGCHLD, SIG_DFL);*/ 604 /*signal(SIGCHLD, SIG_DFL);*/
592 sig_unblock(SIGCHLD); 605 sig_unblock(SIGCHLD);
593 606
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,
47 int rv = 1; /* "no reply received" yet */ 47 int rv = 1; /* "no reply received" yet */
48 struct sockaddr addr; /* for interface name */ 48 struct sockaddr addr; /* for interface name */
49 struct arpMsg arp; 49 struct arpMsg arp;
50 const char *msg;
50 51
51 if (!timeo) 52 if (!timeo)
52 return 1; 53 return 1;
@@ -58,7 +59,7 @@ int FAST_FUNC arpping(uint32_t test_nip,
58 } 59 }
59 60
60 if (setsockopt_broadcast(s) == -1) { 61 if (setsockopt_broadcast(s) == -1) {
61 bb_simple_perror_msg("can't enable bcast on raw socket"); 62 bb_simple_perror_msg("can't enable bcast on ARP socket");
62 goto ret; 63 goto ret;
63 } 64 }
64 65
@@ -131,6 +132,9 @@ int FAST_FUNC arpping(uint32_t test_nip,
131 132
132 ret: 133 ret:
133 close(s); 134 close(s);
134 log1("%srp reply received for this address", rv ? "no a" : "A"); 135 msg = "no ARP reply received for this address";
136 if (rv == 0)
137 msg += 3;
138 log1s(msg);
135 return rv; 139 return rv;
136} 140}
diff --git a/networking/udhcp/common.c b/networking/udhcp/common.c
index f2d6907ad..31e525cb0 100644
--- a/networking/udhcp/common.c
+++ b/networking/udhcp/common.c
@@ -49,6 +49,7 @@ const struct dhcp_optflag dhcp_optflags[] = {
49 { OPTION_U32 , 0x33 }, /* DHCP_LEASE_TIME */ 49 { OPTION_U32 , 0x33 }, /* DHCP_LEASE_TIME */
50 { OPTION_IP , 0x36 }, /* DHCP_SERVER_ID */ 50 { OPTION_IP , 0x36 }, /* DHCP_SERVER_ID */
51 { OPTION_STRING , 0x38 }, /* DHCP_ERR_MESSAGE */ 51 { OPTION_STRING , 0x38 }, /* DHCP_ERR_MESSAGE */
52 { OPTION_STRING , 0x3c }, /* DHCP_VENDOR */
52//TODO: must be combined with 'sname' and 'file' handling: 53//TODO: must be combined with 'sname' and 'file' handling:
53 { OPTION_STRING_HOST , 0x42 }, /* DHCP_TFTP_SERVER_NAME */ 54 { OPTION_STRING_HOST , 0x42 }, /* DHCP_TFTP_SERVER_NAME */
54 { OPTION_STRING , 0x43 }, /* DHCP_BOOT_FILE */ 55 { OPTION_STRING , 0x43 }, /* DHCP_BOOT_FILE */
@@ -83,7 +84,6 @@ const struct dhcp_optflag dhcp_optflags[] = {
83 { OPTION_U8 , 0x35 }, /* DHCP_MESSAGE_TYPE */ 84 { OPTION_U8 , 0x35 }, /* DHCP_MESSAGE_TYPE */
84 { OPTION_U16 , 0x39 }, /* DHCP_MAX_SIZE */ 85 { OPTION_U16 , 0x39 }, /* DHCP_MAX_SIZE */
85//looks like these opts will work just fine even without these defs: 86//looks like these opts will work just fine even without these defs:
86// { OPTION_STRING , 0x3c }, /* DHCP_VENDOR */
87// /* not really a string: */ 87// /* not really a string: */
88// { OPTION_STRING , 0x3d }, /* DHCP_CLIENT_ID */ 88// { OPTION_STRING , 0x3d }, /* DHCP_CLIENT_ID */
89 { 0, 0 } /* zeroed terminating entry */ 89 { 0, 0 } /* zeroed terminating entry */
@@ -120,6 +120,7 @@ const char dhcp_option_strings[] ALIGN1 =
120 "lease" "\0" /* DHCP_LEASE_TIME */ 120 "lease" "\0" /* DHCP_LEASE_TIME */
121 "serverid" "\0" /* DHCP_SERVER_ID */ 121 "serverid" "\0" /* DHCP_SERVER_ID */
122 "message" "\0" /* DHCP_ERR_MESSAGE */ 122 "message" "\0" /* DHCP_ERR_MESSAGE */
123 "vendor" "\0" /* DHCP_VENDOR */
123 "tftp" "\0" /* DHCP_TFTP_SERVER_NAME*/ 124 "tftp" "\0" /* DHCP_TFTP_SERVER_NAME*/
124 "bootfile" "\0" /* DHCP_BOOT_FILE */ 125 "bootfile" "\0" /* DHCP_BOOT_FILE */
125// "userclass" "\0" /* DHCP_USER_CLASS */ 126// "userclass" "\0" /* DHCP_USER_CLASS */
@@ -184,6 +185,13 @@ const uint8_t dhcp_option_lengths[] ALIGN1 = {
184 */ 185 */
185}; 186};
186 187
188#if defined CONFIG_UDHCP_DEBUG && CONFIG_UDHCP_DEBUG >= 1
189void FAST_FUNC log1s(const char *msg)
190{
191 if (dhcp_verbose >= 1)
192 bb_simple_info_msg(msg);
193}
194#endif
187 195
188#if defined CONFIG_UDHCP_DEBUG && CONFIG_UDHCP_DEBUG >= 2 196#if defined CONFIG_UDHCP_DEBUG && CONFIG_UDHCP_DEBUG >= 2
189static void log_option(const char *pfx, const uint8_t *opt) 197static void log_option(const char *pfx, const uint8_t *opt)
@@ -420,6 +428,40 @@ int FAST_FUNC udhcp_str2nip(const char *str, void *arg)
420 return 1; 428 return 1;
421} 429}
422 430
431void* FAST_FUNC udhcp_insert_new_option(
432 struct option_set **opt_list,
433 unsigned code,
434 unsigned length,
435 bool dhcpv6)
436{
437 IF_NOT_UDHCPC6(bool dhcpv6 = 0;)
438 struct option_set *new, **curr;
439
440 log2("attaching option %02x to list", code);
441 new = xmalloc(sizeof(*new));
442 if (!dhcpv6) {
443 new->data = xzalloc(length + OPT_DATA);
444 new->data[OPT_CODE] = code;
445 new->data[OPT_LEN] = length;
446 } else {
447 new->data = xzalloc(length + D6_OPT_DATA);
448 new->data[D6_OPT_CODE] = code >> 8;
449 new->data[D6_OPT_CODE + 1] = code & 0xff;
450 new->data[D6_OPT_LEN] = length >> 8;
451 new->data[D6_OPT_LEN + 1] = length & 0xff;
452 }
453
454 curr = opt_list;
455//FIXME: DHCP6 codes > 255!!
456 while (*curr && (*curr)->data[OPT_CODE] < code)
457 curr = &(*curr)->next;
458
459 new->next = *curr;
460 *curr = new;
461
462 return new->data;
463}
464
423/* udhcp_str2optset: 465/* udhcp_str2optset:
424 * Parse string option representation to binary form and add it to opt_list. 466 * Parse string option representation to binary form and add it to opt_list.
425 * Called to parse "udhcpc -x OPTNAME:OPTVAL" 467 * Called to parse "udhcpc -x OPTNAME:OPTVAL"
@@ -459,32 +501,12 @@ static NOINLINE void attach_option(
459 501
460 existing = udhcp_find_option(*opt_list, optflag->code); 502 existing = udhcp_find_option(*opt_list, optflag->code);
461 if (!existing) { 503 if (!existing) {
462 struct option_set *new, **curr;
463
464 /* make a new option */ 504 /* make a new option */
465 log2("attaching option %02x to list", optflag->code); 505 uint8_t *p = udhcp_insert_new_option(opt_list, optflag->code, length, dhcpv6);
466 new = xmalloc(sizeof(*new)); 506 if (!dhcpv6)
467 if (!dhcpv6) { 507 memcpy(p + OPT_DATA, buffer, length);
468 new->data = xmalloc(length + OPT_DATA); 508 else
469 new->data[OPT_CODE] = optflag->code; 509 memcpy(p + D6_OPT_DATA, buffer, length);
470 new->data[OPT_LEN] = length;
471 memcpy(new->data + OPT_DATA, buffer, length);
472 } else {
473 new->data = xmalloc(length + D6_OPT_DATA);
474 new->data[D6_OPT_CODE] = optflag->code >> 8;
475 new->data[D6_OPT_CODE + 1] = optflag->code & 0xff;
476 new->data[D6_OPT_LEN] = length >> 8;
477 new->data[D6_OPT_LEN + 1] = length & 0xff;
478 memcpy(new->data + D6_OPT_DATA, buffer,
479 length);
480 }
481
482 curr = opt_list;
483 while (*curr && (*curr)->data[OPT_CODE] < optflag->code)
484 curr = &(*curr)->next;
485
486 new->next = *curr;
487 *curr = new;
488 goto ret; 510 goto ret;
489 } 511 }
490 512
diff --git a/networking/udhcp/common.h b/networking/udhcp/common.h
index cc0abd269..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)
275# define IF_UDHCP_VERBOSE(...) __VA_ARGS__ 275# define IF_UDHCP_VERBOSE(...) __VA_ARGS__
276extern unsigned dhcp_verbose; 276extern unsigned dhcp_verbose;
277# define log1(...) do { if (dhcp_verbose >= 1) bb_info_msg(__VA_ARGS__); } while (0) 277# define log1(...) do { if (dhcp_verbose >= 1) bb_info_msg(__VA_ARGS__); } while (0)
278# define log1s(msg) do { if (dhcp_verbose >= 1) bb_simple_info_msg(msg); } while (0) 278//# define log1s(msg) do { if (dhcp_verbose >= 1) bb_simple_info_msg(msg); } while (0)
279void log1s(const char *msg) FAST_FUNC;
279# if CONFIG_UDHCP_DEBUG >= 2 280# if CONFIG_UDHCP_DEBUG >= 2
280void udhcp_dump_packet(struct dhcp_packet *packet) FAST_FUNC; 281void udhcp_dump_packet(struct dhcp_packet *packet) FAST_FUNC;
281# define log2(...) do { if (dhcp_verbose >= 2) bb_info_msg(__VA_ARGS__); } while (0) 282# define log2(...) do { if (dhcp_verbose >= 2) bb_info_msg(__VA_ARGS__); } while (0)
@@ -319,6 +320,16 @@ void udhcp_dump_packet(struct dhcp_packet *packet) FAST_FUNC;
319 320
320/* 2nd param is "uint32_t*" */ 321/* 2nd param is "uint32_t*" */
321int FAST_FUNC udhcp_str2nip(const char *str, void *arg); 322int FAST_FUNC udhcp_str2nip(const char *str, void *arg);
323
324#if !ENABLE_UDHCPC6
325#define udhcp_insert_new_option(opt_list, code, length, dhcpv6) \
326 udhcp_insert_new_option(opt_list, code, length)
327#endif
328void* FAST_FUNC udhcp_insert_new_option(struct option_set **opt_list,
329 unsigned code,
330 unsigned length,
331 bool dhcpv6);
332
322/* 2nd param is "struct option_set**" */ 333/* 2nd param is "struct option_set**" */
323#if !ENABLE_UDHCPC6 334#if !ENABLE_UDHCPC6
324#define udhcp_str2optset(str, arg, optflags, option_strings, dhcpv6) \ 335#define udhcp_str2optset(str, arg, optflags, option_strings, dhcpv6) \
diff --git a/networking/udhcp/d6_dhcpc.c b/networking/udhcp/d6_dhcpc.c
index 0a5cae310..7f288f891 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)
441 return envp; 441 return envp;
442} 442}
443 443
444/* Call a script with a par file and env vars */ 444/* Call a script with env vars */
445static void d6_run_script(const uint8_t *option, const uint8_t *option_end, 445static void d6_run_script(const uint8_t *option, const uint8_t *option_end,
446 const char *name) 446 const char *name)
447{ 447{
@@ -464,7 +464,7 @@ static void d6_run_script(const uint8_t *option, const uint8_t *option_end,
464 free(envp); 464 free(envp);
465} 465}
466 466
467/* Call a script with a par file and no env var */ 467/* Call a script with no env var */
468static void d6_run_script_no_option(const char *name) 468static void d6_run_script_no_option(const char *name)
469{ 469{
470 d6_run_script(NULL, NULL, name); 470 d6_run_script(NULL, NULL, name);
@@ -482,7 +482,6 @@ static ALWAYS_INLINE uint32_t random_xid(void)
482static uint8_t *init_d6_packet(struct d6_packet *packet, char type, uint32_t xid) 482static uint8_t *init_d6_packet(struct d6_packet *packet, char type, uint32_t xid)
483{ 483{
484 uint8_t *ptr; 484 uint8_t *ptr;
485 struct d6_option *clientid;
486 unsigned secs; 485 unsigned secs;
487 486
488 memset(packet, 0, sizeof(*packet)); 487 memset(packet, 0, sizeof(*packet));
@@ -503,9 +502,7 @@ static uint8_t *init_d6_packet(struct d6_packet *packet, char type, uint32_t xid
503 *((uint16_t*)ptr) = (secs < 0xffff) ? htons(secs) : 0xffff; 502 *((uint16_t*)ptr) = (secs < 0xffff) ? htons(secs) : 0xffff;
504 ptr += 2; 503 ptr += 2;
505 504
506 /* add CLIENTID option */ 505 return ptr;
507 clientid = (void*)client_data.clientid;
508 return mempcpy(ptr, clientid, clientid->len + 2+2);
509} 506}
510 507
511static uint8_t *add_d6_client_options(uint8_t *ptr) 508static uint8_t *add_d6_client_options(uint8_t *ptr)
@@ -593,10 +590,10 @@ static NOINLINE int send_d6_info_request(uint32_t xid)
593 struct d6_packet packet; 590 struct d6_packet packet;
594 uint8_t *opt_ptr; 591 uint8_t *opt_ptr;
595 592
596 /* Fill in: msg type, client id */ 593 /* Fill in: msg type */
597 opt_ptr = init_d6_packet(&packet, D6_MSG_INFORMATION_REQUEST, xid); 594 opt_ptr = init_d6_packet(&packet, D6_MSG_INFORMATION_REQUEST, xid);
598 595
599 /* Add options: 596 /* Add options: client-id,
600 * "param req" option according to -O, options specified with -x 597 * "param req" option according to -O, options specified with -x
601 */ 598 */
602 opt_ptr = add_d6_client_options(opt_ptr); 599 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
693 uint8_t *opt_ptr; 690 uint8_t *opt_ptr;
694 unsigned len; 691 unsigned len;
695 692
696 /* Fill in: msg type, client id */ 693 /* Fill in: msg type */
697 opt_ptr = init_d6_packet(&packet, D6_MSG_SOLICIT, xid); 694 opt_ptr = init_d6_packet(&packet, D6_MSG_SOLICIT, xid);
698 695
699 /* Create new IA_NA, optionally with included IAADDR with requested IP */ 696 /* 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
726 opt_ptr = mempcpy(opt_ptr, client6_data.ia_pd, len); 723 opt_ptr = mempcpy(opt_ptr, client6_data.ia_pd, len);
727 } 724 }
728 725
729 /* Add options: 726 /* Add options: client-id,
730 * "param req" option according to -O, options specified with -x 727 * "param req" option according to -O, options specified with -x
731 */ 728 */
732 opt_ptr = add_d6_client_options(opt_ptr); 729 opt_ptr = add_d6_client_options(opt_ptr);
@@ -771,7 +768,7 @@ static NOINLINE int send_d6_select(uint32_t xid)
771 struct d6_packet packet; 768 struct d6_packet packet;
772 uint8_t *opt_ptr; 769 uint8_t *opt_ptr;
773 770
774 /* Fill in: msg type, client id */ 771 /* Fill in: msg type */
775 opt_ptr = init_d6_packet(&packet, D6_MSG_REQUEST, xid); 772 opt_ptr = init_d6_packet(&packet, D6_MSG_REQUEST, xid);
776 773
777 /* server id */ 774 /* server id */
@@ -783,7 +780,7 @@ static NOINLINE int send_d6_select(uint32_t xid)
783 if (client6_data.ia_pd) 780 if (client6_data.ia_pd)
784 opt_ptr = mempcpy(opt_ptr, client6_data.ia_pd, client6_data.ia_pd->len + 2+2); 781 opt_ptr = mempcpy(opt_ptr, client6_data.ia_pd, client6_data.ia_pd->len + 2+2);
785 782
786 /* Add options: 783 /* Add options: client-id,
787 * "param req" option according to -O, options specified with -x 784 * "param req" option according to -O, options specified with -x
788 */ 785 */
789 opt_ptr = add_d6_client_options(opt_ptr); 786 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
844 struct d6_packet packet; 841 struct d6_packet packet;
845 uint8_t *opt_ptr; 842 uint8_t *opt_ptr;
846 843
847 /* Fill in: msg type, client id */ 844 /* Fill in: msg type */
848 opt_ptr = init_d6_packet(&packet, DHCPREQUEST, xid); 845 opt_ptr = init_d6_packet(&packet, DHCPREQUEST, xid);
849 846
850 /* server id */ 847 /* server id */
@@ -856,7 +853,7 @@ static NOINLINE int send_d6_renew(uint32_t xid, struct in6_addr *server_ipv6, st
856 if (client6_data.ia_pd) 853 if (client6_data.ia_pd)
857 opt_ptr = mempcpy(opt_ptr, client6_data.ia_pd, client6_data.ia_pd->len + 2+2); 854 opt_ptr = mempcpy(opt_ptr, client6_data.ia_pd, client6_data.ia_pd->len + 2+2);
858 855
859 /* Add options: 856 /* Add options: client-id,
860 * "param req" option according to -O, options specified with -x 857 * "param req" option according to -O, options specified with -x
861 */ 858 */
862 opt_ptr = add_d6_client_options(opt_ptr); 859 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)
878{ 875{
879 struct d6_packet packet; 876 struct d6_packet packet;
880 uint8_t *opt_ptr; 877 uint8_t *opt_ptr;
878 struct option_set *ci;
881 879
882 /* Fill in: msg type, client id */ 880 /* Fill in: msg type, client id */
883 opt_ptr = init_d6_packet(&packet, D6_MSG_RELEASE, random_xid()); 881 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)
889 /* IA PD */ 887 /* IA PD */
890 if (client6_data.ia_pd) 888 if (client6_data.ia_pd)
891 opt_ptr = mempcpy(opt_ptr, client6_data.ia_pd, client6_data.ia_pd->len + 2+2); 889 opt_ptr = mempcpy(opt_ptr, client6_data.ia_pd, client6_data.ia_pd->len + 2+2);
890 /* Client-id */
891 ci = udhcp_find_option(client_data.options, D6_OPT_CLIENTID);
892 if (ci)
893 opt_ptr = mempcpy(opt_ptr, ci->data, D6_OPT_DATA + 2+2 + 6);
892 894
893 bb_info_msg("sending %s", "release"); 895 bb_info_msg("sending %s", "release");
894 return d6_send_kernel_packet_from_client_data_ifindex( 896 return d6_send_kernel_packet_from_client_data_ifindex(
@@ -952,7 +954,8 @@ static NOINLINE int d6_recv_raw_packet(struct in6_addr *peer_ipv6, struct d6_pac
952 if (peer_ipv6) 954 if (peer_ipv6)
953 *peer_ipv6 = packet.ip6.ip6_src; /* struct copy */ 955 *peer_ipv6 = packet.ip6.ip6_src; /* struct copy */
954 956
955 log1("received %s", "a packet"); 957 log2("received %s", "a packet");
958 /* log2 because more informative msg for valid packets is printed later at log1 level */
956 d6_dump_packet(&packet.data); 959 d6_dump_packet(&packet.data);
957 960
958 bytes -= sizeof(packet.ip6) + sizeof(packet.udp); 961 bytes -= sizeof(packet.ip6) + sizeof(packet.udp);
@@ -1060,9 +1063,6 @@ static int d6_raw_socket(int ifindex)
1060 log1("attached filter to raw socket fd %d", fd); // log? 1063 log1("attached filter to raw socket fd %d", fd); // log?
1061 } 1064 }
1062#endif 1065#endif
1063
1064 log1s("created raw socket");
1065
1066 return fd; 1066 return fd;
1067} 1067}
1068 1068
@@ -1088,6 +1088,8 @@ static void change_listen_mode(int new_mode)
1088 1088
1089static void perform_d6_release(struct in6_addr *server_ipv6, struct in6_addr *our_cur_ipv6) 1089static void perform_d6_release(struct in6_addr *server_ipv6, struct in6_addr *our_cur_ipv6)
1090{ 1090{
1091 change_listen_mode(LISTEN_NONE);
1092
1091 /* send release packet */ 1093 /* send release packet */
1092 if (client_data.state == BOUND 1094 if (client_data.state == BOUND
1093 || client_data.state == RENEWING 1095 || client_data.state == RENEWING
@@ -1105,21 +1107,9 @@ static void perform_d6_release(struct in6_addr *server_ipv6, struct in6_addr *ou
1105 * of the states above. 1107 * of the states above.
1106 */ 1108 */
1107 d6_run_script_no_option("deconfig"); 1109 d6_run_script_no_option("deconfig");
1108 change_listen_mode(LISTEN_NONE);
1109 client_data.state = RELEASED; 1110 client_data.state = RELEASED;
1110} 1111}
1111 1112
1112///static uint8_t* alloc_dhcp_option(int code, const char *str, int extra)
1113///{
1114/// uint8_t *storage;
1115/// int len = strnlen(str, 255);
1116/// storage = xzalloc(len + extra + OPT_DATA);
1117/// storage[OPT_CODE] = code;
1118/// storage[OPT_LEN] = len + extra;
1119/// memcpy(storage + extra + OPT_DATA, str, len);
1120/// return storage;
1121///}
1122
1123#if BB_MMU 1113#if BB_MMU
1124static void client_background(void) 1114static void client_background(void)
1125{ 1115{
@@ -1184,7 +1174,7 @@ int udhcpc6_main(int argc UNUSED_PARAM, char **argv)
1184{ 1174{
1185 const char *str_r; 1175 const char *str_r;
1186 IF_FEATURE_UDHCP_PORT(char *str_P;) 1176 IF_FEATURE_UDHCP_PORT(char *str_P;)
1187 void *clientid_mac_ptr; 1177 uint8_t *clientid_mac_ptr;
1188 llist_t *list_O = NULL; 1178 llist_t *list_O = NULL;
1189 llist_t *list_x = NULL; 1179 llist_t *list_x = NULL;
1190 int tryagain_timeout = 20; 1180 int tryagain_timeout = 20;
@@ -1281,6 +1271,18 @@ int udhcpc6_main(int argc UNUSED_PARAM, char **argv)
1281 free(optstr); 1271 free(optstr);
1282 } 1272 }
1283 1273
1274 clientid_mac_ptr = NULL;
1275 if (!udhcp_find_option(client_data.options, D6_OPT_CLIENTID)) {
1276 /* not set, set the default client ID */
1277 clientid_mac_ptr = udhcp_insert_new_option(
1278 &client_data.options, D6_OPT_CLIENTID,
1279 2+2 + 6, /*dhcp6:*/ 1);
1280 clientid_mac_ptr += 2+2; /* skip option code, len */
1281 clientid_mac_ptr[1] = 3; /* DUID-LL */
1282 clientid_mac_ptr[3] = 1; /* type: ethernet */
1283 clientid_mac_ptr += 2+2; /* skip DUID-LL, ethernet */
1284 }
1285
1284 if (d6_read_interface(client_data.interface, 1286 if (d6_read_interface(client_data.interface,
1285 &client_data.ifindex, 1287 &client_data.ifindex,
1286 &client6_data.ll_ip6, 1288 &client6_data.ll_ip6,
@@ -1289,19 +1291,6 @@ int udhcpc6_main(int argc UNUSED_PARAM, char **argv)
1289 return 1; 1291 return 1;
1290 } 1292 }
1291 1293
1292 /* Create client ID based on mac, set clientid_mac_ptr */
1293 {
1294 struct d6_option *clientid;
1295 clientid = xzalloc(2+2+2+2+6);
1296 clientid->code = D6_OPT_CLIENTID;
1297 clientid->len = 2+2+6;
1298 clientid->data[1] = 3; /* DUID-LL */
1299 clientid->data[3] = 1; /* ethernet */
1300 clientid_mac_ptr = clientid->data + 2+2;
1301 memcpy(clientid_mac_ptr, client_data.client_mac, 6);
1302 client_data.clientid = (void*)clientid;
1303 }
1304
1305#if !BB_MMU 1294#if !BB_MMU
1306 /* on NOMMU reexec (i.e., background) early */ 1295 /* on NOMMU reexec (i.e., background) early */
1307 if (!(opt & OPT_f)) { 1296 if (!(opt & OPT_f)) {
@@ -1321,7 +1310,6 @@ int udhcpc6_main(int argc UNUSED_PARAM, char **argv)
1321 1310
1322 client_data.state = INIT_SELECTING; 1311 client_data.state = INIT_SELECTING;
1323 d6_run_script_no_option("deconfig"); 1312 d6_run_script_no_option("deconfig");
1324 change_listen_mode(LISTEN_RAW);
1325 packet_num = 0; 1313 packet_num = 0;
1326 timeout = 0; 1314 timeout = 0;
1327 lease_remaining = 0; 1315 lease_remaining = 0;
@@ -1356,14 +1344,17 @@ int udhcpc6_main(int argc UNUSED_PARAM, char **argv)
1356 log1("waiting %u seconds", timeout); 1344 log1("waiting %u seconds", timeout);
1357 diff = (unsigned)monotonic_sec(); 1345 diff = (unsigned)monotonic_sec();
1358 retval = poll(pfds, 2, timeout * 1000); 1346 retval = poll(pfds, 2, timeout * 1000);
1347 diff = (unsigned)monotonic_sec() - diff;
1348 lease_remaining -= diff;
1349 if (lease_remaining < 0)
1350 lease_remaining = 0;
1351 timeout -= diff;
1352 if (timeout < 0)
1353 timeout = 0;
1354
1359 if (retval < 0) { 1355 if (retval < 0) {
1360 /* EINTR? A signal was caught, don't panic */ 1356 /* EINTR? A signal was caught, don't panic */
1361 if (errno == EINTR) { 1357 if (errno == EINTR) {
1362 diff = (unsigned)monotonic_sec() - diff;
1363 lease_remaining -= diff;
1364 if (lease_remaining < 0)
1365 lease_remaining = 0;
1366 timeout -= diff;
1367 continue; 1358 continue;
1368 } 1359 }
1369 /* Else: an error occured, panic! */ 1360 /* Else: an error occured, panic! */
@@ -1388,13 +1379,16 @@ int udhcpc6_main(int argc UNUSED_PARAM, char **argv)
1388 goto ret0; /* iface is gone? */ 1379 goto ret0; /* iface is gone? */
1389 } 1380 }
1390 1381
1391 memcpy(clientid_mac_ptr, client_data.client_mac, 6); 1382 if (clientid_mac_ptr)
1383 memcpy(clientid_mac_ptr, client_data.client_mac, 6);
1392 1384
1393 switch (client_data.state) { 1385 switch (client_data.state) {
1394 case INIT_SELECTING: 1386 case INIT_SELECTING:
1395 if (!discover_retries || packet_num < discover_retries) { 1387 if (!discover_retries || packet_num < discover_retries) {
1396 if (packet_num == 0) 1388 if (packet_num == 0) {
1389 change_listen_mode(LISTEN_RAW);
1397 xid = random_xid(); 1390 xid = random_xid();
1391 }
1398 /* multicast */ 1392 /* multicast */
1399 if (opt & OPT_l) 1393 if (opt & OPT_l)
1400 send_d6_info_request(xid); 1394 send_d6_info_request(xid);
@@ -1405,6 +1399,7 @@ int udhcpc6_main(int argc UNUSED_PARAM, char **argv)
1405 continue; 1399 continue;
1406 } 1400 }
1407 leasefail: 1401 leasefail:
1402 change_listen_mode(LISTEN_NONE);
1408 d6_run_script_no_option("leasefail"); 1403 d6_run_script_no_option("leasefail");
1409#if BB_MMU /* -b is not supported on NOMMU */ 1404#if BB_MMU /* -b is not supported on NOMMU */
1410 if (opt & OPT_b) { /* background if no lease */ 1405 if (opt & OPT_b) { /* background if no lease */
@@ -1425,7 +1420,7 @@ int udhcpc6_main(int argc UNUSED_PARAM, char **argv)
1425 retval = 1; 1420 retval = 1;
1426 goto ret; 1421 goto ret;
1427 } 1422 }
1428 /* wait before trying again */ 1423 /* Wait before trying again */
1429 timeout = tryagain_timeout; 1424 timeout = tryagain_timeout;
1430 packet_num = 0; 1425 packet_num = 0;
1431 continue; 1426 continue;
@@ -1441,22 +1436,20 @@ int udhcpc6_main(int argc UNUSED_PARAM, char **argv)
1441 * "discover...select...discover..." loops 1436 * "discover...select...discover..." loops
1442 * were seen in the wild. Treat them similarly 1437 * were seen in the wild. Treat them similarly
1443 * to "no response to discover" case */ 1438 * to "no response to discover" case */
1444 change_listen_mode(LISTEN_RAW);
1445 client_data.state = INIT_SELECTING; 1439 client_data.state = INIT_SELECTING;
1446 goto leasefail; 1440 goto leasefail;
1447 case BOUND: 1441 case BOUND:
1448 /* 1/2 lease passed, enter renewing state */ 1442 /* 1/2 lease passed, enter renewing state */
1449 client_data.state = RENEWING; 1443 client_data.state = RENEWING;
1450 client_data.first_secs = 0; /* make secs field count from 0 */ 1444 client_data.first_secs = 0; /* make secs field count from 0 */
1451 change_listen_mode(LISTEN_KERNEL); 1445 got_SIGUSR1:
1452 log1s("entering renew state"); 1446 log1s("entering renew state");
1447 change_listen_mode(LISTEN_KERNEL);
1453 /* fall right through */ 1448 /* fall right through */
1454 case RENEW_REQUESTED: /* manual (SIGUSR1) renew */ 1449 case RENEW_REQUESTED: /* in manual (SIGUSR1) renew */
1455 case_RENEW_REQUESTED:
1456 case RENEWING: 1450 case RENEWING:
1457 if (packet_num < 3) { 1451 if (packet_num == 0) {
1458 packet_num++; 1452 /* Send an unicast renew request */
1459 /* send an unicast renew request */
1460 /* Sometimes observed to fail (EADDRNOTAVAIL) to bind 1453 /* Sometimes observed to fail (EADDRNOTAVAIL) to bind
1461 * a new UDP socket for sending inside send_renew. 1454 * a new UDP socket for sending inside send_renew.
1462 * I hazard to guess existing listening socket 1455 * I hazard to guess existing listening socket
@@ -1470,27 +1463,36 @@ int udhcpc6_main(int argc UNUSED_PARAM, char **argv)
1470 else 1463 else
1471 send_d6_renew(xid, &srv6_buf, requested_ipv6); 1464 send_d6_renew(xid, &srv6_buf, requested_ipv6);
1472 timeout = discover_timeout; 1465 timeout = discover_timeout;
1473 /* ^^^ used to be = lease_remaining / 2 - WAY too long */ 1466 packet_num++;
1474 continue; 1467 continue;
1468 } /* else: we had sent one packet, but got no reply */
1469 log1s("no response to renew");
1470 if (lease_remaining > 30) {
1471 /* Some lease time remains, try to renew later */
1472 change_listen_mode(LISTEN_NONE);
1473 goto BOUND_for_half_lease;
1475 } 1474 }
1476 /* Timed out, enter rebinding state */ 1475 /* Enter rebinding state */
1477 log1s("entering rebinding state");
1478 client_data.state = REBINDING; 1476 client_data.state = REBINDING;
1479 /* fall right through */ 1477 log1s("entering rebinding state");
1480 case REBINDING:
1481 /* Switch to bcast receive */ 1478 /* Switch to bcast receive */
1482 change_listen_mode(LISTEN_RAW); 1479 change_listen_mode(LISTEN_RAW);
1480 packet_num = 0;
1481 /* fall right through */
1482 case REBINDING:
1483 /* Lease is *really* about to run out, 1483 /* Lease is *really* about to run out,
1484 * try to find DHCP server using broadcast */ 1484 * try to find DHCP server using broadcast */
1485 if (lease_remaining > 0) { 1485 if (lease_remaining > 0 && packet_num < 3) {
1486 if (opt & OPT_l) 1486 if (opt & OPT_l)
1487 send_d6_info_request(xid); 1487 send_d6_info_request(xid);
1488 else /* send a broadcast renew request */ 1488 else /* send a broadcast renew request */
1489 send_d6_renew(xid, /*server_ipv6:*/ NULL, requested_ipv6); 1489 send_d6_renew(xid, /*server_ipv6:*/ NULL, requested_ipv6);
1490 timeout = discover_timeout; 1490 timeout = discover_timeout;
1491 packet_num++;
1491 continue; 1492 continue;
1492 } 1493 }
1493 /* Timed out, enter init state */ 1494 /* Timed out, enter init state */
1495 change_listen_mode(LISTEN_NONE);
1494 bb_simple_info_msg("lease lost, entering init state"); 1496 bb_simple_info_msg("lease lost, entering init state");
1495 d6_run_script_no_option("deconfig"); 1497 d6_run_script_no_option("deconfig");
1496 client_data.state = INIT_SELECTING; 1498 client_data.state = INIT_SELECTING;
@@ -1500,7 +1502,9 @@ int udhcpc6_main(int argc UNUSED_PARAM, char **argv)
1500 continue; 1502 continue;
1501 /* case RELEASED: */ 1503 /* case RELEASED: */
1502 } 1504 }
1503 /* yah, I know, *you* say it would never happen */ 1505 /* RELEASED state (when we got SIGUSR2) ends up here.
1506 * (wait for SIGUSR1 to re-init, or for TERM, etc)
1507 */
1504 timeout = INT_MAX; 1508 timeout = INT_MAX;
1505 continue; /* back to main loop */ 1509 continue; /* back to main loop */
1506 } /* if poll timed out */ 1510 } /* if poll timed out */
@@ -1510,33 +1514,43 @@ int udhcpc6_main(int argc UNUSED_PARAM, char **argv)
1510 /* Is it a signal? */ 1514 /* Is it a signal? */
1511 switch (udhcp_sp_read()) { 1515 switch (udhcp_sp_read()) {
1512 case SIGUSR1: 1516 case SIGUSR1:
1517 if (client_data.state <= REQUESTING)
1518 /* Initial negotiations in progress, do not disturb */
1519 break;
1520 if (client_data.state == REBINDING)
1521 /* Do not go back from rebind to renew state */
1522 break;
1523
1524 if (lease_remaining > 30) /* if renew fails, do not go back to BOUND */
1525 lease_remaining = 30;
1513 client_data.first_secs = 0; /* make secs field count from 0 */ 1526 client_data.first_secs = 0; /* make secs field count from 0 */
1514 bb_simple_info_msg("performing DHCP renew"); 1527 packet_num = 0;
1515 1528
1516 switch (client_data.state) { 1529 switch (client_data.state) {
1517 /* Try to renew/rebind */
1518 case BOUND: 1530 case BOUND:
1519 case RENEWING: 1531 case RENEWING:
1520 case REBINDING: 1532 /* Try to renew/rebind */
1521 change_listen_mode(LISTEN_KERNEL); 1533 change_listen_mode(LISTEN_KERNEL);
1522 client_data.state = RENEW_REQUESTED; 1534 client_data.state = RENEW_REQUESTED;
1523 goto case_RENEW_REQUESTED; 1535 goto got_SIGUSR1;
1524 1536
1525 /* Start things over */ 1537 case RENEW_REQUESTED:
1526 case RENEW_REQUESTED: /* two or more SIGUSR1 received */ 1538 /* Two SIGUSR1 received, start things over */
1539 change_listen_mode(LISTEN_NONE);
1527 d6_run_script_no_option("deconfig"); 1540 d6_run_script_no_option("deconfig");
1528 /* case REQUESTING: break; */ 1541
1529 /* case RELEASED: break; */ 1542 default:
1530 /* case INIT_SELECTING: break; */ 1543 /* case RELEASED: */
1544 /* Wake from SIGUSR2-induced deconfigured state */
1545 change_listen_mode(LISTEN_NONE);
1531 } 1546 }
1532 change_listen_mode(LISTEN_RAW);
1533 client_data.state = INIT_SELECTING; 1547 client_data.state = INIT_SELECTING;
1534 packet_num = 0;
1535 /* Kill any timeouts, user wants this to hurry along */ 1548 /* Kill any timeouts, user wants this to hurry along */
1536 timeout = 0; 1549 timeout = 0;
1537 continue; 1550 continue;
1538 case SIGUSR2: 1551 case SIGUSR2:
1539 perform_d6_release(&srv6_buf, requested_ipv6); 1552 perform_d6_release(&srv6_buf, requested_ipv6);
1553 /* ^^^ switches to LISTEN_NONE */
1540 timeout = INT_MAX; 1554 timeout = INT_MAX;
1541 continue; 1555 continue;
1542 case SIGTERM: 1556 case SIGTERM:
@@ -1594,6 +1608,8 @@ int udhcpc6_main(int argc UNUSED_PARAM, char **argv)
1594 unsigned address_timeout; 1608 unsigned address_timeout;
1595 unsigned prefix_timeout; 1609 unsigned prefix_timeout;
1596 type_is_ok: 1610 type_is_ok:
1611 change_listen_mode(LISTEN_NONE);
1612
1597 address_timeout = 0; 1613 address_timeout = 0;
1598 prefix_timeout = 0; 1614 prefix_timeout = 0;
1599 option = d6_find_option(packet.d6_options, packet_end, D6_OPT_STATUS_CODE); 1615 option = d6_find_option(packet.d6_options, packet_end, D6_OPT_STATUS_CODE);
@@ -1604,7 +1620,6 @@ int udhcpc6_main(int argc UNUSED_PARAM, char **argv)
1604 packet_end, "nak"); 1620 packet_end, "nak");
1605 if (client_data.state != REQUESTING) 1621 if (client_data.state != REQUESTING)
1606 d6_run_script_no_option("deconfig"); 1622 d6_run_script_no_option("deconfig");
1607 change_listen_mode(LISTEN_RAW);
1608 sleep(3); /* avoid excessive network traffic */ 1623 sleep(3); /* avoid excessive network traffic */
1609 client_data.state = INIT_SELECTING; 1624 client_data.state = INIT_SELECTING;
1610 client_data.first_secs = 0; /* make secs field count from 0 */ 1625 client_data.first_secs = 0; /* make secs field count from 0 */
@@ -1626,6 +1641,7 @@ int udhcpc6_main(int argc UNUSED_PARAM, char **argv)
1626 client6_data.server_id = option; 1641 client6_data.server_id = option;
1627 if (packet.d6_msg_type == D6_MSG_ADVERTISE) { 1642 if (packet.d6_msg_type == D6_MSG_ADVERTISE) {
1628 /* enter requesting state */ 1643 /* enter requesting state */
1644 change_listen_mode(LISTEN_RAW);
1629 client_data.state = REQUESTING; 1645 client_data.state = REQUESTING;
1630 timeout = 0; 1646 timeout = 0;
1631 packet_num = 0; 1647 packet_num = 0;
@@ -1809,12 +1825,9 @@ int udhcpc6_main(int argc UNUSED_PARAM, char **argv)
1809 start = monotonic_sec(); 1825 start = monotonic_sec();
1810 d6_run_script(packet.d6_options, packet_end, 1826 d6_run_script(packet.d6_options, packet_end,
1811 (client_data.state == REQUESTING ? "bound" : "renew")); 1827 (client_data.state == REQUESTING ? "bound" : "renew"));
1812 timeout = (unsigned)lease_remaining / 2; 1828 lease_remaining -= (unsigned)monotonic_sec() - start;
1813 timeout -= (unsigned)monotonic_sec() - start; 1829 if (lease_remaining < 0)
1814 packet_num = 0; 1830 lease_remaining = 0;
1815
1816 client_data.state = BOUND;
1817 change_listen_mode(LISTEN_NONE);
1818 if (opt & OPT_q) { /* quit after lease */ 1831 if (opt & OPT_q) { /* quit after lease */
1819 goto ret0; 1832 goto ret0;
1820 } 1833 }
@@ -1827,6 +1840,11 @@ int udhcpc6_main(int argc UNUSED_PARAM, char **argv)
1827 opt = ((opt & ~OPT_b) | OPT_f); 1840 opt = ((opt & ~OPT_b) | OPT_f);
1828 } 1841 }
1829#endif 1842#endif
1843
1844 BOUND_for_half_lease:
1845 timeout = (unsigned)lease_remaining / 2;
1846 client_data.state = BOUND;
1847 packet_num = 0;
1830 continue; /* back to main loop */ 1848 continue; /* back to main loop */
1831 } 1849 }
1832 continue; 1850 continue;
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
44 bb_simple_info_msg("packet with bad magic, ignoring"); 44 bb_simple_info_msg("packet with bad magic, ignoring");
45 return -2; 45 return -2;
46 } 46 }
47 log1("received %s", "a packet"); 47 log2("received %s", "a packet");
48 /* log2 because more informative msg for valid packets is printed later at log1 level */
48 d6_dump_packet(packet); 49 d6_dump_packet(packet);
49 50
50 return bytes; 51 return bytes;
diff --git a/networking/udhcp/dhcpc.c b/networking/udhcp/dhcpc.c
index 6666cbce6..4e3d8ca5e 100644
--- a/networking/udhcp/dhcpc.c
+++ b/networking/udhcp/dhcpc.c
@@ -55,8 +55,7 @@ struct tpacket_auxdata {
55#if ENABLE_LONG_OPTS 55#if ENABLE_LONG_OPTS
56static const char udhcpc_longopts[] ALIGN1 = 56static const char udhcpc_longopts[] ALIGN1 =
57 "clientid-none\0" No_argument "C" 57 "clientid-none\0" No_argument "C"
58 "vendorclass\0" Required_argument "V" 58 "vendorclass\0" Required_argument "V" //deprecated
59 "hostname\0" Required_argument "H"
60 "fqdn\0" Required_argument "F" 59 "fqdn\0" Required_argument "F"
61 "interface\0" Required_argument "i" 60 "interface\0" Required_argument "i"
62 "now\0" No_argument "n" 61 "now\0" No_argument "n"
@@ -84,27 +83,25 @@ static const char udhcpc_longopts[] ALIGN1 =
84enum { 83enum {
85 OPT_C = 1 << 0, 84 OPT_C = 1 << 0,
86 OPT_V = 1 << 1, 85 OPT_V = 1 << 1,
87 OPT_H = 1 << 2, 86 OPT_F = 1 << 2,
88 OPT_h = 1 << 3, 87 OPT_i = 1 << 3,
89 OPT_F = 1 << 4, 88 OPT_n = 1 << 4,
90 OPT_i = 1 << 5, 89 OPT_p = 1 << 5,
91 OPT_n = 1 << 6, 90 OPT_q = 1 << 6,
92 OPT_p = 1 << 7, 91 OPT_R = 1 << 7,
93 OPT_q = 1 << 8, 92 OPT_r = 1 << 8,
94 OPT_R = 1 << 9, 93 OPT_s = 1 << 9,
95 OPT_r = 1 << 10, 94 OPT_T = 1 << 10,
96 OPT_s = 1 << 11, 95 OPT_t = 1 << 11,
97 OPT_T = 1 << 12, 96 OPT_S = 1 << 12,
98 OPT_t = 1 << 13, 97 OPT_A = 1 << 13,
99 OPT_S = 1 << 14, 98 OPT_O = 1 << 14,
100 OPT_A = 1 << 15, 99 OPT_o = 1 << 15,
101 OPT_O = 1 << 16, 100 OPT_x = 1 << 16,
102 OPT_o = 1 << 17, 101 OPT_f = 1 << 17,
103 OPT_x = 1 << 18, 102 OPT_B = 1 << 18,
104 OPT_f = 1 << 19,
105 OPT_B = 1 << 20,
106/* The rest has variable bit positions, need to be clever */ 103/* The rest has variable bit positions, need to be clever */
107 OPTBIT_B = 20, 104 OPTBIT_B = 18,
108 USE_FOR_MMU( OPTBIT_b,) 105 USE_FOR_MMU( OPTBIT_b,)
109 IF_FEATURE_UDHCPC_ARPING(OPTBIT_a,) 106 IF_FEATURE_UDHCPC_ARPING(OPTBIT_a,)
110 IF_FEATURE_UDHCP_PORT( OPTBIT_P,) 107 IF_FEATURE_UDHCP_PORT( OPTBIT_P,)
@@ -569,8 +566,8 @@ static void fill_envp(struct dhcp_packet *packet)
569 } 566 }
570} 567}
571 568
572/* Call a script with a par file and env vars */ 569/* Call a script with env vars */
573static void udhcp_run_script(struct dhcp_packet *packet, const char *name) 570static void d4_run_script(struct dhcp_packet *packet, const char *name)
574{ 571{
575 char *argv[3]; 572 char *argv[3];
576 573
@@ -588,6 +585,10 @@ static void udhcp_run_script(struct dhcp_packet *packet, const char *name)
588 client_data.envp = NULL; 585 client_data.envp = NULL;
589} 586}
590 587
588static void d4_run_script_deconfig(void)
589{
590 d4_run_script(NULL, "deconfig");
591}
591 592
592/*** Sending/receiving packets ***/ 593/*** Sending/receiving packets ***/
593 594
@@ -613,8 +614,6 @@ static void init_packet(struct dhcp_packet *packet, char type)
613 packet->secs = (secs < 0xffff) ? htons(secs) : 0xffff; 614 packet->secs = (secs < 0xffff) ? htons(secs) : 0xffff;
614 615
615 memcpy(packet->chaddr, client_data.client_mac, 6); 616 memcpy(packet->chaddr, client_data.client_mac, 6);
616 if (client_data.clientid)
617 udhcp_add_binary_option(packet, client_data.clientid);
618} 617}
619 618
620static void add_client_options(struct dhcp_packet *packet) 619static void add_client_options(struct dhcp_packet *packet)
@@ -640,13 +639,6 @@ static void add_client_options(struct dhcp_packet *packet)
640 packet->options[end + OPT_DATA + len] = DHCP_END; 639 packet->options[end + OPT_DATA + len] = DHCP_END;
641 } 640 }
642 641
643 if (client_data.vendorclass)
644 udhcp_add_binary_option(packet, client_data.vendorclass);
645 if (client_data.hostname)
646 udhcp_add_binary_option(packet, client_data.hostname);
647 if (client_data.fqdn)
648 udhcp_add_binary_option(packet, client_data.fqdn);
649
650 /* Request broadcast replies if we have no IP addr */ 642 /* Request broadcast replies if we have no IP addr */
651 if ((option_mask32 & OPT_B) && packet->ciaddr == 0) 643 if ((option_mask32 & OPT_B) && packet->ciaddr == 0)
652 packet->flags |= htons(BROADCAST_FLAG); 644 packet->flags |= htons(BROADCAST_FLAG);
@@ -715,7 +707,7 @@ static NOINLINE int send_discover(uint32_t xid, uint32_t requested)
715 707
716 /* Fill in: op, htype, hlen, cookie, chaddr fields, 708 /* Fill in: op, htype, hlen, cookie, chaddr fields,
717 * random xid field (we override it below), 709 * random xid field (we override it below),
718 * client-id option (unless -C), message type option: 710 * message type option:
719 */ 711 */
720 init_packet(&packet, DHCPDISCOVER); 712 init_packet(&packet, DHCPDISCOVER);
721 713
@@ -724,7 +716,6 @@ static NOINLINE int send_discover(uint32_t xid, uint32_t requested)
724 udhcp_add_simple_option(&packet, DHCP_REQUESTED_IP, requested); 716 udhcp_add_simple_option(&packet, DHCP_REQUESTED_IP, requested);
725 717
726 /* Add options: maxsize, 718 /* Add options: maxsize,
727 * optionally: hostname, fqdn, vendorclass,
728 * "param req" option according to -O, options specified with -x 719 * "param req" option according to -O, options specified with -x
729 */ 720 */
730 add_client_options(&packet); 721 add_client_options(&packet);
@@ -758,7 +749,7 @@ static NOINLINE int send_select(uint32_t xid, uint32_t server, uint32_t requeste
758 */ 749 */
759 /* Fill in: op, htype, hlen, cookie, chaddr fields, 750 /* Fill in: op, htype, hlen, cookie, chaddr fields,
760 * random xid field (we override it below), 751 * random xid field (we override it below),
761 * client-id option (unless -C), message type option: 752 * message type option:
762 */ 753 */
763 init_packet(&packet, DHCPREQUEST); 754 init_packet(&packet, DHCPREQUEST);
764 755
@@ -768,7 +759,6 @@ static NOINLINE int send_select(uint32_t xid, uint32_t server, uint32_t requeste
768 udhcp_add_simple_option(&packet, DHCP_SERVER_ID, server); 759 udhcp_add_simple_option(&packet, DHCP_SERVER_ID, server);
769 760
770 /* Add options: maxsize, 761 /* Add options: maxsize,
771 * optionally: hostname, fqdn, vendorclass,
772 * "param req" option according to -O, and options specified with -x 762 * "param req" option according to -O, and options specified with -x
773 */ 763 */
774 add_client_options(&packet); 764 add_client_options(&packet);
@@ -805,7 +795,7 @@ static NOINLINE int send_renew(uint32_t xid, uint32_t server, uint32_t ciaddr)
805 */ 795 */
806 /* Fill in: op, htype, hlen, cookie, chaddr fields, 796 /* Fill in: op, htype, hlen, cookie, chaddr fields,
807 * random xid field (we override it below), 797 * random xid field (we override it below),
808 * client-id option (unless -C), message type option: 798 * message type option:
809 */ 799 */
810 init_packet(&packet, DHCPREQUEST); 800 init_packet(&packet, DHCPREQUEST);
811 801
@@ -813,7 +803,6 @@ static NOINLINE int send_renew(uint32_t xid, uint32_t server, uint32_t ciaddr)
813 packet.ciaddr = ciaddr; 803 packet.ciaddr = ciaddr;
814 804
815 /* Add options: maxsize, 805 /* Add options: maxsize,
816 * optionally: hostname, fqdn, vendorclass,
817 * "param req" option according to -O, and options specified with -x 806 * "param req" option according to -O, and options specified with -x
818 */ 807 */
819 add_client_options(&packet); 808 add_client_options(&packet);
@@ -837,7 +826,7 @@ static NOINLINE int send_decline(/*uint32_t xid,*/ uint32_t server, uint32_t req
837 struct dhcp_packet packet; 826 struct dhcp_packet packet;
838 827
839 /* Fill in: op, htype, hlen, cookie, chaddr, random xid fields, 828 /* Fill in: op, htype, hlen, cookie, chaddr, random xid fields,
840 * client-id option (unless -C), message type option: 829 * message type option:
841 */ 830 */
842 init_packet(&packet, DHCPDECLINE); 831 init_packet(&packet, DHCPDECLINE);
843 832
@@ -854,6 +843,8 @@ static NOINLINE int send_decline(/*uint32_t xid,*/ uint32_t server, uint32_t req
854 843
855 udhcp_add_simple_option(&packet, DHCP_SERVER_ID, server); 844 udhcp_add_simple_option(&packet, DHCP_SERVER_ID, server);
856 845
846//TODO: add client-id opt?
847
857 bb_simple_info_msg("broadcasting decline"); 848 bb_simple_info_msg("broadcasting decline");
858 return raw_bcast_from_client_data_ifindex(&packet, INADDR_ANY); 849 return raw_bcast_from_client_data_ifindex(&packet, INADDR_ANY);
859} 850}
@@ -865,9 +856,10 @@ ALWAYS_INLINE /* one caller, help compiler to use this fact */
865int send_release(uint32_t server, uint32_t ciaddr) 856int send_release(uint32_t server, uint32_t ciaddr)
866{ 857{
867 struct dhcp_packet packet; 858 struct dhcp_packet packet;
859 struct option_set *ci;
868 860
869 /* Fill in: op, htype, hlen, cookie, chaddr, random xid fields, 861 /* Fill in: op, htype, hlen, cookie, chaddr, random xid fields,
870 * client-id option (unless -C), message type option: 862 * message type option:
871 */ 863 */
872 init_packet(&packet, DHCPRELEASE); 864 init_packet(&packet, DHCPRELEASE);
873 865
@@ -876,6 +868,14 @@ int send_release(uint32_t server, uint32_t ciaddr)
876 868
877 udhcp_add_simple_option(&packet, DHCP_SERVER_ID, server); 869 udhcp_add_simple_option(&packet, DHCP_SERVER_ID, server);
878 870
871 /* RFC 2131 section 3.1.6:
872 * If the client used a 'client identifier' when it obtained the lease,
873 * it MUST use the same 'client identifier' in the DHCPRELEASE message.
874 */
875 ci = udhcp_find_option(client_data.options, DHCP_CLIENT_ID);
876 if (ci)
877 udhcp_add_binary_option(&packet, ci->data);
878
879 bb_info_msg("sending %s", "release"); 879 bb_info_msg("sending %s", "release");
880 /* Note: normally we unicast here since "server" is not zero. 880 /* Note: normally we unicast here since "server" is not zero.
881 * However, there _are_ people who run "address-less" DHCP servers, 881 * However, there _are_ people who run "address-less" DHCP servers,
@@ -886,7 +886,7 @@ int send_release(uint32_t server, uint32_t ciaddr)
886 886
887/* Returns -1 on errors that are fatal for the socket, -2 for those that aren't */ 887/* Returns -1 on errors that are fatal for the socket, -2 for those that aren't */
888/* NOINLINE: limit stack usage in caller */ 888/* NOINLINE: limit stack usage in caller */
889static NOINLINE int udhcp_recv_raw_packet(struct dhcp_packet *dhcp_pkt, int fd) 889static NOINLINE int d4_recv_raw_packet(struct dhcp_packet *dhcp_pkt, int fd)
890{ 890{
891 int bytes; 891 int bytes;
892 struct ip_udp_dhcp_packet packet; 892 struct ip_udp_dhcp_packet packet;
@@ -979,11 +979,12 @@ static NOINLINE int udhcp_recv_raw_packet(struct dhcp_packet *dhcp_pkt, int fd)
979 skip_udp_sum_check: 979 skip_udp_sum_check:
980 980
981 if (packet.data.cookie != htonl(DHCP_MAGIC)) { 981 if (packet.data.cookie != htonl(DHCP_MAGIC)) {
982 bb_simple_info_msg("packet with bad magic, ignoring"); 982 log1s("packet with bad magic, ignoring");
983 return -2; 983 return -2;
984 } 984 }
985 985
986 log1("received %s", "a packet"); 986 log2("received %s", "a packet");
987 /* log2 because more informative msg for valid packets is printed later at log1 level */
987 udhcp_dump_packet(&packet.data); 988 udhcp_dump_packet(&packet.data);
988 989
989 bytes -= sizeof(packet.ip) + sizeof(packet.udp); 990 bytes -= sizeof(packet.ip) + sizeof(packet.udp);
@@ -1096,8 +1097,6 @@ static int udhcp_raw_socket(int ifindex)
1096 log1s("can't set PACKET_AUXDATA on raw socket"); 1097 log1s("can't set PACKET_AUXDATA on raw socket");
1097 } 1098 }
1098 1099
1099 log1s("created raw socket");
1100
1101 return fd; 1100 return fd;
1102} 1101}
1103 1102
@@ -1126,6 +1125,8 @@ static void perform_release(uint32_t server_addr, uint32_t requested_ip)
1126 char buffer[sizeof("255.255.255.255")]; 1125 char buffer[sizeof("255.255.255.255")];
1127 struct in_addr temp_addr; 1126 struct in_addr temp_addr;
1128 1127
1128 change_listen_mode(LISTEN_NONE);
1129
1129 /* send release packet */ 1130 /* send release packet */
1130 if (client_data.state == BOUND 1131 if (client_data.state == BOUND
1131 || client_data.state == RENEWING 1132 || client_data.state == RENEWING
@@ -1146,23 +1147,10 @@ static void perform_release(uint32_t server_addr, uint32_t requested_ip)
1146 * Users requested to be notified in all cases, even if not in one 1147 * Users requested to be notified in all cases, even if not in one
1147 * of the states above. 1148 * of the states above.
1148 */ 1149 */
1149 udhcp_run_script(NULL, "deconfig"); 1150 d4_run_script_deconfig();
1150
1151 change_listen_mode(LISTEN_NONE);
1152 client_data.state = RELEASED; 1151 client_data.state = RELEASED;
1153} 1152}
1154 1153
1155static uint8_t* alloc_dhcp_option(int code, const char *str, int extra)
1156{
1157 uint8_t *storage;
1158 int len = strnlen(str, 255);
1159 storage = xzalloc(len + extra + OPT_DATA);
1160 storage[OPT_CODE] = code;
1161 storage[OPT_LEN] = len + extra;
1162 memcpy(storage + extra + OPT_DATA, str, len);
1163 return storage;
1164}
1165
1166#if BB_MMU 1154#if BB_MMU
1167static void client_background(void) 1155static void client_background(void)
1168{ 1156{
@@ -1227,10 +1215,10 @@ int udhcpc_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
1227int udhcpc_main(int argc UNUSED_PARAM, char **argv) 1215int udhcpc_main(int argc UNUSED_PARAM, char **argv)
1228{ 1216{
1229 uint8_t *message; 1217 uint8_t *message;
1230 const char *str_V, *str_h, *str_F, *str_r; 1218 const char *str_V, *str_F, *str_r;
1231 IF_FEATURE_UDHCPC_ARPING(const char *str_a = "2000";) 1219 IF_FEATURE_UDHCPC_ARPING(const char *str_a = "2000";)
1232 IF_FEATURE_UDHCP_PORT(char *str_P;) 1220 IF_FEATURE_UDHCP_PORT(char *str_P;)
1233 void *clientid_mac_ptr; 1221 uint8_t *clientid_mac_ptr;
1234 llist_t *list_O = NULL; 1222 llist_t *list_O = NULL;
1235 llist_t *list_x = NULL; 1223 llist_t *list_x = NULL;
1236 int tryagain_timeout = 20; 1224 int tryagain_timeout = 20;
@@ -1263,14 +1251,14 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv)
1263 /* Parse command line */ 1251 /* Parse command line */
1264 opt = getopt32long(argv, "^" 1252 opt = getopt32long(argv, "^"
1265 /* O,x: list; -T,-t,-A take numeric param */ 1253 /* O,x: list; -T,-t,-A take numeric param */
1266 "CV:H:h:F:i:np:qRr:s:T:+t:+SA:+O:*ox:*fB" 1254 "CV:F:i:np:qRr:s:T:+t:+SA:+O:*ox:*fB"
1267 USE_FOR_MMU("b") 1255 USE_FOR_MMU("b")
1268 IF_FEATURE_UDHCPC_ARPING("a::") 1256 IF_FEATURE_UDHCPC_ARPING("a::")
1269 IF_FEATURE_UDHCP_PORT("P:") 1257 IF_FEATURE_UDHCP_PORT("P:")
1270 "v" 1258 "v"
1271 "\0" IF_UDHCP_VERBOSE("vv") /* -v is a counter */ 1259 "\0" IF_UDHCP_VERBOSE("vv") /* -v is a counter */
1272 , udhcpc_longopts 1260 , udhcpc_longopts
1273 , &str_V, &str_h, &str_h, &str_F 1261 , &str_V, &str_F
1274 , &client_data.interface, &client_data.pidfile /* i,p */ 1262 , &client_data.interface, &client_data.pidfile /* i,p */
1275 , &str_r /* r */ 1263 , &str_r /* r */
1276 , &client_data.script /* s */ 1264 , &client_data.script /* s */
@@ -1281,14 +1269,14 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv)
1281 IF_FEATURE_UDHCP_PORT(, &str_P) 1269 IF_FEATURE_UDHCP_PORT(, &str_P)
1282 IF_UDHCP_VERBOSE(, &dhcp_verbose) 1270 IF_UDHCP_VERBOSE(, &dhcp_verbose)
1283 ); 1271 );
1284 if (opt & (OPT_h|OPT_H)) {
1285 //msg added 2011-11
1286 bb_simple_error_msg("option -h NAME is deprecated, use -x hostname:NAME");
1287 client_data.hostname = alloc_dhcp_option(DHCP_HOST_NAME, str_h, 0);
1288 }
1289 if (opt & OPT_F) { 1272 if (opt & OPT_F) {
1273 char *p;
1274 unsigned len;
1290 /* FQDN option format: [0x51][len][flags][0][0]<fqdn> */ 1275 /* FQDN option format: [0x51][len][flags][0][0]<fqdn> */
1291 client_data.fqdn = alloc_dhcp_option(DHCP_FQDN, str_F, 3); 1276 len = strlen(str_F);
1277 p = udhcp_insert_new_option(
1278 &client_data.options, DHCP_FQDN,
1279 len + 3, /*dhcp6:*/ 0);
1292 /* Flag bits: 0000NEOS 1280 /* Flag bits: 0000NEOS
1293 * S: 1 = Client requests server to update A RR in DNS as well as PTR 1281 * S: 1 = Client requests server to update A RR in DNS as well as PTR
1294 * O: 1 = Server indicates to client that DNS has been updated regardless 1282 * O: 1 = Server indicates to client that DNS has been updated regardless
@@ -1297,9 +1285,10 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv)
1297 * N: 1 = Client requests server to not update DNS (S must be 0 then) 1285 * N: 1 = Client requests server to not update DNS (S must be 0 then)
1298 * Two [0] bytes which follow are deprecated and must be 0. 1286 * Two [0] bytes which follow are deprecated and must be 0.
1299 */ 1287 */
1300 client_data.fqdn[OPT_DATA + 0] = 0x1; 1288 p[OPT_DATA + 0] = 0x1;
1301 /*client_data.fqdn[OPT_DATA + 1] = 0; - xzalloc did it */ 1289 /*p[OPT_DATA + 1] = 0; - xzalloc did it */
1302 /*client_data.fqdn[OPT_DATA + 2] = 0; */ 1290 /*p[OPT_DATA + 2] = 0; */
1291 memcpy(p + OPT_DATA + 3, str_F, len); /* do not store NUL byte */
1303 } 1292 }
1304 if (opt & OPT_r) 1293 if (opt & OPT_r)
1305 requested_ip = inet_addr(str_r); 1294 requested_ip = inet_addr(str_r);
@@ -1335,7 +1324,29 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv)
1335 ); 1324 );
1336 free(optstr); 1325 free(optstr);
1337 } 1326 }
1327 if (str_V[0] != '\0') {
1328 char *p;
1329 unsigned len = strnlen(str_V, 254);
1330 p = udhcp_insert_new_option(
1331 &client_data.options, DHCP_VENDOR,
1332 len, /*dhcp6:*/ 0);
1333 memcpy(p + OPT_DATA, str_V, len); /* do not store NUL byte */
1334 }
1335
1336 clientid_mac_ptr = NULL;
1337 if (!(opt & OPT_C) && !udhcp_find_option(client_data.options, DHCP_CLIENT_ID)) {
1338 /* not suppressed and not set, create default client ID */
1339 clientid_mac_ptr = udhcp_insert_new_option(
1340 &client_data.options, DHCP_CLIENT_ID,
1341 1 + 6, /*dhcp6:*/ 0);
1342 clientid_mac_ptr[OPT_DATA] = 1; /* type: ethernet */
1343 clientid_mac_ptr += OPT_DATA + 1; /* skip option code, len, ethernet */
1344 }
1338 1345
1346 /* Not really necessary (we redo it on every iteration)
1347 * but allows early (before daemonization) detection
1348 * of bad interface name.
1349 */
1339 if (udhcp_read_interface(client_data.interface, 1350 if (udhcp_read_interface(client_data.interface,
1340 &client_data.ifindex, 1351 &client_data.ifindex,
1341 NULL, 1352 NULL,
@@ -1344,24 +1355,6 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv)
1344 return 1; 1355 return 1;
1345 } 1356 }
1346 1357
1347 clientid_mac_ptr = NULL;
1348 if (!(opt & OPT_C) && !udhcp_find_option(client_data.options, DHCP_CLIENT_ID)) {
1349 /* not suppressed and not set, set the default client ID */
1350 client_data.clientid = alloc_dhcp_option(DHCP_CLIENT_ID, "", 7);
1351 client_data.clientid[OPT_DATA] = 1; /* type: ethernet */
1352 clientid_mac_ptr = client_data.clientid + OPT_DATA+1;
1353 memcpy(clientid_mac_ptr, client_data.client_mac, 6);
1354 }
1355 if (str_V[0] != '\0') {
1356 // can drop -V, str_V, client_data.vendorclass,
1357 // but need to add "vendor" to the list of recognized
1358 // string opts for this to work;
1359 // and need to tweak add_client_options() too...
1360 // ...so the question is, should we?
1361 //bb_error_msg("option -V VENDOR is deprecated, use -x vendor:VENDOR");
1362 client_data.vendorclass = alloc_dhcp_option(DHCP_VENDOR, str_V, 0);
1363 }
1364
1365#if !BB_MMU 1358#if !BB_MMU
1366 /* on NOMMU reexec (i.e., background) early */ 1359 /* on NOMMU reexec (i.e., background) early */
1367 if (!(opt & OPT_f)) { 1360 if (!(opt & OPT_f)) {
@@ -1382,8 +1375,7 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv)
1382 srand(monotonic_us()); 1375 srand(monotonic_us());
1383 1376
1384 client_data.state = INIT_SELECTING; 1377 client_data.state = INIT_SELECTING;
1385 udhcp_run_script(NULL, "deconfig"); 1378 d4_run_script_deconfig();
1386 change_listen_mode(LISTEN_RAW);
1387 packet_num = 0; 1379 packet_num = 0;
1388 timeout = 0; 1380 timeout = 0;
1389 lease_remaining = 0; 1381 lease_remaining = 0;
@@ -1417,14 +1409,17 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv)
1417 log1("waiting %u seconds", timeout); 1409 log1("waiting %u seconds", timeout);
1418 diff = (unsigned)monotonic_sec(); 1410 diff = (unsigned)monotonic_sec();
1419 retval = poll(pfds, 2, timeout * 1000); 1411 retval = poll(pfds, 2, timeout * 1000);
1412 diff = (unsigned)monotonic_sec() - diff;
1413 lease_remaining -= diff;
1414 if (lease_remaining < 0)
1415 lease_remaining = 0;
1416 timeout -= diff;
1417 if (timeout < 0)
1418 timeout = 0;
1419
1420 if (retval < 0) { 1420 if (retval < 0) {
1421 /* EINTR? A signal was caught, don't panic */ 1421 /* EINTR? A signal was caught, don't panic */
1422 if (errno == EINTR) { 1422 if (errno == EINTR) {
1423 diff = (unsigned)monotonic_sec() - diff;
1424 lease_remaining -= diff;
1425 if (lease_remaining < 0)
1426 lease_remaining = 0;
1427 timeout -= diff;
1428 continue; 1423 continue;
1429 } 1424 }
1430 /* Else: an error occurred, panic! */ 1425 /* Else: an error occurred, panic! */
@@ -1454,8 +1449,10 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv)
1454 switch (client_data.state) { 1449 switch (client_data.state) {
1455 case INIT_SELECTING: 1450 case INIT_SELECTING:
1456 if (!discover_retries || packet_num < discover_retries) { 1451 if (!discover_retries || packet_num < discover_retries) {
1457 if (packet_num == 0) 1452 if (packet_num == 0) {
1453 change_listen_mode(LISTEN_RAW);
1458 xid = random_xid(); 1454 xid = random_xid();
1455 }
1459 /* broadcast */ 1456 /* broadcast */
1460 send_discover(xid, requested_ip); 1457 send_discover(xid, requested_ip);
1461 timeout = discover_timeout; 1458 timeout = discover_timeout;
@@ -1463,7 +1460,8 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv)
1463 continue; 1460 continue;
1464 } 1461 }
1465 leasefail: 1462 leasefail:
1466 udhcp_run_script(NULL, "leasefail"); 1463 change_listen_mode(LISTEN_NONE);
1464 d4_run_script(NULL, "leasefail");
1467#if BB_MMU /* -b is not supported on NOMMU */ 1465#if BB_MMU /* -b is not supported on NOMMU */
1468 if (opt & OPT_b) { /* background if no lease */ 1466 if (opt & OPT_b) { /* background if no lease */
1469 bb_simple_info_msg("no lease, forking to background"); 1467 bb_simple_info_msg("no lease, forking to background");
@@ -1483,7 +1481,7 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv)
1483 retval = 1; 1481 retval = 1;
1484 goto ret; 1482 goto ret;
1485 } 1483 }
1486 /* wait before trying again */ 1484 /* Wait before trying again */
1487 timeout = tryagain_timeout; 1485 timeout = tryagain_timeout;
1488 packet_num = 0; 1486 packet_num = 0;
1489 continue; 1487 continue;
@@ -1499,22 +1497,20 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv)
1499 * "discover...select...discover..." loops 1497 * "discover...select...discover..." loops
1500 * were seen in the wild. Treat them similarly 1498 * were seen in the wild. Treat them similarly
1501 * to "no response to discover" case */ 1499 * to "no response to discover" case */
1502 change_listen_mode(LISTEN_RAW);
1503 client_data.state = INIT_SELECTING; 1500 client_data.state = INIT_SELECTING;
1504 goto leasefail; 1501 goto leasefail;
1505 case BOUND: 1502 case BOUND:
1506 /* 1/2 lease passed, enter renewing state */ 1503 /* 1/2 lease passed, enter renewing state */
1507 client_data.state = RENEWING; 1504 client_data.state = RENEWING;
1508 client_data.first_secs = 0; /* make secs field count from 0 */ 1505 client_data.first_secs = 0; /* make secs field count from 0 */
1509 change_listen_mode(LISTEN_KERNEL); 1506 got_SIGUSR1:
1510 log1s("entering renew state"); 1507 log1s("entering renew state");
1508 change_listen_mode(LISTEN_KERNEL);
1511 /* fall right through */ 1509 /* fall right through */
1512 case RENEW_REQUESTED: /* manual (SIGUSR1) renew */ 1510 case RENEW_REQUESTED: /* in manual (SIGUSR1) renew */
1513 case_RENEW_REQUESTED:
1514 case RENEWING: 1511 case RENEWING:
1515 if (packet_num < 3) { 1512 if (packet_num == 0) {
1516 packet_num++; 1513 /* Send an unicast renew request */
1517 /* send an unicast renew request */
1518 /* Sometimes observed to fail (EADDRNOTAVAIL) to bind 1514 /* Sometimes observed to fail (EADDRNOTAVAIL) to bind
1519 * a new UDP socket for sending inside send_renew. 1515 * a new UDP socket for sending inside send_renew.
1520 * I hazard to guess existing listening socket 1516 * I hazard to guess existing listening socket
@@ -1525,7 +1521,7 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv)
1525 */ 1521 */
1526 if (send_renew(xid, server_addr, requested_ip) >= 0) { 1522 if (send_renew(xid, server_addr, requested_ip) >= 0) {
1527 timeout = discover_timeout; 1523 timeout = discover_timeout;
1528 /* ^^^ used to be = lease_remaining / 2 - WAY too long */ 1524 packet_num++;
1529 continue; 1525 continue;
1530 } 1526 }
1531 /* else: error sending. 1527 /* else: error sending.
@@ -1533,27 +1529,34 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv)
1533 * which gave us bogus server ID 1.1.1.1 1529 * which gave us bogus server ID 1.1.1.1
1534 * which wasn't reachable (and probably did not exist). 1530 * which wasn't reachable (and probably did not exist).
1535 */ 1531 */
1532 } /* else: we had sent one packet, but got no reply */
1533 log1s("no response to renew");
1534 if (lease_remaining > 30) {
1535 /* Some lease time remains, try to renew later */
1536 change_listen_mode(LISTEN_NONE);
1537 goto BOUND_for_half_lease;
1536 } 1538 }
1537//TODO: if 3 renew's failed (no reply) but remaining lease is large, 1539 /* Enter rebinding state */
1538//it might make sense to make a large pause (~1 hour?) and try later?
1539 /* Timed out or error, enter rebinding state */
1540 log1s("entering rebinding state");
1541 client_data.state = REBINDING; 1540 client_data.state = REBINDING;
1542 /* fall right through */ 1541 log1s("entering rebinding state");
1543 case REBINDING:
1544 /* Switch to bcast receive */ 1542 /* Switch to bcast receive */
1545 change_listen_mode(LISTEN_RAW); 1543 change_listen_mode(LISTEN_RAW);
1544 packet_num = 0;
1545 /* fall right through */
1546 case REBINDING:
1546 /* Lease is *really* about to run out, 1547 /* Lease is *really* about to run out,
1547 * try to find DHCP server using broadcast */ 1548 * try to find DHCP server using broadcast */
1548 if (lease_remaining > 0) { 1549 if (lease_remaining > 0 && packet_num < 3) {
1549 /* send a broadcast renew request */ 1550 /* send a broadcast renew request */
1550 send_renew(xid, 0 /*INADDR_ANY*/, requested_ip); 1551 send_renew(xid, 0 /*INADDR_ANY*/, requested_ip);
1551 timeout = discover_timeout; 1552 timeout = discover_timeout;
1553 packet_num++;
1552 continue; 1554 continue;
1553 } 1555 }
1554 /* Timed out, enter init state */ 1556 /* Timed out, enter init state */
1557 change_listen_mode(LISTEN_NONE);
1555 bb_simple_info_msg("lease lost, entering init state"); 1558 bb_simple_info_msg("lease lost, entering init state");
1556 udhcp_run_script(NULL, "deconfig"); 1559 d4_run_script_deconfig();
1557 client_data.state = INIT_SELECTING; 1560 client_data.state = INIT_SELECTING;
1558 client_data.first_secs = 0; /* make secs field count from 0 */ 1561 client_data.first_secs = 0; /* make secs field count from 0 */
1559 timeout = 0; 1562 timeout = 0;
@@ -1561,7 +1564,9 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv)
1561 continue; 1564 continue;
1562 /* case RELEASED: */ 1565 /* case RELEASED: */
1563 } 1566 }
1564 /* yah, I know, *you* say it would never happen */ 1567 /* RELEASED state (when we got SIGUSR2) ends up here.
1568 * (wait for SIGUSR1 to re-init, or for TERM, etc)
1569 */
1565 timeout = INT_MAX; 1570 timeout = INT_MAX;
1566 continue; /* back to main loop */ 1571 continue; /* back to main loop */
1567 } /* if poll timed out */ 1572 } /* if poll timed out */
@@ -1571,33 +1576,42 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv)
1571 /* Is it a signal? */ 1576 /* Is it a signal? */
1572 switch (udhcp_sp_read()) { 1577 switch (udhcp_sp_read()) {
1573 case SIGUSR1: 1578 case SIGUSR1:
1579 if (client_data.state <= REQUESTING)
1580 /* Initial negotiations in progress, do not disturb */
1581 break;
1582 if (client_data.state == REBINDING)
1583 /* Do not go back from rebind to renew state */
1584 break;
1585
1586 if (lease_remaining > 30) /* if renew fails, do not go back to BOUND */
1587 lease_remaining = 30;
1574 client_data.first_secs = 0; /* make secs field count from 0 */ 1588 client_data.first_secs = 0; /* make secs field count from 0 */
1575 bb_simple_info_msg("performing DHCP renew"); 1589 packet_num = 0;
1576 1590
1577 switch (client_data.state) { 1591 switch (client_data.state) {
1578 /* Try to renew/rebind */
1579 case BOUND: 1592 case BOUND:
1580 case RENEWING: 1593 case RENEWING:
1581 case REBINDING: 1594 /* Try to renew/rebind */
1582 change_listen_mode(LISTEN_KERNEL);
1583 client_data.state = RENEW_REQUESTED; 1595 client_data.state = RENEW_REQUESTED;
1584 goto case_RENEW_REQUESTED; 1596 goto got_SIGUSR1;
1585 1597
1586 /* Start things over */ 1598 case RENEW_REQUESTED:
1587 case RENEW_REQUESTED: /* two or more SIGUSR1 received */ 1599 /* Two SIGUSR1 received, start things over */
1588 udhcp_run_script(NULL, "deconfig"); 1600 change_listen_mode(LISTEN_NONE);
1589 /* case REQUESTING: break; */ 1601 d4_run_script_deconfig();
1590 /* case RELEASED: break; */ 1602
1591 /* case INIT_SELECTING: break; */ 1603 default:
1604 /* case RELEASED: */
1605 /* Wake from SIGUSR2-induced deconfigured state */
1606 change_listen_mode(LISTEN_NONE);
1592 } 1607 }
1593 change_listen_mode(LISTEN_RAW);
1594 client_data.state = INIT_SELECTING; 1608 client_data.state = INIT_SELECTING;
1595 packet_num = 0;
1596 /* Kill any timeouts, user wants this to hurry along */ 1609 /* Kill any timeouts, user wants this to hurry along */
1597 timeout = 0; 1610 timeout = 0;
1598 continue; 1611 continue;
1599 case SIGUSR2: 1612 case SIGUSR2:
1600 perform_release(server_addr, requested_ip); 1613 perform_release(server_addr, requested_ip);
1614 /* ^^^ switches to LISTEN_NONE */
1601 timeout = INT_MAX; 1615 timeout = INT_MAX;
1602 continue; 1616 continue;
1603 case SIGTERM: 1617 case SIGTERM:
@@ -1616,7 +1630,7 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv)
1616 if (client_data.listen_mode == LISTEN_KERNEL) 1630 if (client_data.listen_mode == LISTEN_KERNEL)
1617 len = udhcp_recv_kernel_packet(&packet, client_data.sockfd); 1631 len = udhcp_recv_kernel_packet(&packet, client_data.sockfd);
1618 else 1632 else
1619 len = udhcp_recv_raw_packet(&packet, client_data.sockfd); 1633 len = d4_recv_raw_packet(&packet, client_data.sockfd);
1620 if (len == -1) { 1634 if (len == -1) {
1621 /* Error is severe, reopen socket */ 1635 /* Error is severe, reopen socket */
1622 bb_error_msg("read error: "STRERROR_FMT", reopening socket" STRERROR_ERRNO); 1636 bb_error_msg("read error: "STRERROR_FMT", reopening socket" STRERROR_ERRNO);
@@ -1640,13 +1654,13 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv)
1640 || memcmp(packet.chaddr, client_data.client_mac, 6) != 0 1654 || memcmp(packet.chaddr, client_data.client_mac, 6) != 0
1641 ) { 1655 ) {
1642//FIXME: need to also check that last 10 bytes are zero 1656//FIXME: need to also check that last 10 bytes are zero
1643 log1("chaddr does not match%s", ", ignoring packet"); // log2? 1657 log1("chaddr does not match%s", ", ignoring packet");
1644 continue; 1658 continue;
1645 } 1659 }
1646 1660
1647 message = udhcp_get_option(&packet, DHCP_MESSAGE_TYPE); 1661 message = udhcp_get_option(&packet, DHCP_MESSAGE_TYPE);
1648 if (message == NULL) { 1662 if (message == NULL) {
1649 bb_info_msg("no message type option%s", ", ignoring packet"); 1663 log1("no message type option%s", ", ignoring packet");
1650 continue; 1664 continue;
1651 } 1665 }
1652 1666
@@ -1654,6 +1668,7 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv)
1654 case INIT_SELECTING: 1668 case INIT_SELECTING:
1655 /* Must be a DHCPOFFER */ 1669 /* Must be a DHCPOFFER */
1656 if (*message == DHCPOFFER) { 1670 if (*message == DHCPOFFER) {
1671 struct in_addr temp_addr;
1657 uint8_t *temp; 1672 uint8_t *temp;
1658 1673
1659/* What exactly is server's IP? There are several values. 1674/* What exactly is server's IP? There are several values.
@@ -1689,7 +1704,8 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv)
1689 move_from_unaligned32(server_addr, temp); 1704 move_from_unaligned32(server_addr, temp);
1690 } 1705 }
1691 /*xid = packet.xid; - already is */ 1706 /*xid = packet.xid; - already is */
1692 requested_ip = packet.yiaddr; 1707 temp_addr.s_addr = requested_ip = packet.yiaddr;
1708 log1("received offer of %s", inet_ntoa(temp_addr));
1693 1709
1694 /* enter requesting state */ 1710 /* enter requesting state */
1695 client_data.state = REQUESTING; 1711 client_data.state = REQUESTING;
@@ -1707,14 +1723,15 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv)
1707 char server_str[sizeof("255.255.255.255")]; 1723 char server_str[sizeof("255.255.255.255")];
1708 uint8_t *temp; 1724 uint8_t *temp;
1709 1725
1726 change_listen_mode(LISTEN_NONE);
1727
1710 temp_addr.s_addr = server_addr; 1728 temp_addr.s_addr = server_addr;
1711 strcpy(server_str, inet_ntoa(temp_addr)); 1729 strcpy(server_str, inet_ntoa(temp_addr));
1712 temp_addr.s_addr = packet.yiaddr; 1730 temp_addr.s_addr = packet.yiaddr;
1713 1731
1732 lease_remaining = 60 * 60;
1714 temp = udhcp_get_option32(&packet, DHCP_LEASE_TIME); 1733 temp = udhcp_get_option32(&packet, DHCP_LEASE_TIME);
1715 if (!temp) { 1734 if (temp) {
1716 lease_remaining = 60 * 60;
1717 } else {
1718 uint32_t lease; 1735 uint32_t lease;
1719 /* it IS unaligned sometimes, don't "optimize" */ 1736 /* it IS unaligned sometimes, don't "optimize" */
1720 move_from_unaligned32(lease, temp); 1737 move_from_unaligned32(lease, temp);
@@ -1758,8 +1775,7 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv)
1758 send_decline(/*xid,*/ server_addr, packet.yiaddr); 1775 send_decline(/*xid,*/ server_addr, packet.yiaddr);
1759 1776
1760 if (client_data.state != REQUESTING) 1777 if (client_data.state != REQUESTING)
1761 udhcp_run_script(NULL, "deconfig"); 1778 d4_run_script_deconfig();
1762 change_listen_mode(LISTEN_RAW);
1763 client_data.state = INIT_SELECTING; 1779 client_data.state = INIT_SELECTING;
1764 client_data.first_secs = 0; /* make secs field count from 0 */ 1780 client_data.first_secs = 0; /* make secs field count from 0 */
1765 requested_ip = 0; 1781 requested_ip = 0;
@@ -1769,17 +1785,12 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv)
1769 } 1785 }
1770 } 1786 }
1771#endif 1787#endif
1772
1773 /* enter bound state */ 1788 /* enter bound state */
1774 start = monotonic_sec(); 1789 start = monotonic_sec();
1775 udhcp_run_script(&packet, client_data.state == REQUESTING ? "bound" : "renew"); 1790 d4_run_script(&packet, client_data.state == REQUESTING ? "bound" : "renew");
1776 timeout = (unsigned)lease_remaining / 2; 1791 lease_remaining -= (unsigned)monotonic_sec() - start;
1777//TODO: why / 2? 1792 if (lease_remaining < 0)
1778 timeout -= (unsigned)monotonic_sec() - start; 1793 lease_remaining = 0;
1779 packet_num = 0;
1780
1781 client_data.state = BOUND;
1782 change_listen_mode(LISTEN_NONE);
1783 if (opt & OPT_q) { /* quit after lease */ 1794 if (opt & OPT_q) { /* quit after lease */
1784 goto ret0; 1795 goto ret0;
1785 } 1796 }
@@ -1792,9 +1803,12 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv)
1792 opt = ((opt & ~OPT_b) | OPT_f); 1803 opt = ((opt & ~OPT_b) | OPT_f);
1793 } 1804 }
1794#endif 1805#endif
1806 BOUND_for_half_lease:
1807 timeout = (unsigned)lease_remaining / 2;
1808 client_data.state = BOUND;
1795 /* make future renew packets use different xid */ 1809 /* make future renew packets use different xid */
1796 /* xid = random_xid(); ...but why bother? */ 1810 /* xid = random_xid(); ...but why bother? */
1797 1811 packet_num = 0;
1798 continue; /* back to main loop */ 1812 continue; /* back to main loop */
1799 } 1813 }
1800 if (*message == DHCPNAK) { 1814 if (*message == DHCPNAK) {
@@ -1818,11 +1832,11 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv)
1818 goto non_matching_svid; 1832 goto non_matching_svid;
1819 } 1833 }
1820 /* return to init state */ 1834 /* return to init state */
1835 change_listen_mode(LISTEN_NONE);
1821 bb_info_msg("received %s", "DHCP NAK"); 1836 bb_info_msg("received %s", "DHCP NAK");
1822 udhcp_run_script(&packet, "nak"); 1837 d4_run_script(&packet, "nak");
1823 if (client_data.state != REQUESTING) 1838 if (client_data.state != REQUESTING)
1824 udhcp_run_script(NULL, "deconfig"); 1839 d4_run_script_deconfig();
1825 change_listen_mode(LISTEN_RAW);
1826 sleep(3); /* avoid excessive network traffic */ 1840 sleep(3); /* avoid excessive network traffic */
1827 client_data.state = INIT_SELECTING; 1841 client_data.state = INIT_SELECTING;
1828 client_data.first_secs = 0; /* make secs field count from 0 */ 1842 client_data.first_secs = 0; /* make secs field count from 0 */
diff --git a/networking/udhcp/dhcpc.h b/networking/udhcp/dhcpc.h
index 7ad01ea8f..cd9ead6bd 100644
--- a/networking/udhcp/dhcpc.h
+++ b/networking/udhcp/dhcpc.h
@@ -17,10 +17,6 @@ struct client_data_t {
17 char *pidfile; /* Optionally store the process ID */ 17 char *pidfile; /* Optionally store the process ID */
18 const char *script; /* User script to run at dhcp events */ 18 const char *script; /* User script to run at dhcp events */
19 struct option_set *options; /* list of DHCP options to send to server */ 19 struct option_set *options; /* list of DHCP options to send to server */
20 uint8_t *clientid; /* Optional client id to use */
21 uint8_t *vendorclass; /* Optional vendor class-id to use */
22 uint8_t *hostname; /* Optional hostname to use */
23 uint8_t *fqdn; /* Optional fully qualified domain name to use */
24 llist_t *envp; /* list of DHCP options used for env vars */ 20 llist_t *envp; /* list of DHCP options used for env vars */
25 21
26 unsigned first_secs; 22 unsigned first_secs;
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
295 uint32_t nip; 295 uint32_t nip;
296 struct dyn_lease *lease; 296 struct dyn_lease *lease;
297 297
298 /* ie, 192.168.55.0 */ 298 /* (Addresses ending in .0 or .255 can legitimately be allocated
299 if ((addr & 0xff) == 0) 299 * in various situations, so _don't_ skip these. The user needs
300 goto next_addr; 300 * to choose start_ip and end_ip correctly for a particular
301 /* ie, 192.168.55.255 */ 301 * network environment.) */
302 if ((addr & 0xff) == 0xff) 302
303 goto next_addr;
304 nip = htonl(addr); 303 nip = htonl(addr);
305 /* skip our own address */ 304 /* skip our own address */
306 if (nip == server_data.server_nip) 305 if (nip == server_data.server_nip)
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)
95 bb_simple_info_msg("packet with bad magic, ignoring"); 95 bb_simple_info_msg("packet with bad magic, ignoring");
96 return -2; 96 return -2;
97 } 97 }
98 log1("received %s", "a packet"); 98 log2("received %s", "a packet");
99 /* log2 because more informative msg for valid packets is printed later at log1 level */
99 udhcp_dump_packet(packet); 100 udhcp_dump_packet(packet);
100 101
101 return bytes; 102 return bytes;
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)
82 struct sockaddr_in addr; 82 struct sockaddr_in addr;
83 char *colon; 83 char *colon;
84 84
85 log1("opening listen socket on *:%d %s", port, inf); 85 log2("opening listen socket on *:%d %s", port, inf);
86 fd = xsocket(PF_INET, SOCK_DGRAM, IPPROTO_UDP); 86 fd = xsocket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
87 87
88 setsockopt_reuseaddr(fd); 88 setsockopt_reuseaddr(fd);
diff --git a/runit/runsv.c b/runit/runsv.c
index ecab8cdf5..a4b8af494 100644
--- a/runit/runsv.c
+++ b/runit/runsv.c
@@ -149,17 +149,18 @@ static void warn_cannot(const char *m)
149 warn2_cannot(m, ""); 149 warn2_cannot(m, "");
150} 150}
151 151
152static void s_child(int sig_no UNUSED_PARAM) 152/* SIGCHLD/TERM handler is reentrancy-safe because they are unmasked
153 * only over poll() call, not over memory allocations
154 * or printouts. Do not need to save/restore errno either,
155 * as poll() error is not checked there.
156 */
157static void s_chld_term(int sig_no)
153{ 158{
159 if (sig_no == SIGTERM)
160 sigterm = 1;
154 write(selfpipe.wr, "", 1); 161 write(selfpipe.wr, "", 1);
155} 162}
156 163
157static void s_term(int sig_no UNUSED_PARAM)
158{
159 sigterm = 1;
160 write(selfpipe.wr, "", 1); /* XXX */
161}
162
163static int open_trunc_or_warn(const char *name) 164static int open_trunc_or_warn(const char *name)
164{ 165{
165 /* Why O_NDELAY? */ 166 /* Why O_NDELAY? */
@@ -380,11 +381,14 @@ static void startservice(struct svdir *s)
380 xdup2(logpipe.wr, 1); 381 xdup2(logpipe.wr, 1);
381 } 382 }
382 } 383 }
383 /* Non-ignored signals revert to SIG_DFL on exec anyway */ 384 /* Non-ignored signals revert to SIG_DFL on exec.
384 /*bb_signals(0 385 * But we can get signals BEFORE execl(), unlikely as that may be.
385 + (1 << SIGCHLD) 386 * SIGCHLD is safe (would merely write to selfpipe),
386 + (1 << SIGTERM) 387 * but SIGTERM would set sigterm = 1 (with vfork, we affect parent).
387 , SIG_DFL);*/ 388 * Avoid that.
389 */
390 /*signal(SIGCHLD, SIG_DFL);*/
391 signal(SIGTERM, SIG_DFL);
388 sig_unblock(SIGCHLD); 392 sig_unblock(SIGCHLD);
389 sig_unblock(SIGTERM); 393 sig_unblock(SIGTERM);
390 execv(arg[0], (char**) arg); 394 execv(arg[0], (char**) arg);
@@ -511,9 +515,15 @@ int runsv_main(int argc UNUSED_PARAM, char **argv)
511 ndelay_on(selfpipe.wr); 515 ndelay_on(selfpipe.wr);
512 516
513 sig_block(SIGCHLD); 517 sig_block(SIGCHLD);
514 bb_signals_recursive_norestart(1 << SIGCHLD, s_child);
515 sig_block(SIGTERM); 518 sig_block(SIGTERM);
516 bb_signals_recursive_norestart(1 << SIGTERM, s_term); 519 /* No particular reason why we don't set SA_RESTART
520 * (poll() wouldn't restart regardless of that flag),
521 * we just follow what runit-2.1.2 does:
522 */
523 bb_signals_norestart(0
524 + (1 << SIGCHLD)
525 + (1 << SIGTERM)
526 , s_chld_term);
517 527
518 xchdir(dir); 528 xchdir(dir);
519 /* bss: svd[0].pid = 0; */ 529 /* bss: svd[0].pid = 0; */
@@ -625,6 +635,7 @@ int runsv_main(int argc UNUSED_PARAM, char **argv)
625 sig_unblock(SIGTERM); 635 sig_unblock(SIGTERM);
626 sig_unblock(SIGCHLD); 636 sig_unblock(SIGCHLD);
627 poll(x, 2 + haslog, 3600*1000); 637 poll(x, 2 + haslog, 3600*1000);
638 /* NB: signal handlers can trash errno of poll() */
628 sig_block(SIGTERM); 639 sig_block(SIGTERM);
629 sig_block(SIGCHLD); 640 sig_block(SIGCHLD);
630 641
diff --git a/runit/svlogd.c b/runit/svlogd.c
index 294e31aca..02c305696 100644
--- a/runit/svlogd.c
+++ b/runit/svlogd.c
@@ -412,19 +412,32 @@ static void processorstart(struct logdir *ld)
412 int fd; 412 int fd;
413 413
414 /* child */ 414 /* child */
415 /* Non-ignored signals revert to SIG_DFL on exec anyway */ 415 /* Non-ignored signals revert to SIG_DFL on exec anyway.
416 * But we can get signals BEFORE execl(), this is unlikely
417 * but wouldn't be good...
418 */
416 /*bb_signals(0 419 /*bb_signals(0
417 + (1 << SIGTERM) 420 + (1 << SIGTERM)
421 //+ (1 << SIGCHLD)
418 + (1 << SIGALRM) 422 + (1 << SIGALRM)
419 + (1 << SIGHUP) 423 + (1 << SIGHUP)
420 , SIG_DFL);*/ 424 , SIG_DFL);*/
421 sig_unblock(SIGTERM); 425 /* runit 2.1.2 does not unblock SIGCHLD, a bug? we do: */
422 sig_unblock(SIGALRM); 426 sigprocmask(SIG_UNBLOCK, &blocked_sigset, NULL);
423 sig_unblock(SIGHUP);
424 427
425 if (verbose) 428 if (verbose)
426 bb_error_msg(INFO"processing: %s/%s", ld->name, ld->fnsave); 429 bb_error_msg(INFO"processing: %s/%s", ld->name, ld->fnsave);
427 fd = xopen(ld->fnsave, O_RDONLY|O_NDELAY); 430
431 fd = open_or_warn(ld->fnsave, O_RDONLY|O_NDELAY);
432 /* Used to have xopen() above, but it causes infinite restarts of processor
433 * if file is gone - which can happen even because of _us_!
434 * Users report that if on reboot, time is reset to before existing
435 * logfiles creation time, rmoldest() deletes the newest logfile (!)
436 * and we end up here trying to open this now-deleted file.
437 */
438 if (fd < 0)
439 _exit(0); /* fake "success": do not run processor again */
440
428 xmove_fd(fd, 0); 441 xmove_fd(fd, 0);
429 ld->fnsave[26] = 't'; /* <- that's why we need sv_ch! */ 442 ld->fnsave[26] = 't'; /* <- that's why we need sv_ch! */
430 fd = xopen(ld->fnsave, O_WRONLY|O_NDELAY|O_TRUNC|O_CREAT); 443 fd = xopen(ld->fnsave, O_WRONLY|O_NDELAY|O_TRUNC|O_CREAT);
@@ -1098,10 +1111,10 @@ int svlogd_main(int argc, char **argv)
1098 sigaddset(&blocked_sigset, SIGALRM); 1111 sigaddset(&blocked_sigset, SIGALRM);
1099 sigaddset(&blocked_sigset, SIGHUP); 1112 sigaddset(&blocked_sigset, SIGHUP);
1100 sigprocmask(SIG_BLOCK, &blocked_sigset, NULL); 1113 sigprocmask(SIG_BLOCK, &blocked_sigset, NULL);
1101 bb_signals_recursive_norestart(1 << SIGTERM, sig_term_handler); 1114 bb_signals_norestart(1 << SIGTERM, sig_term_handler);
1102 bb_signals_recursive_norestart(1 << SIGCHLD, sig_child_handler); 1115 bb_signals_norestart(1 << SIGCHLD, sig_child_handler);
1103 bb_signals_recursive_norestart(1 << SIGALRM, sig_alarm_handler); 1116 bb_signals_norestart(1 << SIGALRM, sig_alarm_handler);
1104 bb_signals_recursive_norestart(1 << SIGHUP, sig_hangup_handler); 1117 bb_signals_norestart(1 << SIGHUP, sig_hangup_handler);
1105 1118
1106 /* Without timestamps, we don't have to print each line 1119 /* Without timestamps, we don't have to print each line
1107 * separately, so we can look for _last_ newline, not first, 1120 * separately, so we can look for _last_ newline, not first,
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 @@
4# but users complain that many sed implementations 4# but users complain that many sed implementations
5# are misinterpreting --. 5# are misinterpreting --.
6 6
7export LC_ALL=C
8
7test $# -ge 2 || { echo "Syntax: $0 SRCTREE OBJTREE"; exit 1; } 9test $# -ge 2 || { echo "Syntax: $0 SRCTREE OBJTREE"; exit 1; }
8 10
9# cd to objtree 11# cd to objtree
diff --git a/scripts/kconfig/confdata.c b/scripts/kconfig/confdata.c
index d6c1621b8..2f7fa6618 100644
--- a/scripts/kconfig/confdata.c
+++ b/scripts/kconfig/confdata.c
@@ -342,6 +342,8 @@ int conf_write(const char *name)
342 time_t now; 342 time_t now;
343 int use_timestamp = 1; 343 int use_timestamp = 1;
344 char *env; 344 char *env;
345 char *source_date_epoch;
346 struct tm *build_time;
345 347
346 dirname[0] = 0; 348 dirname[0] = 0;
347 if (name && name[0]) { 349 if (name && name[0]) {
@@ -378,7 +380,16 @@ int conf_write(const char *name)
378 } 380 }
379 sym = sym_lookup("KERNELVERSION", 0); 381 sym = sym_lookup("KERNELVERSION", 0);
380 sym_calc_value(sym); 382 sym_calc_value(sym);
381 time(&now); 383
384 source_date_epoch = getenv("SOURCE_DATE_EPOCH");
385 if (source_date_epoch && *source_date_epoch) {
386 now = strtoull(source_date_epoch, NULL, 10);
387 build_time = gmtime(&now);
388 } else {
389 time(&now);
390 build_time = localtime(&now);
391 }
392
382 env = getenv("KCONFIG_NOTIMESTAMP"); 393 env = getenv("KCONFIG_NOTIMESTAMP");
383 if (env && *env) 394 if (env && *env)
384 use_timestamp = 0; 395 use_timestamp = 0;
@@ -398,14 +409,14 @@ int conf_write(const char *name)
398 if (use_timestamp) { 409 if (use_timestamp) {
399 size_t ret = \ 410 size_t ret = \
400 strftime(buf, sizeof(buf), "#define AUTOCONF_TIMESTAMP " 411 strftime(buf, sizeof(buf), "#define AUTOCONF_TIMESTAMP "
401 "\"%Y-%m-%d %H:%M:%S %Z\"\n", localtime(&now)); 412 "\"%Y-%m-%d %H:%M:%S %Z\"\n", build_time);
402 /* if user has Factory timezone or some other odd install, the 413 /* if user has Factory timezone or some other odd install, the
403 * %Z above will overflow the string leaving us with undefined 414 * %Z above will overflow the string leaving us with undefined
404 * results ... so let's try again without the timezone. 415 * results ... so let's try again without the timezone.
405 */ 416 */
406 if (ret == 0) 417 if (ret == 0)
407 strftime(buf, sizeof(buf), "#define AUTOCONF_TIMESTAMP " 418 strftime(buf, sizeof(buf), "#define AUTOCONF_TIMESTAMP "
408 "\"%Y-%m-%d %H:%M:%S\"\n", localtime(&now)); 419 "\"%Y-%m-%d %H:%M:%S\"\n", build_time);
409 } else { /* bbox */ 420 } else { /* bbox */
410 strcpy(buf, "#define AUTOCONF_TIMESTAMP \"\"\n"); 421 strcpy(buf, "#define AUTOCONF_TIMESTAMP \"\"\n");
411 } 422 }
diff --git a/shell/ash.c b/shell/ash.c
index 3d6f25802..14712ef54 100644
--- a/shell/ash.c
+++ b/shell/ash.c
@@ -262,6 +262,14 @@
262#define BASH_READ_D ENABLE_ASH_BASH_COMPAT 262#define BASH_READ_D ENABLE_ASH_BASH_COMPAT
263#define IF_BASH_READ_D IF_ASH_BASH_COMPAT 263#define IF_BASH_READ_D IF_ASH_BASH_COMPAT
264#define BASH_WAIT_N ENABLE_ASH_BASH_COMPAT 264#define BASH_WAIT_N ENABLE_ASH_BASH_COMPAT
265/* <(...) and >(...) */
266#if HAVE_DEV_FD
267# define BASH_PROCESS_SUBST ENABLE_ASH_BASH_COMPAT
268# define IF_BASH_PROCESS_SUBST IF_ASH_BASH_COMPAT
269#else
270# define BASH_PROCESS_SUBST 0
271# define IF_BASH_PROCESS_SUBST(...)
272#endif
265 273
266#if defined(__ANDROID_API__) && __ANDROID_API__ <= 24 274#if defined(__ANDROID_API__) && __ANDROID_API__ <= 24
267/* Bionic at least up to version 24 has no glob() */ 275/* Bionic at least up to version 24 has no glob() */
@@ -973,6 +981,12 @@ out2str(const char *p)
973#define CTLENDARI ((unsigned char)'\207') 981#define CTLENDARI ((unsigned char)'\207')
974#define CTLQUOTEMARK ((unsigned char)'\210') 982#define CTLQUOTEMARK ((unsigned char)'\210')
975#define CTL_LAST CTLQUOTEMARK 983#define CTL_LAST CTLQUOTEMARK
984#if BASH_PROCESS_SUBST
985# define CTLTOPROC ((unsigned char)'\211')
986# define CTLFROMPROC ((unsigned char)'\212')
987# undef CTL_LAST
988# define CTL_LAST CTLFROMPROC
989#endif
976 990
977/* variable substitution byte (follows CTLVAR) */ 991/* variable substitution byte (follows CTLVAR) */
978#define VSTYPE 0x0f /* type of variable substitution */ 992#define VSTYPE 0x0f /* type of variable substitution */
@@ -1244,6 +1258,10 @@ trace_puts_quoted(char *s)
1244 case CTLESC: c = 'e'; goto backslash; 1258 case CTLESC: c = 'e'; goto backslash;
1245 case CTLVAR: c = 'v'; goto backslash; 1259 case CTLVAR: c = 'v'; goto backslash;
1246 case CTLBACKQ: c = 'q'; goto backslash; 1260 case CTLBACKQ: c = 'q'; goto backslash;
1261#if BASH_PROCESS_SUBST
1262 case CTLTOPROC: c = 'p'; goto backslash;
1263 case CTLFROMPROC: c = 'P'; goto backslash;
1264#endif
1247 backslash: 1265 backslash:
1248 putc('\\', tracefile); 1266 putc('\\', tracefile);
1249 putc(c, tracefile); 1267 putc(c, tracefile);
@@ -1405,8 +1423,17 @@ sharg(union node *arg, FILE *fp)
1405 case CTLENDVAR: 1423 case CTLENDVAR:
1406 putc('}', fp); 1424 putc('}', fp);
1407 break; 1425 break;
1426#if BASH_PROCESS_SUBST
1427 case CTLTOPROC:
1428 putc('>', fp);
1429 goto backq;
1430 case CTLFROMPROC:
1431 putc('<', fp);
1432 goto backq;
1433#endif
1408 case CTLBACKQ: 1434 case CTLBACKQ:
1409 putc('$', fp); 1435 putc('$', fp);
1436 IF_BASH_PROCESS_SUBST(backq:)
1410 putc('(', fp); 1437 putc('(', fp);
1411 shtree(bqlist->n, -1, NULL, fp); 1438 shtree(bqlist->n, -1, NULL, fp);
1412 putc(')', fp); 1439 putc(')', fp);
@@ -3663,8 +3690,13 @@ static const uint8_t syntax_index_table[] ALIGN1 = {
3663 /* 134 CTLARI */ CCTL_CCTL_CCTL_CCTL, 3690 /* 134 CTLARI */ CCTL_CCTL_CCTL_CCTL,
3664 /* 135 CTLENDARI */ CCTL_CCTL_CCTL_CCTL, 3691 /* 135 CTLENDARI */ CCTL_CCTL_CCTL_CCTL,
3665 /* 136 CTLQUOTEMARK */ CCTL_CCTL_CCTL_CCTL, 3692 /* 136 CTLQUOTEMARK */ CCTL_CCTL_CCTL_CCTL,
3693#if BASH_PROCESS_SUBST
3694 /* 137 CTLTOPROC */ CCTL_CCTL_CCTL_CCTL,
3695 /* 138 CTLFROMPROC */ CCTL_CCTL_CCTL_CCTL,
3696#else
3666 /* 137 */ CWORD_CWORD_CWORD_CWORD, 3697 /* 137 */ CWORD_CWORD_CWORD_CWORD,
3667 /* 138 */ CWORD_CWORD_CWORD_CWORD, 3698 /* 138 */ CWORD_CWORD_CWORD_CWORD,
3699#endif
3668 /* 139 */ CWORD_CWORD_CWORD_CWORD, 3700 /* 139 */ CWORD_CWORD_CWORD_CWORD,
3669 /* 140 */ CWORD_CWORD_CWORD_CWORD, 3701 /* 140 */ CWORD_CWORD_CWORD_CWORD,
3670 /* 141 */ CWORD_CWORD_CWORD_CWORD, 3702 /* 141 */ CWORD_CWORD_CWORD_CWORD,
@@ -5377,9 +5409,24 @@ cmdputs(const char *s)
5377 quoted >>= 1; 5409 quoted >>= 1;
5378 subtype = 0; 5410 subtype = 0;
5379 goto dostr; 5411 goto dostr;
5412#if BASH_PROCESS_SUBST
5413 case CTLBACKQ:
5414 c = '$';
5415 str = "(...)";
5416 break;
5417 case CTLTOPROC:
5418 c = '>';
5419 str = "(...)";
5420 break;
5421 case CTLFROMPROC:
5422 c = '<';
5423 str = "(...)";
5424 break;
5425#else
5380 case CTLBACKQ: 5426 case CTLBACKQ:
5381 str = "$(...)"; 5427 str = "$(...)";
5382 goto dostr; 5428 goto dostr;
5429#endif
5383#if ENABLE_FEATURE_SH_MATH 5430#if ENABLE_FEATURE_SH_MATH
5384 case CTLARI: 5431 case CTLARI:
5385 str = "$(("; 5432 str = "$((";
@@ -6446,6 +6493,21 @@ redirectsafe(union node *redir, int flags)
6446 return err; 6493 return err;
6447} 6494}
6448 6495
6496#if BASH_PROCESS_SUBST
6497static void
6498pushfd(int fd)
6499{
6500 struct redirtab *sv;
6501
6502 sv = ckzalloc(sizeof(*sv) + sizeof(sv->two_fd[0]));
6503 sv->pair_count = 1;
6504 sv->two_fd[0].orig_fd = fd;
6505 sv->two_fd[0].moved_to = CLOSED;
6506 sv->next = redirlist;
6507 redirlist = sv;
6508}
6509#endif
6510
6449static struct redirtab* 6511static struct redirtab*
6450pushredir(union node *redir) 6512pushredir(union node *redir)
6451{ 6513{
@@ -7084,10 +7146,20 @@ evaltreenr(union node *n, int flags)
7084} 7146}
7085 7147
7086static void FAST_FUNC 7148static void FAST_FUNC
7087evalbackcmd(union node *n, struct backcmd *result) 7149evalbackcmd(union node *n, struct backcmd *result
7150 IF_BASH_PROCESS_SUBST(, int ctl))
7088{ 7151{
7089 int pip[2]; 7152 int pip[2];
7090 struct job *jp; 7153 struct job *jp;
7154#if BASH_PROCESS_SUBST
7155 /* determine end of pipe used by parent (ip) and child (ic) */
7156 const int ip = (ctl == CTLTOPROC);
7157 const int ic = !(ctl == CTLTOPROC);
7158#else
7159 const int ctl = CTLBACKQ;
7160 const int ip = 0;
7161 const int ic = 1;
7162#endif
7091 IF_PLATFORM_MINGW32(struct forkshell fs); 7163 IF_PLATFORM_MINGW32(struct forkshell fs);
7092 7164
7093 result->fd = -1; 7165 result->fd = -1;
@@ -7100,23 +7172,26 @@ evalbackcmd(union node *n, struct backcmd *result)
7100 7172
7101 if (pipe(pip) < 0) 7173 if (pipe(pip) < 0)
7102 ash_msg_and_raise_perror("can't create pipe"); 7174 ash_msg_and_raise_perror("can't create pipe");
7103 jp = makejob(/*n,*/ 1); 7175 /* process substitution uses NULL job/node, like openhere() */
7176 jp = (ctl == CTLBACKQ) ? makejob(/*n,*/ 1) : NULL;
7104#if ENABLE_PLATFORM_MINGW32 7177#if ENABLE_PLATFORM_MINGW32
7105 memset(&fs, 0, sizeof(fs)); 7178 memset(&fs, 0, sizeof(fs));
7106 fs.fpid = FS_EVALBACKCMD; 7179 fs.fpid = FS_EVALBACKCMD;
7107 fs.n = n; 7180 fs.n = n;
7108 fs.fd[0] = pip[0]; 7181 fs.fd[0] = pip[0];
7109 fs.fd[1] = pip[1]; 7182 fs.fd[1] = pip[1];
7110 spawn_forkshell(&fs, jp, n, FORK_NOJOB); 7183 fs.fd[2] = ctl;
7184 spawn_forkshell(&fs, jp, (ctl == CTLBACKQ) ? n : NULL, FORK_NOJOB);
7111#else 7185#else
7112 if (forkshell(jp, n, FORK_NOJOB) == 0) { 7186 if (forkshell(jp, (ctl == CTLBACKQ) ? n : NULL, FORK_NOJOB) == 0) {
7113 /* child */ 7187 /* child */
7114 FORCE_INT_ON; 7188 FORCE_INT_ON;
7115 close(pip[0]); 7189 close(pip[ip]);
7116 if (pip[1] != 1) { 7190 /* ic is index of child end of pipe *and* fd to connect it to */
7117 /*close(1);*/ 7191 if (pip[ic] != ic) {
7118 dup2_or_raise(pip[1], 1); 7192 /*close(ic);*/
7119 close(pip[1]); 7193 dup2_or_raise(pip[ic], ic);
7194 close(pip[ic]);
7120 } 7195 }
7121/* TODO: eflag clearing makes the following not abort: 7196/* TODO: eflag clearing makes the following not abort:
7122 * ash -c 'set -e; z=$(false;echo foo); echo $z' 7197 * ash -c 'set -e; z=$(false;echo foo); echo $z'
@@ -7133,8 +7208,18 @@ evalbackcmd(union node *n, struct backcmd *result)
7133 } 7208 }
7134#endif 7209#endif
7135 /* parent */ 7210 /* parent */
7136 close(pip[1]); 7211#if BASH_PROCESS_SUBST
7137 result->fd = pip[0]; 7212 if (ctl != CTLBACKQ) {
7213 int fd = fcntl(pip[ip], F_DUPFD, 64);
7214 if (fd > 0) {
7215 close(pip[ip]);
7216 pip[ip] = fd;
7217 }
7218 pushfd(pip[ip]);
7219 }
7220#endif
7221 close(pip[ic]);
7222 result->fd = pip[ip];
7138 result->jp = jp; 7223 result->jp = jp;
7139 7224
7140 out: 7225 out:
@@ -7146,8 +7231,11 @@ evalbackcmd(union node *n, struct backcmd *result)
7146 * Expand stuff in backwards quotes. 7231 * Expand stuff in backwards quotes.
7147 */ 7232 */
7148static void 7233static void
7149expbackq(union node *cmd, int flag) 7234expbackq(union node *cmd, int flag IF_BASH_PROCESS_SUBST(, int ctl))
7150{ 7235{
7236#if !BASH_PROCESS_SUBST
7237 const int ctl = CTLBACKQ;
7238#endif
7151 struct backcmd in; 7239 struct backcmd in;
7152 int i; 7240 int i;
7153 char buf[128]; 7241 char buf[128];
@@ -7162,9 +7250,15 @@ expbackq(union node *cmd, int flag)
7162 INT_OFF; 7250 INT_OFF;
7163 startloc = expdest - (char *)stackblock(); 7251 startloc = expdest - (char *)stackblock();
7164 pushstackmark(&smark, startloc); 7252 pushstackmark(&smark, startloc);
7165 evalbackcmd(cmd, &in); 7253 evalbackcmd(cmd, &in IF_BASH_PROCESS_SUBST(, ctl));
7166 popstackmark(&smark); 7254 popstackmark(&smark);
7167 7255
7256 if (ctl != CTLBACKQ) {
7257 sprintf(buf, DEV_FD_PREFIX"%d", in.fd);
7258 strtodest(buf, BASESYNTAX);
7259 goto done;
7260 }
7261
7168 p = in.buf; 7262 p = in.buf;
7169 i = in.nleft; 7263 i = in.nleft;
7170 if (i == 0) 7264 if (i == 0)
@@ -7186,6 +7280,7 @@ expbackq(union node *cmd, int flag)
7186 close(in.fd); 7280 close(in.fd);
7187 back_exitstatus = waitforjob(in.jp); 7281 back_exitstatus = waitforjob(in.jp);
7188 } 7282 }
7283 done:
7189 INT_ON; 7284 INT_ON;
7190 7285
7191 /* Eat all trailing newlines */ 7286 /* Eat all trailing newlines */
@@ -7274,6 +7369,10 @@ argstr(char *p, int flag)
7274 CTLESC, 7369 CTLESC,
7275 CTLVAR, 7370 CTLVAR,
7276 CTLBACKQ, 7371 CTLBACKQ,
7372#if BASH_PROCESS_SUBST
7373 CTLTOPROC,
7374 CTLFROMPROC,
7375#endif
7277#if ENABLE_FEATURE_SH_MATH 7376#if ENABLE_FEATURE_SH_MATH
7278 CTLARI, 7377 CTLARI,
7279 CTLENDARI, 7378 CTLENDARI,
@@ -7373,8 +7472,12 @@ argstr(char *p, int flag)
7373 p = evalvar(p, flag | inquotes); 7472 p = evalvar(p, flag | inquotes);
7374 TRACE(("argstr: evalvar:'%s'\n", (char *)stackblock())); 7473 TRACE(("argstr: evalvar:'%s'\n", (char *)stackblock()));
7375 goto start; 7474 goto start;
7475#if BASH_PROCESS_SUBST
7476 case CTLTOPROC:
7477 case CTLFROMPROC:
7478#endif
7376 case CTLBACKQ: 7479 case CTLBACKQ:
7377 expbackq(argbackq->n, flag | inquotes); 7480 expbackq(argbackq->n, flag | inquotes IF_BASH_PROCESS_SUBST(, c));
7378 goto start; 7481 goto start;
7379#if ENABLE_FEATURE_SH_MATH 7482#if ENABLE_FEATURE_SH_MATH
7380 case CTLARI: 7483 case CTLARI:
@@ -12995,8 +13098,9 @@ realeofmark(const char *eofmark)
12995#define CHECKEND() {goto checkend; checkend_return:;} 13098#define CHECKEND() {goto checkend; checkend_return:;}
12996#define PARSEREDIR() {goto parseredir; parseredir_return:;} 13099#define PARSEREDIR() {goto parseredir; parseredir_return:;}
12997#define PARSESUB() {goto parsesub; parsesub_return:;} 13100#define PARSESUB() {goto parsesub; parsesub_return:;}
12998#define PARSEBACKQOLD() {oldstyle = 1; goto parsebackq; parsebackq_oldreturn:;} 13101#define PARSEBACKQOLD() {style = OLD; goto parsebackq; parsebackq_oldreturn:;}
12999#define PARSEBACKQNEW() {oldstyle = 0; goto parsebackq; parsebackq_newreturn:;} 13102#define PARSEBACKQNEW() {style = NEW; goto parsebackq; parsebackq_newreturn:;}
13103#define PARSEPROCSUB() {style = PSUB; goto parsebackq; parsebackq_psreturn:;}
13000#define PARSEARITH() {goto parsearith; parsearith_return:;} 13104#define PARSEARITH() {goto parsearith; parsearith_return:;}
13001static int 13105static int
13002readtoken1(int c, int syntax, char *eofmark, int striptabs) 13106readtoken1(int c, int syntax, char *eofmark, int striptabs)
@@ -13007,7 +13111,9 @@ readtoken1(int c, int syntax, char *eofmark, int striptabs)
13007 size_t len; 13111 size_t len;
13008 struct nodelist *bqlist; 13112 struct nodelist *bqlist;
13009 smallint quotef; 13113 smallint quotef;
13010 smallint oldstyle; 13114 smallint style;
13115 enum { OLD, NEW, PSUB };
13116#define oldstyle (style == OLD)
13011 smallint pssyntax; /* we are expanding a prompt string */ 13117 smallint pssyntax; /* we are expanding a prompt string */
13012 IF_BASH_DOLLAR_SQUOTE(smallint bash_dollar_squote = 0;) 13118 IF_BASH_DOLLAR_SQUOTE(smallint bash_dollar_squote = 0;)
13013 /* syntax stack */ 13119 /* syntax stack */
@@ -13189,6 +13295,15 @@ readtoken1(int c, int syntax, char *eofmark, int striptabs)
13189 pungetc(); 13295 pungetc();
13190 } 13296 }
13191#endif 13297#endif
13298#if BASH_PROCESS_SUBST
13299 if (c == '<' || c == '>') {
13300 if (pgetc() == '(') {
13301 PARSEPROCSUB();
13302 break;
13303 }
13304 pungetc();
13305 }
13306#endif
13192 goto endword; /* exit outer loop */ 13307 goto endword; /* exit outer loop */
13193 } 13308 }
13194 IF_ASH_ALIAS(if (c != PEOA)) 13309 IF_ASH_ALIAS(if (c != PEOA))
@@ -13673,9 +13788,18 @@ parsebackq: {
13673 memcpy(out, str, savelen); 13788 memcpy(out, str, savelen);
13674 STADJUST(savelen, out); 13789 STADJUST(savelen, out);
13675 } 13790 }
13676 USTPUTC(CTLBACKQ, out); 13791#if BASH_PROCESS_SUBST
13792 if (style == PSUB)
13793 USTPUTC(c == '<' ? CTLFROMPROC : CTLTOPROC, out);
13794 else
13795#endif
13796 USTPUTC(CTLBACKQ, out);
13677 if (oldstyle) 13797 if (oldstyle)
13678 goto parsebackq_oldreturn; 13798 goto parsebackq_oldreturn;
13799#if BASH_PROCESS_SUBST
13800 else if (style == PSUB)
13801 goto parsebackq_psreturn;
13802#endif
13679 goto parsebackq_newreturn; 13803 goto parsebackq_newreturn;
13680} 13804}
13681 13805
@@ -14130,6 +14254,9 @@ cmdloop(int top)
14130 if (doing_jobctl) 14254 if (doing_jobctl)
14131 showjobs(SHOW_CHANGED|SHOW_STDERR); 14255 showjobs(SHOW_CHANGED|SHOW_STDERR);
14132#endif 14256#endif
14257#if BASH_PROCESS_SUBST
14258 unwindredir(NULL);
14259#endif
14133 inter = 0; 14260 inter = 0;
14134 if (iflag && top) { 14261 if (iflag && top) {
14135 inter++; 14262 inter++;
@@ -14864,6 +14991,10 @@ readcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
14864 } 14991 }
14865 } 14992 }
14866 14993
14994 if (!ENABLE_ASH_BASH_COMPAT && !argptr) {
14995 bb_simple_error_msg("read: need variable name");
14996 return 1;
14997 }
14867 params.argv = argptr; 14998 params.argv = argptr;
14868 params.setvar = setvar0; 14999 params.setvar = setvar0;
14869 params.ifs = bltinlookup("IFS"); /* can be NULL */ 15000 params.ifs = bltinlookup("IFS"); /* can be NULL */
@@ -15578,15 +15709,27 @@ forkshell_openhere(struct forkshell *fs)
15578static void 15709static void
15579forkshell_evalbackcmd(struct forkshell *fs) 15710forkshell_evalbackcmd(struct forkshell *fs)
15580{ 15711{
15712#if BASH_PROCESS_SUBST
15713 /* determine end of pipe used by parent (ip) and child (ic) */
15714 const int ctl = fs->fd[2];
15715 const int ip = (ctl == CTLTOPROC);
15716 const int ic = !(ctl == CTLTOPROC);
15717#else
15718 const int ip = 0;
15719 const int ic = 1;
15720#endif
15581 union node *n = fs->n; 15721 union node *n = fs->n;
15582 int pip[2] = {fs->fd[0], fs->fd[1]}; 15722 int pip[2];
15723
15724 pip[ip] = fs->fd[ip];
15725 pip[ic] = fs->fd[ic];
15583 15726
15584 FORCE_INT_ON; 15727 FORCE_INT_ON;
15585 close(pip[0]); 15728 close(pip[ip]);
15586 if (pip[1] != 1) { 15729 if (pip[ic] != ic) {
15587 /*close(1);*/ 15730 /*close(ic);*/
15588 dup2_or_raise(pip[1], 1); 15731 dup2_or_raise(pip[ic], ic);
15589 close(pip[1]); 15732 close(pip[ic]);
15590 } 15733 }
15591 eflag = 0; 15734 eflag = 0;
15592 ifsfree(); 15735 ifsfree();
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 @@
1From: Herbert Xu <herbert@xxxxxxxxxxxxxxxxxxx>
2Date: Thu, 19 Apr 2018 18:16:12 +0800
3
4> ash originally had support for omitting the fork when expanding a
5> builtin in backquotes. dash has gradually been removing this support,
6> most recently in commit 66b614e29038e31745c4a5d296f64f8d64f5c377
7> ("[EVAL] Remove unused EV_BACKCMD flag").
8>
9> Some traces still remain, however. Remove:
10>
11> - the buf and nleft elements of the backcmd structure;
12> - a misleading comment regarding handling of builtins.
13>
14> Signed-off-by: Ron Yorston <rmy@xxxxxxxxxxxx>
15
16Unfortunately we may need this at some point in the future due
17to changes in POSIX. So let's keep it around for now until we
18get things such as `jobs -p` to work.
19
20*************************************
21
22From: Ron Yorston <rmy@xxxxxxxxxxxx>
23Date: Thu, 19 Apr 2018 17:18:47 +0100
24
25>Unfortunately we may need this at some point in the future due
26>to changes in POSIX. So let's keep it around for now until we
27>get things such as `jobs -p` to work.
28
29As you wish.
30
31Something even more trivial I noticed later: the TRACE at the end of
32expbackq incorrectly refers to the function as evalbackq.
33
34*************************************
35
36Date: Tue, 10 Apr 2018 13:23:35 +0100
37From: Ron Yorston <rmy@pobox.com>
38To: busybox@busybox.net
39Subject: [PATCH] ash: remove unnecessary code in backquote expansion
40
41Some traces remain of ash's ancient support for omitting the fork when
42expanding a builtin command in backquotes.
43
44Remove:
45
46- the buf and nleft elements of the backcmd structure;
47- a misleading comment regarding handling of builtins.
48
49I've submitted a similar patch to dash.
50
51Signed-off-by: Ron Yorston <rmy@pobox.com>
52---
53 shell/ash.c | 37 +++++++++----------------------------
54 1 file changed, 9 insertions(+), 28 deletions(-)
55
56diff --git a/shell/ash.c b/shell/ash.c
57index 45c747dbc..6f1458722 100644
58--- a/shell/ash.c
59+++ b/shell/ash.c
60@@ -6356,15 +6356,12 @@ exptilde(char *startp, char *p, int flags)
61 }
62
63 /*
64- * Execute a command inside back quotes. If it's a builtin command, we
65- * want to save its output in a block obtained from malloc. Otherwise
66- * we fork off a subprocess and get the output of the command via a pipe.
67- * Should be called with interrupts off.
68+ * Execute a command inside back quotes. We fork off a subprocess and
69+ * get the output of the command via a pipe. Should be called with
70+ * interrupts off.
71 */
72 struct backcmd { /* result of evalbackcmd */
73 int fd; /* file descriptor to read from */
74- int nleft; /* number of chars in buffer */
75- char *buf; /* buffer */
76 struct job *jp; /* job structure for command */
77 };
78
79@@ -6394,8 +6391,6 @@ evalbackcmd(union node *n, struct backcmd *result)
80 struct job *jp;
81
82 result->fd = -1;
83- result->buf = NULL;
84- result->nleft = 0;
85 result->jp = NULL;
86 if (n == NULL) {
87 goto out;
88@@ -6432,8 +6427,7 @@ evalbackcmd(union node *n, struct backcmd *result)
89 result->jp = jp;
90
91 out:
92- TRACE(("evalbackcmd done: fd=%d buf=0x%x nleft=%d jp=0x%x\n",
93- result->fd, result->buf, result->nleft, result->jp));
94+ TRACE(("evalbackcmd done: fd=%d jp=0x%x\n", result->fd, result->jp));
95 }
96
97 /*
98@@ -6445,7 +6439,6 @@ expbackq(union node *cmd, int flag)
99 struct backcmd in;
100 int i;
101 char buf[128];
102- char *p;
103 char *dest;
104 int startloc;
105 int syntax = flag & EXP_QUOTED ? DQSYNTAX : BASESYNTAX;
106@@ -6457,24 +6450,12 @@ expbackq(union node *cmd, int flag)
107 evalbackcmd(cmd, &in);
108 popstackmark(&smark);
109
110- p = in.buf;
111- i = in.nleft;
112- if (i == 0)
113- goto read;
114- for (;;) {
115- memtodest(p, i, syntax, flag & QUOTES_ESC);
116- read:
117- if (in.fd < 0)
118- break;
119- i = nonblock_immune_read(in.fd, buf, sizeof(buf));
120- TRACE(("expbackq: read returns %d\n", i));
121- if (i <= 0)
122- break;
123- p = buf;
124- }
125-
126- free(in.buf);
127 if (in.fd >= 0) {
128+ while ((i = nonblock_immune_read(in.fd, buf, sizeof(buf))) > 0) {
129+ TRACE(("expbackq: read returns %d\n", i));
130+ memtodest(buf, i, syntax, flag & QUOTES_ESC);
131+ }
132+
133 close(in.fd);
134 back_exitstatus = waitforjob(in.jp);
135 }
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 @@
1hello 1
2hello 2
3hello 3
4<(echo "hello 0")
5hello 4
6HI THERE
7hello error
8hello error
9hello 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 @@
1# simplest case
2cat <(echo "hello 1")
3
4# can have more than one
5cat <(echo "hello 2") <(echo "hello 3")
6
7# doesn't work in quotes
8echo "<(echo \"hello 0\")"
9
10# process substitution can be nested inside command substitution
11echo $(cat <(echo "hello 4"))
12
13# example from http://wiki.bash-hackers.org/syntax/expansion/proc_subst
14# process substitutions can be passed to a function as parameters or
15# variables
16f() {
17 cat "$1" >"$x"
18}
19x=>(tr '[:lower:]' '[:upper:]') f <(echo 'hi there')
20
21# process substitution can be combined with redirection on exec
22rm -f err
23# save stderr
24exec 4>&2
25# copy stderr to a file
26exec 2> >(tee err)
27echo "hello error" >&2
28sync
29# restore stderr
30exec 2>&4
31cat err
32rm -f err
33echo "hello stderr" >&2
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)
4251 || endofname(command->argv[0])[0] != '\0' 4251 || endofname(command->argv[0])[0] != '\0'
4252 ) { 4252 ) {
4253 /* bash says just "not a valid identifier" */ 4253 /* bash says just "not a valid identifier" */
4254 syntax_error("not a valid identifier in for"); 4254 syntax_error("bad variable name in for");
4255 return 1; 4255 return 1;
4256 } 4256 }
4257 /* Force FOR to have just one word (variable name) */ 4257 /* Force FOR to have just one word (variable name) */
@@ -10799,10 +10799,17 @@ static int FAST_FUNC builtin_read(char **argv)
10799 */ 10799 */
10800 params.read_flags = getopt32(argv, 10800 params.read_flags = getopt32(argv,
10801# if BASH_READ_D 10801# if BASH_READ_D
10802 "!srn:p:t:u:d:", &params.opt_n, &params.opt_p, &params.opt_t, &params.opt_u, &params.opt_d 10802 IF_NOT_HUSH_BASH_COMPAT("^")
10803 "!srn:p:t:u:d:" IF_NOT_HUSH_BASH_COMPAT("\0" "-1"/*min 1 arg*/),
10804 &params.opt_n, &params.opt_p, &params.opt_t, &params.opt_u, &params.opt_d
10803# else 10805# else
10804 "!srn:p:t:u:", &params.opt_n, &params.opt_p, &params.opt_t, &params.opt_u 10806 IF_NOT_HUSH_BASH_COMPAT("^")
10807 "!srn:p:t:u:" IF_NOT_HUSH_BASH_COMPAT("\0" "-1"/*min 1 arg*/),
10808 &params.opt_n, &params.opt_p, &params.opt_t, &params.opt_u
10805# endif 10809# endif
10810//TODO: print "read: need variable name"
10811//for the case of !BASH "read" with no args (now it fails silently)
10812//(or maybe extend getopt32() to emit a message if "-1" fails)
10806 ); 10813 );
10807 if ((uint32_t)params.read_flags == (uint32_t)-1) 10814 if ((uint32_t)params.read_flags == (uint32_t)-1)
10808 return EXIT_FAILURE; 10815 return EXIT_FAILURE;
diff --git a/shell/shell_common.c b/shell/shell_common.c
index 1897fee3b..fff356c04 100644
--- a/shell/shell_common.c
+++ b/shell/shell_common.c
@@ -65,7 +65,7 @@ shell_builtin_read(struct builtin_read_params *params)
65 while (*pp) { 65 while (*pp) {
66 if (endofname(*pp)[0] != '\0') { 66 if (endofname(*pp)[0] != '\0') {
67 /* Mimic bash message */ 67 /* Mimic bash message */
68 bb_error_msg("read: '%s': not a valid identifier", *pp); 68 bb_error_msg("read: '%s': bad variable name", *pp);
69 return (const char *)(uintptr_t)1; 69 return (const char *)(uintptr_t)1;
70 } 70 }
71 pp++; 71 pp++;
@@ -296,7 +296,7 @@ shell_builtin_read(struct builtin_read_params *params)
296 * without variable names (bash compat). 296 * without variable names (bash compat).
297 * Thus, "read" and "read REPLY" are not the same. 297 * Thus, "read" and "read REPLY" are not the same.
298 */ 298 */
299 if (!params->opt_d && argv[0]) { 299 if (argv[0]) {
300/* http://www.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_06_05 */ 300/* http://www.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_06_05 */
301 const char *is_ifs = strchr(ifs, c); 301 const char *is_ifs = strchr(ifs, c);
302 if (startword && is_ifs) { 302 if (startword && is_ifs) {
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)
226 226
227 signal(SIGHUP, SIG_IGN); 227 signal(SIGHUP, SIG_IGN);
228 /* We want klogd_read to not be restarted, thus _norestart: */ 228 /* We want klogd_read to not be restarted, thus _norestart: */
229 bb_signals_recursive_norestart(BB_FATAL_SIGS, record_signo); 229 bb_signals_norestart(BB_FATAL_SIGS, record_signo);
230 230
231 syslog(LOG_NOTICE, "klogd started: %s", bb_banner); 231 syslog(LOG_NOTICE, "klogd started: %s", bb_banner);
232 232