diff options
author | Ron Yorston <rmy@pobox.com> | 2012-03-22 15:21:20 +0000 |
---|---|---|
committer | Ron Yorston <rmy@pobox.com> | 2012-03-22 15:21:20 +0000 |
commit | 0d8b2c4a929ea9d3ac37499319fe0d8e7941a0c2 (patch) | |
tree | 6709ddd6071a9c238ba69233540bbcfe560c6a44 /shell | |
parent | 67758035a4fe040c6ac69b39d61bcd6bddd7b827 (diff) | |
parent | 56a3b82e9692a25ef9c9269e88feac0d579ce8e8 (diff) | |
download | busybox-w32-0d8b2c4a929ea9d3ac37499319fe0d8e7941a0c2.tar.gz busybox-w32-0d8b2c4a929ea9d3ac37499319fe0d8e7941a0c2.tar.bz2 busybox-w32-0d8b2c4a929ea9d3ac37499319fe0d8e7941a0c2.zip |
Merge commit '56a3b82e9692a25ef9c9269e88feac0d579ce8e8' into merge
Conflicts:
coreutils/ls.c
include/platform.h
libbb/bb_basename.c
Diffstat (limited to 'shell')
-rw-r--r-- | shell/ash.c | 8 | ||||
-rw-r--r-- | shell/hush.c | 717 | ||||
-rw-r--r-- | shell/hush_test/hush-misc/assignment4.right | 1 | ||||
-rwxr-xr-x | shell/hush_test/hush-misc/assignment4.tests | 3 | ||||
-rw-r--r-- | shell/hush_test/hush-misc/while3.right | 1 | ||||
-rwxr-xr-x | shell/hush_test/hush-misc/while3.tests | 4 | ||||
-rw-r--r-- | shell/hush_test/hush-trap/signal_read1.right | 1 | ||||
-rwxr-xr-x | shell/hush_test/hush-trap/signal_read1.tests | 5 | ||||
-rw-r--r-- | shell/hush_test/hush-trap/signal_read2.right | 2 | ||||
-rwxr-xr-x | shell/hush_test/hush-trap/signal_read2.tests | 7 | ||||
-rwxr-xr-x | shell/hush_test/run-all | 10 | ||||
-rw-r--r-- | shell/shell_common.c | 43 |
12 files changed, 537 insertions, 265 deletions
diff --git a/shell/ash.c b/shell/ash.c index eaaa71967..a809bf181 100644 --- a/shell/ash.c +++ b/shell/ash.c | |||
@@ -6276,7 +6276,7 @@ expbackq(union node *cmd, int quoted, int quotes) | |||
6276 | read: | 6276 | read: |
6277 | if (in.fd < 0) | 6277 | if (in.fd < 0) |
6278 | break; | 6278 | break; |
6279 | i = nonblock_safe_read(in.fd, buf, sizeof(buf)); | 6279 | i = nonblock_immune_read(in.fd, buf, sizeof(buf), /*loop_on_EINTR:*/ 1); |
6280 | TRACE(("expbackq: read returns %d\n", i)); | 6280 | TRACE(("expbackq: read returns %d\n", i)); |
6281 | if (i <= 0) | 6281 | if (i <= 0) |
6282 | break; | 6282 | break; |
@@ -10120,7 +10120,7 @@ preadfd(void) | |||
10120 | #if ENABLE_FEATURE_EDITING | 10120 | #if ENABLE_FEATURE_EDITING |
10121 | retry: | 10121 | retry: |
10122 | if (!iflag || g_parsefile->pf_fd != STDIN_FILENO) | 10122 | if (!iflag || g_parsefile->pf_fd != STDIN_FILENO) |
10123 | nr = nonblock_safe_read(g_parsefile->pf_fd, buf, IBUFSIZ - 1); | 10123 | nr = nonblock_immune_read(g_parsefile->pf_fd, buf, IBUFSIZ - 1, /*loop_on_EINTR:*/ 1); |
10124 | else { | 10124 | else { |
10125 | int timeout = -1; | 10125 | int timeout = -1; |
10126 | # if ENABLE_ASH_IDLE_TIMEOUT | 10126 | # if ENABLE_ASH_IDLE_TIMEOUT |
@@ -10166,10 +10166,10 @@ preadfd(void) | |||
10166 | } | 10166 | } |
10167 | } | 10167 | } |
10168 | #else | 10168 | #else |
10169 | nr = nonblock_safe_read(g_parsefile->pf_fd, buf, IBUFSIZ - 1); | 10169 | nr = nonblock_immune_read(g_parsefile->pf_fd, buf, IBUFSIZ - 1, /*loop_on_EINTR:*/ 1); |
10170 | #endif | 10170 | #endif |
10171 | 10171 | ||
10172 | #if 0 /* disabled: nonblock_safe_read() handles this problem */ | 10172 | #if 0 /* disabled: nonblock_immune_read() handles this problem */ |
10173 | if (nr < 0) { | 10173 | if (nr < 0) { |
10174 | if (parsefile->fd == 0 && errno == EWOULDBLOCK) { | 10174 | if (parsefile->fd == 0 && errno == EWOULDBLOCK) { |
10175 | int flags = fcntl(0, F_GETFL); | 10175 | int flags = fcntl(0, F_GETFL); |
diff --git a/shell/hush.c b/shell/hush.c index d3e957c2f..1082738a2 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 |
@@ -256,7 +260,7 @@ | |||
256 | * therefore we don't show them either. | 260 | * therefore we don't show them either. |
257 | */ | 261 | */ |
258 | //usage:#define hush_trivial_usage | 262 | //usage:#define hush_trivial_usage |
259 | //usage: "[-nx] [-c 'SCRIPT' [ARG0 [ARGS]] / FILE [ARGS]]" | 263 | //usage: "[-nxl] [-c 'SCRIPT' [ARG0 [ARGS]] / FILE [ARGS]]" |
260 | //usage:#define hush_full_usage "\n\n" | 264 | //usage:#define hush_full_usage "\n\n" |
261 | //usage: "Unix shell interpreter" | 265 | //usage: "Unix shell interpreter" |
262 | 266 | ||
@@ -445,6 +449,15 @@ enum { | |||
445 | /* Used for initialization: o_string foo = NULL_O_STRING; */ | 449 | /* Used for initialization: o_string foo = NULL_O_STRING; */ |
446 | #define NULL_O_STRING { NULL } | 450 | #define NULL_O_STRING { NULL } |
447 | 451 | ||
452 | #ifndef debug_printf_parse | ||
453 | static const char *const assignment_flag[] = { | ||
454 | "MAYBE_ASSIGNMENT", | ||
455 | "DEFINITELY_ASSIGNMENT", | ||
456 | "NOT_ASSIGNMENT", | ||
457 | "WORD_IS_KEYWORD", | ||
458 | }; | ||
459 | #endif | ||
460 | |||
448 | typedef struct in_str { | 461 | typedef struct in_str { |
449 | const char *p; | 462 | const char *p; |
450 | /* eof_flag=1: last char in ->p is really an EOF */ | 463 | /* eof_flag=1: last char in ->p is really an EOF */ |
@@ -764,7 +777,6 @@ struct globals { | |||
764 | smalluint last_exitcode; | 777 | smalluint last_exitcode; |
765 | /* are global_argv and global_argv[1..n] malloced? (note: not [0]) */ | 778 | /* are global_argv and global_argv[1..n] malloced? (note: not [0]) */ |
766 | smalluint global_args_malloced; | 779 | smalluint global_args_malloced; |
767 | smalluint inherited_set_is_saved; | ||
768 | /* how many non-NULL argv's we have. NB: $# + 1 */ | 780 | /* how many non-NULL argv's we have. NB: $# + 1 */ |
769 | int global_argc; | 781 | int global_argc; |
770 | char **global_argv; | 782 | char **global_argv; |
@@ -792,15 +804,27 @@ struct globals { | |||
792 | unsigned handled_SIGCHLD; | 804 | unsigned handled_SIGCHLD; |
793 | smallint we_have_children; | 805 | smallint we_have_children; |
794 | #endif | 806 | #endif |
795 | /* which signals have non-DFL handler (even with no traps set)? */ | 807 | /* Which signals have non-DFL handler (even with no traps set)? |
796 | unsigned non_DFL_mask; | 808 | * Set at the start to: |
809 | * (SIGQUIT + maybe SPECIAL_INTERACTIVE_SIGS + maybe SPECIAL_JOBSTOP_SIGS) | ||
810 | * SPECIAL_INTERACTIVE_SIGS are cleared after fork. | ||
811 | * The rest is cleared right before execv syscalls. | ||
812 | * Other than these two times, never modified. | ||
813 | */ | ||
814 | unsigned special_sig_mask; | ||
815 | #if ENABLE_HUSH_JOB | ||
816 | unsigned fatal_sig_mask; | ||
817 | # define G_fatal_sig_mask G.fatal_sig_mask | ||
818 | #else | ||
819 | # define G_fatal_sig_mask 0 | ||
820 | #endif | ||
797 | char **traps; /* char *traps[NSIG] */ | 821 | char **traps; /* char *traps[NSIG] */ |
798 | sigset_t blocked_set; | 822 | sigset_t pending_set; |
799 | sigset_t inherited_set; | ||
800 | #if HUSH_DEBUG | 823 | #if HUSH_DEBUG |
801 | unsigned long memleak_value; | 824 | unsigned long memleak_value; |
802 | int debug_indent; | 825 | int debug_indent; |
803 | #endif | 826 | #endif |
827 | struct sigaction sa; | ||
804 | char user_input_buf[ENABLE_FEATURE_EDITING ? CONFIG_FEATURE_EDITING_MAX_LEN : 2]; | 828 | char user_input_buf[ENABLE_FEATURE_EDITING ? CONFIG_FEATURE_EDITING_MAX_LEN : 2]; |
805 | }; | 829 | }; |
806 | #define G (*ptr_to_globals) | 830 | #define G (*ptr_to_globals) |
@@ -809,6 +833,9 @@ struct globals { | |||
809 | * is global, thus "G." prefix is a useful hint */ | 833 | * is global, thus "G." prefix is a useful hint */ |
810 | #define INIT_G() do { \ | 834 | #define INIT_G() do { \ |
811 | SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \ | 835 | SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \ |
836 | /* memset(&G.sa, 0, sizeof(G.sa)); */ \ | ||
837 | sigfillset(&G.sa.sa_mask); \ | ||
838 | G.sa.sa_flags = SA_RESTART; \ | ||
812 | } while (0) | 839 | } while (0) |
813 | 840 | ||
814 | 841 | ||
@@ -1319,12 +1346,14 @@ static void restore_G_args(save_arg_t *sv, char **argv) | |||
1319 | * "echo $$; sleep 5 & wait; ls -l" + "kill -INT <pid>" | 1346 | * "echo $$; sleep 5 & wait; ls -l" + "kill -INT <pid>" |
1320 | * Example 3: this does not wait 5 sec, but executes ls: | 1347 | * Example 3: this does not wait 5 sec, but executes ls: |
1321 | * "sleep 5; ls -l" + press ^C | 1348 | * "sleep 5; ls -l" + press ^C |
1349 | * Example 4: this does not wait and does not execute ls: | ||
1350 | * "sleep 5 & wait; ls -l" + press ^C | ||
1322 | * | 1351 | * |
1323 | * (What happens to signals which are IGN on shell start?) | 1352 | * (What happens to signals which are IGN on shell start?) |
1324 | * (What happens with signal mask on shell start?) | 1353 | * (What happens with signal mask on shell start?) |
1325 | * | 1354 | * |
1326 | * Implementation in hush | 1355 | * Old implementation |
1327 | * ====================== | 1356 | * ================== |
1328 | * We use in-kernel pending signal mask to determine which signals were sent. | 1357 | * We use in-kernel pending signal mask to determine which signals were sent. |
1329 | * We block all signals which we don't want to take action immediately, | 1358 | * We block all signals which we don't want to take action immediately, |
1330 | * i.e. we block all signals which need to have special handling as described | 1359 | * i.e. we block all signals which need to have special handling as described |
@@ -1332,11 +1361,11 @@ static void restore_G_args(save_arg_t *sv, char **argv) | |||
1332 | * After each pipe execution, we extract any pending signals via sigtimedwait() | 1361 | * After each pipe execution, we extract any pending signals via sigtimedwait() |
1333 | * and act on them. | 1362 | * and act on them. |
1334 | * | 1363 | * |
1335 | * unsigned non_DFL_mask: a mask of such "special" signals | 1364 | * unsigned special_sig_mask: a mask of such "special" signals |
1336 | * sigset_t blocked_set: current blocked signal set | 1365 | * sigset_t blocked_set: current blocked signal set |
1337 | * | 1366 | * |
1338 | * "trap - SIGxxx": | 1367 | * "trap - SIGxxx": |
1339 | * clear bit in blocked_set unless it is also in non_DFL_mask | 1368 | * clear bit in blocked_set unless it is also in special_sig_mask |
1340 | * "trap 'cmd' SIGxxx": | 1369 | * "trap 'cmd' SIGxxx": |
1341 | * set bit in blocked_set (even if 'cmd' is '') | 1370 | * set bit in blocked_set (even if 'cmd' is '') |
1342 | * after [v]fork, if we plan to be a shell: | 1371 | * after [v]fork, if we plan to be a shell: |
@@ -1355,6 +1384,49 @@ static void restore_G_args(save_arg_t *sv, char **argv) | |||
1355 | * Standard says "When a subshell is entered, traps that are not being ignored | 1384 | * Standard says "When a subshell is entered, traps that are not being ignored |
1356 | * are set to the default actions". bash interprets it so that traps which | 1385 | * are set to the default actions". bash interprets it so that traps which |
1357 | * are set to '' (ignore) are NOT reset to defaults. We do the same. | 1386 | * are set to '' (ignore) are NOT reset to defaults. We do the same. |
1387 | * | ||
1388 | * Problem: the above approach makes it unwieldy to catch signals while | ||
1389 | * we are in read builtin, of while we read commands from stdin: | ||
1390 | * masked signals are not visible! | ||
1391 | * | ||
1392 | * New implementation | ||
1393 | * ================== | ||
1394 | * We record each signal we are interested in by installing signal handler | ||
1395 | * for them - a bit like emulating kernel pending signal mask in userspace. | ||
1396 | * We are interested in: signals which need to have special handling | ||
1397 | * as described above, and all signals which have traps set. | ||
1398 | * Signals are rocorded in pending_set. | ||
1399 | * After each pipe execution, we extract any pending signals | ||
1400 | * and act on them. | ||
1401 | * | ||
1402 | * unsigned special_sig_mask: a mask of shell-special signals. | ||
1403 | * unsigned fatal_sig_mask: a mask of signals on which we restore tty pgrp. | ||
1404 | * char *traps[sig] if trap for sig is set (even if it's ''). | ||
1405 | * sigset_t pending_set: set of sigs we received. | ||
1406 | * | ||
1407 | * "trap - SIGxxx": | ||
1408 | * if sig is in special_sig_mask, set handler back to: | ||
1409 | * record_pending_signo, or to IGN if it's a tty stop signal | ||
1410 | * if sig is in fatal_sig_mask, set handler back to sigexit. | ||
1411 | * else: set handler back to SIG_DFL | ||
1412 | * "trap 'cmd' SIGxxx": | ||
1413 | * set handler to record_pending_signo. | ||
1414 | * "trap '' SIGxxx": | ||
1415 | * set handler to SIG_IGN. | ||
1416 | * after [v]fork, if we plan to be a shell: | ||
1417 | * set signals with special interactive handling to SIG_DFL | ||
1418 | * (because child shell is not interactive), | ||
1419 | * unset all traps except '' (note: regardless of child shell's type - {}, (), etc) | ||
1420 | * after [v]fork, if we plan to exec: | ||
1421 | * POSIX says fork clears pending signal mask in child - no need to clear it. | ||
1422 | * | ||
1423 | * To make wait builtin interruptible, we handle SIGCHLD as special signal, | ||
1424 | * otherwise (if we leave it SIG_DFL) sigsuspend in wait builtin will not wake up on it. | ||
1425 | * | ||
1426 | * Note (compat): | ||
1427 | * Standard says "When a subshell is entered, traps that are not being ignored | ||
1428 | * are set to the default actions". bash interprets it so that traps which | ||
1429 | * are set to '' (ignore) are NOT reset to defaults. We do the same. | ||
1358 | */ | 1430 | */ |
1359 | enum { | 1431 | enum { |
1360 | SPECIAL_INTERACTIVE_SIGS = 0 | 1432 | SPECIAL_INTERACTIVE_SIGS = 0 |
@@ -1362,21 +1434,43 @@ enum { | |||
1362 | | (1 << SIGINT) | 1434 | | (1 << SIGINT) |
1363 | | (1 << SIGHUP) | 1435 | | (1 << SIGHUP) |
1364 | , | 1436 | , |
1365 | SPECIAL_JOB_SIGS = 0 | 1437 | SPECIAL_JOBSTOP_SIGS = 0 |
1366 | #if ENABLE_HUSH_JOB | 1438 | #if ENABLE_HUSH_JOB |
1367 | | (1 << SIGTTIN) | 1439 | | (1 << SIGTTIN) |
1368 | | (1 << SIGTTOU) | 1440 | | (1 << SIGTTOU) |
1369 | | (1 << SIGTSTP) | 1441 | | (1 << SIGTSTP) |
1370 | #endif | 1442 | #endif |
1443 | , | ||
1371 | }; | 1444 | }; |
1372 | 1445 | ||
1373 | #if ENABLE_HUSH_FAST | 1446 | static void record_pending_signo(int sig) |
1374 | static void SIGCHLD_handler(int sig UNUSED_PARAM) | ||
1375 | { | 1447 | { |
1376 | G.count_SIGCHLD++; | 1448 | sigaddset(&G.pending_set, sig); |
1449 | #if ENABLE_HUSH_FAST | ||
1450 | if (sig == SIGCHLD) { | ||
1451 | G.count_SIGCHLD++; | ||
1377 | //bb_error_msg("[%d] SIGCHLD_handler: G.count_SIGCHLD:%d G.handled_SIGCHLD:%d", getpid(), G.count_SIGCHLD, G.handled_SIGCHLD); | 1452 | //bb_error_msg("[%d] SIGCHLD_handler: G.count_SIGCHLD:%d G.handled_SIGCHLD:%d", getpid(), G.count_SIGCHLD, G.handled_SIGCHLD); |
1378 | } | 1453 | } |
1379 | #endif | 1454 | #endif |
1455 | } | ||
1456 | |||
1457 | static sighandler_t install_sighandler(int sig, sighandler_t handler) | ||
1458 | { | ||
1459 | struct sigaction old_sa; | ||
1460 | |||
1461 | /* We could use signal() to install handlers... almost: | ||
1462 | * except that we need to mask ALL signals while handlers run. | ||
1463 | * I saw signal nesting in strace, race window isn't small. | ||
1464 | * SA_RESTART is also needed, but in Linux, signal() | ||
1465 | * sets SA_RESTART too. | ||
1466 | */ | ||
1467 | /* memset(&G.sa, 0, sizeof(G.sa)); - already done */ | ||
1468 | /* sigfillset(&G.sa.sa_mask); - already done */ | ||
1469 | /* G.sa.sa_flags = SA_RESTART; - already done */ | ||
1470 | G.sa.sa_handler = handler; | ||
1471 | sigaction(sig, &G.sa, &old_sa); | ||
1472 | return old_sa.sa_handler; | ||
1473 | } | ||
1380 | 1474 | ||
1381 | #if ENABLE_HUSH_JOB | 1475 | #if ENABLE_HUSH_JOB |
1382 | 1476 | ||
@@ -1393,13 +1487,15 @@ static void SIGCHLD_handler(int sig UNUSED_PARAM) | |||
1393 | static void sigexit(int sig) NORETURN; | 1487 | static void sigexit(int sig) NORETURN; |
1394 | static void sigexit(int sig) | 1488 | static void sigexit(int sig) |
1395 | { | 1489 | { |
1396 | /* Disable all signals: job control, SIGPIPE, etc. */ | ||
1397 | sigprocmask_allsigs(SIG_BLOCK); | ||
1398 | |||
1399 | /* Careful: we can end up here after [v]fork. Do not restore | 1490 | /* Careful: we can end up here after [v]fork. Do not restore |
1400 | * tty pgrp then, only top-level shell process does that */ | 1491 | * tty pgrp then, only top-level shell process does that */ |
1401 | if (G_saved_tty_pgrp && getpid() == G.root_pid) | 1492 | if (G_saved_tty_pgrp && getpid() == G.root_pid) { |
1493 | /* Disable all signals: job control, SIGPIPE, etc. | ||
1494 | * Mostly paranoid measure, to prevent infinite SIGTTOU. | ||
1495 | */ | ||
1496 | sigprocmask_allsigs(SIG_BLOCK); | ||
1402 | tcsetpgrp(G_interactive_fd, G_saved_tty_pgrp); | 1497 | tcsetpgrp(G_interactive_fd, G_saved_tty_pgrp); |
1498 | } | ||
1403 | 1499 | ||
1404 | /* Not a signal, just exit */ | 1500 | /* Not a signal, just exit */ |
1405 | if (sig <= 0) | 1501 | if (sig <= 0) |
@@ -1414,15 +1510,39 @@ static void sigexit(int sig) | |||
1414 | 1510 | ||
1415 | #endif | 1511 | #endif |
1416 | 1512 | ||
1513 | static sighandler_t pick_sighandler(unsigned sig) | ||
1514 | { | ||
1515 | sighandler_t handler = SIG_DFL; | ||
1516 | if (sig < sizeof(unsigned)*8) { | ||
1517 | unsigned sigmask = (1 << sig); | ||
1518 | |||
1519 | #if ENABLE_HUSH_JOB | ||
1520 | /* is sig fatal? */ | ||
1521 | if (G_fatal_sig_mask & sigmask) | ||
1522 | handler = sigexit; | ||
1523 | else | ||
1524 | #endif | ||
1525 | /* sig has special handling? */ | ||
1526 | if (G.special_sig_mask & sigmask) { | ||
1527 | handler = record_pending_signo; | ||
1528 | /* TTIN/TTOU/TSTP can't be set to record_pending_signo | ||
1529 | * in order to ignore them: they will be raised | ||
1530 | * in an endless loop when we try to do some | ||
1531 | * terminal ioctls! We do have to _ignore_ these. | ||
1532 | */ | ||
1533 | if (SPECIAL_JOBSTOP_SIGS & sigmask) | ||
1534 | handler = SIG_IGN; | ||
1535 | } | ||
1536 | } | ||
1537 | return handler; | ||
1538 | } | ||
1539 | |||
1417 | /* Restores tty foreground process group, and exits. */ | 1540 | /* Restores tty foreground process group, and exits. */ |
1418 | static void hush_exit(int exitcode) NORETURN; | 1541 | static void hush_exit(int exitcode) NORETURN; |
1419 | static void hush_exit(int exitcode) | 1542 | static void hush_exit(int exitcode) |
1420 | { | 1543 | { |
1421 | fflush_all(); | 1544 | fflush_all(); |
1422 | if (G.exiting <= 0 && G.traps && G.traps[0] && G.traps[0][0]) { | 1545 | if (G.exiting <= 0 && G.traps && G.traps[0] && G.traps[0][0]) { |
1423 | /* Prevent recursion: | ||
1424 | * trap "echo Hi; exit" EXIT; exit | ||
1425 | */ | ||
1426 | char *argv[3]; | 1546 | char *argv[3]; |
1427 | /* argv[0] is unused */ | 1547 | /* argv[0] is unused */ |
1428 | argv[1] = G.traps[0]; | 1548 | argv[1] = G.traps[0]; |
@@ -1459,28 +1579,30 @@ static void hush_exit(int exitcode) | |||
1459 | } | 1579 | } |
1460 | 1580 | ||
1461 | 1581 | ||
1462 | static int check_and_run_traps(int sig) | 1582 | //TODO: return a mask of ALL handled sigs? |
1583 | static int check_and_run_traps(void) | ||
1463 | { | 1584 | { |
1464 | /* I want it in rodata, not in bss. | ||
1465 | * gcc 4.2.1 puts it in rodata only if it has { 0, 0 } | ||
1466 | * initializer. But other compilers may still use bss. | ||
1467 | * TODO: find more portable solution. | ||
1468 | */ | ||
1469 | static const struct timespec zero_timespec = { 0, 0 }; | ||
1470 | smalluint save_rcode; | ||
1471 | int last_sig = 0; | 1585 | int last_sig = 0; |
1472 | 1586 | ||
1473 | if (sig) | ||
1474 | goto jump_in; | ||
1475 | while (1) { | 1587 | while (1) { |
1476 | sig = sigtimedwait(&G.blocked_set, NULL, &zero_timespec); | 1588 | int sig; |
1477 | if (sig <= 0) | 1589 | |
1590 | if (sigisemptyset(&G.pending_set)) | ||
1478 | break; | 1591 | break; |
1479 | jump_in: | 1592 | sig = 0; |
1480 | last_sig = sig; | 1593 | do { |
1594 | sig++; | ||
1595 | if (sigismember(&G.pending_set, sig)) { | ||
1596 | sigdelset(&G.pending_set, sig); | ||
1597 | goto got_sig; | ||
1598 | } | ||
1599 | } while (sig < NSIG); | ||
1600 | break; | ||
1601 | got_sig: | ||
1481 | if (G.traps && G.traps[sig]) { | 1602 | if (G.traps && G.traps[sig]) { |
1482 | if (G.traps[sig][0]) { | 1603 | if (G.traps[sig][0]) { |
1483 | /* We have user-defined handler */ | 1604 | /* We have user-defined handler */ |
1605 | smalluint save_rcode; | ||
1484 | char *argv[3]; | 1606 | char *argv[3]; |
1485 | /* argv[0] is unused */ | 1607 | /* argv[0] is unused */ |
1486 | argv[1] = G.traps[sig]; | 1608 | argv[1] = G.traps[sig]; |
@@ -1488,21 +1610,17 @@ static int check_and_run_traps(int sig) | |||
1488 | save_rcode = G.last_exitcode; | 1610 | save_rcode = G.last_exitcode; |
1489 | builtin_eval(argv); | 1611 | builtin_eval(argv); |
1490 | G.last_exitcode = save_rcode; | 1612 | G.last_exitcode = save_rcode; |
1613 | last_sig = sig; | ||
1491 | } /* else: "" trap, ignoring signal */ | 1614 | } /* else: "" trap, ignoring signal */ |
1492 | continue; | 1615 | continue; |
1493 | } | 1616 | } |
1494 | /* not a trap: special action */ | 1617 | /* not a trap: special action */ |
1495 | switch (sig) { | 1618 | switch (sig) { |
1496 | #if ENABLE_HUSH_FAST | ||
1497 | case SIGCHLD: | ||
1498 | G.count_SIGCHLD++; | ||
1499 | //bb_error_msg("[%d] check_and_run_traps: G.count_SIGCHLD:%d G.handled_SIGCHLD:%d", getpid(), G.count_SIGCHLD, G.handled_SIGCHLD); | ||
1500 | break; | ||
1501 | #endif | ||
1502 | case SIGINT: | 1619 | case SIGINT: |
1503 | /* Builtin was ^C'ed, make it look prettier: */ | 1620 | /* Builtin was ^C'ed, make it look prettier: */ |
1504 | bb_putchar('\n'); | 1621 | bb_putchar('\n'); |
1505 | G.flag_SIGINT = 1; | 1622 | G.flag_SIGINT = 1; |
1623 | last_sig = sig; | ||
1506 | break; | 1624 | break; |
1507 | #if ENABLE_HUSH_JOB | 1625 | #if ENABLE_HUSH_JOB |
1508 | case SIGHUP: { | 1626 | case SIGHUP: { |
@@ -1519,8 +1637,23 @@ static int check_and_run_traps(int sig) | |||
1519 | sigexit(SIGHUP); | 1637 | sigexit(SIGHUP); |
1520 | } | 1638 | } |
1521 | #endif | 1639 | #endif |
1640 | #if ENABLE_HUSH_FAST | ||
1641 | case SIGCHLD: | ||
1642 | G.count_SIGCHLD++; | ||
1643 | //bb_error_msg("[%d] check_and_run_traps: G.count_SIGCHLD:%d G.handled_SIGCHLD:%d", getpid(), G.count_SIGCHLD, G.handled_SIGCHLD); | ||
1644 | /* Note: | ||
1645 | * We dont do 'last_sig = sig' here -> NOT returning this sig. | ||
1646 | * This simplifies wait builtin a bit. | ||
1647 | */ | ||
1648 | break; | ||
1649 | #endif | ||
1522 | default: /* ignored: */ | 1650 | default: /* ignored: */ |
1523 | /* SIGTERM, SIGQUIT, SIGTTIN, SIGTTOU, SIGTSTP */ | 1651 | /* SIGTERM, SIGQUIT, SIGTTIN, SIGTTOU, SIGTSTP */ |
1652 | /* Note: | ||
1653 | * We dont do 'last_sig = sig' here -> NOT returning this sig. | ||
1654 | * Example: wait is not interrupted by TERM | ||
1655 | * in interactive shell, because TERM is ignored. | ||
1656 | */ | ||
1524 | break; | 1657 | break; |
1525 | } | 1658 | } |
1526 | } | 1659 | } |
@@ -1911,7 +2044,7 @@ static void get_user_input(struct in_str *i) | |||
1911 | * only after <Enter>. (^C will work) */ | 2044 | * only after <Enter>. (^C will work) */ |
1912 | r = read_line_input(G.line_input_state, prompt_str, G.user_input_buf, CONFIG_FEATURE_EDITING_MAX_LEN-1, /*timeout*/ -1); | 2045 | r = read_line_input(G.line_input_state, prompt_str, G.user_input_buf, CONFIG_FEATURE_EDITING_MAX_LEN-1, /*timeout*/ -1); |
1913 | /* catch *SIGINT* etc (^C is handled by read_line_input) */ | 2046 | /* catch *SIGINT* etc (^C is handled by read_line_input) */ |
1914 | check_and_run_traps(0); | 2047 | check_and_run_traps(); |
1915 | } while (r == 0 || G.flag_SIGINT); /* repeat if ^C or SIGINT */ | 2048 | } while (r == 0 || G.flag_SIGINT); /* repeat if ^C or SIGINT */ |
1916 | i->eof_flag = (r < 0); | 2049 | i->eof_flag = (r < 0); |
1917 | if (i->eof_flag) { /* EOF/error detected */ | 2050 | if (i->eof_flag) { /* EOF/error detected */ |
@@ -1921,11 +2054,18 @@ static void get_user_input(struct in_str *i) | |||
1921 | # else | 2054 | # else |
1922 | do { | 2055 | do { |
1923 | G.flag_SIGINT = 0; | 2056 | G.flag_SIGINT = 0; |
1924 | fputs(prompt_str, stdout); | 2057 | if (i->last_char == '\0' || i->last_char == '\n') { |
2058 | /* Why check_and_run_traps here? Try this interactively: | ||
2059 | * $ trap 'echo INT' INT; (sleep 2; kill -INT $$) & | ||
2060 | * $ <[enter], repeatedly...> | ||
2061 | * Without check_and_run_traps, handler never runs. | ||
2062 | */ | ||
2063 | check_and_run_traps(); | ||
2064 | fputs(prompt_str, stdout); | ||
2065 | } | ||
1925 | fflush_all(); | 2066 | fflush_all(); |
1926 | G.user_input_buf[0] = r = fgetc(i->file); | 2067 | G.user_input_buf[0] = r = fgetc(i->file); |
1927 | /*G.user_input_buf[1] = '\0'; - already is and never changed */ | 2068 | /*G.user_input_buf[1] = '\0'; - already is and never changed */ |
1928 | //do we need check_and_run_traps(0)? (maybe only if stdin) | ||
1929 | } while (G.flag_SIGINT); | 2069 | } while (G.flag_SIGINT); |
1930 | i->eof_flag = (r == EOF); | 2070 | i->eof_flag = (r == EOF); |
1931 | # endif | 2071 | # endif |
@@ -2902,24 +3042,24 @@ static const struct reserved_combo* match_reserved_word(o_string *word) | |||
2902 | */ | 3042 | */ |
2903 | static const struct reserved_combo reserved_list[] = { | 3043 | static const struct reserved_combo reserved_list[] = { |
2904 | # if ENABLE_HUSH_IF | 3044 | # if ENABLE_HUSH_IF |
2905 | { "!", RES_NONE, NOT_ASSIGNMENT , 0 }, | 3045 | { "!", RES_NONE, NOT_ASSIGNMENT , 0 }, |
2906 | { "if", RES_IF, WORD_IS_KEYWORD, FLAG_THEN | FLAG_START }, | 3046 | { "if", RES_IF, MAYBE_ASSIGNMENT, FLAG_THEN | FLAG_START }, |
2907 | { "then", RES_THEN, WORD_IS_KEYWORD, FLAG_ELIF | FLAG_ELSE | FLAG_FI }, | 3047 | { "then", RES_THEN, MAYBE_ASSIGNMENT, FLAG_ELIF | FLAG_ELSE | FLAG_FI }, |
2908 | { "elif", RES_ELIF, WORD_IS_KEYWORD, FLAG_THEN }, | 3048 | { "elif", RES_ELIF, MAYBE_ASSIGNMENT, FLAG_THEN }, |
2909 | { "else", RES_ELSE, WORD_IS_KEYWORD, FLAG_FI }, | 3049 | { "else", RES_ELSE, MAYBE_ASSIGNMENT, FLAG_FI }, |
2910 | { "fi", RES_FI, NOT_ASSIGNMENT , FLAG_END }, | 3050 | { "fi", RES_FI, NOT_ASSIGNMENT , FLAG_END }, |
2911 | # endif | 3051 | # endif |
2912 | # if ENABLE_HUSH_LOOPS | 3052 | # if ENABLE_HUSH_LOOPS |
2913 | { "for", RES_FOR, NOT_ASSIGNMENT , FLAG_IN | FLAG_DO | FLAG_START }, | 3053 | { "for", RES_FOR, NOT_ASSIGNMENT , FLAG_IN | FLAG_DO | FLAG_START }, |
2914 | { "while", RES_WHILE, WORD_IS_KEYWORD, FLAG_DO | FLAG_START }, | 3054 | { "while", RES_WHILE, MAYBE_ASSIGNMENT, FLAG_DO | FLAG_START }, |
2915 | { "until", RES_UNTIL, WORD_IS_KEYWORD, FLAG_DO | FLAG_START }, | 3055 | { "until", RES_UNTIL, MAYBE_ASSIGNMENT, FLAG_DO | FLAG_START }, |
2916 | { "in", RES_IN, NOT_ASSIGNMENT , FLAG_DO }, | 3056 | { "in", RES_IN, NOT_ASSIGNMENT , FLAG_DO }, |
2917 | { "do", RES_DO, WORD_IS_KEYWORD, FLAG_DONE }, | 3057 | { "do", RES_DO, MAYBE_ASSIGNMENT, FLAG_DONE }, |
2918 | { "done", RES_DONE, NOT_ASSIGNMENT , FLAG_END }, | 3058 | { "done", RES_DONE, NOT_ASSIGNMENT , FLAG_END }, |
2919 | # endif | 3059 | # endif |
2920 | # if ENABLE_HUSH_CASE | 3060 | # if ENABLE_HUSH_CASE |
2921 | { "case", RES_CASE, NOT_ASSIGNMENT , FLAG_MATCH | FLAG_START }, | 3061 | { "case", RES_CASE, NOT_ASSIGNMENT , FLAG_MATCH | FLAG_START }, |
2922 | { "esac", RES_ESAC, NOT_ASSIGNMENT , FLAG_END }, | 3062 | { "esac", RES_ESAC, NOT_ASSIGNMENT , FLAG_END }, |
2923 | # endif | 3063 | # endif |
2924 | }; | 3064 | }; |
2925 | const struct reserved_combo *r; | 3065 | const struct reserved_combo *r; |
@@ -2985,6 +3125,7 @@ static int reserved_word(o_string *word, struct parse_context *ctx) | |||
2985 | ctx->ctx_res_w = r->res; | 3125 | ctx->ctx_res_w = r->res; |
2986 | ctx->old_flag = r->flag; | 3126 | ctx->old_flag = r->flag; |
2987 | word->o_assignment = r->assignment_flag; | 3127 | word->o_assignment = r->assignment_flag; |
3128 | debug_printf_parse("word->o_assignment='%s'\n", assignment_flag[word->o_assignment]); | ||
2988 | 3129 | ||
2989 | if (ctx->old_flag & FLAG_END) { | 3130 | if (ctx->old_flag & FLAG_END) { |
2990 | struct parse_context *old; | 3131 | struct parse_context *old; |
@@ -3051,18 +3192,6 @@ static int done_word(o_string *word, struct parse_context *ctx) | |||
3051 | debug_printf_parse("word stored in rd_filename: '%s'\n", word->data); | 3192 | debug_printf_parse("word stored in rd_filename: '%s'\n", word->data); |
3052 | ctx->pending_redirect = NULL; | 3193 | ctx->pending_redirect = NULL; |
3053 | } else { | 3194 | } else { |
3054 | /* If this word wasn't an assignment, next ones definitely | ||
3055 | * can't be assignments. Even if they look like ones. */ | ||
3056 | if (word->o_assignment != DEFINITELY_ASSIGNMENT | ||
3057 | && word->o_assignment != WORD_IS_KEYWORD | ||
3058 | ) { | ||
3059 | word->o_assignment = NOT_ASSIGNMENT; | ||
3060 | } else { | ||
3061 | if (word->o_assignment == DEFINITELY_ASSIGNMENT) | ||
3062 | command->assignment_cnt++; | ||
3063 | word->o_assignment = MAYBE_ASSIGNMENT; | ||
3064 | } | ||
3065 | |||
3066 | #if HAS_KEYWORDS | 3195 | #if HAS_KEYWORDS |
3067 | # if ENABLE_HUSH_CASE | 3196 | # if ENABLE_HUSH_CASE |
3068 | if (ctx->ctx_dsemicolon | 3197 | if (ctx->ctx_dsemicolon |
@@ -3082,8 +3211,9 @@ static int done_word(o_string *word, struct parse_context *ctx) | |||
3082 | && ctx->ctx_res_w != RES_CASE | 3211 | && ctx->ctx_res_w != RES_CASE |
3083 | # endif | 3212 | # endif |
3084 | ) { | 3213 | ) { |
3085 | debug_printf_parse("checking '%s' for reserved-ness\n", word->data); | 3214 | int reserved = reserved_word(word, ctx); |
3086 | if (reserved_word(word, ctx)) { | 3215 | debug_printf_parse("checking for reserved-ness: %d\n", reserved); |
3216 | if (reserved) { | ||
3087 | o_reset_to_empty_unquoted(word); | 3217 | o_reset_to_empty_unquoted(word); |
3088 | debug_printf_parse("done_word return %d\n", | 3218 | debug_printf_parse("done_word return %d\n", |
3089 | (ctx->ctx_res_w == RES_SNTX)); | 3219 | (ctx->ctx_res_w == RES_SNTX)); |
@@ -3104,6 +3234,23 @@ static int done_word(o_string *word, struct parse_context *ctx) | |||
3104 | "groups and arglists don't mix\n"); | 3234 | "groups and arglists don't mix\n"); |
3105 | return 1; | 3235 | return 1; |
3106 | } | 3236 | } |
3237 | |||
3238 | /* If this word wasn't an assignment, next ones definitely | ||
3239 | * can't be assignments. Even if they look like ones. */ | ||
3240 | if (word->o_assignment != DEFINITELY_ASSIGNMENT | ||
3241 | && word->o_assignment != WORD_IS_KEYWORD | ||
3242 | ) { | ||
3243 | word->o_assignment = NOT_ASSIGNMENT; | ||
3244 | } else { | ||
3245 | if (word->o_assignment == DEFINITELY_ASSIGNMENT) { | ||
3246 | command->assignment_cnt++; | ||
3247 | debug_printf_parse("++assignment_cnt=%d\n", command->assignment_cnt); | ||
3248 | } | ||
3249 | debug_printf_parse("word->o_assignment was:'%s'\n", assignment_flag[word->o_assignment]); | ||
3250 | word->o_assignment = MAYBE_ASSIGNMENT; | ||
3251 | } | ||
3252 | debug_printf_parse("word->o_assignment='%s'\n", assignment_flag[word->o_assignment]); | ||
3253 | |||
3107 | if (word->has_quoted_part | 3254 | if (word->has_quoted_part |
3108 | /* optimization: and if it's ("" or '') or ($v... or `cmd`...): */ | 3255 | /* optimization: and if it's ("" or '') or ($v... or `cmd`...): */ |
3109 | && (word->data[0] == '\0' || word->data[0] == SPECIAL_VAR_SYMBOL) | 3256 | && (word->data[0] == '\0' || word->data[0] == SPECIAL_VAR_SYMBOL) |
@@ -3322,6 +3469,7 @@ static char *fetch_till_str(o_string *as_string, | |||
3322 | int ch; | 3469 | int ch; |
3323 | 3470 | ||
3324 | goto jump_in; | 3471 | goto jump_in; |
3472 | |||
3325 | while (1) { | 3473 | while (1) { |
3326 | ch = i_getch(input); | 3474 | ch = i_getch(input); |
3327 | if (ch != EOF) | 3475 | if (ch != EOF) |
@@ -4127,6 +4275,7 @@ static struct pipe *parse_stream(char **pstring, | |||
4127 | && is_well_formed_var_name(dest.data, '=') | 4275 | && is_well_formed_var_name(dest.data, '=') |
4128 | ) { | 4276 | ) { |
4129 | dest.o_assignment = DEFINITELY_ASSIGNMENT; | 4277 | dest.o_assignment = DEFINITELY_ASSIGNMENT; |
4278 | debug_printf_parse("dest.o_assignment='%s'\n", assignment_flag[dest.o_assignment]); | ||
4130 | } | 4279 | } |
4131 | continue; | 4280 | continue; |
4132 | } | 4281 | } |
@@ -4176,6 +4325,7 @@ static struct pipe *parse_stream(char **pstring, | |||
4176 | heredoc_cnt = 0; | 4325 | heredoc_cnt = 0; |
4177 | } | 4326 | } |
4178 | dest.o_assignment = MAYBE_ASSIGNMENT; | 4327 | dest.o_assignment = MAYBE_ASSIGNMENT; |
4328 | debug_printf_parse("dest.o_assignment='%s'\n", assignment_flag[dest.o_assignment]); | ||
4179 | ch = ';'; | 4329 | ch = ';'; |
4180 | /* note: if (is_blank) continue; | 4330 | /* note: if (is_blank) continue; |
4181 | * will still trigger for us */ | 4331 | * will still trigger for us */ |
@@ -4225,6 +4375,7 @@ static struct pipe *parse_stream(char **pstring, | |||
4225 | } | 4375 | } |
4226 | done_pipe(&ctx, PIPE_SEQ); | 4376 | done_pipe(&ctx, PIPE_SEQ); |
4227 | dest.o_assignment = MAYBE_ASSIGNMENT; | 4377 | dest.o_assignment = MAYBE_ASSIGNMENT; |
4378 | debug_printf_parse("dest.o_assignment='%s'\n", assignment_flag[dest.o_assignment]); | ||
4228 | /* Do we sit outside of any if's, loops or case's? */ | 4379 | /* Do we sit outside of any if's, loops or case's? */ |
4229 | if (!HAS_KEYWORDS | 4380 | if (!HAS_KEYWORDS |
4230 | IF_HAS_KEYWORDS(|| (ctx.ctx_res_w == RES_NONE && ctx.old_flag == 0)) | 4381 | IF_HAS_KEYWORDS(|| (ctx.ctx_res_w == RES_NONE && ctx.old_flag == 0)) |
@@ -4331,6 +4482,7 @@ static struct pipe *parse_stream(char **pstring, | |||
4331 | /* ch is a special char and thus this word | 4482 | /* ch is a special char and thus this word |
4332 | * cannot be an assignment */ | 4483 | * cannot be an assignment */ |
4333 | dest.o_assignment = NOT_ASSIGNMENT; | 4484 | dest.o_assignment = NOT_ASSIGNMENT; |
4485 | debug_printf_parse("dest.o_assignment='%s'\n", assignment_flag[dest.o_assignment]); | ||
4334 | } | 4486 | } |
4335 | 4487 | ||
4336 | /* Note: nommu_addchr(&ctx.as_string, ch) is already done */ | 4488 | /* Note: nommu_addchr(&ctx.as_string, ch) is already done */ |
@@ -4385,11 +4537,11 @@ static struct pipe *parse_stream(char **pstring, | |||
4385 | break; | 4537 | break; |
4386 | #if ENABLE_HUSH_TICK | 4538 | #if ENABLE_HUSH_TICK |
4387 | case '`': { | 4539 | case '`': { |
4388 | unsigned pos; | 4540 | USE_FOR_NOMMU(unsigned pos;) |
4389 | 4541 | ||
4390 | o_addchr(&dest, SPECIAL_VAR_SYMBOL); | 4542 | o_addchr(&dest, SPECIAL_VAR_SYMBOL); |
4391 | o_addchr(&dest, '`'); | 4543 | o_addchr(&dest, '`'); |
4392 | pos = dest.length; | 4544 | USE_FOR_NOMMU(pos = dest.length;) |
4393 | if (!add_till_backquote(&dest, input, /*in_dquote:*/ 0)) | 4545 | if (!add_till_backquote(&dest, input, /*in_dquote:*/ 0)) |
4394 | goto parse_error; | 4546 | goto parse_error; |
4395 | # if !BB_MMU | 4547 | # if !BB_MMU |
@@ -4429,6 +4581,7 @@ static struct pipe *parse_stream(char **pstring, | |||
4429 | /* We just finished a cmd. New one may start | 4581 | /* We just finished a cmd. New one may start |
4430 | * with an assignment */ | 4582 | * with an assignment */ |
4431 | dest.o_assignment = MAYBE_ASSIGNMENT; | 4583 | dest.o_assignment = MAYBE_ASSIGNMENT; |
4584 | debug_printf_parse("dest.o_assignment='%s'\n", assignment_flag[dest.o_assignment]); | ||
4432 | break; | 4585 | break; |
4433 | case '&': | 4586 | case '&': |
4434 | if (done_word(&dest, &ctx)) { | 4587 | if (done_word(&dest, &ctx)) { |
@@ -5323,6 +5476,25 @@ void re_execute_shell(char ***to_free, const char *s, | |||
5323 | char *g_argv0, char **g_argv, | 5476 | char *g_argv0, char **g_argv, |
5324 | char **builtin_argv) NORETURN; | 5477 | char **builtin_argv) NORETURN; |
5325 | 5478 | ||
5479 | static void switch_off_special_sigs(unsigned mask) | ||
5480 | { | ||
5481 | unsigned sig = 0; | ||
5482 | while ((mask >>= 1) != 0) { | ||
5483 | sig++; | ||
5484 | if (!(mask & 1)) | ||
5485 | continue; | ||
5486 | if (G.traps) { | ||
5487 | if (G.traps[sig] && !G.traps[sig][0]) | ||
5488 | /* trap is '', has to remain SIG_IGN */ | ||
5489 | continue; | ||
5490 | free(G.traps[sig]); | ||
5491 | G.traps[sig] = NULL; | ||
5492 | } | ||
5493 | /* We are here only if no trap or trap was not '' */ | ||
5494 | install_sighandler(sig, SIG_DFL); | ||
5495 | } | ||
5496 | } | ||
5497 | |||
5326 | static void reset_traps_to_defaults(void) | 5498 | static void reset_traps_to_defaults(void) |
5327 | { | 5499 | { |
5328 | /* This function is always called in a child shell | 5500 | /* This function is always called in a child shell |
@@ -5336,47 +5508,36 @@ static void reset_traps_to_defaults(void) | |||
5336 | * Testcase: (while :; do :; done) + ^Z should background. | 5508 | * Testcase: (while :; do :; done) + ^Z should background. |
5337 | * Same goes for SIGTERM, SIGHUP, SIGINT. | 5509 | * Same goes for SIGTERM, SIGHUP, SIGINT. |
5338 | */ | 5510 | */ |
5339 | if (!G.traps && !(G.non_DFL_mask & SPECIAL_INTERACTIVE_SIGS)) | 5511 | mask = (G.special_sig_mask & SPECIAL_INTERACTIVE_SIGS) | G_fatal_sig_mask; |
5340 | return; /* already no traps and no SPECIAL_INTERACTIVE_SIGS */ | 5512 | if (!G.traps && !mask) |
5341 | 5513 | return; /* already no traps and no special sigs */ | |
5342 | /* Switching off SPECIAL_INTERACTIVE_SIGS. | ||
5343 | * Stupid. It can be done with *single* &= op, but we can't use | ||
5344 | * the fact that G.blocked_set is implemented as a bitmask | ||
5345 | * in libc... */ | ||
5346 | mask = (SPECIAL_INTERACTIVE_SIGS >> 1); | ||
5347 | sig = 1; | ||
5348 | while (1) { | ||
5349 | if (mask & 1) { | ||
5350 | /* Careful. Only if no trap or trap is not "" */ | ||
5351 | if (!G.traps || !G.traps[sig] || G.traps[sig][0]) | ||
5352 | sigdelset(&G.blocked_set, sig); | ||
5353 | } | ||
5354 | mask >>= 1; | ||
5355 | if (!mask) | ||
5356 | break; | ||
5357 | sig++; | ||
5358 | } | ||
5359 | /* Our homegrown sig mask is saner to work with :) */ | ||
5360 | G.non_DFL_mask &= ~SPECIAL_INTERACTIVE_SIGS; | ||
5361 | 5514 | ||
5362 | /* Resetting all traps to default except empty ones */ | 5515 | /* Switch off special sigs */ |
5363 | mask = G.non_DFL_mask; | 5516 | switch_off_special_sigs(mask); |
5364 | if (G.traps) for (sig = 0; sig < NSIG; sig++, mask >>= 1) { | 5517 | #if ENABLE_HUSH_JOB |
5365 | if (!G.traps[sig] || !G.traps[sig][0]) | 5518 | G_fatal_sig_mask = 0; |
5366 | continue; | 5519 | #endif |
5520 | G.special_sig_mask &= ~SPECIAL_INTERACTIVE_SIGS; | ||
5521 | /* SIGQUIT,SIGCHLD and maybe SPECIAL_JOBSTOP_SIGS | ||
5522 | * remain set in G.special_sig_mask */ | ||
5523 | |||
5524 | if (!G.traps) | ||
5525 | return; | ||
5526 | |||
5527 | /* Reset all sigs to default except ones with empty traps */ | ||
5528 | for (sig = 0; sig < NSIG; sig++) { | ||
5529 | if (!G.traps[sig]) | ||
5530 | continue; /* no trap: nothing to do */ | ||
5531 | if (!G.traps[sig][0]) | ||
5532 | continue; /* empty trap: has to remain SIG_IGN */ | ||
5533 | /* sig has non-empty trap, reset it: */ | ||
5367 | free(G.traps[sig]); | 5534 | free(G.traps[sig]); |
5368 | G.traps[sig] = NULL; | 5535 | G.traps[sig] = NULL; |
5369 | /* There is no signal for 0 (EXIT) */ | 5536 | /* There is no signal for trap 0 (EXIT) */ |
5370 | if (sig == 0) | 5537 | if (sig == 0) |
5371 | continue; | 5538 | continue; |
5372 | /* There was a trap handler, we just removed it. | 5539 | install_sighandler(sig, pick_sighandler(sig)); |
5373 | * But if sig still has non-DFL handling, | ||
5374 | * we should not unblock the sig. */ | ||
5375 | if (mask & 1) | ||
5376 | continue; | ||
5377 | sigdelset(&G.blocked_set, sig); | ||
5378 | } | 5540 | } |
5379 | sigprocmask(SIG_SETMASK, &G.blocked_set, NULL); | ||
5380 | } | 5541 | } |
5381 | 5542 | ||
5382 | #else /* !BB_MMU */ | 5543 | #else /* !BB_MMU */ |
@@ -5487,9 +5648,6 @@ static void re_execute_shell(char ***to_free, const char *s, | |||
5487 | * _inside_ group (just before echo 1), it works. | 5648 | * _inside_ group (just before echo 1), it works. |
5488 | * | 5649 | * |
5489 | * I conclude it means we don't need to pass active traps here. | 5650 | * I conclude it means we don't need to pass active traps here. |
5490 | * Even if we would use signal handlers instead of signal masking | ||
5491 | * in order to implement trap handling, | ||
5492 | * exec syscall below resets signals to SIG_DFL for us. | ||
5493 | */ | 5651 | */ |
5494 | *pp++ = (char *) "-c"; | 5652 | *pp++ = (char *) "-c"; |
5495 | *pp++ = (char *) s; | 5653 | *pp++ = (char *) s; |
@@ -5506,7 +5664,9 @@ static void re_execute_shell(char ***to_free, const char *s, | |||
5506 | 5664 | ||
5507 | do_exec: | 5665 | do_exec: |
5508 | debug_printf_exec("re_execute_shell pid:%d cmd:'%s'\n", getpid(), s); | 5666 | debug_printf_exec("re_execute_shell pid:%d cmd:'%s'\n", getpid(), s); |
5509 | sigprocmask(SIG_SETMASK, &G.inherited_set, NULL); | 5667 | /* Don't propagate SIG_IGN to the child */ |
5668 | if (SPECIAL_JOBSTOP_SIGS != 0) | ||
5669 | switch_off_special_sigs(G.special_sig_mask & SPECIAL_JOBSTOP_SIGS); | ||
5510 | execve(bb_busybox_exec_path, argv, pp); | 5670 | execve(bb_busybox_exec_path, argv, pp); |
5511 | /* Fallback. Useful for init=/bin/hush usage etc */ | 5671 | /* Fallback. Useful for init=/bin/hush usage etc */ |
5512 | if (argv[0][0] == '/') | 5672 | if (argv[0][0] == '/') |
@@ -6160,7 +6320,9 @@ static void execvp_or_die(char **argv) NORETURN; | |||
6160 | static void execvp_or_die(char **argv) | 6320 | static void execvp_or_die(char **argv) |
6161 | { | 6321 | { |
6162 | debug_printf_exec("execing '%s'\n", argv[0]); | 6322 | debug_printf_exec("execing '%s'\n", argv[0]); |
6163 | sigprocmask(SIG_SETMASK, &G.inherited_set, NULL); | 6323 | /* Don't propagate SIG_IGN to the child */ |
6324 | if (SPECIAL_JOBSTOP_SIGS != 0) | ||
6325 | switch_off_special_sigs(G.special_sig_mask & SPECIAL_JOBSTOP_SIGS); | ||
6164 | execvp(argv[0], argv); | 6326 | execvp(argv[0], argv); |
6165 | bb_perror_msg("can't execute '%s'", argv[0]); | 6327 | bb_perror_msg("can't execute '%s'", argv[0]); |
6166 | _exit(127); /* bash compat */ | 6328 | _exit(127); /* bash compat */ |
@@ -6292,7 +6454,9 @@ static NOINLINE void pseudo_exec_argv(nommu_save_t *nommu_save, | |||
6292 | # endif | 6454 | # endif |
6293 | /* Re-exec ourselves */ | 6455 | /* Re-exec ourselves */ |
6294 | debug_printf_exec("re-execing applet '%s'\n", argv[0]); | 6456 | debug_printf_exec("re-execing applet '%s'\n", argv[0]); |
6295 | sigprocmask(SIG_SETMASK, &G.inherited_set, NULL); | 6457 | /* Don't propagate SIG_IGN to the child */ |
6458 | if (SPECIAL_JOBSTOP_SIGS != 0) | ||
6459 | switch_off_special_sigs(G.special_sig_mask & SPECIAL_JOBSTOP_SIGS); | ||
6296 | execv(bb_busybox_exec_path, argv); | 6460 | execv(bb_busybox_exec_path, argv); |
6297 | /* If they called chroot or otherwise made the binary no longer | 6461 | /* If they called chroot or otherwise made the binary no longer |
6298 | * executable, fall through */ | 6462 | * executable, fall through */ |
@@ -6991,9 +7155,6 @@ static NOINLINE int run_pipe(struct pipe *pi) | |||
6991 | if (setup_redirects(command, NULL)) | 7155 | if (setup_redirects(command, NULL)) |
6992 | _exit(1); | 7156 | _exit(1); |
6993 | 7157 | ||
6994 | /* Restore default handlers just prior to exec */ | ||
6995 | /*signal(SIGCHLD, SIG_DFL); - so far we don't have any handlers */ | ||
6996 | |||
6997 | /* Stores to nommu_save list of env vars putenv'ed | 7158 | /* Stores to nommu_save list of env vars putenv'ed |
6998 | * (NOMMU, on MMU we don't need that) */ | 7159 | * (NOMMU, on MMU we don't need that) */ |
6999 | /* cast away volatility... */ | 7160 | /* cast away volatility... */ |
@@ -7271,7 +7432,7 @@ static int run_list(struct pipe *pi) | |||
7271 | * and we don't need to wait for anything. */ | 7432 | * and we don't need to wait for anything. */ |
7272 | G.last_exitcode = rcode; | 7433 | G.last_exitcode = rcode; |
7273 | debug_printf_exec(": builtin/func exitcode %d\n", rcode); | 7434 | debug_printf_exec(": builtin/func exitcode %d\n", rcode); |
7274 | check_and_run_traps(0); | 7435 | check_and_run_traps(); |
7275 | #if ENABLE_HUSH_LOOPS | 7436 | #if ENABLE_HUSH_LOOPS |
7276 | /* Was it "break" or "continue"? */ | 7437 | /* Was it "break" or "continue"? */ |
7277 | if (G.flag_break_continue) { | 7438 | if (G.flag_break_continue) { |
@@ -7303,7 +7464,7 @@ static int run_list(struct pipe *pi) | |||
7303 | /* even bash 3.2 doesn't do that well with nested bg: | 7464 | /* even bash 3.2 doesn't do that well with nested bg: |
7304 | * try "{ { sleep 10; echo DEEP; } & echo HERE; } &". | 7465 | * try "{ { sleep 10; echo DEEP; } & echo HERE; } &". |
7305 | * I'm NOT treating inner &'s as jobs */ | 7466 | * I'm NOT treating inner &'s as jobs */ |
7306 | check_and_run_traps(0); | 7467 | check_and_run_traps(); |
7307 | #if ENABLE_HUSH_JOB | 7468 | #if ENABLE_HUSH_JOB |
7308 | if (G.run_list_level == 1) | 7469 | if (G.run_list_level == 1) |
7309 | insert_bg_job(pi); | 7470 | insert_bg_job(pi); |
@@ -7318,13 +7479,13 @@ static int run_list(struct pipe *pi) | |||
7318 | /* Waits for completion, then fg's main shell */ | 7479 | /* Waits for completion, then fg's main shell */ |
7319 | rcode = checkjobs_and_fg_shell(pi); | 7480 | rcode = checkjobs_and_fg_shell(pi); |
7320 | debug_printf_exec(": checkjobs_and_fg_shell exitcode %d\n", rcode); | 7481 | debug_printf_exec(": checkjobs_and_fg_shell exitcode %d\n", rcode); |
7321 | check_and_run_traps(0); | 7482 | check_and_run_traps(); |
7322 | } else | 7483 | } else |
7323 | #endif | 7484 | #endif |
7324 | { /* This one just waits for completion */ | 7485 | { /* This one just waits for completion */ |
7325 | rcode = checkjobs(pi); | 7486 | rcode = checkjobs(pi); |
7326 | debug_printf_exec(": checkjobs exitcode %d\n", rcode); | 7487 | debug_printf_exec(": checkjobs exitcode %d\n", rcode); |
7327 | check_and_run_traps(0); | 7488 | check_and_run_traps(); |
7328 | } | 7489 | } |
7329 | G.last_exitcode = rcode; | 7490 | G.last_exitcode = rcode; |
7330 | } | 7491 | } |
@@ -7337,7 +7498,10 @@ static int run_list(struct pipe *pi) | |||
7337 | #endif | 7498 | #endif |
7338 | #if ENABLE_HUSH_LOOPS | 7499 | #if ENABLE_HUSH_LOOPS |
7339 | /* Beware of "while false; true; do ..."! */ | 7500 | /* Beware of "while false; true; do ..."! */ |
7340 | if (pi->next && pi->next->res_word == RES_DO) { | 7501 | if (pi->next |
7502 | && (pi->next->res_word == RES_DO || pi->next->res_word == RES_DONE) | ||
7503 | /* check for RES_DONE is needed for "while ...; do \n done" case */ | ||
7504 | ) { | ||
7341 | if (rword == RES_WHILE) { | 7505 | if (rword == RES_WHILE) { |
7342 | if (rcode) { | 7506 | if (rcode) { |
7343 | /* "while false; do...done" - exitcode 0 */ | 7507 | /* "while false; do...done" - exitcode 0 */ |
@@ -7395,88 +7559,81 @@ static int run_and_free_list(struct pipe *pi) | |||
7395 | } | 7559 | } |
7396 | 7560 | ||
7397 | 7561 | ||
7562 | static void install_sighandlers(unsigned mask) | ||
7563 | { | ||
7564 | sighandler_t old_handler; | ||
7565 | unsigned sig = 0; | ||
7566 | while ((mask >>= 1) != 0) { | ||
7567 | sig++; | ||
7568 | if (!(mask & 1)) | ||
7569 | continue; | ||
7570 | old_handler = install_sighandler(sig, pick_sighandler(sig)); | ||
7571 | /* POSIX allows shell to re-enable SIGCHLD | ||
7572 | * even if it was SIG_IGN on entry. | ||
7573 | * Therefore we skip IGN check for it: | ||
7574 | */ | ||
7575 | if (sig == SIGCHLD) | ||
7576 | continue; | ||
7577 | if (old_handler == SIG_IGN) { | ||
7578 | /* oops... restore back to IGN, and record this fact */ | ||
7579 | install_sighandler(sig, old_handler); | ||
7580 | if (!G.traps) | ||
7581 | G.traps = xzalloc(sizeof(G.traps[0]) * NSIG); | ||
7582 | free(G.traps[sig]); | ||
7583 | G.traps[sig] = xzalloc(1); /* == xstrdup(""); */ | ||
7584 | } | ||
7585 | } | ||
7586 | } | ||
7587 | |||
7398 | /* Called a few times only (or even once if "sh -c") */ | 7588 | /* Called a few times only (or even once if "sh -c") */ |
7399 | static void init_sigmasks(void) | 7589 | static void install_special_sighandlers(void) |
7400 | { | 7590 | { |
7401 | unsigned sig; | ||
7402 | unsigned mask; | 7591 | unsigned mask; |
7403 | sigset_t old_blocked_set; | ||
7404 | |||
7405 | if (!G.inherited_set_is_saved) { | ||
7406 | sigprocmask(SIG_SETMASK, NULL, &G.blocked_set); | ||
7407 | G.inherited_set = G.blocked_set; | ||
7408 | } | ||
7409 | old_blocked_set = G.blocked_set; | ||
7410 | 7592 | ||
7411 | mask = (1 << SIGQUIT); | 7593 | /* Which signals are shell-special? */ |
7594 | mask = (1 << SIGQUIT) | (1 << SIGCHLD); | ||
7412 | if (G_interactive_fd) { | 7595 | if (G_interactive_fd) { |
7413 | mask = (1 << SIGQUIT) | SPECIAL_INTERACTIVE_SIGS; | 7596 | mask |= SPECIAL_INTERACTIVE_SIGS; |
7414 | if (G_saved_tty_pgrp) /* we have ctty, job control sigs work */ | 7597 | if (G_saved_tty_pgrp) /* we have ctty, job control sigs work */ |
7415 | mask |= SPECIAL_JOB_SIGS; | 7598 | mask |= SPECIAL_JOBSTOP_SIGS; |
7416 | } | 7599 | } |
7417 | G.non_DFL_mask = mask; | 7600 | /* Careful, do not re-install handlers we already installed */ |
7418 | 7601 | if (G.special_sig_mask != mask) { | |
7419 | sig = 0; | 7602 | unsigned diff = mask & ~G.special_sig_mask; |
7420 | while (mask) { | 7603 | G.special_sig_mask = mask; |
7421 | if (mask & 1) | 7604 | install_sighandlers(diff); |
7422 | sigaddset(&G.blocked_set, sig); | ||
7423 | mask >>= 1; | ||
7424 | sig++; | ||
7425 | } | 7605 | } |
7426 | sigdelset(&G.blocked_set, SIGCHLD); | ||
7427 | |||
7428 | if (memcmp(&old_blocked_set, &G.blocked_set, sizeof(old_blocked_set)) != 0) | ||
7429 | sigprocmask(SIG_SETMASK, &G.blocked_set, NULL); | ||
7430 | |||
7431 | /* POSIX allows shell to re-enable SIGCHLD | ||
7432 | * even if it was SIG_IGN on entry */ | ||
7433 | #if ENABLE_HUSH_FAST | ||
7434 | G.count_SIGCHLD++; /* ensure it is != G.handled_SIGCHLD */ | ||
7435 | if (!G.inherited_set_is_saved) | ||
7436 | signal(SIGCHLD, SIGCHLD_handler); | ||
7437 | #else | ||
7438 | if (!G.inherited_set_is_saved) | ||
7439 | signal(SIGCHLD, SIG_DFL); | ||
7440 | #endif | ||
7441 | |||
7442 | G.inherited_set_is_saved = 1; | ||
7443 | } | 7606 | } |
7444 | 7607 | ||
7445 | #if ENABLE_HUSH_JOB | 7608 | #if ENABLE_HUSH_JOB |
7446 | /* helper */ | 7609 | /* helper */ |
7447 | static void maybe_set_to_sigexit(int sig) | ||
7448 | { | ||
7449 | void (*handler)(int); | ||
7450 | /* non_DFL_mask'ed signals are, well, masked, | ||
7451 | * no need to set handler for them. | ||
7452 | */ | ||
7453 | if (!((G.non_DFL_mask >> sig) & 1)) { | ||
7454 | handler = signal(sig, sigexit); | ||
7455 | if (handler == SIG_IGN) /* oops... restore back to IGN! */ | ||
7456 | signal(sig, handler); | ||
7457 | } | ||
7458 | } | ||
7459 | /* Set handlers to restore tty pgrp and exit */ | 7610 | /* Set handlers to restore tty pgrp and exit */ |
7460 | static void set_fatal_handlers(void) | 7611 | static void install_fatal_sighandlers(void) |
7461 | { | 7612 | { |
7462 | /* We _must_ restore tty pgrp on fatal signals */ | 7613 | unsigned mask; |
7463 | if (HUSH_DEBUG) { | 7614 | |
7464 | maybe_set_to_sigexit(SIGILL ); | 7615 | /* We will restore tty pgrp on these signals */ |
7465 | maybe_set_to_sigexit(SIGFPE ); | 7616 | mask = 0 |
7466 | maybe_set_to_sigexit(SIGBUS ); | 7617 | + (1 << SIGILL ) * HUSH_DEBUG |
7467 | maybe_set_to_sigexit(SIGSEGV); | 7618 | + (1 << SIGFPE ) * HUSH_DEBUG |
7468 | maybe_set_to_sigexit(SIGTRAP); | 7619 | + (1 << SIGBUS ) * HUSH_DEBUG |
7469 | } /* else: hush is perfect. what SEGV? */ | 7620 | + (1 << SIGSEGV) * HUSH_DEBUG |
7470 | maybe_set_to_sigexit(SIGABRT); | 7621 | + (1 << SIGTRAP) * HUSH_DEBUG |
7622 | + (1 << SIGABRT) | ||
7471 | /* bash 3.2 seems to handle these just like 'fatal' ones */ | 7623 | /* bash 3.2 seems to handle these just like 'fatal' ones */ |
7472 | maybe_set_to_sigexit(SIGPIPE); | 7624 | + (1 << SIGPIPE) |
7473 | maybe_set_to_sigexit(SIGALRM); | 7625 | + (1 << SIGALRM) |
7474 | /* if we are interactive, SIGHUP, SIGTERM and SIGINT are masked. | 7626 | /* if we are interactive, SIGHUP, SIGTERM and SIGINT are special sigs. |
7475 | * if we aren't interactive... but in this case | 7627 | * if we aren't interactive... but in this case |
7476 | * we never want to restore pgrp on exit, and this fn is not called */ | 7628 | * we never want to restore pgrp on exit, and this fn is not called |
7477 | /*maybe_set_to_sigexit(SIGHUP );*/ | 7629 | */ |
7478 | /*maybe_set_to_sigexit(SIGTERM);*/ | 7630 | /*+ (1 << SIGHUP )*/ |
7479 | /*maybe_set_to_sigexit(SIGINT );*/ | 7631 | /*+ (1 << SIGTERM)*/ |
7632 | /*+ (1 << SIGINT )*/ | ||
7633 | ; | ||
7634 | G_fatal_sig_mask = mask; | ||
7635 | |||
7636 | install_sighandlers(mask); | ||
7480 | } | 7637 | } |
7481 | #endif | 7638 | #endif |
7482 | 7639 | ||
@@ -7522,6 +7679,10 @@ static int set_mode(int state, char mode, const char *o_opt) | |||
7522 | int hush_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | 7679 | int hush_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
7523 | int hush_main(int argc, char **argv) | 7680 | int hush_main(int argc, char **argv) |
7524 | { | 7681 | { |
7682 | enum { | ||
7683 | OPT_login = (1 << 0), | ||
7684 | }; | ||
7685 | unsigned flags; | ||
7525 | int opt; | 7686 | int opt; |
7526 | unsigned builtin_argc; | 7687 | unsigned builtin_argc; |
7527 | char **e; | 7688 | char **e; |
@@ -7529,8 +7690,11 @@ int hush_main(int argc, char **argv) | |||
7529 | struct variable *shell_ver; | 7690 | struct variable *shell_ver; |
7530 | 7691 | ||
7531 | INIT_G(); | 7692 | INIT_G(); |
7532 | if (EXIT_SUCCESS) /* if EXIT_SUCCESS == 0, it is already done */ | 7693 | if (EXIT_SUCCESS != 0) /* if EXIT_SUCCESS == 0, it is already done */ |
7533 | G.last_exitcode = EXIT_SUCCESS; | 7694 | G.last_exitcode = EXIT_SUCCESS; |
7695 | #if ENABLE_HUSH_FAST | ||
7696 | G.count_SIGCHLD++; /* ensure it is != G.handled_SIGCHLD */ | ||
7697 | #endif | ||
7534 | #if !BB_MMU | 7698 | #if !BB_MMU |
7535 | G.argv0_for_re_execing = argv[0]; | 7699 | G.argv0_for_re_execing = argv[0]; |
7536 | #endif | 7700 | #endif |
@@ -7622,8 +7786,6 @@ int hush_main(int argc, char **argv) | |||
7622 | # endif | 7786 | # endif |
7623 | #endif | 7787 | #endif |
7624 | 7788 | ||
7625 | G.global_argc = argc; | ||
7626 | G.global_argv = argv; | ||
7627 | /* Initialize some more globals to non-zero values */ | 7789 | /* Initialize some more globals to non-zero values */ |
7628 | cmdedit_update_prompt(); | 7790 | cmdedit_update_prompt(); |
7629 | 7791 | ||
@@ -7635,17 +7797,18 @@ int hush_main(int argc, char **argv) | |||
7635 | } | 7797 | } |
7636 | 7798 | ||
7637 | /* Shell is non-interactive at first. We need to call | 7799 | /* Shell is non-interactive at first. We need to call |
7638 | * init_sigmasks() if we are going to execute "sh <script>", | 7800 | * install_special_sighandlers() if we are going to execute "sh <script>", |
7639 | * "sh -c <cmds>" or login shell's /etc/profile and friends. | 7801 | * "sh -c <cmds>" or login shell's /etc/profile and friends. |
7640 | * If we later decide that we are interactive, we run init_sigmasks() | 7802 | * If we later decide that we are interactive, we run install_special_sighandlers() |
7641 | * in order to intercept (more) signals. | 7803 | * in order to intercept (more) signals. |
7642 | */ | 7804 | */ |
7643 | 7805 | ||
7644 | /* Parse options */ | 7806 | /* Parse options */ |
7645 | /* http://www.opengroup.org/onlinepubs/9699919799/utilities/sh.html */ | 7807 | /* http://www.opengroup.org/onlinepubs/9699919799/utilities/sh.html */ |
7808 | flags = (argv[0] && argv[0][0] == '-') ? OPT_login : 0; | ||
7646 | builtin_argc = 0; | 7809 | builtin_argc = 0; |
7647 | while (1) { | 7810 | while (1) { |
7648 | opt = getopt(argc, argv, "+c:xins" | 7811 | opt = getopt(argc, argv, "+c:xinsl" |
7649 | #if !BB_MMU | 7812 | #if !BB_MMU |
7650 | "<:$:R:V:" | 7813 | "<:$:R:V:" |
7651 | # if ENABLE_HUSH_FUNCTIONS | 7814 | # if ENABLE_HUSH_FUNCTIONS |
@@ -7677,7 +7840,7 @@ int hush_main(int argc, char **argv) | |||
7677 | /* -c 'builtin' [BARGV...] "" ARG0 [ARG1...] */ | 7840 | /* -c 'builtin' [BARGV...] "" ARG0 [ARG1...] */ |
7678 | const struct built_in_command *x; | 7841 | const struct built_in_command *x; |
7679 | 7842 | ||
7680 | init_sigmasks(); | 7843 | install_special_sighandlers(); |
7681 | x = find_builtin(optarg); | 7844 | x = find_builtin(optarg); |
7682 | if (x) { /* paranoia */ | 7845 | if (x) { /* paranoia */ |
7683 | G.global_argc -= builtin_argc; /* skip [BARGV...] "" */ | 7846 | G.global_argc -= builtin_argc; /* skip [BARGV...] "" */ |
@@ -7694,7 +7857,7 @@ int hush_main(int argc, char **argv) | |||
7694 | G.global_argv[0] = argv[0]; | 7857 | G.global_argv[0] = argv[0]; |
7695 | G.global_argc++; | 7858 | G.global_argc++; |
7696 | } /* else -c 'script' ARG0 [ARG1...]: $0 is ARG0 */ | 7859 | } /* else -c 'script' ARG0 [ARG1...]: $0 is ARG0 */ |
7697 | init_sigmasks(); | 7860 | install_special_sighandlers(); |
7698 | parse_and_run_string(optarg); | 7861 | parse_and_run_string(optarg); |
7699 | goto final_return; | 7862 | goto final_return; |
7700 | case 'i': | 7863 | case 'i': |
@@ -7706,6 +7869,9 @@ int hush_main(int argc, char **argv) | |||
7706 | /* "-s" means "read from stdin", but this is how we always | 7869 | /* "-s" means "read from stdin", but this is how we always |
7707 | * operate, so simply do nothing here. */ | 7870 | * operate, so simply do nothing here. */ |
7708 | break; | 7871 | break; |
7872 | case 'l': | ||
7873 | flags |= OPT_login; | ||
7874 | break; | ||
7709 | #if !BB_MMU | 7875 | #if !BB_MMU |
7710 | case '<': /* "big heredoc" support */ | 7876 | case '<': /* "big heredoc" support */ |
7711 | full_write1_str(optarg); | 7877 | full_write1_str(optarg); |
@@ -7726,15 +7892,14 @@ int hush_main(int argc, char **argv) | |||
7726 | empty_trap_mask = bb_strtoull(optarg, &optarg, 16); | 7892 | empty_trap_mask = bb_strtoull(optarg, &optarg, 16); |
7727 | if (empty_trap_mask != 0) { | 7893 | if (empty_trap_mask != 0) { |
7728 | int sig; | 7894 | int sig; |
7729 | init_sigmasks(); | 7895 | install_special_sighandlers(); |
7730 | G.traps = xzalloc(sizeof(G.traps[0]) * NSIG); | 7896 | G.traps = xzalloc(sizeof(G.traps[0]) * NSIG); |
7731 | for (sig = 1; sig < NSIG; sig++) { | 7897 | for (sig = 1; sig < NSIG; sig++) { |
7732 | if (empty_trap_mask & (1LL << sig)) { | 7898 | if (empty_trap_mask & (1LL << sig)) { |
7733 | G.traps[sig] = xzalloc(1); /* == xstrdup(""); */ | 7899 | G.traps[sig] = xzalloc(1); /* == xstrdup(""); */ |
7734 | sigaddset(&G.blocked_set, sig); | 7900 | install_sighandler(sig, SIG_IGN); |
7735 | } | 7901 | } |
7736 | } | 7902 | } |
7737 | sigprocmask(SIG_SETMASK, &G.blocked_set, NULL); | ||
7738 | } | 7903 | } |
7739 | # if ENABLE_HUSH_LOOPS | 7904 | # if ENABLE_HUSH_LOOPS |
7740 | optarg++; | 7905 | optarg++; |
@@ -7772,19 +7937,24 @@ int hush_main(int argc, char **argv) | |||
7772 | } | 7937 | } |
7773 | } /* option parsing loop */ | 7938 | } /* option parsing loop */ |
7774 | 7939 | ||
7940 | /* Skip options. Try "hush -l": $1 should not be "-l"! */ | ||
7941 | G.global_argc = argc - (optind - 1); | ||
7942 | G.global_argv = argv + (optind - 1); | ||
7943 | G.global_argv[0] = argv[0]; | ||
7944 | |||
7775 | if (!G.root_pid) { | 7945 | if (!G.root_pid) { |
7776 | G.root_pid = getpid(); | 7946 | G.root_pid = getpid(); |
7777 | G.root_ppid = getppid(); | 7947 | G.root_ppid = getppid(); |
7778 | } | 7948 | } |
7779 | 7949 | ||
7780 | /* If we are login shell... */ | 7950 | /* If we are login shell... */ |
7781 | if (argv[0] && argv[0][0] == '-') { | 7951 | if (flags & OPT_login) { |
7782 | FILE *input; | 7952 | FILE *input; |
7783 | debug_printf("sourcing /etc/profile\n"); | 7953 | debug_printf("sourcing /etc/profile\n"); |
7784 | input = fopen_for_read("/etc/profile"); | 7954 | input = fopen_for_read("/etc/profile"); |
7785 | if (input != NULL) { | 7955 | if (input != NULL) { |
7786 | close_on_exec_on(fileno(input)); | 7956 | close_on_exec_on(fileno(input)); |
7787 | init_sigmasks(); | 7957 | install_special_sighandlers(); |
7788 | parse_and_run_file(input); | 7958 | parse_and_run_file(input); |
7789 | fclose(input); | 7959 | fclose(input); |
7790 | } | 7960 | } |
@@ -7797,19 +7967,19 @@ int hush_main(int argc, char **argv) | |||
7797 | */ | 7967 | */ |
7798 | } | 7968 | } |
7799 | 7969 | ||
7800 | if (argv[optind]) { | 7970 | if (G.global_argv[1]) { |
7801 | FILE *input; | 7971 | FILE *input; |
7802 | /* | 7972 | /* |
7803 | * "bash <script>" (which is never interactive (unless -i?)) | 7973 | * "bash <script>" (which is never interactive (unless -i?)) |
7804 | * sources $BASH_ENV here (without scanning $PATH). | 7974 | * sources $BASH_ENV here (without scanning $PATH). |
7805 | * If called as sh, does the same but with $ENV. | 7975 | * If called as sh, does the same but with $ENV. |
7806 | */ | 7976 | */ |
7807 | debug_printf("running script '%s'\n", argv[optind]); | 7977 | G.global_argc--; |
7808 | G.global_argv = argv + optind; | 7978 | G.global_argv++; |
7809 | G.global_argc = argc - optind; | 7979 | debug_printf("running script '%s'\n", G.global_argv[0]); |
7810 | input = xfopen_for_read(argv[optind]); | 7980 | input = xfopen_for_read(G.global_argv[0]); |
7811 | close_on_exec_on(fileno(input)); | 7981 | close_on_exec_on(fileno(input)); |
7812 | init_sigmasks(); | 7982 | install_special_sighandlers(); |
7813 | parse_and_run_file(input); | 7983 | parse_and_run_file(input); |
7814 | #if ENABLE_FEATURE_CLEAN_UP | 7984 | #if ENABLE_FEATURE_CLEAN_UP |
7815 | fclose(input); | 7985 | fclose(input); |
@@ -7818,7 +7988,7 @@ int hush_main(int argc, char **argv) | |||
7818 | } | 7988 | } |
7819 | 7989 | ||
7820 | /* Up to here, shell was non-interactive. Now it may become one. | 7990 | /* Up to here, shell was non-interactive. Now it may become one. |
7821 | * NB: don't forget to (re)run init_sigmasks() as needed. | 7991 | * NB: don't forget to (re)run install_special_sighandlers() as needed. |
7822 | */ | 7992 | */ |
7823 | 7993 | ||
7824 | /* A shell is interactive if the '-i' flag was given, | 7994 | /* A shell is interactive if the '-i' flag was given, |
@@ -7870,12 +8040,12 @@ int hush_main(int argc, char **argv) | |||
7870 | } | 8040 | } |
7871 | } | 8041 | } |
7872 | 8042 | ||
7873 | /* Block some signals */ | 8043 | /* Install more signal handlers */ |
7874 | init_sigmasks(); | 8044 | install_special_sighandlers(); |
7875 | 8045 | ||
7876 | if (G_saved_tty_pgrp) { | 8046 | if (G_saved_tty_pgrp) { |
7877 | /* Set other signals to restore saved_tty_pgrp */ | 8047 | /* Set other signals to restore saved_tty_pgrp */ |
7878 | set_fatal_handlers(); | 8048 | install_fatal_sighandlers(); |
7879 | /* Put ourselves in our own process group | 8049 | /* Put ourselves in our own process group |
7880 | * (bash, too, does this only if ctty is available) */ | 8050 | * (bash, too, does this only if ctty is available) */ |
7881 | bb_setpgrp(); /* is the same as setpgid(our_pid, our_pid); */ | 8051 | bb_setpgrp(); /* is the same as setpgid(our_pid, our_pid); */ |
@@ -7886,7 +8056,7 @@ int hush_main(int argc, char **argv) | |||
7886 | * (we reset die_sleep = 0 whereever we [v]fork) */ | 8056 | * (we reset die_sleep = 0 whereever we [v]fork) */ |
7887 | enable_restore_tty_pgrp_on_exit(); /* sets die_sleep = -1 */ | 8057 | enable_restore_tty_pgrp_on_exit(); /* sets die_sleep = -1 */ |
7888 | } else { | 8058 | } else { |
7889 | init_sigmasks(); | 8059 | install_special_sighandlers(); |
7890 | } | 8060 | } |
7891 | #elif ENABLE_HUSH_INTERACTIVE | 8061 | #elif ENABLE_HUSH_INTERACTIVE |
7892 | /* No job control compiled in, only prompt/line editing */ | 8062 | /* No job control compiled in, only prompt/line editing */ |
@@ -7903,10 +8073,10 @@ int hush_main(int argc, char **argv) | |||
7903 | if (G_interactive_fd) { | 8073 | if (G_interactive_fd) { |
7904 | close_on_exec_on(G_interactive_fd); | 8074 | close_on_exec_on(G_interactive_fd); |
7905 | } | 8075 | } |
7906 | init_sigmasks(); | 8076 | install_special_sighandlers(); |
7907 | #else | 8077 | #else |
7908 | /* We have interactiveness code disabled */ | 8078 | /* We have interactiveness code disabled */ |
7909 | init_sigmasks(); | 8079 | install_special_sighandlers(); |
7910 | #endif | 8080 | #endif |
7911 | /* bash: | 8081 | /* bash: |
7912 | * if interactive but not a login shell, sources ~/.bashrc | 8082 | * if interactive but not a login shell, sources ~/.bashrc |
@@ -8040,7 +8210,7 @@ static int FAST_FUNC builtin_exec(char **argv) | |||
8040 | tcsetpgrp(G_interactive_fd, G_saved_tty_pgrp); | 8210 | tcsetpgrp(G_interactive_fd, G_saved_tty_pgrp); |
8041 | 8211 | ||
8042 | /* TODO: if exec fails, bash does NOT exit! We do. | 8212 | /* TODO: if exec fails, bash does NOT exit! We do. |
8043 | * We'll need to undo sigprocmask (it's inside execvp_or_die) | 8213 | * We'll need to undo trap cleanup (it's inside execvp_or_die) |
8044 | * and tcsetpgrp, and this is inherently racy. | 8214 | * and tcsetpgrp, and this is inherently racy. |
8045 | */ | 8215 | */ |
8046 | execvp_or_die(argv); | 8216 | execvp_or_die(argv); |
@@ -8237,6 +8407,8 @@ static int FAST_FUNC builtin_trap(char **argv) | |||
8237 | process_sig_list: | 8407 | process_sig_list: |
8238 | ret = EXIT_SUCCESS; | 8408 | ret = EXIT_SUCCESS; |
8239 | while (*argv) { | 8409 | while (*argv) { |
8410 | sighandler_t handler; | ||
8411 | |||
8240 | sig = get_signum(*argv++); | 8412 | sig = get_signum(*argv++); |
8241 | if (sig < 0 || sig >= NSIG) { | 8413 | if (sig < 0 || sig >= NSIG) { |
8242 | ret = EXIT_FAILURE; | 8414 | ret = EXIT_FAILURE; |
@@ -8255,18 +8427,13 @@ static int FAST_FUNC builtin_trap(char **argv) | |||
8255 | if (sig == 0) | 8427 | if (sig == 0) |
8256 | continue; | 8428 | continue; |
8257 | 8429 | ||
8258 | if (new_cmd) { | 8430 | if (new_cmd) |
8259 | sigaddset(&G.blocked_set, sig); | 8431 | handler = (new_cmd[0] ? record_pending_signo : SIG_IGN); |
8260 | } else { | 8432 | else |
8261 | /* There was a trap handler, we are removing it | 8433 | /* We are removing trap handler */ |
8262 | * (if sig has non-DFL handling, | 8434 | handler = pick_sighandler(sig); |
8263 | * we don't need to do anything) */ | 8435 | install_sighandler(sig, handler); |
8264 | if (sig < 32 && (G.non_DFL_mask & (1 << sig))) | ||
8265 | continue; | ||
8266 | sigdelset(&G.blocked_set, sig); | ||
8267 | } | ||
8268 | } | 8436 | } |
8269 | sigprocmask(SIG_SETMASK, &G.blocked_set, NULL); | ||
8270 | return ret; | 8437 | return ret; |
8271 | } | 8438 | } |
8272 | 8439 | ||
@@ -8467,6 +8634,27 @@ static int FAST_FUNC builtin_pwd(char **argv UNUSED_PARAM) | |||
8467 | return EXIT_SUCCESS; | 8634 | return EXIT_SUCCESS; |
8468 | } | 8635 | } |
8469 | 8636 | ||
8637 | /* Interruptibility of read builtin in bash | ||
8638 | * (tested on bash-4.2.8 by sending signals (not by ^C)): | ||
8639 | * | ||
8640 | * Empty trap makes read ignore corresponding signal, for any signal. | ||
8641 | * | ||
8642 | * SIGINT: | ||
8643 | * - terminates non-interactive shell; | ||
8644 | * - interrupts read in interactive shell; | ||
8645 | * if it has non-empty trap: | ||
8646 | * - executes trap and returns to command prompt in interactive shell; | ||
8647 | * - executes trap and returns to read in non-interactive shell; | ||
8648 | * SIGTERM: | ||
8649 | * - is ignored (does not interrupt) read in interactive shell; | ||
8650 | * - terminates non-interactive shell; | ||
8651 | * if it has non-empty trap: | ||
8652 | * - executes trap and returns to read; | ||
8653 | * SIGHUP: | ||
8654 | * - terminates shell (regardless of interactivity); | ||
8655 | * if it has non-empty trap: | ||
8656 | * - executes trap and returns to read; | ||
8657 | */ | ||
8470 | static int FAST_FUNC builtin_read(char **argv) | 8658 | static int FAST_FUNC builtin_read(char **argv) |
8471 | { | 8659 | { |
8472 | const char *r; | 8660 | const char *r; |
@@ -8474,6 +8662,7 @@ static int FAST_FUNC builtin_read(char **argv) | |||
8474 | char *opt_p = NULL; | 8662 | char *opt_p = NULL; |
8475 | char *opt_t = NULL; | 8663 | char *opt_t = NULL; |
8476 | char *opt_u = NULL; | 8664 | char *opt_u = NULL; |
8665 | const char *ifs; | ||
8477 | int read_flags; | 8666 | int read_flags; |
8478 | 8667 | ||
8479 | /* "!": do not abort on errors. | 8668 | /* "!": do not abort on errors. |
@@ -8483,10 +8672,12 @@ static int FAST_FUNC builtin_read(char **argv) | |||
8483 | if (read_flags == (uint32_t)-1) | 8672 | if (read_flags == (uint32_t)-1) |
8484 | return EXIT_FAILURE; | 8673 | return EXIT_FAILURE; |
8485 | argv += optind; | 8674 | argv += optind; |
8675 | ifs = get_local_var_value("IFS"); /* can be NULL */ | ||
8486 | 8676 | ||
8677 | again: | ||
8487 | r = shell_builtin_read(set_local_var_from_halves, | 8678 | r = shell_builtin_read(set_local_var_from_halves, |
8488 | argv, | 8679 | argv, |
8489 | get_local_var_value("IFS"), /* can be NULL */ | 8680 | ifs, |
8490 | read_flags, | 8681 | read_flags, |
8491 | opt_n, | 8682 | opt_n, |
8492 | opt_p, | 8683 | opt_p, |
@@ -8494,6 +8685,12 @@ static int FAST_FUNC builtin_read(char **argv) | |||
8494 | opt_u | 8685 | opt_u |
8495 | ); | 8686 | ); |
8496 | 8687 | ||
8688 | if ((uintptr_t)r == 1 && errno == EINTR) { | ||
8689 | unsigned sig = check_and_run_traps(); | ||
8690 | if (sig && sig != SIGINT) | ||
8691 | goto again; | ||
8692 | } | ||
8693 | |||
8497 | if ((uintptr_t)r > 1) { | 8694 | if ((uintptr_t)r > 1) { |
8498 | bb_error_msg("%s", r); | 8695 | bb_error_msg("%s", r); |
8499 | r = (char*)(uintptr_t)1; | 8696 | r = (char*)(uintptr_t)1; |
@@ -8726,7 +8923,7 @@ static int FAST_FUNC builtin_unset(char **argv) | |||
8726 | static int FAST_FUNC builtin_wait(char **argv) | 8923 | static int FAST_FUNC builtin_wait(char **argv) |
8727 | { | 8924 | { |
8728 | int ret = EXIT_SUCCESS; | 8925 | int ret = EXIT_SUCCESS; |
8729 | int status, sig; | 8926 | int status; |
8730 | 8927 | ||
8731 | argv = skip_dash_dash(argv); | 8928 | argv = skip_dash_dash(argv); |
8732 | if (argv[0] == NULL) { | 8929 | if (argv[0] == NULL) { |
@@ -8746,25 +8943,53 @@ static int FAST_FUNC builtin_wait(char **argv) | |||
8746 | * ^C <-- after ~4 sec from keyboard | 8943 | * ^C <-- after ~4 sec from keyboard |
8747 | * $ | 8944 | * $ |
8748 | */ | 8945 | */ |
8749 | sigaddset(&G.blocked_set, SIGCHLD); | ||
8750 | sigprocmask(SIG_SETMASK, &G.blocked_set, NULL); | ||
8751 | while (1) { | 8946 | while (1) { |
8752 | checkjobs(NULL); | 8947 | int sig; |
8753 | if (errno == ECHILD) | 8948 | sigset_t oldset, allsigs; |
8949 | |||
8950 | /* waitpid is not interruptible by SA_RESTARTed | ||
8951 | * signals which we use. Thus, this ugly dance: | ||
8952 | */ | ||
8953 | |||
8954 | /* Make sure possible SIGCHLD is stored in kernel's | ||
8955 | * pending signal mask before we call waitpid. | ||
8956 | * Or else we may race with SIGCHLD, lose it, | ||
8957 | * and get stuck in sigwaitinfo... | ||
8958 | */ | ||
8959 | sigfillset(&allsigs); | ||
8960 | sigprocmask(SIG_SETMASK, &allsigs, &oldset); | ||
8961 | |||
8962 | if (!sigisemptyset(&G.pending_set)) { | ||
8963 | /* Crap! we raced with some signal! */ | ||
8964 | // sig = 0; | ||
8965 | goto restore; | ||
8966 | } | ||
8967 | |||
8968 | checkjobs(NULL); /* waitpid(WNOHANG) inside */ | ||
8969 | if (errno == ECHILD) { | ||
8970 | sigprocmask(SIG_SETMASK, &oldset, NULL); | ||
8971 | break; | ||
8972 | } | ||
8973 | |||
8974 | /* Wait for SIGCHLD or any other signal */ | ||
8975 | //sig = sigwaitinfo(&allsigs, NULL); | ||
8976 | /* It is vitally important for sigsuspend that SIGCHLD has non-DFL handler! */ | ||
8977 | /* Note: sigsuspend invokes signal handler */ | ||
8978 | sigsuspend(&oldset); | ||
8979 | restore: | ||
8980 | sigprocmask(SIG_SETMASK, &oldset, NULL); | ||
8981 | |||
8982 | /* So, did we get a signal? */ | ||
8983 | //if (sig > 0) | ||
8984 | // raise(sig); /* run handler */ | ||
8985 | sig = check_and_run_traps(); | ||
8986 | if (sig /*&& sig != SIGCHLD - always true */) { | ||
8987 | /* see note 2 */ | ||
8988 | ret = 128 + sig; | ||
8754 | break; | 8989 | break; |
8755 | /* Wait for SIGCHLD or any other signal of interest */ | ||
8756 | /* sigtimedwait with infinite timeout: */ | ||
8757 | sig = sigwaitinfo(&G.blocked_set, NULL); | ||
8758 | if (sig > 0) { | ||
8759 | sig = check_and_run_traps(sig); | ||
8760 | if (sig && sig != SIGCHLD) { /* see note 2 */ | ||
8761 | ret = 128 + sig; | ||
8762 | break; | ||
8763 | } | ||
8764 | } | 8990 | } |
8991 | /* SIGCHLD, or no signal, or ignored one, such as SIGQUIT. Repeat */ | ||
8765 | } | 8992 | } |
8766 | sigdelset(&G.blocked_set, SIGCHLD); | ||
8767 | sigprocmask(SIG_SETMASK, &G.blocked_set, NULL); | ||
8768 | return ret; | 8993 | return ret; |
8769 | } | 8994 | } |
8770 | 8995 | ||
diff --git a/shell/hush_test/hush-misc/assignment4.right b/shell/hush_test/hush-misc/assignment4.right new file mode 100644 index 000000000..31c896f62 --- /dev/null +++ b/shell/hush_test/hush-misc/assignment4.right | |||
@@ -0,0 +1 @@ | |||
Done:0 | |||
diff --git a/shell/hush_test/hush-misc/assignment4.tests b/shell/hush_test/hush-misc/assignment4.tests new file mode 100755 index 000000000..6f46d0a33 --- /dev/null +++ b/shell/hush_test/hush-misc/assignment4.tests | |||
@@ -0,0 +1,3 @@ | |||
1 | # There was a bug where we misinterpreted assignments after 'do': | ||
2 | for i in 1; do eval b=; done | ||
3 | echo Done:$? | ||
diff --git a/shell/hush_test/hush-misc/while3.right b/shell/hush_test/hush-misc/while3.right new file mode 100644 index 000000000..7c4d7beb0 --- /dev/null +++ b/shell/hush_test/hush-misc/while3.right | |||
@@ -0,0 +1 @@ | |||
OK:0 | |||
diff --git a/shell/hush_test/hush-misc/while3.tests b/shell/hush_test/hush-misc/while3.tests new file mode 100755 index 000000000..9132b5f4d --- /dev/null +++ b/shell/hush_test/hush-misc/while3.tests | |||
@@ -0,0 +1,4 @@ | |||
1 | while false; do | ||
2 | # bash will require at least ":" here... | ||
3 | done | ||
4 | echo OK:$? | ||
diff --git a/shell/hush_test/hush-trap/signal_read1.right b/shell/hush_test/hush-trap/signal_read1.right new file mode 100644 index 000000000..2870a8e70 --- /dev/null +++ b/shell/hush_test/hush-trap/signal_read1.right | |||
@@ -0,0 +1 @@ | |||
Got HUP:0 | |||
diff --git a/shell/hush_test/hush-trap/signal_read1.tests b/shell/hush_test/hush-trap/signal_read1.tests new file mode 100755 index 000000000..1105479a3 --- /dev/null +++ b/shell/hush_test/hush-trap/signal_read1.tests | |||
@@ -0,0 +1,5 @@ | |||
1 | (sleep 1; kill -HUP $$) & | ||
2 | trap 'echo "Got HUP:$?"; exit' HUP | ||
3 | while true; do | ||
4 | read ignored | ||
5 | done | ||
diff --git a/shell/hush_test/hush-trap/signal_read2.right b/shell/hush_test/hush-trap/signal_read2.right new file mode 100644 index 000000000..71a6bc16d --- /dev/null +++ b/shell/hush_test/hush-trap/signal_read2.right | |||
@@ -0,0 +1,2 @@ | |||
1 | HUP | ||
2 | Done:129 | ||
diff --git a/shell/hush_test/hush-trap/signal_read2.tests b/shell/hush_test/hush-trap/signal_read2.tests new file mode 100755 index 000000000..eab5b9b5b --- /dev/null +++ b/shell/hush_test/hush-trap/signal_read2.tests | |||
@@ -0,0 +1,7 @@ | |||
1 | $THIS_SH -c ' | ||
2 | (sleep 1; kill -HUP $$) & | ||
3 | while true; do | ||
4 | read ignored | ||
5 | done | ||
6 | ' | ||
7 | echo "Done:$?" | ||
diff --git a/shell/hush_test/run-all b/shell/hush_test/run-all index 256f152dc..64a7abc47 100755 --- a/shell/hush_test/run-all +++ b/shell/hush_test/run-all | |||
@@ -48,8 +48,9 @@ do_test() | |||
48 | *.orig|*~) ;; | 48 | *.orig|*~) ;; |
49 | #*) echo $x ; sh $x ;; | 49 | #*) echo $x ; sh $x ;; |
50 | *) | 50 | *) |
51 | echo -n "$1/$x:" | ||
51 | sh "$x" >"../$1-$x.fail" 2>&1 && \ | 52 | sh "$x" >"../$1-$x.fail" 2>&1 && \ |
52 | { echo "$1/$x: ok"; rm "../$1-$x.fail"; } || echo "$1/$x: fail"; | 53 | { { echo " ok"; rm "../$1-$x.fail"; } || echo " fail"; } |
53 | ;; | 54 | ;; |
54 | esac | 55 | esac |
55 | done | 56 | done |
@@ -60,6 +61,7 @@ do_test() | |||
60 | name="${x%%.tests}" | 61 | name="${x%%.tests}" |
61 | test -f "$name.right" || continue | 62 | test -f "$name.right" || continue |
62 | # echo Running test: "$x" | 63 | # echo Running test: "$x" |
64 | echo -n "$1/$x:" | ||
63 | ( | 65 | ( |
64 | "$THIS_SH" "./$x" >"$name.xx" 2>&1 | 66 | "$THIS_SH" "./$x" >"$name.xx" 2>&1 |
65 | # filter C library differences | 67 | # filter C library differences |
@@ -70,9 +72,9 @@ do_test() | |||
70 | diff -u "$name.xx" "$name.right" >"../$1-$x.fail" && rm -f "$name.xx" "../$1-$x.fail" | 72 | diff -u "$name.xx" "$name.right" >"../$1-$x.fail" && rm -f "$name.xx" "../$1-$x.fail" |
71 | ) | 73 | ) |
72 | case $? in | 74 | case $? in |
73 | 0) echo "$1/$x: ok";; | 75 | 0) echo " ok";; |
74 | 77) echo "$1/$x: skip (feature disabled)";; | 76 | 77) echo " skip (feature disabled)";; |
75 | *) echo "$1/$x: fail"; tret=1;; | 77 | *) echo " fail"; tret=1;; |
76 | esac | 78 | esac |
77 | done | 79 | done |
78 | exit ${tret} | 80 | exit ${tret} |
diff --git a/shell/shell_common.c b/shell/shell_common.c index 75f4b3e54..4329ca05c 100644 --- a/shell/shell_common.c +++ b/shell/shell_common.c | |||
@@ -36,6 +36,10 @@ int FAST_FUNC is_well_formed_var_name(const char *s, char terminator) | |||
36 | 36 | ||
37 | /* read builtin */ | 37 | /* read builtin */ |
38 | 38 | ||
39 | /* Needs to be interruptible: shell mush handle traps and shell-special signals | ||
40 | * while inside read. To implement this, be sure to not loop on EINTR | ||
41 | * and return errno == EINTR reliably. | ||
42 | */ | ||
39 | //TODO: use more efficient setvar() which takes a pointer to malloced "VAR=VAL" | 43 | //TODO: use more efficient setvar() which takes a pointer to malloced "VAR=VAL" |
40 | //string. hush naturally has it, and ash has setvareq(). | 44 | //string. hush naturally has it, and ash has setvareq(). |
41 | //Here we can simply store "VAR=" at buffer start and store read data directly | 45 | //Here we can simply store "VAR=" at buffer start and store read data directly |
@@ -51,6 +55,7 @@ shell_builtin_read(void FAST_FUNC (*setvar)(const char *name, const char *val), | |||
51 | const char *opt_u | 55 | const char *opt_u |
52 | ) | 56 | ) |
53 | { | 57 | { |
58 | unsigned err; | ||
54 | unsigned end_ms; /* -t TIMEOUT */ | 59 | unsigned end_ms; /* -t TIMEOUT */ |
55 | int fd; /* -u FD */ | 60 | int fd; /* -u FD */ |
56 | int nchars; /* -n NUM */ | 61 | int nchars; /* -n NUM */ |
@@ -62,6 +67,8 @@ shell_builtin_read(void FAST_FUNC (*setvar)(const char *name, const char *val), | |||
62 | int startword; | 67 | int startword; |
63 | smallint backslash; | 68 | smallint backslash; |
64 | 69 | ||
70 | errno = err = 0; | ||
71 | |||
65 | pp = argv; | 72 | pp = argv; |
66 | while (*pp) { | 73 | while (*pp) { |
67 | if (!is_well_formed_var_name(*pp, '\0')) { | 74 | if (!is_well_formed_var_name(*pp, '\0')) { |
@@ -152,28 +159,40 @@ shell_builtin_read(void FAST_FUNC (*setvar)(const char *name, const char *val), | |||
152 | bufpos = 0; | 159 | bufpos = 0; |
153 | do { | 160 | do { |
154 | char c; | 161 | char c; |
162 | struct pollfd pfd[1]; | ||
163 | int timeout; | ||
155 | 164 | ||
156 | if (end_ms) { | 165 | if ((bufpos & 0xff) == 0) |
157 | int timeout; | 166 | buffer = xrealloc(buffer, bufpos + 0x100); |
158 | struct pollfd pfd[1]; | ||
159 | 167 | ||
160 | pfd[0].fd = fd; | 168 | timeout = -1; |
161 | pfd[0].events = POLLIN; | 169 | if (end_ms) { |
162 | timeout = end_ms - (unsigned)monotonic_ms(); | 170 | timeout = end_ms - (unsigned)monotonic_ms(); |
163 | if (timeout <= 0 /* already late? */ | 171 | if (timeout <= 0) { /* already late? */ |
164 | || safe_poll(pfd, 1, timeout) != 1 /* no? wait... */ | ||
165 | ) { /* timed out! */ | ||
166 | retval = (const char *)(uintptr_t)1; | 172 | retval = (const char *)(uintptr_t)1; |
167 | goto ret; | 173 | goto ret; |
168 | } | 174 | } |
169 | } | 175 | } |
170 | 176 | ||
171 | if ((bufpos & 0xff) == 0) | 177 | /* We must poll even if timeout is -1: |
172 | buffer = xrealloc(buffer, bufpos + 0x100); | 178 | * we want to be interrupted if signal arrives, |
173 | if (nonblock_safe_read(fd, &buffer[bufpos], 1) != 1) { | 179 | * regardless of SA_RESTART-ness of that signal! |
180 | */ | ||
181 | errno = 0; | ||
182 | pfd[0].fd = fd; | ||
183 | pfd[0].events = POLLIN; | ||
184 | if (poll(pfd, 1, timeout) != 1) { | ||
185 | /* timed out, or EINTR */ | ||
186 | err = errno; | ||
187 | retval = (const char *)(uintptr_t)1; | ||
188 | goto ret; | ||
189 | } | ||
190 | if (read(fd, &buffer[bufpos], 1) != 1) { | ||
191 | err = errno; | ||
174 | retval = (const char *)(uintptr_t)1; | 192 | retval = (const char *)(uintptr_t)1; |
175 | break; | 193 | break; |
176 | } | 194 | } |
195 | |||
177 | c = buffer[bufpos]; | 196 | c = buffer[bufpos]; |
178 | if (c == '\0' || (ENABLE_PLATFORM_MINGW32 && c == '\r')) | 197 | if (c == '\0' || (ENABLE_PLATFORM_MINGW32 && c == '\r')) |
179 | continue; | 198 | continue; |
@@ -240,6 +259,8 @@ shell_builtin_read(void FAST_FUNC (*setvar)(const char *name, const char *val), | |||
240 | free(buffer); | 259 | free(buffer); |
241 | if (read_flags & BUILTIN_READ_SILENT) | 260 | if (read_flags & BUILTIN_READ_SILENT) |
242 | tcsetattr(fd, TCSANOW, &old_tty); | 261 | tcsetattr(fd, TCSANOW, &old_tty); |
262 | |||
263 | errno = err; | ||
243 | return retval; | 264 | return retval; |
244 | } | 265 | } |
245 | 266 | ||