diff options
author | Denis Vlasenko <vda.linux@googlemail.com> | 2007-04-28 16:45:22 +0000 |
---|---|---|
committer | Denis Vlasenko <vda.linux@googlemail.com> | 2007-04-28 16:45:22 +0000 |
commit | 3ac0e00553adcd7962dc99214fda5d4c3a2d811b (patch) | |
tree | c53f05d6dc123d769a516ad49bce1c3ed23a396f /shell/hush.c | |
parent | 18e19f2b0d36c0d9566d871942dfe282e9cf5a28 (diff) | |
download | busybox-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.c | 214 |
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 */ | ||
445 | static 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 | } | ||
458 | static void set_jobctrl_sighandler(void (*handler)(int)) | ||
459 | { | ||
460 | signal(SIGTSTP, handler); | ||
461 | signal(SIGTTIN, handler); | ||
462 | signal(SIGTTOU, handler); | ||
463 | } | ||
464 | static 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 | |||
472 | static 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 | |||
444 | struct nofork_save_area nofork_save; | 480 | struct nofork_save_area nofork_save; |
445 | static sigjmp_buf nofork_jb; | 481 | static sigjmp_buf nofork_jb; |
446 | static struct pipe *nofork_pipe; | 482 | static 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 */ | ||
520 | static 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 | } | ||
534 | static void set_jobctrl_sighandler(void (*handler)(int)) | ||
535 | { | ||
536 | signal(SIGTSTP, handler); | ||
537 | signal(SIGTTIN, handler); | ||
538 | signal(SIGTTOU, handler); | ||
539 | } | ||
540 | static 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 | |||
548 | static const char *set_cwd(void) | 555 | static 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 | ||
1277 | static 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 | |||
1269 | static void insert_bg_job(struct pipe *pi) | 1306 | static 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 */ | ||
1497 | static 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, | |||
2252 | static struct pipe *new_pipe(void) | 2292 | static 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 | ||
3022 | int hush_main(int argc, char **argv); | 3062 | int hush_main(int argc, char **argv); |