aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDenys Vlasenko <vda.linux@googlemail.com>2017-08-04 14:28:16 +0200
committerDenys Vlasenko <vda.linux@googlemail.com>2017-08-04 14:28:16 +0200
commit49e6bf2db92d896a71d08eb364069ba50fa82781 (patch)
treeb0940fde4d99a73e1a27c7ad0b610fe7e3f1be0c
parent3346b4afc5c81d53eae4e7fc7e12ebd6fa573a4e (diff)
downloadbusybox-w32-49e6bf2db92d896a71d08eb364069ba50fa82781.tar.gz
busybox-w32-49e6bf2db92d896a71d08eb364069ba50fa82781.tar.bz2
busybox-w32-49e6bf2db92d896a71d08eb364069ba50fa82781.zip
sheel: improve comments on signal handling
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
-rw-r--r--NOFORK_NOEXEC.lst4
-rw-r--r--docs/nofork_noexec.txt24
-rw-r--r--shell/ash.c27
-rw-r--r--shell/hush.c7
4 files changed, 45 insertions, 17 deletions
diff --git a/NOFORK_NOEXEC.lst b/NOFORK_NOEXEC.lst
index 12ae1cd55..14019bf7d 100644
--- a/NOFORK_NOEXEC.lst
+++ b/NOFORK_NOEXEC.lst
@@ -4,10 +4,12 @@ Why can't be NOFORK:
4interactive: may wait for user input, ^C has to work 4interactive: may wait for user input, ^C has to work
5spawner: "tool PROG ARGS" which changes program's environment - must fork 5spawner: "tool PROG ARGS" which changes program's environment - must fork
6changes state: e.g. environment, signal handlers 6changes state: e.g. environment, signal handlers
7alloc+xfunc: xmalloc, then xfunc - leaks memory if xfunc dies
8open+xfunc: opens fd, then calls xfunc - fd is leaked if xfunc dies
7runner: sometimes may run for long(ish) time, and/or works with network: 9runner: sometimes may run for long(ish) time, and/or works with network:
8 ^C has to work (cat BIGFILE, chmod -R, ftpget, nc) 10 ^C has to work (cat BIGFILE, chmod -R, ftpget, nc)
9 11
10"runners" can become eligible after hush is taught ^C to interrupt NOFORKs! 12"runners" can become eligible after shell is taught ^C to interrupt NOFORKs!
11 13
12Why can't be NOEXEC: 14Why can't be NOEXEC:
13suid: runs under different uid - must fork+exec 15suid: runs under different uid - must fork+exec
diff --git a/docs/nofork_noexec.txt b/docs/nofork_noexec.txt
index cfb02145a..b45a4be89 100644
--- a/docs/nofork_noexec.txt
+++ b/docs/nofork_noexec.txt
@@ -64,15 +64,27 @@ This poses much more serious limitations on what applet can do:
64* do not use shared global data, or save/restore shared global data 64* do not use shared global data, or save/restore shared global data
65 (e.g. bb_common_bufsiz1) prior to returning. 65 (e.g. bb_common_bufsiz1) prior to returning.
66 - getopt32() is ok to use. You do not need to save/restore option_mask32, 66 - getopt32() is ok to use. You do not need to save/restore option_mask32,
67 it is already done by core code. 67 xfunc_error_retval, and logmode - it is already done by core code.
68* if you allocate memory, you can use xmalloc() only on the very first 68* if you allocate memory, you can use xmalloc() only on the very first
69 allocation. All other allocations should use malloc[_or_warn](). 69 allocation. All other allocations should use malloc[_or_warn]().
70 After first allocation, you cannot use any xfuncs. 70 After first allocation, you cannot use any xfuncs.
71 Otherwise, failing xfunc will return to caller applet 71 Otherwise, failing xfunc will return to caller applet
72 without freeing malloced data! 72 without freeing malloced data!
73* All allocated data, opened files, signal handlers, termios settings, 73* the same applies to other resources, such as open fds: no xfuncs after
74 O_NONBLOCK flags etc should be freed/closed/restored prior to return. 74 acquiring them!
75* ... 75* All allocated data, opened files, signal handlers, termios settings
76 etc should be freed/closed/restored prior to return.
77
78Currently, ash shell signal handling is implemented in a way that signals
79have non-SA_RESTARTed handlers. This means that system calls can
80return EINTR. An example of such problem is "yes" applet:
81it is implemented so that it has a writing loop, this loop is exited on
82any write error, and in the case of user pressing ^C the error was EINTR.
83The problem is, the error causes stdout FILE* object to get into error
84state, needing clearerr() - or else subsequent shell output will also
85not work. ("yes" has been downgraded to NOEXEC, since hush signal handling
86does not have this problem - which makes "yes" to not exit on ^C (bug).
87But stray EINTRs can be seen in any NOFORK under ash, until ash is fixed).
76 88
77NOFORK applets give the most of speed advantage, but are trickiest 89NOFORK applets give the most of speed advantage, but are trickiest
78to implement. In order to minimize amount of bugs and maintenance, 90to implement. In order to minimize amount of bugs and maintenance,
@@ -82,6 +94,8 @@ frequently executed from shell/find/xargs, particularly in shell
82script loops. Applets which mess with signal handlers, termios etc 94script loops. Applets which mess with signal handlers, termios etc
83are probably not worth the effort. 95are probably not worth the effort.
84 96
97Applets which must be interruptible by ^C in shells can not be NOFORKs.
98
85Any NOFORK applet is also a NOEXEC applet. 99Any NOFORK applet is also a NOEXEC applet.
86 100
87 101
@@ -94,7 +108,7 @@ API to call NOFORK applets is two functions:
94 108
95First one is directly used by shells if FEATURE_SH_NOFORK=y. 109First one is directly used by shells if FEATURE_SH_NOFORK=y.
96Second one is used by many applets, but main users are xargs and find. 110Second one is used by many applets, but main users are xargs and find.
97It itself calls run_nofork_applet(), if argv[0] turned out to be a name 111It itself calls run_nofork_applet(), if argv[0] is a name
98of a NOFORK applet. 112of a NOFORK applet.
99 113
100run_nofork_applet() saves/inits/restores option parsing, xfunc_error_retval, 114run_nofork_applet() saves/inits/restores option parsing, xfunc_error_retval,
diff --git a/shell/ash.c b/shell/ash.c
index 8c9f4adc6..ca9926b54 100644
--- a/shell/ash.c
+++ b/shell/ash.c
@@ -3536,9 +3536,12 @@ setsignal(int signo)
3536#endif 3536#endif
3537 } 3537 }
3538 } 3538 }
3539//TODO: if !rootshell, we reset SIGQUIT to DFL, 3539 /* if !rootshell, we reset SIGQUIT to DFL,
3540//whereas we have to restore it to what shell got on entry 3540 * whereas we have to restore it to what shell got on entry.
3541//from the parent. See comment above 3541 * This is handled by the fact that if signal was IGNored on entry,
3542 * then cur_act is S_HARD_IGN and we never change its sigaction
3543 * (see code below).
3544 */
3542 3545
3543 if (signo == SIGCHLD) 3546 if (signo == SIGCHLD)
3544 new_act = S_CATCH; 3547 new_act = S_CATCH;
@@ -3566,6 +3569,8 @@ setsignal(int signo)
3566 if (cur_act == S_HARD_IGN || cur_act == new_act) 3569 if (cur_act == S_HARD_IGN || cur_act == new_act)
3567 return; 3570 return;
3568 3571
3572 *t = new_act;
3573
3569 act.sa_handler = SIG_DFL; 3574 act.sa_handler = SIG_DFL;
3570 switch (new_act) { 3575 switch (new_act) {
3571 case S_CATCH: 3576 case S_CATCH:
@@ -3575,16 +3580,13 @@ setsignal(int signo)
3575 act.sa_handler = SIG_IGN; 3580 act.sa_handler = SIG_IGN;
3576 break; 3581 break;
3577 } 3582 }
3578
3579 /* flags and mask matter only if !DFL and !IGN, but we do it 3583 /* flags and mask matter only if !DFL and !IGN, but we do it
3580 * for all cases for more deterministic behavior: 3584 * for all cases for more deterministic behavior:
3581 */ 3585 */
3582 act.sa_flags = 0; 3586 act.sa_flags = 0; //TODO: why not SA_RESTART?
3583 sigfillset(&act.sa_mask); 3587 sigfillset(&act.sa_mask);
3584 3588
3585 sigaction_set(signo, &act); 3589 sigaction_set(signo, &act);
3586
3587 *t = new_act;
3588} 3590}
3589 3591
3590/* mode flags for set_curjob */ 3592/* mode flags for set_curjob */
@@ -13429,7 +13431,9 @@ readcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
13429 INT_ON; 13431 INT_ON;
13430 13432
13431 if ((uintptr_t)r == 1 && errno == EINTR) { 13433 if ((uintptr_t)r == 1 && errno == EINTR) {
13432 /* to get SIGCHLD: sleep 1 & read x; echo $x */ 13434 /* To get SIGCHLD: sleep 1 & read x; echo $x
13435 * Correct behavior is to not exit "read"
13436 */
13433 if (pending_sig == 0) 13437 if (pending_sig == 0)
13434 goto again; 13438 goto again;
13435 } 13439 }
@@ -13544,13 +13548,14 @@ exitshell(void)
13544 /* NOTREACHED */ 13548 /* NOTREACHED */
13545} 13549}
13546 13550
13547static void 13551/* Don't inline: conserve stack of caller from having our locals too */
13552static NOINLINE void
13548init(void) 13553init(void)
13549{ 13554{
13550 /* we will never free this */ 13555 /* we will never free this */
13551 basepf.next_to_pgetc = basepf.buf = ckmalloc(IBUFSIZ); 13556 basepf.next_to_pgetc = basepf.buf = ckmalloc(IBUFSIZ);
13552 13557
13553 sigmode[SIGCHLD - 1] = S_DFL; 13558 sigmode[SIGCHLD - 1] = S_DFL; /* ensure we install handler even if it is SIG_IGNed */
13554 setsignal(SIGCHLD); 13559 setsignal(SIGCHLD);
13555 13560
13556 /* bash re-enables SIGHUP which is SIG_IGNed on entry. 13561 /* bash re-enables SIGHUP which is SIG_IGNed on entry.
@@ -13561,7 +13566,6 @@ init(void)
13561 { 13566 {
13562 char **envp; 13567 char **envp;
13563 const char *p; 13568 const char *p;
13564 struct stat st1, st2;
13565 13569
13566 initvar(); 13570 initvar();
13567 for (envp = environ; envp && *envp; envp++) { 13571 for (envp = environ; envp && *envp; envp++) {
@@ -13587,6 +13591,7 @@ init(void)
13587#endif 13591#endif
13588 p = lookupvar("PWD"); 13592 p = lookupvar("PWD");
13589 if (p) { 13593 if (p) {
13594 struct stat st1, st2;
13590 if (p[0] != '/' || stat(p, &st1) || stat(".", &st2) 13595 if (p[0] != '/' || stat(p, &st1) || stat(".", &st2)
13591 || st1.st_dev != st2.st_dev || st1.st_ino != st2.st_ino 13596 || st1.st_dev != st2.st_dev || st1.st_ino != st2.st_ino
13592 ) { 13597 ) {
diff --git a/shell/hush.c b/shell/hush.c
index b04f793f1..bb80f422c 100644
--- a/shell/hush.c
+++ b/shell/hush.c
@@ -1979,6 +1979,9 @@ static int check_and_run_traps(void)
1979 break; 1979 break;
1980#if ENABLE_HUSH_JOB 1980#if ENABLE_HUSH_JOB
1981 case SIGHUP: { 1981 case SIGHUP: {
1982//TODO: why are we doing this? ash and dash don't do this,
1983//they have no handler for SIGHUP at all,
1984//they rely on kernel to send SIGHUP+SIGCONT to orphaned process groups
1982 struct pipe *job; 1985 struct pipe *job;
1983 debug_printf_exec("%s: sig:%d default SIGHUP handler\n", __func__, sig); 1986 debug_printf_exec("%s: sig:%d default SIGHUP handler\n", __func__, sig);
1984 /* bash is observed to signal whole process groups, 1987 /* bash is observed to signal whole process groups,
@@ -8646,6 +8649,10 @@ static void install_sighandlers(unsigned mask)
8646 */ 8649 */
8647 if (sig == SIGCHLD) 8650 if (sig == SIGCHLD)
8648 continue; 8651 continue;
8652 /* bash re-enables SIGHUP which is SIG_IGNed on entry.
8653 * Try: "trap '' HUP; bash; echo RET" and type "kill -HUP $$"
8654 */
8655 //if (sig == SIGHUP) continue; - TODO?
8649 if (old_handler == SIG_IGN) { 8656 if (old_handler == SIG_IGN) {
8650 /* oops... restore back to IGN, and record this fact */ 8657 /* oops... restore back to IGN, and record this fact */
8651 install_sighandler(sig, old_handler); 8658 install_sighandler(sig, old_handler);