diff options
author | Denis Vlasenko <vda.linux@googlemail.com> | 2008-12-03 10:36:26 +0000 |
---|---|---|
committer | Denis Vlasenko <vda.linux@googlemail.com> | 2008-12-03 10:36:26 +0000 |
commit | f8535ccd65d21db9762be8715a6107889a50977f (patch) | |
tree | a2f67c68da868ef88ccdc0ae4ca1aa437a95a109 | |
parent | 0c68a874e7d996c62c8d00b0c0289180bdce1590 (diff) | |
download | busybox-w32-f8535ccd65d21db9762be8715a6107889a50977f.tar.gz busybox-w32-f8535ccd65d21db9762be8715a6107889a50977f.tar.bz2 busybox-w32-f8535ccd65d21db9762be8715a6107889a50977f.zip |
ash: finally installed it as /bin/sh on my machine.
some breakage noticed, the most dire is mishandled ^C.
fixing it.
function old new delta
blocking_wait_with_raise_on_sig - 40 +40
waitforjob 85 100 +15
setsignal 280 278 -2
evalvar 1376 1374 -2
waitcmd 186 182 -4
dowait 350 316 -34
redirect 1231 1185 -46
------------------------------------------------------------------------------
(add/remove: 1/0 grow/shrink: 1/5 up/down: 55/-88) Total: -33 bytes
-rw-r--r-- | shell/ash.c | 198 |
1 files changed, 131 insertions, 67 deletions
diff --git a/shell/ash.c b/shell/ash.c index 9eff1b38e..913779ab4 100644 --- a/shell/ash.c +++ b/shell/ash.c | |||
@@ -201,14 +201,13 @@ struct globals_misc { | |||
201 | /* | 201 | /* |
202 | * Sigmode records the current value of the signal handlers for the various | 202 | * Sigmode records the current value of the signal handlers for the various |
203 | * modes. A value of zero means that the current handler is not known. | 203 | * modes. A value of zero means that the current handler is not known. |
204 | * S_HARD_IGN indicates that the signal was ignored on entry to the shell, | 204 | * S_HARD_IGN indicates that the signal was ignored on entry to the shell. |
205 | */ | 205 | */ |
206 | char sigmode[NSIG - 1]; | 206 | char sigmode[NSIG - 1]; |
207 | #define S_DFL 1 /* default signal handling (SIG_DFL) */ | 207 | #define S_DFL 1 /* default signal handling (SIG_DFL) */ |
208 | #define S_CATCH 2 /* signal is caught */ | 208 | #define S_CATCH 2 /* signal is caught */ |
209 | #define S_IGN 3 /* signal is ignored (SIG_IGN) */ | 209 | #define S_IGN 3 /* signal is ignored (SIG_IGN) */ |
210 | #define S_HARD_IGN 4 /* signal is ignored permenantly */ | 210 | #define S_HARD_IGN 4 /* signal is ignored permenantly */ |
211 | #define S_RESET 5 /* temporary - to reset a hard ignored sig */ | ||
212 | 211 | ||
213 | /* indicates specified signal received */ | 212 | /* indicates specified signal received */ |
214 | char gotsig[NSIG - 1]; /* offset by 1: "signal" 0 is meaningless */ | 213 | char gotsig[NSIG - 1]; /* offset by 1: "signal" 0 is meaningless */ |
@@ -368,7 +367,7 @@ force_int_on(void) | |||
368 | } while (0) | 367 | } while (0) |
369 | 368 | ||
370 | /* | 369 | /* |
371 | * Ignore a signal. Only one usage site - in forkchild() | 370 | * Ignore a signal. Avoids unnecessary system calls. |
372 | */ | 371 | */ |
373 | static void | 372 | static void |
374 | ignoresig(int signo) | 373 | ignoresig(int signo) |
@@ -3295,81 +3294,90 @@ static void setjobctl(int); | |||
3295 | static void | 3294 | static void |
3296 | setsignal(int signo) | 3295 | setsignal(int signo) |
3297 | { | 3296 | { |
3298 | int action; | 3297 | char *t; |
3299 | char *t, tsig; | 3298 | char cur_act, new_act; |
3300 | struct sigaction act; | 3299 | struct sigaction act; |
3301 | 3300 | ||
3302 | t = trap[signo]; | 3301 | t = trap[signo]; |
3303 | action = S_IGN; | 3302 | new_act = S_DFL; |
3304 | if (t == NULL) | 3303 | if (t != NULL) { /* trap for this sig is set */ |
3305 | action = S_DFL; | 3304 | new_act = S_CATCH; |
3306 | else if (*t != '\0') | 3305 | if (t[0] == '\0') /* trap is "": ignore this sig */ |
3307 | action = S_CATCH; | 3306 | new_act = S_IGN; |
3308 | if (rootshell && action == S_DFL) { | 3307 | } |
3308 | |||
3309 | if (rootshell && new_act == S_DFL) { | ||
3309 | switch (signo) { | 3310 | switch (signo) { |
3310 | case SIGINT: | 3311 | case SIGINT: |
3311 | if (iflag || minusc || sflag == 0) | 3312 | if (iflag || minusc || sflag == 0) |
3312 | action = S_CATCH; | 3313 | new_act = S_CATCH; |
3313 | break; | 3314 | break; |
3314 | case SIGQUIT: | 3315 | case SIGQUIT: |
3315 | #if DEBUG | 3316 | #if DEBUG |
3316 | if (debug) | 3317 | if (debug) |
3317 | break; | 3318 | break; |
3318 | #endif | 3319 | #endif |
3319 | /* FALLTHROUGH */ | 3320 | /* man bash: |
3321 | * "In all cases, bash ignores SIGQUIT. Non-builtin | ||
3322 | * commands run by bash have signal handlers | ||
3323 | * set to the values inherited by the shell | ||
3324 | * from its parent". */ | ||
3325 | new_act = S_IGN; | ||
3326 | break; | ||
3320 | case SIGTERM: | 3327 | case SIGTERM: |
3321 | if (iflag) | 3328 | if (iflag) |
3322 | action = S_IGN; | 3329 | new_act = S_IGN; |
3323 | break; | 3330 | break; |
3324 | #if JOBS | 3331 | #if JOBS |
3325 | case SIGTSTP: | 3332 | case SIGTSTP: |
3326 | case SIGTTOU: | 3333 | case SIGTTOU: |
3327 | if (mflag) | 3334 | if (mflag) |
3328 | action = S_IGN; | 3335 | new_act = S_IGN; |
3329 | break; | 3336 | break; |
3330 | #endif | 3337 | #endif |
3331 | } | 3338 | } |
3332 | } | 3339 | } |
3340 | //TODO: if !rootshell, we reset SIGQUIT to DFL, | ||
3341 | //whereas we have to restore it to what shell got on entry | ||
3342 | //from the parent. See comment above | ||
3333 | 3343 | ||
3334 | t = &sigmode[signo - 1]; | 3344 | t = &sigmode[signo - 1]; |
3335 | tsig = *t; | 3345 | cur_act = *t; |
3336 | if (tsig == 0) { | 3346 | if (cur_act == 0) { |
3337 | /* | 3347 | /* current setting is not yet known */ |
3338 | * current setting unknown | 3348 | if (sigaction(signo, NULL, &act)) { |
3339 | */ | 3349 | /* pretend it worked; maybe we should give a warning, |
3340 | if (sigaction(signo, NULL, &act) == -1) { | 3350 | * but other shells don't. We don't alter sigmode, |
3341 | /* | 3351 | * so we retry every time. |
3342 | * Pretend it worked; maybe we should give a warning | 3352 | * btw, in Linux it never fails. --vda */ |
3343 | * here, but other shells don't. We don't alter | ||
3344 | * sigmode, so that we retry every time. | ||
3345 | */ | ||
3346 | return; | 3353 | return; |
3347 | } | 3354 | } |
3348 | tsig = S_RESET; /* force to be set */ | ||
3349 | if (act.sa_handler == SIG_IGN) { | 3355 | if (act.sa_handler == SIG_IGN) { |
3350 | tsig = S_HARD_IGN; | 3356 | cur_act = S_HARD_IGN; |
3351 | if (mflag | 3357 | if (mflag |
3352 | && (signo == SIGTSTP || signo == SIGTTIN || signo == SIGTTOU) | 3358 | && (signo == SIGTSTP || signo == SIGTTIN || signo == SIGTTOU) |
3353 | ) { | 3359 | ) { |
3354 | tsig = S_IGN; /* don't hard ignore these */ | 3360 | cur_act = S_IGN; /* don't hard ignore these */ |
3355 | } | 3361 | } |
3356 | } | 3362 | } |
3357 | } | 3363 | } |
3358 | if (tsig == S_HARD_IGN || tsig == action) | 3364 | if (cur_act == S_HARD_IGN || cur_act == new_act) |
3359 | return; | 3365 | return; |
3366 | |||
3360 | act.sa_handler = SIG_DFL; | 3367 | act.sa_handler = SIG_DFL; |
3361 | switch (action) { | 3368 | switch (new_act) { |
3362 | case S_CATCH: | 3369 | case S_CATCH: |
3363 | act.sa_handler = onsig; | 3370 | act.sa_handler = onsig; |
3371 | act.sa_flags = 0; /* matters only if !DFL and !IGN */ | ||
3372 | sigfillset(&act.sa_mask); /* ditto */ | ||
3364 | break; | 3373 | break; |
3365 | case S_IGN: | 3374 | case S_IGN: |
3366 | act.sa_handler = SIG_IGN; | 3375 | act.sa_handler = SIG_IGN; |
3367 | break; | 3376 | break; |
3368 | } | 3377 | } |
3369 | *t = action; | ||
3370 | act.sa_flags = 0; | ||
3371 | sigfillset(&act.sa_mask); | ||
3372 | sigaction_set(signo, &act); | 3378 | sigaction_set(signo, &act); |
3379 | |||
3380 | *t = new_act; | ||
3373 | } | 3381 | } |
3374 | 3382 | ||
3375 | /* mode flags for set_curjob */ | 3383 | /* mode flags for set_curjob */ |
@@ -3790,15 +3798,9 @@ dowait(int wait_flags, struct job *job) | |||
3790 | pid = waitpid(-1, &status, | 3798 | pid = waitpid(-1, &status, |
3791 | (doing_jobctl ? (wait_flags | WUNTRACED) : wait_flags)); | 3799 | (doing_jobctl ? (wait_flags | WUNTRACED) : wait_flags)); |
3792 | TRACE(("wait returns pid=%d, status=0x%x\n", pid, status)); | 3800 | TRACE(("wait returns pid=%d, status=0x%x\n", pid, status)); |
3793 | 3801 | if (pid <= 0) | |
3794 | if (pid <= 0) { | ||
3795 | /* If we were doing blocking wait and (probably) got EINTR, | ||
3796 | * check for pending sigs received while waiting. | ||
3797 | * (NB: can be moved into callers if needed) */ | ||
3798 | if (wait_flags == DOWAIT_BLOCK && pendingsig) | ||
3799 | raise_exception(EXSIG); | ||
3800 | return pid; | 3802 | return pid; |
3801 | } | 3803 | |
3802 | INT_OFF; | 3804 | INT_OFF; |
3803 | thisjob = NULL; | 3805 | thisjob = NULL; |
3804 | for (jp = curjob; jp; jp = jp->prev_job) { | 3806 | for (jp = curjob; jp; jp = jp->prev_job) { |
@@ -3870,6 +3872,15 @@ dowait(int wait_flags, struct job *job) | |||
3870 | return pid; | 3872 | return pid; |
3871 | } | 3873 | } |
3872 | 3874 | ||
3875 | static int | ||
3876 | blocking_wait_with_raise_on_sig(struct job *job) | ||
3877 | { | ||
3878 | pid_t pid = dowait(DOWAIT_BLOCK, job); | ||
3879 | if (pid <= 0 && pendingsig) | ||
3880 | raise_exception(EXSIG); | ||
3881 | return pid; | ||
3882 | } | ||
3883 | |||
3873 | #if JOBS | 3884 | #if JOBS |
3874 | static void | 3885 | static void |
3875 | showjob(FILE *out, struct job *jp, int mode) | 3886 | showjob(FILE *out, struct job *jp, int mode) |
@@ -3949,7 +3960,7 @@ showjobs(FILE *out, int mode) | |||
3949 | 3960 | ||
3950 | TRACE(("showjobs(%x) called\n", mode)); | 3961 | TRACE(("showjobs(%x) called\n", mode)); |
3951 | 3962 | ||
3952 | /* If not even one job changed, there is nothing to do */ | 3963 | /* Handle all finished jobs */ |
3953 | while (dowait(DOWAIT_NONBLOCK, NULL) > 0) | 3964 | while (dowait(DOWAIT_NONBLOCK, NULL) > 0) |
3954 | continue; | 3965 | continue; |
3955 | 3966 | ||
@@ -4041,7 +4052,14 @@ waitcmd(int argc UNUSED_PARAM, char **argv) | |||
4041 | jp->waited = 1; | 4052 | jp->waited = 1; |
4042 | jp = jp->prev_job; | 4053 | jp = jp->prev_job; |
4043 | } | 4054 | } |
4044 | dowait(DOWAIT_BLOCK, NULL); | 4055 | /* man bash: |
4056 | * "When bash is waiting for an asynchronous command via | ||
4057 | * the wait builtin, the reception of a signal for which a trap | ||
4058 | * has been set will cause the wait builtin to return immediately | ||
4059 | * with an exit status greater than 128, immediately after which | ||
4060 | * the trap is executed." | ||
4061 | * Do we do it that way? */ | ||
4062 | blocking_wait_with_raise_on_sig(NULL); | ||
4045 | } | 4063 | } |
4046 | } | 4064 | } |
4047 | 4065 | ||
@@ -4061,11 +4079,10 @@ waitcmd(int argc UNUSED_PARAM, char **argv) | |||
4061 | job = getjob(*argv, 0); | 4079 | job = getjob(*argv, 0); |
4062 | /* loop until process terminated or stopped */ | 4080 | /* loop until process terminated or stopped */ |
4063 | while (job->state == JOBRUNNING) | 4081 | while (job->state == JOBRUNNING) |
4064 | dowait(DOWAIT_BLOCK, NULL); | 4082 | blocking_wait_with_raise_on_sig(NULL); |
4065 | job->waited = 1; | 4083 | job->waited = 1; |
4066 | retval = getstatus(job); | 4084 | retval = getstatus(job); |
4067 | repeat: | 4085 | repeat: ; |
4068 | ; | ||
4069 | } while (*++argv); | 4086 | } while (*++argv); |
4070 | 4087 | ||
4071 | ret: | 4088 | ret: |
@@ -4492,6 +4509,10 @@ forkchild(struct job *jp, /*union node *n,*/ int mode) | |||
4492 | oldlvl = shlvl; | 4509 | oldlvl = shlvl; |
4493 | shlvl++; | 4510 | shlvl++; |
4494 | 4511 | ||
4512 | /* man bash: "Non-builtin commands run by bash have signal handlers | ||
4513 | * set to the values inherited by the shell from its parent". | ||
4514 | * Do we do it correctly? */ | ||
4515 | |||
4495 | closescript(); | 4516 | closescript(); |
4496 | clear_traps(); | 4517 | clear_traps(); |
4497 | #if JOBS | 4518 | #if JOBS |
@@ -4504,8 +4525,8 @@ forkchild(struct job *jp, /*union node *n,*/ int mode) | |||
4504 | pgrp = getpid(); | 4525 | pgrp = getpid(); |
4505 | else | 4526 | else |
4506 | pgrp = jp->ps[0].pid; | 4527 | pgrp = jp->ps[0].pid; |
4507 | /* This can fail because we are doing it in the parent also */ | 4528 | /* this can fail because we are doing it in the parent also */ |
4508 | (void)setpgid(0, pgrp); | 4529 | setpgid(0, pgrp); |
4509 | if (mode == FORK_FG) | 4530 | if (mode == FORK_FG) |
4510 | xtcsetpgrp(ttyfd, pgrp); | 4531 | xtcsetpgrp(ttyfd, pgrp); |
4511 | setsignal(SIGTSTP); | 4532 | setsignal(SIGTSTP); |
@@ -4513,6 +4534,8 @@ forkchild(struct job *jp, /*union node *n,*/ int mode) | |||
4513 | } else | 4534 | } else |
4514 | #endif | 4535 | #endif |
4515 | if (mode == FORK_BG) { | 4536 | if (mode == FORK_BG) { |
4537 | /* man bash: "When job control is not in effect, | ||
4538 | * asynchronous commands ignore SIGINT and SIGQUIT" */ | ||
4516 | ignoresig(SIGINT); | 4539 | ignoresig(SIGINT); |
4517 | ignoresig(SIGQUIT); | 4540 | ignoresig(SIGQUIT); |
4518 | if (jp->nprocs == 0) { | 4541 | if (jp->nprocs == 0) { |
@@ -4521,10 +4544,18 @@ forkchild(struct job *jp, /*union node *n,*/ int mode) | |||
4521 | ash_msg_and_raise_error("can't open %s", bb_dev_null); | 4544 | ash_msg_and_raise_error("can't open %s", bb_dev_null); |
4522 | } | 4545 | } |
4523 | } | 4546 | } |
4524 | if (!oldlvl && iflag) { | 4547 | if (!oldlvl) { |
4525 | setsignal(SIGINT); | 4548 | if (iflag) { /* why if iflag only? */ |
4549 | setsignal(SIGINT); | ||
4550 | setsignal(SIGTERM); | ||
4551 | } | ||
4552 | /* man bash: | ||
4553 | * "In all cases, bash ignores SIGQUIT. Non-builtin | ||
4554 | * commands run by bash have signal handlers | ||
4555 | * set to the values inherited by the shell | ||
4556 | * from its parent". | ||
4557 | * Take care of the second rule: */ | ||
4526 | setsignal(SIGQUIT); | 4558 | setsignal(SIGQUIT); |
4527 | setsignal(SIGTERM); | ||
4528 | } | 4559 | } |
4529 | for (jp = curjob; jp; jp = jp->prev_job) | 4560 | for (jp = curjob; jp; jp = jp->prev_job) |
4530 | freejob(jp); | 4561 | freejob(jp); |
@@ -4596,12 +4627,12 @@ forkshell(struct job *jp, union node *n, int mode) | |||
4596 | /* | 4627 | /* |
4597 | * Wait for job to finish. | 4628 | * Wait for job to finish. |
4598 | * | 4629 | * |
4599 | * Under job control we have the problem that while a child process is | 4630 | * Under job control we have the problem that while a child process |
4600 | * running interrupts generated by the user are sent to the child but not | 4631 | * is running interrupts generated by the user are sent to the child |
4601 | * to the shell. This means that an infinite loop started by an inter- | 4632 | * but not to the shell. This means that an infinite loop started by |
4602 | * active user may be hard to kill. With job control turned off, an | 4633 | * an interactive user may be hard to kill. With job control turned off, |
4603 | * interactive user may place an interactive program inside a loop. If | 4634 | * an interactive user may place an interactive program inside a loop. |
4604 | * the interactive program catches interrupts, the user doesn't want | 4635 | * If the interactive program catches interrupts, the user doesn't want |
4605 | * these interrupts to also abort the loop. The approach we take here | 4636 | * these interrupts to also abort the loop. The approach we take here |
4606 | * is to have the shell ignore interrupt signals while waiting for a | 4637 | * is to have the shell ignore interrupt signals while waiting for a |
4607 | * foreground process to terminate, and then send itself an interrupt | 4638 | * foreground process to terminate, and then send itself an interrupt |
@@ -4619,9 +4650,44 @@ waitforjob(struct job *jp) | |||
4619 | int st; | 4650 | int st; |
4620 | 4651 | ||
4621 | TRACE(("waitforjob(%%%d) called\n", jobno(jp))); | 4652 | TRACE(("waitforjob(%%%d) called\n", jobno(jp))); |
4653 | |||
4654 | INT_OFF; | ||
4622 | while (jp->state == JOBRUNNING) { | 4655 | while (jp->state == JOBRUNNING) { |
4656 | /* In non-interactive shells, we _can_ get | ||
4657 | * a keyboard signal here and be EINTRed, | ||
4658 | * but we just loop back, waiting for command to complete. | ||
4659 | * | ||
4660 | * man bash: | ||
4661 | * "If bash is waiting for a command to complete and receives | ||
4662 | * a signal for which a trap has been set, the trap | ||
4663 | * will not be executed until the command completes." | ||
4664 | * | ||
4665 | * Reality is that even if trap is not set, bash | ||
4666 | * will not act on the signal until command completes. | ||
4667 | * Try this. sleep5intoff.c: | ||
4668 | * #include <signal.h> | ||
4669 | * #include <unistd.h> | ||
4670 | * int main() { | ||
4671 | * sigset_t set; | ||
4672 | * sigemptyset(&set); | ||
4673 | * sigaddset(&set, SIGINT); | ||
4674 | * sigaddset(&set, SIGQUIT); | ||
4675 | * sigprocmask(SIG_BLOCK, &set, NULL); | ||
4676 | * sleep(5); | ||
4677 | * return 0; | ||
4678 | * } | ||
4679 | * $ bash -c './sleep5intoff; echo hi' | ||
4680 | * ^C^C^C^C <--- pressing ^C once a second | ||
4681 | * $ _ | ||
4682 | * TODO: we do not execute "echo hi" as bash does: | ||
4683 | * $ bash -c './sleep5intoff; echo hi' | ||
4684 | * ^\^\^\^\hi <--- pressing ^\ (SIGQUIT) | ||
4685 | * $ _ | ||
4686 | */ | ||
4623 | dowait(DOWAIT_BLOCK, jp); | 4687 | dowait(DOWAIT_BLOCK, jp); |
4624 | } | 4688 | } |
4689 | INT_ON; | ||
4690 | |||
4625 | st = getstatus(jp); | 4691 | st = getstatus(jp); |
4626 | #if JOBS | 4692 | #if JOBS |
4627 | if (jp->jobctl) { | 4693 | if (jp->jobctl) { |
@@ -4757,12 +4823,10 @@ openhere(union node *redir) | |||
4757 | if (forkshell((struct job *)NULL, (union node *)NULL, FORK_NOJOB) == 0) { | 4823 | if (forkshell((struct job *)NULL, (union node *)NULL, FORK_NOJOB) == 0) { |
4758 | /* child */ | 4824 | /* child */ |
4759 | close(pip[0]); | 4825 | close(pip[0]); |
4760 | signal(SIGINT, SIG_IGN); | 4826 | ignoresig(SIGINT); //signal(SIGINT, SIG_IGN); |
4761 | signal(SIGQUIT, SIG_IGN); | 4827 | ignoresig(SIGQUIT); //signal(SIGQUIT, SIG_IGN); |
4762 | signal(SIGHUP, SIG_IGN); | 4828 | ignoresig(SIGHUP); //signal(SIGHUP, SIG_IGN); |
4763 | #ifdef SIGTSTP | 4829 | ignoresig(SIGTSTP); //signal(SIGTSTP, SIG_IGN); |
4764 | signal(SIGTSTP, SIG_IGN); | ||
4765 | #endif | ||
4766 | signal(SIGPIPE, SIG_DFL); | 4830 | signal(SIGPIPE, SIG_DFL); |
4767 | if (redir->type == NHERE) | 4831 | if (redir->type == NHERE) |
4768 | full_write(pip[1], redir->nhere.doc->narg.text, len); | 4832 | full_write(pip[1], redir->nhere.doc->narg.text, len); |