diff options
author | Denys Vlasenko <vda.linux@googlemail.com> | 2011-05-08 21:23:43 +0200 |
---|---|---|
committer | Denys Vlasenko <vda.linux@googlemail.com> | 2011-05-08 21:23:43 +0200 |
commit | 80542bad2f1df9d99b579c9eeb3c2675c14c72c0 (patch) | |
tree | 906cdea5609e0272fda16dc02caa3c683912c228 /shell/hush.c | |
parent | 80c5b6893d4708b3683ad9a51c990a326a8f1dff (diff) | |
download | busybox-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>
Diffstat (limited to 'shell/hush.c')
-rw-r--r-- | shell/hush.c | 98 |
1 files changed, 96 insertions, 2 deletions
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 */ | ||
8527 | static void record_signal(int sig) | ||
8528 | { | ||
8529 | sigaddset(&G.detected_set, sig); | ||
8530 | } | ||
8487 | static int FAST_FUNC builtin_read(char **argv) | 8531 | static 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; |