summaryrefslogtreecommitdiff
path: root/shell/hush.c
diff options
context:
space:
mode:
authorDenis Vlasenko <vda.linux@googlemail.com>2007-04-28 16:45:22 +0000
committerDenis Vlasenko <vda.linux@googlemail.com>2007-04-28 16:45:22 +0000
commit3ac0e00553adcd7962dc99214fda5d4c3a2d811b (patch)
treec53f05d6dc123d769a516ad49bce1c3ed23a396f /shell/hush.c
parent18e19f2b0d36c0d9566d871942dfe282e9cf5a28 (diff)
downloadbusybox-w32-3ac0e00553adcd7962dc99214fda5d4c3a2d811b.tar.gz
busybox-w32-3ac0e00553adcd7962dc99214fda5d4c3a2d811b.tar.bz2
busybox-w32-3ac0e00553adcd7962dc99214fda5d4c3a2d811b.zip
hush: better signal handling across ctrl-z
Diffstat (limited to 'shell/hush.c')
-rw-r--r--shell/hush.c214
1 files changed, 127 insertions, 87 deletions
diff --git a/shell/hush.c b/shell/hush.c
index b2ff0cb2e..d249dd59e 100644
--- a/shell/hush.c
+++ b/shell/hush.c
@@ -193,7 +193,7 @@ struct pipe {
193 int jobid; /* job number */ 193 int jobid; /* job number */
194 int num_progs; /* total number of programs in job */ 194 int num_progs; /* total number of programs in job */
195 int running_progs; /* number of programs running (not exited) */ 195 int running_progs; /* number of programs running (not exited) */
196 char *text; /* name of job */ 196 char *cmdtext; /* name of job */
197 char *cmdbuf; /* buffer various argv's point into */ 197 char *cmdbuf; /* buffer various argv's point into */
198 pid_t pgrp; /* process group ID for the job */ 198 pid_t pgrp; /* process group ID for the job */
199 struct child_prog *progs; /* array of commands in pipe */ 199 struct child_prog *progs; /* array of commands in pipe */
@@ -441,6 +441,42 @@ static void signal_SA_RESTART(int sig, void (*handler)(int))
441 sigaction(sig, &sa, NULL); 441 sigaction(sig, &sa, NULL);
442} 442}
443 443
444/* Signals are grouped, we handle them in batches */
445static void set_fatal_sighandler(void (*handler)(int))
446{
447 signal(SIGILL , handler);
448 signal(SIGTRAP, handler);
449 signal(SIGABRT, handler);
450 signal(SIGFPE , handler);
451 signal(SIGBUS , handler);
452 signal(SIGSEGV, handler);
453 /* bash 3.2 seems to handle these just like 'fatal' ones */
454 signal(SIGHUP , handler);
455 signal(SIGPIPE, handler);
456 signal(SIGALRM, handler);
457}
458static void set_jobctrl_sighandler(void (*handler)(int))
459{
460 signal(SIGTSTP, handler);
461 signal(SIGTTIN, handler);
462 signal(SIGTTOU, handler);
463}
464static void set_misc_sighandler(void (*handler)(int))
465{
466 signal(SIGINT , handler);
467 signal(SIGQUIT, handler);
468 signal(SIGTERM, handler);
469}
470/* SIGCHLD is special and handled separately */
471
472static void set_every_sighandler(void (*handler)(int))
473{
474 set_fatal_sighandler(handler);
475 set_jobctrl_sighandler(handler);
476 set_misc_sighandler(handler);
477 signal(SIGCHLD, handler);
478}
479
444struct nofork_save_area nofork_save; 480struct nofork_save_area nofork_save;
445static sigjmp_buf nofork_jb; 481static sigjmp_buf nofork_jb;
446static struct pipe *nofork_pipe; 482static struct pipe *nofork_pipe;
@@ -460,7 +496,7 @@ static void handler_ctrl_z(int sig)
460 if (!pid) { /* child */ 496 if (!pid) { /* child */
461 debug_jobs_printf("setting pgrp for child\n"); 497 debug_jobs_printf("setting pgrp for child\n");
462 setpgrp(); 498 setpgrp();
463 signal(SIGTSTP, SIG_DFL); /* make child do default action (stop) */ 499 set_every_sighandler(SIG_DFL);
464 raise(SIGTSTP); /* resend TSTP so that child will be stopped */ 500 raise(SIGTSTP); /* resend TSTP so that child will be stopped */
465 debug_jobs_printf("returning to child\n"); 501 debug_jobs_printf("returning to child\n");
466 /* return to nofork, it will eventually exit now, 502 /* return to nofork, it will eventually exit now,
@@ -516,35 +552,6 @@ static void hush_exit(int exitcode)
516 sigexit(- (exitcode & 0xff)); 552 sigexit(- (exitcode & 0xff));
517} 553}
518 554
519/* Signals are grouped, we handle them in batches */
520static void set_fatal_sighandler(void (*handler)(int))
521{
522 signal(SIGILL , handler);
523 signal(SIGTRAP, handler);
524 signal(SIGABRT, handler);
525 signal(SIGFPE , handler);
526 signal(SIGBUS , handler);
527 signal(SIGSEGV, handler);
528 /* bash 3.2 seems to handle these just like 'fatal' ones,
529 * but _without_ printing signal name. TODO: mimic this too? */
530 signal(SIGHUP , handler);
531 signal(SIGPIPE, handler);
532 signal(SIGALRM, handler);
533}
534static void set_jobctrl_sighandler(void (*handler)(int))
535{
536 signal(SIGTSTP, handler);
537 signal(SIGTTIN, handler);
538 signal(SIGTTOU, handler);
539}
540static void set_misc_sighandler(void (*handler)(int))
541{
542 signal(SIGINT , handler);
543 signal(SIGQUIT, handler);
544 signal(SIGTERM, handler);
545}
546/* SIGCHLD is special and handled separately */
547
548static const char *set_cwd(void) 555static const char *set_cwd(void)
549{ 556{
550 if (cwd == bb_msg_unknown) 557 if (cwd == bb_msg_unknown)
@@ -719,6 +726,7 @@ static int builtin_fg_bg(char **argv)
719 if (i < 0) { 726 if (i < 0) {
720 if (errno == ESRCH) { 727 if (errno == ESRCH) {
721 delete_finished_bg_job(pi); 728 delete_finished_bg_job(pi);
729 return EXIT_SUCCESS;
722 } else { 730 } else {
723 bb_perror_msg("kill (SIGCONT)"); 731 bb_perror_msg("kill (SIGCONT)");
724 } 732 }
@@ -759,7 +767,7 @@ static int builtin_jobs(char **argv ATTRIBUTE_UNUSED)
759 else 767 else
760 status_string = "Running"; 768 status_string = "Running";
761 769
762 printf(JOB_STATUS_FORMAT, job->jobid, status_string, job->text); 770 printf(JOB_STATUS_FORMAT, job->jobid, status_string, job->cmdtext);
763 } 771 }
764 return EXIT_SUCCESS; 772 return EXIT_SUCCESS;
765} 773}
@@ -1266,6 +1274,35 @@ static void pseudo_exec(struct child_prog *child)
1266 _exit(EXIT_SUCCESS); 1274 _exit(EXIT_SUCCESS);
1267} 1275}
1268 1276
1277static const char *get_cmdtext(struct pipe *pi)
1278{
1279 char **argv;
1280 char *p;
1281 int len;
1282
1283 /* This is subtle. ->cmdtext is created only on first backgrounding.
1284 * (Think "cat, <ctrl-z>, fg, <ctrl-z>, fg, <ctrl-z>...." here...)
1285 * On subsequent bg argv can be trashed, but we won't use it */
1286 if (pi->cmdtext)
1287 return pi->cmdtext;
1288 argv = pi->progs[0].argv;
1289 if (!argv || !argv[0])
1290 return (pi->cmdtext = xzalloc(1));
1291
1292 len = 0;
1293 do len += strlen(*argv) + 1; while (*++argv);
1294 pi->cmdtext = p = xmalloc(len);
1295 argv = pi->progs[0].argv;
1296 do {
1297 len = strlen(*argv);
1298 memcpy(p, *argv, len);
1299 p += len;
1300 *p++ = ' ';
1301 } while (*++argv);
1302 p[-1] = '\0';
1303 return pi->cmdtext;
1304}
1305
1269static void insert_bg_job(struct pipe *pi) 1306static void insert_bg_job(struct pipe *pi)
1270{ 1307{
1271 struct pipe *thejob; 1308 struct pipe *thejob;
@@ -1288,27 +1325,17 @@ static void insert_bg_job(struct pipe *pi)
1288 1325
1289 /* physically copy the struct job */ 1326 /* physically copy the struct job */
1290 memcpy(thejob, pi, sizeof(struct pipe)); 1327 memcpy(thejob, pi, sizeof(struct pipe));
1291// (pi->num_progs+1) is one-too-many I think? 1328 thejob->progs = xmalloc(sizeof(pi->progs[0]) * pi->num_progs);
1292 thejob->progs = xmalloc(sizeof(pi->progs[0]) * (pi->num_progs+1)); 1329 memcpy(thejob->progs, pi->progs, sizeof(pi->progs[0]) * pi->num_progs);
1293 memcpy(thejob->progs, pi->progs, sizeof(pi->progs[0]) * (pi->num_progs+1));
1294 thejob->next = NULL; 1330 thejob->next = NULL;
1295 /*seems to be wrong:*/ 1331 /*seems to be wrong:*/
1296 /*thejob->running_progs = thejob->num_progs;*/ 1332 /*thejob->running_progs = thejob->num_progs;*/
1297 /*thejob->stopped_progs = 0;*/ 1333 /*thejob->stopped_progs = 0;*/
1298 thejob->text = xmalloc(BUFSIZ); /* cmdedit buffer size */ 1334 thejob->cmdtext = xstrdup(get_cmdtext(pi));
1299 //if (pi->progs[0] && pi->progs[0].argv && pi->progs[0].argv[0])
1300 {
1301 // FIXME: overflow check? and also trim the size, BUFSIZ can be 4K!
1302 char *bar = thejob->text;
1303 char **foo = pi->progs[0].argv;
1304 if (foo)
1305 while (*foo)
1306 bar += sprintf(bar, "%s ", *foo++);
1307 }
1308 1335
1309 /* we don't wait for background thejobs to return -- append it 1336 /* we don't wait for background thejobs to return -- append it
1310 to the list of backgrounded thejobs and leave it alone */ 1337 to the list of backgrounded thejobs and leave it alone */
1311 printf("[%d] %d\n", thejob->jobid, thejob->progs[0].pid); 1338 printf("[%d] %d %s\n", thejob->jobid, thejob->progs[0].pid, thejob->cmdtext);
1312 last_bg_pid = thejob->progs[0].pid; 1339 last_bg_pid = thejob->progs[0].pid;
1313 last_jobid = thejob->jobid; 1340 last_jobid = thejob->jobid;
1314} 1341}
@@ -1433,7 +1460,7 @@ static int checkjobs(struct pipe* fg_pipe)
1433 pi->running_progs--; 1460 pi->running_progs--;
1434 if (!pi->running_progs) { 1461 if (!pi->running_progs) {
1435 printf(JOB_STATUS_FORMAT, pi->jobid, 1462 printf(JOB_STATUS_FORMAT, pi->jobid,
1436 "Done", pi->text); 1463 "Done", pi->cmdtext);
1437 delete_finished_bg_job(pi); 1464 delete_finished_bg_job(pi);
1438 } 1465 }
1439 } else { 1466 } else {
@@ -1466,6 +1493,32 @@ static int checkjobs_and_fg_shell(struct pipe* fg_pipe)
1466 return rcode; 1493 return rcode;
1467} 1494}
1468 1495
1496/* run_pipe_real's helper */
1497static int run_single_fg_nofork(struct pipe *pi, const struct bb_applet *a,
1498 char **argv)
1499{
1500 int rcode;
1501 /* TSTP handler will store pid etc in pi */
1502 nofork_pipe = pi;
1503 save_nofork_data(&nofork_save);
1504 if (sigsetjmp(nofork_jb, 1) == 0) {
1505 signal_SA_RESTART(SIGTSTP, handler_ctrl_z);
1506 rcode = run_nofork_applet_prime(&nofork_save, a, argv);
1507 if (--nofork_save.saved != 0) {
1508 /* Ctrl-Z forked, we are child */
1509 exit(rcode);
1510 }
1511 return rcode;
1512 }
1513 /* Ctrl-Z forked, we are parent.
1514 * Sighandler has longjmped us here */
1515 signal(SIGTSTP, SIG_IGN);
1516 debug_jobs_printf("Exiting nofork early\n");
1517 restore_nofork_data(&nofork_save);
1518 insert_bg_job(pi);
1519 return 0;
1520}
1521
1469/* run_pipe_real() starts all the jobs, but doesn't wait for anything 1522/* run_pipe_real() starts all the jobs, but doesn't wait for anything
1470 * to finish. See checkjobs(). 1523 * to finish. See checkjobs().
1471 * 1524 *
@@ -1575,6 +1628,7 @@ static int run_pipe_real(struct pipe *pi)
1575 * This is perfect for work that comes after exec(). 1628 * This is perfect for work that comes after exec().
1576 * Is it really safe for inline use? Experimentally, 1629 * Is it really safe for inline use? Experimentally,
1577 * things seem to work with glibc. */ 1630 * things seem to work with glibc. */
1631// TODO: fflush(NULL)?
1578 setup_redirects(child, squirrel); 1632 setup_redirects(child, squirrel);
1579 rcode = x->function(argv + i); 1633 rcode = x->function(argv + i);
1580 restore_redirects(squirrel); 1634 restore_redirects(squirrel);
@@ -1586,32 +1640,16 @@ static int run_pipe_real(struct pipe *pi)
1586 const struct bb_applet *a = find_applet_by_name(argv[i]); 1640 const struct bb_applet *a = find_applet_by_name(argv[i]);
1587 if (a && a->nofork) { 1641 if (a && a->nofork) {
1588 setup_redirects(child, squirrel); 1642 setup_redirects(child, squirrel);
1589 /* TSTP handler will store pid etc in pi */ 1643 rcode = run_single_fg_nofork(pi, a, argv + i);
1590 nofork_pipe = pi; 1644 restore_redirects(squirrel);
1591 save_nofork_data(&nofork_save); 1645 return rcode;
1592 if (sigsetjmp(nofork_jb, 1) == 0) {
1593 signal_SA_RESTART(SIGTSTP, handler_ctrl_z);
1594 rcode = run_nofork_applet_prime(&nofork_save, a, argv + i);
1595 if (--nofork_save.saved != 0)
1596 /* Ctrl-Z! forked, we are child */
1597 exit(rcode);
1598 restore_redirects(squirrel);
1599 return rcode;
1600 } else {
1601 /* Ctrl-Z, forked, we are parent.
1602 * Sighandler has longjmped us here */
1603 signal(SIGTSTP, SIG_IGN);
1604 debug_jobs_printf("Exiting nofork early\n");
1605 restore_nofork_data(&nofork_save);
1606 restore_redirects(squirrel);
1607 insert_bg_job(pi);
1608 return 0;
1609 }
1610 } 1646 }
1611 } 1647 }
1612#endif 1648#endif
1613 } 1649 }
1614 1650
1651 /* Going to fork a child per each pipe member */
1652
1615 /* Disable job control signals for shell (parent) and 1653 /* Disable job control signals for shell (parent) and
1616 * for initial child code after fork */ 1654 * for initial child code after fork */
1617 set_jobctrl_sighandler(SIG_IGN); 1655 set_jobctrl_sighandler(SIG_IGN);
@@ -1639,6 +1677,8 @@ static int run_pipe_real(struct pipe *pi)
1639 /* Every child adds itself to new process group 1677 /* Every child adds itself to new process group
1640 * with pgid == pid of first child in pipe */ 1678 * with pgid == pid of first child in pipe */
1641 if (interactive_fd) { 1679 if (interactive_fd) {
1680 /* Don't do pgrp restore anymore on fatal signals */
1681 set_fatal_sighandler(SIG_DFL);
1642 if (pi->pgrp < 0) /* true for 1st process only */ 1682 if (pi->pgrp < 0) /* true for 1st process only */
1643 pi->pgrp = getpid(); 1683 pi->pgrp = getpid();
1644 if (setpgid(0, pi->pgrp) == 0 && pi->followup != PIPE_BG) { 1684 if (setpgid(0, pi->pgrp) == 0 && pi->followup != PIPE_BG) {
@@ -1646,8 +1686,6 @@ static int run_pipe_real(struct pipe *pi)
1646 * to avoid races */ 1686 * to avoid races */
1647 tcsetpgrp(interactive_fd, pi->pgrp); 1687 tcsetpgrp(interactive_fd, pi->pgrp);
1648 } 1688 }
1649 /* Don't do pgrp restore anymore on fatal signals */
1650 set_fatal_sighandler(SIG_DFL);
1651 } 1689 }
1652 // in non-interactive case fatal sigs are already SIG_DFL 1690 // in non-interactive case fatal sigs are already SIG_DFL
1653 close_all(); 1691 close_all();
@@ -1878,6 +1916,8 @@ static int free_pipe(struct pipe *pi, int indent)
1878 } 1916 }
1879 free(pi->progs); /* children are an array, they get freed all at once */ 1917 free(pi->progs); /* children are an array, they get freed all at once */
1880 pi->progs = NULL; 1918 pi->progs = NULL;
1919 free(pi->cmdtext);
1920 pi->cmdtext = NULL;
1881 return ret_code; 1921 return ret_code;
1882} 1922}
1883 1923
@@ -2252,12 +2292,13 @@ static int setup_redirect(struct p_context *ctx, int fd, redir_type style,
2252static struct pipe *new_pipe(void) 2292static struct pipe *new_pipe(void)
2253{ 2293{
2254 struct pipe *pi; 2294 struct pipe *pi;
2255 pi = xmalloc(sizeof(struct pipe)); 2295 pi = xzalloc(sizeof(struct pipe));
2256 pi->num_progs = 0; 2296 /*pi->num_progs = 0;*/
2257 pi->progs = NULL; 2297 /*pi->progs = NULL;*/
2258 pi->next = NULL; 2298 /*pi->next = NULL;*/
2259 pi->followup = 0; /* invalid */ 2299 /*pi->followup = 0; invalid */
2260 pi->r_mode = RES_NONE; 2300 if (RES_NONE)
2301 pi->r_mode = RES_NONE;
2261 return pi; 2302 return pi;
2262} 2303}
2263 2304
@@ -2424,13 +2465,14 @@ static int done_command(struct p_context *ctx)
2424 pi->progs = xrealloc(pi->progs, sizeof(*pi->progs) * (pi->num_progs+1)); 2465 pi->progs = xrealloc(pi->progs, sizeof(*pi->progs) * (pi->num_progs+1));
2425 2466
2426 prog = pi->progs + pi->num_progs; 2467 prog = pi->progs + pi->num_progs;
2427 prog->redirects = NULL; 2468 memset(prog, 0, sizeof(*prog));
2428 prog->argv = NULL; 2469 /*prog->redirects = NULL;*/
2429 prog->is_stopped = 0; 2470 /*prog->argv = NULL;
2430 prog->group = NULL; 2471 /*prog->is_stopped = 0;*/
2431 prog->glob_result.gl_pathv = NULL; 2472 /*prog->group = NULL;*/
2473 /*prog->glob_result.gl_pathv = NULL;*/
2432 prog->family = pi; 2474 prog->family = pi;
2433 prog->sp = 0; 2475 /*prog->sp = 0;*/
2434 ctx->child = prog; 2476 ctx->child = prog;
2435 prog->type = ctx->type; 2477 prog->type = ctx->type;
2436 2478
@@ -3008,15 +3050,13 @@ static void setup_job_control(void)
3008 set_misc_sighandler(SIG_IGN); 3050 set_misc_sighandler(SIG_IGN);
3009//huh? signal(SIGCHLD, SIG_IGN); 3051//huh? signal(SIGCHLD, SIG_IGN);
3010 3052
3011 /* We _must_ do cleanup on fatal signals */ 3053 /* We _must_ restore tty pgrp fatal signals */
3012 set_fatal_sighandler(sigexit); 3054 set_fatal_sighandler(sigexit);
3013 3055
3014 /* Put ourselves in our own process group. */ 3056 /* Put ourselves in our own process group. */
3015 shell_pgrp = getpid(); 3057 setpgrp(); /* is the same as setpgid(our_pid, our_pid); */
3016 setpgrp(); /* is the same as setpgid(shell_pgrp, shell_pgrp); */
3017
3018 /* Grab control of the terminal. */ 3058 /* Grab control of the terminal. */
3019 tcsetpgrp(interactive_fd, shell_pgrp); 3059 tcsetpgrp(interactive_fd, getpid());
3020} 3060}
3021 3061
3022int hush_main(int argc, char **argv); 3062int hush_main(int argc, char **argv);