aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDenis Vlasenko <vda.linux@googlemail.com>2008-12-03 10:36:26 +0000
committerDenis Vlasenko <vda.linux@googlemail.com>2008-12-03 10:36:26 +0000
commitf8535ccd65d21db9762be8715a6107889a50977f (patch)
treea2f67c68da868ef88ccdc0ae4ca1aa437a95a109
parent0c68a874e7d996c62c8d00b0c0289180bdce1590 (diff)
downloadbusybox-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.c198
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 */
373static void 372static void
374ignoresig(int signo) 373ignoresig(int signo)
@@ -3295,81 +3294,90 @@ static void setjobctl(int);
3295static void 3294static void
3296setsignal(int signo) 3295setsignal(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
3875static int
3876blocking_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
3874static void 3885static void
3875showjob(FILE *out, struct job *jp, int mode) 3886showjob(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);