aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDenys Vlasenko <vda.linux@googlemail.com>2011-05-08 21:23:43 +0200
committerDenys Vlasenko <vda.linux@googlemail.com>2011-05-08 21:23:43 +0200
commit80542bad2f1df9d99b579c9eeb3c2675c14c72c0 (patch)
tree906cdea5609e0272fda16dc02caa3c683912c228
parent80c5b6893d4708b3683ad9a51c990a326a8f1dff (diff)
downloadbusybox-w32-80542bad2f1df9d99b579c9eeb3c2675c14c72c0.tar.gz
busybox-w32-80542bad2f1df9d99b579c9eeb3c2675c14c72c0.tar.bz2
busybox-w32-80542bad2f1df9d99b579c9eeb3c2675c14c72c0.zip
hush: make read builtin interruptible.
function old new delta builtin_read 185 471 +286 check_and_run_traps 200 262 +62 nonblock_immune_read 73 119 +46 sigismember - 44 +44 record_signal - 21 +21 sigisemptyset - 16 +16 ... ------------------------------------------------------------------------------ (add/remove: 5/0 grow/shrink: 7/5 up/down: 483/-46) Total: 437 bytes Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
-rw-r--r--include/libbb.h2
-rw-r--r--libbb/read_printf.c9
-rw-r--r--shell/ash.c6
-rw-r--r--shell/hush.c98
-rw-r--r--shell/shell_common.c17
5 files changed, 120 insertions, 12 deletions
diff --git a/include/libbb.h b/include/libbb.h
index 4ea94e77f..56dfa61b7 100644
--- a/include/libbb.h
+++ b/include/libbb.h
@@ -672,7 +672,7 @@ void* xrealloc_vector_helper(void *vector, unsigned sizeof_and_shift, int idx) F
672 672
673 673
674extern ssize_t safe_read(int fd, void *buf, size_t count) FAST_FUNC; 674extern ssize_t safe_read(int fd, void *buf, size_t count) FAST_FUNC;
675extern ssize_t nonblock_immune_read(int fd, void *buf, size_t count) FAST_FUNC; 675extern ssize_t nonblock_immune_read(int fd, void *buf, size_t count, int loop_on_EINTR) FAST_FUNC;
676// NB: will return short read on error, not -1, 676// NB: will return short read on error, not -1,
677// if some data was read before error occurred 677// if some data was read before error occurred
678extern ssize_t full_read(int fd, void *buf, size_t count) FAST_FUNC; 678extern ssize_t full_read(int fd, void *buf, size_t count) FAST_FUNC;
diff --git a/libbb/read_printf.c b/libbb/read_printf.c
index 0e6fbf662..192f83d6e 100644
--- a/libbb/read_printf.c
+++ b/libbb/read_printf.c
@@ -55,19 +55,20 @@
55 * which detects EAGAIN and uses poll() to wait on the fd. 55 * which detects EAGAIN and uses poll() to wait on the fd.
56 * Thankfully, poll() doesn't care about O_NONBLOCK flag. 56 * Thankfully, poll() doesn't care about O_NONBLOCK flag.
57 */ 57 */
58ssize_t FAST_FUNC nonblock_immune_read(int fd, void *buf, size_t count) 58ssize_t FAST_FUNC nonblock_immune_read(int fd, void *buf, size_t count, int loop_on_EINTR)
59{ 59{
60 struct pollfd pfd[1]; 60 struct pollfd pfd[1];
61 ssize_t n; 61 ssize_t n;
62 62
63 while (1) { 63 while (1) {
64 n = safe_read(fd, buf, count); 64 n = loop_on_EINTR ? safe_read(fd, buf, count) : read(fd, buf, count);
65 if (n >= 0 || errno != EAGAIN) 65 if (n >= 0 || errno != EAGAIN)
66 return n; 66 return n;
67 /* fd is in O_NONBLOCK mode. Wait using poll and repeat */ 67 /* fd is in O_NONBLOCK mode. Wait using poll and repeat */
68 pfd[0].fd = fd; 68 pfd[0].fd = fd;
69 pfd[0].events = POLLIN; 69 pfd[0].events = POLLIN;
70 safe_poll(pfd, 1, -1); /* note: this pulls in printf */ 70 /* note: safe_poll pulls in printf */
71 loop_on_EINTR ? safe_poll(pfd, 1, -1) : poll(pfd, 1, -1);
71 } 72 }
72} 73}
73 74
@@ -90,7 +91,7 @@ char* FAST_FUNC xmalloc_reads(int fd, size_t *maxsz_p)
90 p = buf + sz; 91 p = buf + sz;
91 sz += 128; 92 sz += 128;
92 } 93 }
93 if (nonblock_immune_read(fd, p, 1) != 1) { 94 if (nonblock_immune_read(fd, p, 1, /*loop_on_EINTR:*/ 1) != 1) {
94 /* EOF/error */ 95 /* EOF/error */
95 if (p == buf) { /* we read nothing */ 96 if (p == buf) { /* we read nothing */
96 free(buf); 97 free(buf);
diff --git a/shell/ash.c b/shell/ash.c
index b1b11bd1b..d48cd016f 100644
--- a/shell/ash.c
+++ b/shell/ash.c
@@ -5918,7 +5918,7 @@ expbackq(union node *cmd, int quoted, int quotes)
5918 read: 5918 read:
5919 if (in.fd < 0) 5919 if (in.fd < 0)
5920 break; 5920 break;
5921 i = nonblock_immune_read(in.fd, buf, sizeof(buf)); 5921 i = nonblock_immune_read(in.fd, buf, sizeof(buf), /*loop_on_EINTR:*/ 1);
5922 TRACE(("expbackq: read returns %d\n", i)); 5922 TRACE(("expbackq: read returns %d\n", i));
5923 if (i <= 0) 5923 if (i <= 0)
5924 break; 5924 break;
@@ -9617,7 +9617,7 @@ preadfd(void)
9617#if ENABLE_FEATURE_EDITING 9617#if ENABLE_FEATURE_EDITING
9618 retry: 9618 retry:
9619 if (!iflag || g_parsefile->pf_fd != STDIN_FILENO) 9619 if (!iflag || g_parsefile->pf_fd != STDIN_FILENO)
9620 nr = nonblock_immune_read(g_parsefile->pf_fd, buf, IBUFSIZ - 1); 9620 nr = nonblock_immune_read(g_parsefile->pf_fd, buf, IBUFSIZ - 1, /*loop_on_EINTR:*/ 1);
9621 else { 9621 else {
9622 int timeout = -1; 9622 int timeout = -1;
9623# if ENABLE_ASH_IDLE_TIMEOUT 9623# if ENABLE_ASH_IDLE_TIMEOUT
@@ -9663,7 +9663,7 @@ preadfd(void)
9663 } 9663 }
9664 } 9664 }
9665#else 9665#else
9666 nr = nonblock_immune_read(g_parsefile->pf_fd, buf, IBUFSIZ - 1); 9666 nr = nonblock_immune_read(g_parsefile->pf_fd, buf, IBUFSIZ - 1, /*loop_on_EINTR:*/ 1);
9667#endif 9667#endif
9668 9668
9669#if 0 /* disabled: nonblock_immune_read() handles this problem */ 9669#if 0 /* disabled: nonblock_immune_read() handles this problem */
diff --git a/shell/hush.c b/shell/hush.c
index bcd458427..0b17b222d 100644
--- a/shell/hush.c
+++ b/shell/hush.c
@@ -795,8 +795,15 @@ struct globals {
795 /* which signals have non-DFL handler (even with no traps set)? */ 795 /* which signals have non-DFL handler (even with no traps set)? */
796 unsigned non_DFL_mask; 796 unsigned non_DFL_mask;
797 char **traps; /* char *traps[NSIG] */ 797 char **traps; /* char *traps[NSIG] */
798 sigset_t blocked_set; 798 /* Signal mask on the entry to the (top-level) shell. Never modified. */
799 sigset_t inherited_set; 799 sigset_t inherited_set;
800 /* Starts equal to inherited_set,
801 * but shell-special signals are added and SIGCHLD is removed.
802 * When a trap is set/cleared, signal is added to/removed from it:
803 */
804 sigset_t blocked_set;
805 /* Used by read() */
806 sigset_t detected_set;
800#if HUSH_DEBUG 807#if HUSH_DEBUG
801 unsigned long memleak_value; 808 unsigned long memleak_value;
802 int debug_indent; 809 int debug_indent;
@@ -1476,6 +1483,17 @@ static int check_and_run_traps(int sig)
1476 goto got_sig; 1483 goto got_sig;
1477 1484
1478 while (1) { 1485 while (1) {
1486 if (!sigisemptyset(&G.detected_set)) {
1487 sig = 0;
1488 do {
1489 sig++;
1490 if (sigismember(&G.detected_set, sig)) {
1491 sigdelset(&G.detected_set, sig);
1492 goto got_sig;
1493 }
1494 } while (sig < NSIG);
1495 }
1496
1479 sig = sigtimedwait(&G.blocked_set, NULL, &zero_timespec); 1497 sig = sigtimedwait(&G.blocked_set, NULL, &zero_timespec);
1480 if (sig <= 0) 1498 if (sig <= 0)
1481 break; 1499 break;
@@ -8484,6 +8502,32 @@ static int FAST_FUNC builtin_pwd(char **argv UNUSED_PARAM)
8484 return EXIT_SUCCESS; 8502 return EXIT_SUCCESS;
8485} 8503}
8486 8504
8505/* Interruptibility of read builtin in bash
8506 * (tested on bash-4.2.8 by sending signals (not by ^C)):
8507 *
8508 * Empty trap makes read ignore corresponding signal, for any signal.
8509 *
8510 * SIGINT:
8511 * - terminates non-interactive shell;
8512 * - interrupts read in interactive shell;
8513 * if it has non-empty trap:
8514 * - executes trap and returns to command prompt in interactive shell;
8515 * - executes trap and returns to read in non-interactive shell;
8516 * SIGTERM:
8517 * - is ignored (does not interrupt) read in interactive shell;
8518 * - terminates non-interactive shell;
8519 * if it has non-empty trap:
8520 * - executes trap and returns to read;
8521 * SIGHUP:
8522 * - terminates shell (regardless of interactivity);
8523 * if it has non-empty trap:
8524 * - executes trap and returns to read;
8525 */
8526/* helper */
8527static void record_signal(int sig)
8528{
8529 sigaddset(&G.detected_set, sig);
8530}
8487static int FAST_FUNC builtin_read(char **argv) 8531static int FAST_FUNC builtin_read(char **argv)
8488{ 8532{
8489 const char *r; 8533 const char *r;
@@ -8491,7 +8535,9 @@ static int FAST_FUNC builtin_read(char **argv)
8491 char *opt_p = NULL; 8535 char *opt_p = NULL;
8492 char *opt_t = NULL; 8536 char *opt_t = NULL;
8493 char *opt_u = NULL; 8537 char *opt_u = NULL;
8538 const char *ifs;
8494 int read_flags; 8539 int read_flags;
8540 sigset_t saved_blkd_set;
8495 8541
8496 /* "!": do not abort on errors. 8542 /* "!": do not abort on errors.
8497 * Option string must start with "sr" to match BUILTIN_READ_xxx 8543 * Option string must start with "sr" to match BUILTIN_READ_xxx
@@ -8500,10 +8546,47 @@ static int FAST_FUNC builtin_read(char **argv)
8500 if (read_flags == (uint32_t)-1) 8546 if (read_flags == (uint32_t)-1)
8501 return EXIT_FAILURE; 8547 return EXIT_FAILURE;
8502 argv += optind; 8548 argv += optind;
8549 ifs = get_local_var_value("IFS"); /* can be NULL */
8550
8551 again:
8552 /* We need to temporarily unblock and record signals around read */
8553
8554 saved_blkd_set = G.blocked_set;
8555 {
8556 unsigned sig;
8557 struct sigaction sa, old_sa;
8558
8559 memset(&sa, 0, sizeof(sa));
8560 sigfillset(&sa.sa_mask);
8561 /*sa.sa_flags = 0;*/
8562 sa.sa_handler = record_signal;
8563
8564 sig = 0;
8565 do {
8566 sig++;
8567 if (sigismember(&G.blocked_set, sig)) {
8568 char *sig_trap = (G.traps && G.traps[sig]) ? G.traps[sig] : NULL;
8569 /* If has a nonempty trap... */
8570 if ((sig_trap && sig_trap[0])
8571 /* ...or has no trap and is SIGINT or SIGHUP */
8572 || (!sig_trap && (sig == SIGINT || sig == SIGHUP))
8573 ) {
8574 sigaction(sig, &sa, &old_sa);
8575 if (old_sa.sa_handler == SIG_IGN) /* oops... restore back to IGN! */
8576 sigaction_set(sig, &old_sa);
8577 else
8578 sigdelset(&G.blocked_set, sig);
8579 }
8580 }
8581 } while (sig < NSIG-1);
8582 }
8583
8584 if (memcmp(&saved_blkd_set, &G.blocked_set, sizeof(saved_blkd_set)) != 0)
8585 sigprocmask_set(&G.blocked_set);
8503 8586
8504 r = shell_builtin_read(set_local_var_from_halves, 8587 r = shell_builtin_read(set_local_var_from_halves,
8505 argv, 8588 argv,
8506 get_local_var_value("IFS"), /* can be NULL */ 8589 ifs,
8507 read_flags, 8590 read_flags,
8508 opt_n, 8591 opt_n,
8509 opt_p, 8592 opt_p,
@@ -8511,6 +8594,17 @@ static int FAST_FUNC builtin_read(char **argv)
8511 opt_u 8594 opt_u
8512 ); 8595 );
8513 8596
8597 if (memcmp(&saved_blkd_set, &G.blocked_set, sizeof(saved_blkd_set)) != 0) {
8598 G.blocked_set = saved_blkd_set;
8599 sigprocmask_set(&G.blocked_set);
8600 }
8601
8602 if ((uintptr_t)r == 1 && errno == EINTR) {
8603 unsigned sig = check_and_run_traps(0);
8604 if (sig && sig != SIGINT)
8605 goto again;
8606 }
8607
8514 if ((uintptr_t)r > 1) { 8608 if ((uintptr_t)r > 1) {
8515 bb_error_msg("%s", r); 8609 bb_error_msg("%s", r);
8516 r = (char*)(uintptr_t)1; 8610 r = (char*)(uintptr_t)1;
diff --git a/shell/shell_common.c b/shell/shell_common.c
index 86a6493ed..a5c455c8e 100644
--- a/shell/shell_common.c
+++ b/shell/shell_common.c
@@ -36,6 +36,10 @@ int FAST_FUNC is_well_formed_var_name(const char *s, char terminator)
36 36
37/* read builtin */ 37/* read builtin */
38 38
39/* Needs to be interruptible: shell mush handle traps and shell-special signals
40 * while inside read. To implement this, be sure to not loop on EINTR
41 * and return errno == EINTR reliably.
42 */
39//TODO: use more efficient setvar() which takes a pointer to malloced "VAR=VAL" 43//TODO: use more efficient setvar() which takes a pointer to malloced "VAR=VAL"
40//string. hush naturally has it, and ash has setvareq(). 44//string. hush naturally has it, and ash has setvareq().
41//Here we can simply store "VAR=" at buffer start and store read data directly 45//Here we can simply store "VAR=" at buffer start and store read data directly
@@ -51,6 +55,7 @@ shell_builtin_read(void FAST_FUNC (*setvar)(const char *name, const char *val),
51 const char *opt_u 55 const char *opt_u
52) 56)
53{ 57{
58 unsigned err;
54 unsigned end_ms; /* -t TIMEOUT */ 59 unsigned end_ms; /* -t TIMEOUT */
55 int fd; /* -u FD */ 60 int fd; /* -u FD */
56 int nchars; /* -n NUM */ 61 int nchars; /* -n NUM */
@@ -62,6 +67,8 @@ shell_builtin_read(void FAST_FUNC (*setvar)(const char *name, const char *val),
62 int startword; 67 int startword;
63 smallint backslash; 68 smallint backslash;
64 69
70 errno = err = 0;
71
65 pp = argv; 72 pp = argv;
66 while (*pp) { 73 while (*pp) {
67 if (!is_well_formed_var_name(*pp, '\0')) { 74 if (!is_well_formed_var_name(*pp, '\0')) {
@@ -153,6 +160,8 @@ shell_builtin_read(void FAST_FUNC (*setvar)(const char *name, const char *val),
153 do { 160 do {
154 char c; 161 char c;
155 162
163 errno = 0;
164
156 if (end_ms) { 165 if (end_ms) {
157 int timeout; 166 int timeout;
158 struct pollfd pfd[1]; 167 struct pollfd pfd[1];
@@ -161,8 +170,9 @@ shell_builtin_read(void FAST_FUNC (*setvar)(const char *name, const char *val),
161 pfd[0].events = POLLIN; 170 pfd[0].events = POLLIN;
162 timeout = end_ms - (unsigned)monotonic_ms(); 171 timeout = end_ms - (unsigned)monotonic_ms();
163 if (timeout <= 0 /* already late? */ 172 if (timeout <= 0 /* already late? */
164 || safe_poll(pfd, 1, timeout) != 1 /* no? wait... */ 173 || poll(pfd, 1, timeout) != 1 /* no? wait... */
165 ) { /* timed out! */ 174 ) { /* timed out! */
175 err = errno;
166 retval = (const char *)(uintptr_t)1; 176 retval = (const char *)(uintptr_t)1;
167 goto ret; 177 goto ret;
168 } 178 }
@@ -170,7 +180,8 @@ shell_builtin_read(void FAST_FUNC (*setvar)(const char *name, const char *val),
170 180
171 if ((bufpos & 0xff) == 0) 181 if ((bufpos & 0xff) == 0)
172 buffer = xrealloc(buffer, bufpos + 0x100); 182 buffer = xrealloc(buffer, bufpos + 0x100);
173 if (nonblock_immune_read(fd, &buffer[bufpos], 1) != 1) { 183 if (nonblock_immune_read(fd, &buffer[bufpos], 1, /*loop_on_EINTR:*/ 0) != 1) {
184 err = errno;
174 retval = (const char *)(uintptr_t)1; 185 retval = (const char *)(uintptr_t)1;
175 break; 186 break;
176 } 187 }
@@ -240,6 +251,8 @@ shell_builtin_read(void FAST_FUNC (*setvar)(const char *name, const char *val),
240 free(buffer); 251 free(buffer);
241 if (read_flags & BUILTIN_READ_SILENT) 252 if (read_flags & BUILTIN_READ_SILENT)
242 tcsetattr(fd, TCSANOW, &old_tty); 253 tcsetattr(fd, TCSANOW, &old_tty);
254
255 errno = err;
243 return retval; 256 return retval;
244} 257}
245 258