diff options
| author | Denys Vlasenko <vda.linux@googlemail.com> | 2011-05-11 23:56:11 +0200 |
|---|---|---|
| committer | Denys Vlasenko <vda.linux@googlemail.com> | 2011-05-11 23:56:11 +0200 |
| commit | 9d6cbafe728c9100a35e29ba7a3729c5303e73ea (patch) | |
| tree | 585f11bce3f1afad08ad00453fe355a460f5fff1 /shell | |
| parent | 10c0131a8a1b3db7fd6b23b72ebd7b33afc7b018 (diff) | |
| download | busybox-w32-9d6cbafe728c9100a35e29ba7a3729c5303e73ea.tar.gz busybox-w32-9d6cbafe728c9100a35e29ba7a3729c5303e73ea.tar.bz2 busybox-w32-9d6cbafe728c9100a35e29ba7a3729c5303e73ea.zip | |
hush: replace signal handling machinery
With new version of signal handling, read builtin should be less buggy
wrt signals.
function old new delta
install_sighandlers - 121 +121
switch_off_special_sigs - 84 +84
pick_sighandler - 58 +58
install_special_sighandlers - 47 +47
builtin_wait 284 319 +35
record_pending_signo - 21 +21
execvp_or_die 43 48 +5
file_get 290 288 -2
run_list 1004 998 -6
static.zero_timespec 8 - -8
sigprocmask_set 14 - -14
sigwaitinfo 23 - -23
record_signal 23 - -23
__GI_sigwaitinfo 23 - -23
sigtimedwait 25 - -25
builtin_trap 417 392 -25
__GI_sigtimedwait 25 - -25
hush_main 1003 965 -38
check_and_run_traps 263 217 -46
__rt_sigtimedwait 52 - -52
reset_traps_to_defaults 213 126 -87
init_sigmasks 198 - -198
builtin_read 536 197 -339
------------------------------------------------------------------------------
(add/remove: 5/10 grow/shrink: 2/7 up/down: 371/-934) Total: -563 bytes
text data bss dec hex filename
903075 936 17736 921747 e1093 busybox_old
902547 936 17736 921219 e0e83 busybox_unstripped
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
Diffstat (limited to 'shell')
| -rw-r--r-- | shell/hush.c | 510 |
1 files changed, 278 insertions, 232 deletions
diff --git a/shell/hush.c b/shell/hush.c index 509bd415b..b2c3a752e 100644 --- a/shell/hush.c +++ b/shell/hush.c | |||
| @@ -106,6 +106,10 @@ | |||
| 106 | # define PIPE_BUF 4096 /* amount of buffering in a pipe */ | 106 | # define PIPE_BUF 4096 /* amount of buffering in a pipe */ |
| 107 | #endif | 107 | #endif |
| 108 | 108 | ||
| 109 | /* Not every libc has sighandler_t. Fix it */ | ||
| 110 | typedef void (*hush_sighandler_t)(int); | ||
| 111 | #define sighandler_t hush_sighandler_t | ||
| 112 | |||
| 109 | //config:config HUSH | 113 | //config:config HUSH |
| 110 | //config: bool "hush" | 114 | //config: bool "hush" |
| 111 | //config: default y | 115 | //config: default y |
| @@ -764,7 +768,6 @@ struct globals { | |||
| 764 | smalluint last_exitcode; | 768 | smalluint last_exitcode; |
| 765 | /* are global_argv and global_argv[1..n] malloced? (note: not [0]) */ | 769 | /* are global_argv and global_argv[1..n] malloced? (note: not [0]) */ |
| 766 | smalluint global_args_malloced; | 770 | smalluint global_args_malloced; |
| 767 | smalluint inherited_set_is_saved; | ||
| 768 | /* how many non-NULL argv's we have. NB: $# + 1 */ | 771 | /* how many non-NULL argv's we have. NB: $# + 1 */ |
| 769 | int global_argc; | 772 | int global_argc; |
| 770 | char **global_argv; | 773 | char **global_argv; |
| @@ -794,21 +797,20 @@ struct globals { | |||
| 794 | #endif | 797 | #endif |
| 795 | /* Which signals have non-DFL handler (even with no traps set)? | 798 | /* Which signals have non-DFL handler (even with no traps set)? |
| 796 | * Set at the start to: | 799 | * Set at the start to: |
| 797 | * (SIGQUIT + maybe SPECIAL_INTERACTIVE_SIGS + maybe SPECIAL_JOB_SIGS) | 800 | * (SIGQUIT + maybe SPECIAL_INTERACTIVE_SIGS + maybe SPECIAL_JOBSTOP_SIGS) |
| 798 | * SPECIAL_INTERACTIVE_SIGS are cleared after fork. | 801 | * SPECIAL_INTERACTIVE_SIGS are cleared after fork. |
| 802 | * The rest is cleared right before execv syscalls. | ||
| 799 | * Other than these two times, never modified. | 803 | * Other than these two times, never modified. |
| 800 | */ | 804 | */ |
| 801 | unsigned special_sig_mask; | 805 | unsigned special_sig_mask; |
| 806 | #if ENABLE_HUSH_JOB | ||
| 807 | unsigned fatal_sig_mask; | ||
| 808 | #define G_fatal_sig_mask G.fatal_sig_mask | ||
| 809 | #else | ||
| 810 | #define G_fatal_sig_mask 0 | ||
| 811 | #endif | ||
| 802 | char **traps; /* char *traps[NSIG] */ | 812 | char **traps; /* char *traps[NSIG] */ |
| 803 | /* Signal mask on the entry to the (top-level) shell. Never modified. */ | 813 | sigset_t pending_set; |
| 804 | sigset_t inherited_set; | ||
| 805 | /* Starts equal to inherited_set, | ||
| 806 | * but shell-special signals are added and SIGCHLD is removed. | ||
| 807 | * When a trap is set/cleared, signal is added to/removed from it: | ||
| 808 | */ | ||
| 809 | sigset_t blocked_set; | ||
| 810 | /* Used by read() */ | ||
| 811 | sigset_t detected_set; | ||
| 812 | #if HUSH_DEBUG | 814 | #if HUSH_DEBUG |
| 813 | unsigned long memleak_value; | 815 | unsigned long memleak_value; |
| 814 | int debug_indent; | 816 | int debug_indent; |
| @@ -1337,8 +1339,8 @@ static void restore_G_args(save_arg_t *sv, char **argv) | |||
| 1337 | * (What happens to signals which are IGN on shell start?) | 1339 | * (What happens to signals which are IGN on shell start?) |
| 1338 | * (What happens with signal mask on shell start?) | 1340 | * (What happens with signal mask on shell start?) |
| 1339 | * | 1341 | * |
| 1340 | * Implementation in hush | 1342 | * Old implementation |
| 1341 | * ====================== | 1343 | * ================== |
| 1342 | * We use in-kernel pending signal mask to determine which signals were sent. | 1344 | * We use in-kernel pending signal mask to determine which signals were sent. |
| 1343 | * We block all signals which we don't want to take action immediately, | 1345 | * We block all signals which we don't want to take action immediately, |
| 1344 | * i.e. we block all signals which need to have special handling as described | 1346 | * i.e. we block all signals which need to have special handling as described |
| @@ -1369,6 +1371,49 @@ static void restore_G_args(save_arg_t *sv, char **argv) | |||
| 1369 | * Standard says "When a subshell is entered, traps that are not being ignored | 1371 | * Standard says "When a subshell is entered, traps that are not being ignored |
| 1370 | * are set to the default actions". bash interprets it so that traps which | 1372 | * are set to the default actions". bash interprets it so that traps which |
| 1371 | * are set to '' (ignore) are NOT reset to defaults. We do the same. | 1373 | * are set to '' (ignore) are NOT reset to defaults. We do the same. |
| 1374 | * | ||
| 1375 | * Problem: the above approach makes it unwieldy to catch signals while | ||
| 1376 | * we are in read builtin, of while we read commands from stdin: | ||
| 1377 | * masked signals are not visible! | ||
| 1378 | * | ||
| 1379 | * New implementation | ||
| 1380 | * ================== | ||
| 1381 | * We record each signal we are interested in by installing signal handler | ||
| 1382 | * for them - a bit like emulating kernel pending signal mask in userspace. | ||
| 1383 | * We are interested in: signals which need to have special handling | ||
| 1384 | * as described above, and all signals which have traps set. | ||
| 1385 | * Signals are rocorded in pending_set. | ||
| 1386 | * After each pipe execution, we extract any pending signals | ||
| 1387 | * and act on them. | ||
| 1388 | * | ||
| 1389 | * unsigned special_sig_mask: a mask of shell-special signals. | ||
| 1390 | * unsigned fatal_sig_mask: a mask of signals on which we restore tty pgrp. | ||
| 1391 | * char *traps[sig] if trap for sig is set (even if it's ''). | ||
| 1392 | * sigset_t pending_set: set of sigs we received. | ||
| 1393 | * | ||
| 1394 | * "trap - SIGxxx": | ||
| 1395 | * if sig is in special_sig_mask, set handler back to: | ||
| 1396 | * record_pending_signo, or to IGN if it's a tty stop signal | ||
| 1397 | * if sig is in fatal_sig_mask, set handler back to sigexit. | ||
| 1398 | * else: set handler back to SIG_DFL | ||
| 1399 | * "trap 'cmd' SIGxxx": | ||
| 1400 | * set handler to record_pending_signo. | ||
| 1401 | * "trap '' SIGxxx": | ||
| 1402 | * set handler to SIG_IGN. | ||
| 1403 | * after [v]fork, if we plan to be a shell: | ||
| 1404 | * set signals with special interactive handling to SIG_DFL | ||
| 1405 | * (because child shell is not interactive), | ||
| 1406 | * unset all traps except '' (note: regardless of child shell's type - {}, (), etc) | ||
| 1407 | * after [v]fork, if we plan to exec: | ||
| 1408 | * POSIX says fork clears pending signal mask in child - no need to clear it. | ||
| 1409 | * | ||
| 1410 | * To make wait builtin interruptible, we handle SIGCHLD as special signal, | ||
| 1411 | * otherwise (if we leave it SIG_DFL) sigsuspend in wait builtin will not wake up on it. | ||
| 1412 | * | ||
| 1413 | * Note (compat): | ||
| 1414 | * Standard says "When a subshell is entered, traps that are not being ignored | ||
| 1415 | * are set to the default actions". bash interprets it so that traps which | ||
| 1416 | * are set to '' (ignore) are NOT reset to defaults. We do the same. | ||
| 1372 | */ | 1417 | */ |
| 1373 | enum { | 1418 | enum { |
| 1374 | SPECIAL_INTERACTIVE_SIGS = 0 | 1419 | SPECIAL_INTERACTIVE_SIGS = 0 |
| @@ -1376,26 +1421,25 @@ enum { | |||
| 1376 | | (1 << SIGINT) | 1421 | | (1 << SIGINT) |
| 1377 | | (1 << SIGHUP) | 1422 | | (1 << SIGHUP) |
| 1378 | , | 1423 | , |
| 1379 | SPECIAL_JOB_SIGS = 0 | 1424 | SPECIAL_JOBSTOP_SIGS = 0 |
| 1380 | #if ENABLE_HUSH_JOB | 1425 | #if ENABLE_HUSH_JOB |
| 1381 | | (1 << SIGTTIN) | 1426 | | (1 << SIGTTIN) |
| 1382 | | (1 << SIGTTOU) | 1427 | | (1 << SIGTTOU) |
| 1383 | | (1 << SIGTSTP) | 1428 | | (1 << SIGTSTP) |
| 1384 | #endif | 1429 | #endif |
| 1430 | , | ||
| 1385 | }; | 1431 | }; |
| 1386 | 1432 | ||
| 1387 | static void sigprocmask_set(sigset_t *set) | 1433 | static void record_pending_signo(int sig) |
| 1388 | { | 1434 | { |
| 1389 | sigprocmask(SIG_SETMASK, set, NULL); | 1435 | sigaddset(&G.pending_set, sig); |
| 1390 | } | ||
| 1391 | |||
| 1392 | #if ENABLE_HUSH_FAST | 1436 | #if ENABLE_HUSH_FAST |
| 1393 | static void SIGCHLD_handler(int sig UNUSED_PARAM) | 1437 | if (sig == SIGCHLD) { |
| 1394 | { | 1438 | G.count_SIGCHLD++; |
| 1395 | G.count_SIGCHLD++; | ||
| 1396 | //bb_error_msg("[%d] SIGCHLD_handler: G.count_SIGCHLD:%d G.handled_SIGCHLD:%d", getpid(), G.count_SIGCHLD, G.handled_SIGCHLD); | 1439 | //bb_error_msg("[%d] SIGCHLD_handler: G.count_SIGCHLD:%d G.handled_SIGCHLD:%d", getpid(), G.count_SIGCHLD, G.handled_SIGCHLD); |
| 1397 | } | 1440 | } |
| 1398 | #endif | 1441 | #endif |
| 1442 | } | ||
| 1399 | 1443 | ||
| 1400 | #if ENABLE_HUSH_JOB | 1444 | #if ENABLE_HUSH_JOB |
| 1401 | 1445 | ||
| @@ -1433,6 +1477,31 @@ static void sigexit(int sig) | |||
| 1433 | 1477 | ||
| 1434 | #endif | 1478 | #endif |
| 1435 | 1479 | ||
| 1480 | static sighandler_t pick_sighandler(unsigned sig) | ||
| 1481 | { | ||
| 1482 | sighandler_t handler = SIG_DFL; | ||
| 1483 | if (sig < sizeof(unsigned)*8) { | ||
| 1484 | unsigned sigmask = (1 << sig); | ||
| 1485 | |||
| 1486 | #if ENABLE_HUSH_JOB | ||
| 1487 | /* sig is fatal? */ | ||
| 1488 | if (G_fatal_sig_mask & sigmask) | ||
| 1489 | handler = sigexit; | ||
| 1490 | #endif | ||
| 1491 | /* sig has special handling? */ | ||
| 1492 | else if (G.special_sig_mask & sigmask) | ||
| 1493 | handler = record_pending_signo; | ||
| 1494 | /* TTIN/TTOU/TSTS can't be set to record_pending_signo | ||
| 1495 | * in order to ignore them: they will be raised | ||
| 1496 | * in an endless loop then when we try to do some | ||
| 1497 | * terminal ioctls! We do nave to _ignore_ these. | ||
| 1498 | */ | ||
| 1499 | if (SPECIAL_JOBSTOP_SIGS & sigmask) | ||
| 1500 | handler = SIG_IGN; | ||
| 1501 | } | ||
| 1502 | return handler; | ||
| 1503 | } | ||
| 1504 | |||
| 1436 | /* Restores tty foreground process group, and exits. */ | 1505 | /* Restores tty foreground process group, and exits. */ |
| 1437 | static void hush_exit(int exitcode) NORETURN; | 1506 | static void hush_exit(int exitcode) NORETURN; |
| 1438 | static void hush_exit(int exitcode) | 1507 | static void hush_exit(int exitcode) |
| @@ -1478,39 +1547,30 @@ static void hush_exit(int exitcode) | |||
| 1478 | } | 1547 | } |
| 1479 | 1548 | ||
| 1480 | 1549 | ||
| 1481 | static int check_and_run_traps(int sig) | 1550 | //TODO: return a mask of ALL handled sigs? |
| 1551 | static int check_and_run_traps(void) | ||
| 1482 | { | 1552 | { |
| 1483 | /* I want it in rodata, not in bss. | ||
| 1484 | * gcc 4.2.1 puts it in rodata only if it has { 0, 0 } | ||
| 1485 | * initializer. But other compilers may still use bss. | ||
| 1486 | * TODO: find more portable solution. | ||
| 1487 | */ | ||
| 1488 | static const struct timespec zero_timespec = { 0, 0 }; | ||
| 1489 | smalluint save_rcode; | ||
| 1490 | int last_sig = 0; | 1553 | int last_sig = 0; |
| 1491 | 1554 | ||
| 1492 | if (sig) | ||
| 1493 | goto got_sig; | ||
| 1494 | |||
| 1495 | while (1) { | 1555 | while (1) { |
| 1496 | if (!sigisemptyset(&G.detected_set)) { | 1556 | int sig; |
| 1497 | sig = 0; | ||
| 1498 | do { | ||
| 1499 | sig++; | ||
| 1500 | if (sigismember(&G.detected_set, sig)) { | ||
| 1501 | sigdelset(&G.detected_set, sig); | ||
| 1502 | goto got_sig; | ||
| 1503 | } | ||
| 1504 | } while (sig < NSIG); | ||
| 1505 | } | ||
| 1506 | 1557 | ||
| 1507 | sig = sigtimedwait(&G.blocked_set, NULL, &zero_timespec); | 1558 | if (sigisemptyset(&G.pending_set)) |
| 1508 | if (sig <= 0) | ||
| 1509 | break; | 1559 | break; |
| 1560 | sig = 0; | ||
| 1561 | do { | ||
| 1562 | sig++; | ||
| 1563 | if (sigismember(&G.pending_set, sig)) { | ||
| 1564 | sigdelset(&G.pending_set, sig); | ||
| 1565 | goto got_sig; | ||
| 1566 | } | ||
| 1567 | } while (sig < NSIG); | ||
| 1568 | break; | ||
| 1510 | got_sig: | 1569 | got_sig: |
| 1511 | if (G.traps && G.traps[sig]) { | 1570 | if (G.traps && G.traps[sig]) { |
| 1512 | if (G.traps[sig][0]) { | 1571 | if (G.traps[sig][0]) { |
| 1513 | /* We have user-defined handler */ | 1572 | /* We have user-defined handler */ |
| 1573 | smalluint save_rcode; | ||
| 1514 | char *argv[3]; | 1574 | char *argv[3]; |
| 1515 | /* argv[0] is unused */ | 1575 | /* argv[0] is unused */ |
| 1516 | argv[1] = G.traps[sig]; | 1576 | argv[1] = G.traps[sig]; |
| @@ -1524,12 +1584,6 @@ static int check_and_run_traps(int sig) | |||
| 1524 | } | 1584 | } |
| 1525 | /* not a trap: special action */ | 1585 | /* not a trap: special action */ |
| 1526 | switch (sig) { | 1586 | switch (sig) { |
| 1527 | #if ENABLE_HUSH_FAST | ||
| 1528 | case SIGCHLD: | ||
| 1529 | G.count_SIGCHLD++; | ||
| 1530 | //bb_error_msg("[%d] check_and_run_traps: G.count_SIGCHLD:%d G.handled_SIGCHLD:%d", getpid(), G.count_SIGCHLD, G.handled_SIGCHLD); | ||
| 1531 | break; | ||
| 1532 | #endif | ||
| 1533 | case SIGINT: | 1587 | case SIGINT: |
| 1534 | /* Builtin was ^C'ed, make it look prettier: */ | 1588 | /* Builtin was ^C'ed, make it look prettier: */ |
| 1535 | bb_putchar('\n'); | 1589 | bb_putchar('\n'); |
| @@ -1551,11 +1605,21 @@ static int check_and_run_traps(int sig) | |||
| 1551 | sigexit(SIGHUP); | 1605 | sigexit(SIGHUP); |
| 1552 | } | 1606 | } |
| 1553 | #endif | 1607 | #endif |
| 1608 | #if ENABLE_HUSH_FAST | ||
| 1609 | case SIGCHLD: | ||
| 1610 | G.count_SIGCHLD++; | ||
| 1611 | //bb_error_msg("[%d] check_and_run_traps: G.count_SIGCHLD:%d G.handled_SIGCHLD:%d", getpid(), G.count_SIGCHLD, G.handled_SIGCHLD); | ||
| 1612 | /* Note: | ||
| 1613 | * We dont do 'last_sig = sig' here -> NOT returning this sig. | ||
| 1614 | * This simplifies wait builtin a bit. | ||
| 1615 | */ | ||
| 1616 | break; | ||
| 1617 | #endif | ||
| 1554 | default: /* ignored: */ | 1618 | default: /* ignored: */ |
| 1555 | /* SIGTERM, SIGQUIT, SIGTTIN, SIGTTOU, SIGTSTP */ | 1619 | /* SIGTERM, SIGQUIT, SIGTTIN, SIGTTOU, SIGTSTP */ |
| 1556 | /* note: | 1620 | /* Note: |
| 1557 | * we dont do 'last_sig = sig' here -> NOT returning this sig. | 1621 | * We dont do 'last_sig = sig' here -> NOT returning this sig. |
| 1558 | * example: wait is not interrupted by TERM | 1622 | * Example: wait is not interrupted by TERM |
| 1559 | * in interactive shell, because TERM is ignored. | 1623 | * in interactive shell, because TERM is ignored. |
| 1560 | */ | 1624 | */ |
| 1561 | break; | 1625 | break; |
| @@ -1948,7 +2012,7 @@ static void get_user_input(struct in_str *i) | |||
| 1948 | * only after <Enter>. (^C will work) */ | 2012 | * only after <Enter>. (^C will work) */ |
| 1949 | r = read_line_input(G.line_input_state, prompt_str, G.user_input_buf, CONFIG_FEATURE_EDITING_MAX_LEN-1, /*timeout*/ -1); | 2013 | r = read_line_input(G.line_input_state, prompt_str, G.user_input_buf, CONFIG_FEATURE_EDITING_MAX_LEN-1, /*timeout*/ -1); |
| 1950 | /* catch *SIGINT* etc (^C is handled by read_line_input) */ | 2014 | /* catch *SIGINT* etc (^C is handled by read_line_input) */ |
| 1951 | check_and_run_traps(0); | 2015 | check_and_run_traps(); |
| 1952 | } while (r == 0 || G.flag_SIGINT); /* repeat if ^C or SIGINT */ | 2016 | } while (r == 0 || G.flag_SIGINT); /* repeat if ^C or SIGINT */ |
| 1953 | i->eof_flag = (r < 0); | 2017 | i->eof_flag = (r < 0); |
| 1954 | if (i->eof_flag) { /* EOF/error detected */ | 2018 | if (i->eof_flag) { /* EOF/error detected */ |
| @@ -1964,7 +2028,7 @@ static void get_user_input(struct in_str *i) | |||
| 1964 | * $ <[enter], repeatedly...> | 2028 | * $ <[enter], repeatedly...> |
| 1965 | * Without check_and_run_traps, handler never runs. | 2029 | * Without check_and_run_traps, handler never runs. |
| 1966 | */ | 2030 | */ |
| 1967 | check_and_run_traps(0); | 2031 | check_and_run_traps(); |
| 1968 | fputs(prompt_str, stdout); | 2032 | fputs(prompt_str, stdout); |
| 1969 | } | 2033 | } |
| 1970 | fflush_all(); | 2034 | fflush_all(); |
| @@ -5368,6 +5432,25 @@ void re_execute_shell(char ***to_free, const char *s, | |||
| 5368 | char *g_argv0, char **g_argv, | 5432 | char *g_argv0, char **g_argv, |
| 5369 | char **builtin_argv) NORETURN; | 5433 | char **builtin_argv) NORETURN; |
| 5370 | 5434 | ||
| 5435 | static void switch_off_special_sigs(unsigned mask) | ||
| 5436 | { | ||
| 5437 | unsigned sig = 0; | ||
| 5438 | while ((mask >>= 1) != 0) { | ||
| 5439 | sig++; | ||
| 5440 | if (!(mask & 1)) | ||
| 5441 | continue; | ||
| 5442 | if (G.traps) { | ||
| 5443 | if (G.traps[sig] && !G.traps[sig][0]) | ||
| 5444 | /* trap is '', has to remain SIG_IGN */ | ||
| 5445 | continue; | ||
| 5446 | free(G.traps[sig]); | ||
| 5447 | G.traps[sig] = NULL; | ||
| 5448 | } | ||
| 5449 | /* We are here only if no trap or trap was not '' */ | ||
| 5450 | signal(sig, SIG_DFL); | ||
| 5451 | } | ||
| 5452 | } | ||
| 5453 | |||
| 5371 | static void reset_traps_to_defaults(void) | 5454 | static void reset_traps_to_defaults(void) |
| 5372 | { | 5455 | { |
| 5373 | /* This function is always called in a child shell | 5456 | /* This function is always called in a child shell |
| @@ -5381,44 +5464,35 @@ static void reset_traps_to_defaults(void) | |||
| 5381 | * Testcase: (while :; do :; done) + ^Z should background. | 5464 | * Testcase: (while :; do :; done) + ^Z should background. |
| 5382 | * Same goes for SIGTERM, SIGHUP, SIGINT. | 5465 | * Same goes for SIGTERM, SIGHUP, SIGINT. |
| 5383 | */ | 5466 | */ |
| 5384 | if (!G.traps && !(G.special_sig_mask & SPECIAL_INTERACTIVE_SIGS)) | 5467 | mask = (G.special_sig_mask & SPECIAL_INTERACTIVE_SIGS) | G_fatal_sig_mask; |
| 5385 | return; /* already no traps and no SPECIAL_INTERACTIVE_SIGS */ | 5468 | if (!G.traps && !mask) |
| 5386 | 5469 | return; /* already no traps and no special sigs */ | |
| 5387 | /* Switching off SPECIAL_INTERACTIVE_SIGS. | 5470 | |
| 5388 | * Stupid. It can be done with *single* &= op, but we can't use | 5471 | /* Switch off special sigs */ |
| 5389 | * the fact that G.blocked_set is implemented as a bitmask | 5472 | switch_off_special_sigs(mask); |
| 5390 | * in libc... */ | 5473 | #if ENABLE_HUSH_JOB |
| 5391 | mask = SPECIAL_INTERACTIVE_SIGS; | 5474 | G_fatal_sig_mask = 0; |
| 5392 | sig = 0; | 5475 | #endif |
| 5393 | while ((mask >>= 1) != 0) { | ||
| 5394 | sig++; | ||
| 5395 | if (mask & 1) { | ||
| 5396 | /* Careful. Only if no trap or trap is not "" */ | ||
| 5397 | if (!G.traps || !G.traps[sig] || G.traps[sig][0]) | ||
| 5398 | sigdelset(&G.blocked_set, sig); | ||
| 5399 | } | ||
| 5400 | } | ||
| 5401 | /* Our homegrown sig mask is saner to work with :) */ | ||
| 5402 | G.special_sig_mask &= ~SPECIAL_INTERACTIVE_SIGS; | 5476 | G.special_sig_mask &= ~SPECIAL_INTERACTIVE_SIGS; |
| 5477 | /* SIGQUIT and maybe SPECIAL_JOBSTOP_SIGS remain set in G.special_sig_mask */ | ||
| 5403 | 5478 | ||
| 5404 | /* Resetting all traps to default except empty ones */ | 5479 | if (!G.traps) |
| 5405 | mask = G.special_sig_mask; | 5480 | return; |
| 5406 | if (G.traps) for (sig = 0; sig < NSIG; sig++, mask >>= 1) { | 5481 | |
| 5407 | if (!G.traps[sig] || !G.traps[sig][0]) | 5482 | /* Reset all sigs to default except ones with empty traps */ |
| 5408 | continue; | 5483 | for (sig = 0; sig < NSIG; sig++) { |
| 5484 | if (!G.traps[sig]) | ||
| 5485 | continue; /* no trap: nothing to do */ | ||
| 5486 | if (!G.traps[sig][0]) | ||
| 5487 | continue; /* empty trap: has to remain SIG_IGN */ | ||
| 5488 | /* sig has non-empty trap, reset it: */ | ||
| 5409 | free(G.traps[sig]); | 5489 | free(G.traps[sig]); |
| 5410 | G.traps[sig] = NULL; | 5490 | G.traps[sig] = NULL; |
| 5411 | /* There is no signal for 0 (EXIT) */ | 5491 | /* There is no signal for trap 0 (EXIT) */ |
| 5412 | if (sig == 0) | 5492 | if (sig == 0) |
| 5413 | continue; | 5493 | continue; |
| 5414 | /* There was a trap handler, we just removed it. | 5494 | signal(sig, pick_sighandler(sig)); |
| 5415 | * But if sig still has non-DFL handling, | ||
| 5416 | * we should not unblock the sig. */ | ||
| 5417 | if (mask & 1) | ||
| 5418 | continue; | ||
| 5419 | sigdelset(&G.blocked_set, sig); | ||
| 5420 | } | 5495 | } |
| 5421 | sigprocmask_set(&G.blocked_set); | ||
| 5422 | } | 5496 | } |
| 5423 | 5497 | ||
| 5424 | #else /* !BB_MMU */ | 5498 | #else /* !BB_MMU */ |
| @@ -5463,6 +5537,7 @@ static void re_execute_shell(char ***to_free, const char *s, | |||
| 5463 | for (sig = 1; sig < NSIG; sig++) { | 5537 | for (sig = 1; sig < NSIG; sig++) { |
| 5464 | if (G.traps[sig] && !G.traps[sig][0]) | 5538 | if (G.traps[sig] && !G.traps[sig][0]) |
| 5465 | empty_trap_mask |= 1LL << sig; | 5539 | empty_trap_mask |= 1LL << sig; |
| 5540 | ///vda: optimize | ||
| 5466 | } | 5541 | } |
| 5467 | } | 5542 | } |
| 5468 | 5543 | ||
| @@ -5548,7 +5623,7 @@ static void re_execute_shell(char ***to_free, const char *s, | |||
| 5548 | 5623 | ||
| 5549 | do_exec: | 5624 | do_exec: |
| 5550 | debug_printf_exec("re_execute_shell pid:%d cmd:'%s'\n", getpid(), s); | 5625 | debug_printf_exec("re_execute_shell pid:%d cmd:'%s'\n", getpid(), s); |
| 5551 | sigprocmask_set(&G.inherited_set); | 5626 | switch_off_special_sigs(G.special_sig_mask & SPECIAL_JOBSTOP_SIGS); |
| 5552 | execve(bb_busybox_exec_path, argv, pp); | 5627 | execve(bb_busybox_exec_path, argv, pp); |
| 5553 | /* Fallback. Useful for init=/bin/hush usage etc */ | 5628 | /* Fallback. Useful for init=/bin/hush usage etc */ |
| 5554 | if (argv[0][0] == '/') | 5629 | if (argv[0][0] == '/') |
| @@ -6202,7 +6277,7 @@ static void execvp_or_die(char **argv) NORETURN; | |||
| 6202 | static void execvp_or_die(char **argv) | 6277 | static void execvp_or_die(char **argv) |
| 6203 | { | 6278 | { |
| 6204 | debug_printf_exec("execing '%s'\n", argv[0]); | 6279 | debug_printf_exec("execing '%s'\n", argv[0]); |
| 6205 | sigprocmask_set(&G.inherited_set); | 6280 | switch_off_special_sigs(G.special_sig_mask & SPECIAL_JOBSTOP_SIGS); |
| 6206 | execvp(argv[0], argv); | 6281 | execvp(argv[0], argv); |
| 6207 | bb_perror_msg("can't execute '%s'", argv[0]); | 6282 | bb_perror_msg("can't execute '%s'", argv[0]); |
| 6208 | _exit(127); /* bash compat */ | 6283 | _exit(127); /* bash compat */ |
| @@ -6334,7 +6409,7 @@ static NOINLINE void pseudo_exec_argv(nommu_save_t *nommu_save, | |||
| 6334 | # endif | 6409 | # endif |
| 6335 | /* Re-exec ourselves */ | 6410 | /* Re-exec ourselves */ |
| 6336 | debug_printf_exec("re-execing applet '%s'\n", argv[0]); | 6411 | debug_printf_exec("re-execing applet '%s'\n", argv[0]); |
| 6337 | sigprocmask_set(&G.inherited_set); | 6412 | switch_off_special_sigs(G.special_sig_mask & SPECIAL_JOBSTOP_SIGS); |
| 6338 | execv(bb_busybox_exec_path, argv); | 6413 | execv(bb_busybox_exec_path, argv); |
| 6339 | /* If they called chroot or otherwise made the binary no longer | 6414 | /* If they called chroot or otherwise made the binary no longer |
| 6340 | * executable, fall through */ | 6415 | * executable, fall through */ |
| @@ -7033,9 +7108,6 @@ static NOINLINE int run_pipe(struct pipe *pi) | |||
| 7033 | if (setup_redirects(command, NULL)) | 7108 | if (setup_redirects(command, NULL)) |
| 7034 | _exit(1); | 7109 | _exit(1); |
| 7035 | 7110 | ||
| 7036 | /* Restore default handlers just prior to exec */ | ||
| 7037 | /*signal(SIGCHLD, SIG_DFL); - so far we don't have any handlers */ | ||
| 7038 | |||
| 7039 | /* Stores to nommu_save list of env vars putenv'ed | 7111 | /* Stores to nommu_save list of env vars putenv'ed |
| 7040 | * (NOMMU, on MMU we don't need that) */ | 7112 | * (NOMMU, on MMU we don't need that) */ |
| 7041 | /* cast away volatility... */ | 7113 | /* cast away volatility... */ |
| @@ -7313,7 +7385,7 @@ static int run_list(struct pipe *pi) | |||
| 7313 | * and we don't need to wait for anything. */ | 7385 | * and we don't need to wait for anything. */ |
| 7314 | G.last_exitcode = rcode; | 7386 | G.last_exitcode = rcode; |
| 7315 | debug_printf_exec(": builtin/func exitcode %d\n", rcode); | 7387 | debug_printf_exec(": builtin/func exitcode %d\n", rcode); |
| 7316 | check_and_run_traps(0); | 7388 | check_and_run_traps(); |
| 7317 | #if ENABLE_HUSH_LOOPS | 7389 | #if ENABLE_HUSH_LOOPS |
| 7318 | /* Was it "break" or "continue"? */ | 7390 | /* Was it "break" or "continue"? */ |
| 7319 | if (G.flag_break_continue) { | 7391 | if (G.flag_break_continue) { |
| @@ -7345,7 +7417,7 @@ static int run_list(struct pipe *pi) | |||
| 7345 | /* even bash 3.2 doesn't do that well with nested bg: | 7417 | /* even bash 3.2 doesn't do that well with nested bg: |
| 7346 | * try "{ { sleep 10; echo DEEP; } & echo HERE; } &". | 7418 | * try "{ { sleep 10; echo DEEP; } & echo HERE; } &". |
| 7347 | * I'm NOT treating inner &'s as jobs */ | 7419 | * I'm NOT treating inner &'s as jobs */ |
| 7348 | check_and_run_traps(0); | 7420 | check_and_run_traps(); |
| 7349 | #if ENABLE_HUSH_JOB | 7421 | #if ENABLE_HUSH_JOB |
| 7350 | if (G.run_list_level == 1) | 7422 | if (G.run_list_level == 1) |
| 7351 | insert_bg_job(pi); | 7423 | insert_bg_job(pi); |
| @@ -7360,13 +7432,13 @@ static int run_list(struct pipe *pi) | |||
| 7360 | /* Waits for completion, then fg's main shell */ | 7432 | /* Waits for completion, then fg's main shell */ |
| 7361 | rcode = checkjobs_and_fg_shell(pi); | 7433 | rcode = checkjobs_and_fg_shell(pi); |
| 7362 | debug_printf_exec(": checkjobs_and_fg_shell exitcode %d\n", rcode); | 7434 | debug_printf_exec(": checkjobs_and_fg_shell exitcode %d\n", rcode); |
| 7363 | check_and_run_traps(0); | 7435 | check_and_run_traps(); |
| 7364 | } else | 7436 | } else |
| 7365 | #endif | 7437 | #endif |
| 7366 | { /* This one just waits for completion */ | 7438 | { /* This one just waits for completion */ |
| 7367 | rcode = checkjobs(pi); | 7439 | rcode = checkjobs(pi); |
| 7368 | debug_printf_exec(": checkjobs exitcode %d\n", rcode); | 7440 | debug_printf_exec(": checkjobs exitcode %d\n", rcode); |
| 7369 | check_and_run_traps(0); | 7441 | check_and_run_traps(); |
| 7370 | } | 7442 | } |
| 7371 | G.last_exitcode = rcode; | 7443 | G.last_exitcode = rcode; |
| 7372 | } | 7444 | } |
| @@ -7437,58 +7509,61 @@ static int run_and_free_list(struct pipe *pi) | |||
| 7437 | } | 7509 | } |
| 7438 | 7510 | ||
| 7439 | 7511 | ||
| 7512 | static void install_sighandlers(unsigned mask) | ||
| 7513 | { | ||
| 7514 | sighandler_t old_handler; | ||
| 7515 | unsigned sig = 0; | ||
| 7516 | while ((mask >>= 1) != 0) { | ||
| 7517 | sig++; | ||
| 7518 | if (!(mask & 1)) | ||
| 7519 | continue; | ||
| 7520 | old_handler = signal(sig, pick_sighandler(sig)); | ||
| 7521 | /* POSIX allows shell to re-enable SIGCHLD | ||
| 7522 | * even if it was SIG_IGN on entry. | ||
| 7523 | * Therefore we skip IGN check for it: | ||
| 7524 | */ | ||
| 7525 | if (sig == SIGCHLD) | ||
| 7526 | continue; | ||
| 7527 | if (old_handler == SIG_IGN) { | ||
| 7528 | /* oops... restore back to IGN, and record this fact */ | ||
| 7529 | signal(sig, old_handler); | ||
| 7530 | if (!G.traps) | ||
| 7531 | G.traps = xzalloc(sizeof(G.traps[0]) * NSIG); | ||
| 7532 | free(G.traps[sig]); | ||
| 7533 | G.traps[sig] = xzalloc(1); /* == xstrdup(""); */ | ||
| 7534 | } | ||
| 7535 | } | ||
| 7536 | } | ||
| 7537 | |||
| 7440 | /* Called a few times only (or even once if "sh -c") */ | 7538 | /* Called a few times only (or even once if "sh -c") */ |
| 7441 | static void init_sigmasks(void) | 7539 | static void install_special_sighandlers(void) |
| 7442 | { | 7540 | { |
| 7443 | unsigned sig; | ||
| 7444 | unsigned mask; | 7541 | unsigned mask; |
| 7445 | 7542 | ||
| 7446 | /* POSIX allows shell to re-enable SIGCHLD | 7543 | if (G.special_sig_mask != 0) |
| 7447 | * even if it was SIG_IGN on entry */ | 7544 | return; |
| 7448 | if (!G.inherited_set_is_saved) { | ||
| 7449 | #if ENABLE_HUSH_FAST | ||
| 7450 | signal(SIGCHLD, SIGCHLD_handler); | ||
| 7451 | #else | ||
| 7452 | signal(SIGCHLD, SIG_DFL); | ||
| 7453 | #endif | ||
| 7454 | sigprocmask(SIG_SETMASK, NULL, &G.blocked_set); | ||
| 7455 | G.inherited_set = G.blocked_set; | ||
| 7456 | } | ||
| 7457 | 7545 | ||
| 7458 | /* Which signals are shell-special? */ | 7546 | /* Which signals are shell-special? */ |
| 7459 | mask = (1 << SIGQUIT); | 7547 | mask = (1 << SIGQUIT) | (1 << SIGCHLD); |
| 7460 | if (G_interactive_fd) { | 7548 | if (G_interactive_fd) { |
| 7461 | mask |= SPECIAL_INTERACTIVE_SIGS; | 7549 | mask |= SPECIAL_INTERACTIVE_SIGS; |
| 7462 | if (G_saved_tty_pgrp) /* we have ctty, job control sigs work */ | 7550 | if (G_saved_tty_pgrp) /* we have ctty, job control sigs work */ |
| 7463 | mask |= SPECIAL_JOB_SIGS; | 7551 | mask |= SPECIAL_JOBSTOP_SIGS; |
| 7464 | } | 7552 | } |
| 7465 | G.special_sig_mask = mask; | 7553 | G.special_sig_mask = mask; |
| 7466 | 7554 | ||
| 7467 | /* Block them. And unblock SIGCHLD */ | 7555 | install_sighandlers(mask); |
| 7468 | sig = 0; | ||
| 7469 | while ((mask >>= 1) != 0) { | ||
| 7470 | sig++; | ||
| 7471 | if (mask & 1) | ||
| 7472 | sigaddset(&G.blocked_set, sig); | ||
| 7473 | } | ||
| 7474 | sigdelset(&G.blocked_set, SIGCHLD); | ||
| 7475 | |||
| 7476 | if (memcmp(&G.inherited_set, &G.blocked_set, sizeof(G.inherited_set)) != 0) | ||
| 7477 | sigprocmask_set(&G.blocked_set); | ||
| 7478 | |||
| 7479 | G.inherited_set_is_saved = 1; | ||
| 7480 | } | 7556 | } |
| 7481 | 7557 | ||
| 7482 | #if ENABLE_HUSH_JOB | 7558 | #if ENABLE_HUSH_JOB |
| 7483 | /* helper */ | 7559 | /* helper */ |
| 7484 | /* Set handlers to restore tty pgrp and exit */ | 7560 | /* Set handlers to restore tty pgrp and exit */ |
| 7485 | static void set_fatal_handlers_to_sigexit(void) | 7561 | static void install_fatal_sighandlers(void) |
| 7486 | { | 7562 | { |
| 7487 | void (*handler)(int); | 7563 | unsigned mask; |
| 7488 | unsigned fatal_sigs, sig; | ||
| 7489 | 7564 | ||
| 7490 | /* We will restore tty pgrp on these signals */ | 7565 | /* We will restore tty pgrp on these signals */ |
| 7491 | fatal_sigs = 0 | 7566 | mask = 0 |
| 7492 | + (1 << SIGILL ) * HUSH_DEBUG | 7567 | + (1 << SIGILL ) * HUSH_DEBUG |
| 7493 | + (1 << SIGFPE ) * HUSH_DEBUG | 7568 | + (1 << SIGFPE ) * HUSH_DEBUG |
| 7494 | + (1 << SIGBUS ) * HUSH_DEBUG | 7569 | + (1 << SIGBUS ) * HUSH_DEBUG |
| @@ -7505,22 +7580,13 @@ static void set_fatal_handlers_to_sigexit(void) | |||
| 7505 | /*+ (1 << SIGTERM)*/ | 7580 | /*+ (1 << SIGTERM)*/ |
| 7506 | /*+ (1 << SIGINT )*/ | 7581 | /*+ (1 << SIGINT )*/ |
| 7507 | ; | 7582 | ; |
| 7508 | 7583 | /* special_sig_mask'ed signals are set to record_pending_signo | |
| 7509 | /* special_sig_mask'ed signals are, well, masked, | ||
| 7510 | * no need to set handler for them. | 7584 | * no need to set handler for them. |
| 7511 | */ | 7585 | */ |
| 7512 | fatal_sigs &= ~G.special_sig_mask; | 7586 | /*mask &= ~G.special_sig_mask; - they never overlap */ |
| 7587 | G_fatal_sig_mask = mask; | ||
| 7513 | 7588 | ||
| 7514 | /* For each sig in fatal_sigs... */ | 7589 | install_sighandlers(mask); |
| 7515 | sig = 0; | ||
| 7516 | while ((fatal_sigs >>= 1) != 0) { | ||
| 7517 | sig++; | ||
| 7518 | if (!(fatal_sigs & 1)) | ||
| 7519 | continue; | ||
| 7520 | handler = signal(sig, sigexit); | ||
| 7521 | if (handler == SIG_IGN) /* oops... restore back to IGN! */ | ||
| 7522 | signal(sig, handler); | ||
| 7523 | } | ||
| 7524 | } | 7590 | } |
| 7525 | #endif | 7591 | #endif |
| 7526 | 7592 | ||
| @@ -7682,10 +7748,11 @@ int hush_main(int argc, char **argv) | |||
| 7682 | } | 7748 | } |
| 7683 | 7749 | ||
| 7684 | /* Shell is non-interactive at first. We need to call | 7750 | /* Shell is non-interactive at first. We need to call |
| 7685 | * init_sigmasks() if we are going to execute "sh <script>", | 7751 | * install_special_sighandlers() if we are going to execute "sh <script>", |
| 7686 | * "sh -c <cmds>" or login shell's /etc/profile and friends. | 7752 | * "sh -c <cmds>" or login shell's /etc/profile and friends. |
| 7687 | * If we later decide that we are interactive, we run init_sigmasks() | 7753 | * If we later decide that we are interactive, we run install_special_sighandlers() |
| 7688 | * in order to intercept (more) signals. | 7754 | * in order to intercept (more) signals. |
| 7755 | //FIXME: re-running is currently most likely broken, it's a no-op. | ||
| 7689 | */ | 7756 | */ |
| 7690 | 7757 | ||
| 7691 | /* Parse options */ | 7758 | /* Parse options */ |
| @@ -7724,7 +7791,7 @@ int hush_main(int argc, char **argv) | |||
| 7724 | /* -c 'builtin' [BARGV...] "" ARG0 [ARG1...] */ | 7791 | /* -c 'builtin' [BARGV...] "" ARG0 [ARG1...] */ |
| 7725 | const struct built_in_command *x; | 7792 | const struct built_in_command *x; |
| 7726 | 7793 | ||
| 7727 | init_sigmasks(); | 7794 | install_special_sighandlers(); |
| 7728 | x = find_builtin(optarg); | 7795 | x = find_builtin(optarg); |
| 7729 | if (x) { /* paranoia */ | 7796 | if (x) { /* paranoia */ |
| 7730 | G.global_argc -= builtin_argc; /* skip [BARGV...] "" */ | 7797 | G.global_argc -= builtin_argc; /* skip [BARGV...] "" */ |
| @@ -7741,7 +7808,7 @@ int hush_main(int argc, char **argv) | |||
| 7741 | G.global_argv[0] = argv[0]; | 7808 | G.global_argv[0] = argv[0]; |
| 7742 | G.global_argc++; | 7809 | G.global_argc++; |
| 7743 | } /* else -c 'script' ARG0 [ARG1...]: $0 is ARG0 */ | 7810 | } /* else -c 'script' ARG0 [ARG1...]: $0 is ARG0 */ |
| 7744 | init_sigmasks(); | 7811 | install_special_sighandlers(); |
| 7745 | parse_and_run_string(optarg); | 7812 | parse_and_run_string(optarg); |
| 7746 | goto final_return; | 7813 | goto final_return; |
| 7747 | case 'i': | 7814 | case 'i': |
| @@ -7773,15 +7840,15 @@ int hush_main(int argc, char **argv) | |||
| 7773 | empty_trap_mask = bb_strtoull(optarg, &optarg, 16); | 7840 | empty_trap_mask = bb_strtoull(optarg, &optarg, 16); |
| 7774 | if (empty_trap_mask != 0) { | 7841 | if (empty_trap_mask != 0) { |
| 7775 | int sig; | 7842 | int sig; |
| 7776 | init_sigmasks(); | 7843 | install_special_sighandlers(); |
| 7777 | G.traps = xzalloc(sizeof(G.traps[0]) * NSIG); | 7844 | G.traps = xzalloc(sizeof(G.traps[0]) * NSIG); |
| 7778 | for (sig = 1; sig < NSIG; sig++) { | 7845 | for (sig = 1; sig < NSIG; sig++) { |
| 7846 | ///vda: fixme: more efficient code | ||
| 7779 | if (empty_trap_mask & (1LL << sig)) { | 7847 | if (empty_trap_mask & (1LL << sig)) { |
| 7780 | G.traps[sig] = xzalloc(1); /* == xstrdup(""); */ | 7848 | G.traps[sig] = xzalloc(1); /* == xstrdup(""); */ |
| 7781 | sigaddset(&G.blocked_set, sig); | 7849 | signal(sig, SIG_IGN); |
| 7782 | } | 7850 | } |
| 7783 | } | 7851 | } |
| 7784 | sigprocmask_set(&G.blocked_set); | ||
| 7785 | } | 7852 | } |
| 7786 | # if ENABLE_HUSH_LOOPS | 7853 | # if ENABLE_HUSH_LOOPS |
| 7787 | optarg++; | 7854 | optarg++; |
| @@ -7831,7 +7898,7 @@ int hush_main(int argc, char **argv) | |||
| 7831 | input = fopen_for_read("/etc/profile"); | 7898 | input = fopen_for_read("/etc/profile"); |
| 7832 | if (input != NULL) { | 7899 | if (input != NULL) { |
| 7833 | close_on_exec_on(fileno(input)); | 7900 | close_on_exec_on(fileno(input)); |
| 7834 | init_sigmasks(); | 7901 | install_special_sighandlers(); |
| 7835 | parse_and_run_file(input); | 7902 | parse_and_run_file(input); |
| 7836 | fclose(input); | 7903 | fclose(input); |
| 7837 | } | 7904 | } |
| @@ -7856,7 +7923,7 @@ int hush_main(int argc, char **argv) | |||
| 7856 | G.global_argc = argc - optind; | 7923 | G.global_argc = argc - optind; |
| 7857 | input = xfopen_for_read(argv[optind]); | 7924 | input = xfopen_for_read(argv[optind]); |
| 7858 | close_on_exec_on(fileno(input)); | 7925 | close_on_exec_on(fileno(input)); |
| 7859 | init_sigmasks(); | 7926 | install_special_sighandlers(); |
| 7860 | parse_and_run_file(input); | 7927 | parse_and_run_file(input); |
| 7861 | #if ENABLE_FEATURE_CLEAN_UP | 7928 | #if ENABLE_FEATURE_CLEAN_UP |
| 7862 | fclose(input); | 7929 | fclose(input); |
| @@ -7865,7 +7932,7 @@ int hush_main(int argc, char **argv) | |||
| 7865 | } | 7932 | } |
| 7866 | 7933 | ||
| 7867 | /* Up to here, shell was non-interactive. Now it may become one. | 7934 | /* Up to here, shell was non-interactive. Now it may become one. |
| 7868 | * NB: don't forget to (re)run init_sigmasks() as needed. | 7935 | * NB: don't forget to (re)run install_special_sighandlers() as needed. |
| 7869 | */ | 7936 | */ |
| 7870 | 7937 | ||
| 7871 | /* A shell is interactive if the '-i' flag was given, | 7938 | /* A shell is interactive if the '-i' flag was given, |
| @@ -7918,11 +7985,11 @@ int hush_main(int argc, char **argv) | |||
| 7918 | } | 7985 | } |
| 7919 | 7986 | ||
| 7920 | /* Block some signals */ | 7987 | /* Block some signals */ |
| 7921 | init_sigmasks(); | 7988 | install_special_sighandlers(); |
| 7922 | 7989 | ||
| 7923 | if (G_saved_tty_pgrp) { | 7990 | if (G_saved_tty_pgrp) { |
| 7924 | /* Set other signals to restore saved_tty_pgrp */ | 7991 | /* Set other signals to restore saved_tty_pgrp */ |
| 7925 | set_fatal_handlers_to_sigexit(); | 7992 | install_fatal_sighandlers(); |
| 7926 | /* Put ourselves in our own process group | 7993 | /* Put ourselves in our own process group |
| 7927 | * (bash, too, does this only if ctty is available) */ | 7994 | * (bash, too, does this only if ctty is available) */ |
| 7928 | bb_setpgrp(); /* is the same as setpgid(our_pid, our_pid); */ | 7995 | bb_setpgrp(); /* is the same as setpgid(our_pid, our_pid); */ |
| @@ -7933,7 +8000,7 @@ int hush_main(int argc, char **argv) | |||
| 7933 | * (we reset die_sleep = 0 whereever we [v]fork) */ | 8000 | * (we reset die_sleep = 0 whereever we [v]fork) */ |
| 7934 | enable_restore_tty_pgrp_on_exit(); /* sets die_sleep = -1 */ | 8001 | enable_restore_tty_pgrp_on_exit(); /* sets die_sleep = -1 */ |
| 7935 | } else { | 8002 | } else { |
| 7936 | init_sigmasks(); | 8003 | install_special_sighandlers(); |
| 7937 | } | 8004 | } |
| 7938 | #elif ENABLE_HUSH_INTERACTIVE | 8005 | #elif ENABLE_HUSH_INTERACTIVE |
| 7939 | /* No job control compiled in, only prompt/line editing */ | 8006 | /* No job control compiled in, only prompt/line editing */ |
| @@ -7950,10 +8017,10 @@ int hush_main(int argc, char **argv) | |||
| 7950 | if (G_interactive_fd) { | 8017 | if (G_interactive_fd) { |
| 7951 | close_on_exec_on(G_interactive_fd); | 8018 | close_on_exec_on(G_interactive_fd); |
| 7952 | } | 8019 | } |
| 7953 | init_sigmasks(); | 8020 | install_special_sighandlers(); |
| 7954 | #else | 8021 | #else |
| 7955 | /* We have interactiveness code disabled */ | 8022 | /* We have interactiveness code disabled */ |
| 7956 | init_sigmasks(); | 8023 | install_special_sighandlers(); |
| 7957 | #endif | 8024 | #endif |
| 7958 | /* bash: | 8025 | /* bash: |
| 7959 | * if interactive but not a login shell, sources ~/.bashrc | 8026 | * if interactive but not a login shell, sources ~/.bashrc |
| @@ -8087,7 +8154,7 @@ static int FAST_FUNC builtin_exec(char **argv) | |||
| 8087 | tcsetpgrp(G_interactive_fd, G_saved_tty_pgrp); | 8154 | tcsetpgrp(G_interactive_fd, G_saved_tty_pgrp); |
| 8088 | 8155 | ||
| 8089 | /* TODO: if exec fails, bash does NOT exit! We do. | 8156 | /* TODO: if exec fails, bash does NOT exit! We do. |
| 8090 | * We'll need to undo sigprocmask (it's inside execvp_or_die) | 8157 | * We'll need to undo trap cleanup (it's inside execvp_or_die) |
| 8091 | * and tcsetpgrp, and this is inherently racy. | 8158 | * and tcsetpgrp, and this is inherently racy. |
| 8092 | */ | 8159 | */ |
| 8093 | execvp_or_die(argv); | 8160 | execvp_or_die(argv); |
| @@ -8284,6 +8351,8 @@ static int FAST_FUNC builtin_trap(char **argv) | |||
| 8284 | process_sig_list: | 8351 | process_sig_list: |
| 8285 | ret = EXIT_SUCCESS; | 8352 | ret = EXIT_SUCCESS; |
| 8286 | while (*argv) { | 8353 | while (*argv) { |
| 8354 | sighandler_t handler; | ||
| 8355 | |||
| 8287 | sig = get_signum(*argv++); | 8356 | sig = get_signum(*argv++); |
| 8288 | if (sig < 0 || sig >= NSIG) { | 8357 | if (sig < 0 || sig >= NSIG) { |
| 8289 | ret = EXIT_FAILURE; | 8358 | ret = EXIT_FAILURE; |
| @@ -8302,18 +8371,13 @@ static int FAST_FUNC builtin_trap(char **argv) | |||
| 8302 | if (sig == 0) | 8371 | if (sig == 0) |
| 8303 | continue; | 8372 | continue; |
| 8304 | 8373 | ||
| 8305 | if (new_cmd) { | 8374 | if (new_cmd) |
| 8306 | sigaddset(&G.blocked_set, sig); | 8375 | handler = (new_cmd[0] ? record_pending_signo : SIG_IGN); |
| 8307 | } else { | 8376 | else |
| 8308 | /* There was a trap handler, we are removing it | 8377 | /* We are removing trap handler */ |
| 8309 | * (if sig has non-DFL handling, | 8378 | handler = pick_sighandler(sig); |
| 8310 | * we don't need to do anything) */ | 8379 | signal(sig, handler); |
| 8311 | if (sig < sizeof(G.special_sig_mask)*8 && (G.special_sig_mask & (1 << sig))) | ||
| 8312 | continue; | ||
| 8313 | sigdelset(&G.blocked_set, sig); | ||
| 8314 | } | ||
| 8315 | } | 8380 | } |
| 8316 | sigprocmask_set(&G.blocked_set); | ||
| 8317 | return ret; | 8381 | return ret; |
| 8318 | } | 8382 | } |
| 8319 | 8383 | ||
| @@ -8535,11 +8599,6 @@ static int FAST_FUNC builtin_pwd(char **argv UNUSED_PARAM) | |||
| 8535 | * if it has non-empty trap: | 8599 | * if it has non-empty trap: |
| 8536 | * - executes trap and returns to read; | 8600 | * - executes trap and returns to read; |
| 8537 | */ | 8601 | */ |
| 8538 | /* helper */ | ||
| 8539 | static void record_signal(int sig) | ||
| 8540 | { | ||
| 8541 | sigaddset(&G.detected_set, sig); | ||
| 8542 | } | ||
| 8543 | static int FAST_FUNC builtin_read(char **argv) | 8602 | static int FAST_FUNC builtin_read(char **argv) |
| 8544 | { | 8603 | { |
| 8545 | const char *r; | 8604 | const char *r; |
| @@ -8549,7 +8608,6 @@ static int FAST_FUNC builtin_read(char **argv) | |||
| 8549 | char *opt_u = NULL; | 8608 | char *opt_u = NULL; |
| 8550 | const char *ifs; | 8609 | const char *ifs; |
| 8551 | int read_flags; | 8610 | int read_flags; |
| 8552 | sigset_t saved_blkd_set; | ||
| 8553 | 8611 | ||
| 8554 | /* "!": do not abort on errors. | 8612 | /* "!": do not abort on errors. |
| 8555 | * Option string must start with "sr" to match BUILTIN_READ_xxx | 8613 | * Option string must start with "sr" to match BUILTIN_READ_xxx |
| @@ -8561,41 +8619,6 @@ static int FAST_FUNC builtin_read(char **argv) | |||
| 8561 | ifs = get_local_var_value("IFS"); /* can be NULL */ | 8619 | ifs = get_local_var_value("IFS"); /* can be NULL */ |
| 8562 | 8620 | ||
| 8563 | again: | 8621 | again: |
| 8564 | /* We need to temporarily unblock and record signals around read */ | ||
| 8565 | |||
| 8566 | saved_blkd_set = G.blocked_set; | ||
| 8567 | { | ||
| 8568 | unsigned sig; | ||
| 8569 | struct sigaction sa, old_sa; | ||
| 8570 | |||
| 8571 | memset(&sa, 0, sizeof(sa)); | ||
| 8572 | sigfillset(&sa.sa_mask); | ||
| 8573 | sa.sa_flags = SA_RESTART; | ||
| 8574 | sa.sa_handler = record_signal; | ||
| 8575 | |||
| 8576 | sig = 0; | ||
| 8577 | do { | ||
| 8578 | sig++; | ||
| 8579 | if (sigismember(&G.blocked_set, sig)) { | ||
| 8580 | char *sig_trap = (G.traps && G.traps[sig]) ? G.traps[sig] : NULL; | ||
| 8581 | /* If has a nonempty trap... */ | ||
| 8582 | if ((sig_trap && sig_trap[0]) | ||
| 8583 | /* ...or has no trap and is SIGINT or SIGHUP */ | ||
| 8584 | || (!sig_trap && (sig == SIGINT || sig == SIGHUP)) | ||
| 8585 | ) { | ||
| 8586 | sigaction(sig, &sa, &old_sa); | ||
| 8587 | if (old_sa.sa_handler == SIG_IGN) /* oops... restore back to IGN! */ | ||
| 8588 | sigaction_set(sig, &old_sa); | ||
| 8589 | else | ||
| 8590 | sigdelset(&G.blocked_set, sig); | ||
| 8591 | } | ||
| 8592 | } | ||
| 8593 | } while (sig < NSIG-1); | ||
| 8594 | } | ||
| 8595 | |||
| 8596 | if (memcmp(&saved_blkd_set, &G.blocked_set, sizeof(saved_blkd_set)) != 0) | ||
| 8597 | sigprocmask_set(&G.blocked_set); | ||
| 8598 | |||
| 8599 | r = shell_builtin_read(set_local_var_from_halves, | 8622 | r = shell_builtin_read(set_local_var_from_halves, |
| 8600 | argv, | 8623 | argv, |
| 8601 | ifs, | 8624 | ifs, |
| @@ -8606,13 +8629,8 @@ static int FAST_FUNC builtin_read(char **argv) | |||
| 8606 | opt_u | 8629 | opt_u |
| 8607 | ); | 8630 | ); |
| 8608 | 8631 | ||
| 8609 | if (memcmp(&saved_blkd_set, &G.blocked_set, sizeof(saved_blkd_set)) != 0) { | ||
| 8610 | G.blocked_set = saved_blkd_set; | ||
| 8611 | sigprocmask_set(&G.blocked_set); | ||
| 8612 | } | ||
| 8613 | |||
| 8614 | if ((uintptr_t)r == 1 && errno == EINTR) { | 8632 | if ((uintptr_t)r == 1 && errno == EINTR) { |
| 8615 | unsigned sig = check_and_run_traps(0); | 8633 | unsigned sig = check_and_run_traps(); |
| 8616 | if (sig && sig != SIGINT) | 8634 | if (sig && sig != SIGINT) |
| 8617 | goto again; | 8635 | goto again; |
| 8618 | } | 8636 | } |
| @@ -8849,7 +8867,7 @@ static int FAST_FUNC builtin_unset(char **argv) | |||
| 8849 | static int FAST_FUNC builtin_wait(char **argv) | 8867 | static int FAST_FUNC builtin_wait(char **argv) |
| 8850 | { | 8868 | { |
| 8851 | int ret = EXIT_SUCCESS; | 8869 | int ret = EXIT_SUCCESS; |
| 8852 | int status, sig; | 8870 | int status; |
| 8853 | 8871 | ||
| 8854 | argv = skip_dash_dash(argv); | 8872 | argv = skip_dash_dash(argv); |
| 8855 | if (argv[0] == NULL) { | 8873 | if (argv[0] == NULL) { |
| @@ -8869,25 +8887,53 @@ static int FAST_FUNC builtin_wait(char **argv) | |||
| 8869 | * ^C <-- after ~4 sec from keyboard | 8887 | * ^C <-- after ~4 sec from keyboard |
| 8870 | * $ | 8888 | * $ |
| 8871 | */ | 8889 | */ |
| 8872 | sigaddset(&G.blocked_set, SIGCHLD); | ||
| 8873 | sigprocmask_set(&G.blocked_set); | ||
| 8874 | while (1) { | 8890 | while (1) { |
| 8875 | checkjobs(NULL); | 8891 | int sig; |
| 8876 | if (errno == ECHILD) | 8892 | sigset_t oldset, allsigs; |
| 8893 | |||
| 8894 | /* waitpid is not interruptible by SA_RESTARTed | ||
| 8895 | * signals which we use. Thus, this ugly dance: | ||
| 8896 | */ | ||
| 8897 | |||
| 8898 | /* Make sure possible SIGCHLD is stored in kernel's | ||
| 8899 | * pending signal mask before we call waitpid. | ||
| 8900 | * Or else we may race with SIGCHLD, lose it, | ||
| 8901 | * and get stuck in sigwaitinfo... | ||
| 8902 | */ | ||
| 8903 | sigfillset(&allsigs); | ||
| 8904 | sigprocmask(SIG_SETMASK, &allsigs, &oldset); | ||
| 8905 | |||
| 8906 | if (!sigisemptyset(&G.pending_set)) { | ||
| 8907 | /* Crap! we raced with some signal! */ | ||
| 8908 | // sig = 0; | ||
| 8909 | goto restore; | ||
| 8910 | } | ||
| 8911 | |||
| 8912 | checkjobs(NULL); /* waitpid(WNOHANG) inside */ | ||
| 8913 | if (errno == ECHILD) { | ||
| 8914 | sigprocmask(SIG_SETMASK, &oldset, NULL); | ||
| 8915 | break; | ||
| 8916 | } | ||
| 8917 | |||
| 8918 | /* Wait for SIGCHLD or any other signal */ | ||
| 8919 | //sig = sigwaitinfo(&allsigs, NULL); | ||
| 8920 | /* It is vitally important for sigsuspend that SIGCHLD has non-DFL handler! */ | ||
| 8921 | /* Note: sigsuspend invokes signal handler */ | ||
| 8922 | sigsuspend(&oldset); | ||
| 8923 | restore: | ||
| 8924 | sigprocmask(SIG_SETMASK, &oldset, NULL); | ||
| 8925 | |||
| 8926 | /* So, did we get a signal? */ | ||
| 8927 | //if (sig > 0) | ||
| 8928 | // raise(sig); /* run handler */ | ||
| 8929 | sig = check_and_run_traps(); | ||
| 8930 | if (sig /*&& sig != SIGCHLD - always true */) { | ||
| 8931 | /* see note 2 */ | ||
| 8932 | ret = 128 + sig; | ||
| 8877 | break; | 8933 | break; |
| 8878 | /* Wait for SIGCHLD or any other signal of interest */ | ||
| 8879 | /* sigtimedwait with infinite timeout: */ | ||
| 8880 | sig = sigwaitinfo(&G.blocked_set, NULL); | ||
| 8881 | if (sig > 0) { | ||
| 8882 | sig = check_and_run_traps(sig); | ||
| 8883 | if (sig && sig != SIGCHLD) { /* see note 2 */ | ||
| 8884 | ret = 128 + sig; | ||
| 8885 | break; | ||
| 8886 | } | ||
| 8887 | } | 8934 | } |
| 8935 | /* SIGCHLD, or no signal, or ignored one, such as SIGQUIT. Repeat */ | ||
| 8888 | } | 8936 | } |
| 8889 | sigdelset(&G.blocked_set, SIGCHLD); | ||
| 8890 | sigprocmask_set(&G.blocked_set); | ||
| 8891 | return ret; | 8937 | return ret; |
| 8892 | } | 8938 | } |
| 8893 | 8939 | ||
