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/hush.c | |
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 '')
-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 | ||